source: mainline/uspace/srv/fs/exfat/exfat_directory.c@ 2747dd85

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 2747dd85 was 04aaed8f, checked in by Maurizio Lombardi <m.lombardi85@…>, 14 years ago

exfat: Ticket #370, fix a bug in the exfat_directory_write_file() function which leads to
dentry corruption when the length of the filename is > 15 chars.

  • Property mode set to 100644
File size: 11.4 KB
Line 
1/*
2 * Copyright (c) 2011 Oleg Romanenko
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup fs
30 * @{
31 */
32
33/**
34 * @file exfat_directory.c
35 * @brief Functions that work with FAT directory.
36 */
37
38#include "exfat.h"
39#include "exfat_directory.h"
40#include "exfat_fat.h"
41#include <libblock.h>
42#include <errno.h>
43#include <byteorder.h>
44#include <mem.h>
45#include <malloc.h>
46#include <str.h>
47#include <align.h>
48
49void exfat_directory_init(exfat_directory_t *di)
50{
51 di->b = NULL;
52 di->nodep = NULL;
53 di->bs = NULL;
54 di->blocks = 0;
55 di->pos = 0;
56 di->bnum = 0;
57 di->last = false;
58 di->fragmented = false;
59 di->firstc = 0;
60}
61
62int exfat_directory_open(exfat_node_t *nodep, exfat_directory_t *di)
63{
64 exfat_directory_init(di);
65 di->nodep = nodep;
66 if (di->nodep->type != EXFAT_DIRECTORY)
67 return EINVAL;
68 di->service_id = nodep->idx->service_id;
69 di->fragmented = nodep->fragmented;
70 di->firstc = nodep->firstc;
71
72 di->bs = block_bb_get(di->service_id);
73/* di->blocks = nodep->size / BPS(di->bs); */
74 di->blocks = ROUND_UP(nodep->size, BPS(di->bs)) / BPS(di->bs);
75 return EOK;
76}
77
78int exfat_directory_open_parent(exfat_directory_t *di,
79 service_id_t service_id, exfat_cluster_t firstc, bool fragmented)
80{
81 exfat_directory_init(di);
82 di->service_id = service_id;
83 di->fragmented = fragmented;
84 di->firstc = firstc;
85 di->bs = block_bb_get(service_id);
86 di->blocks = 0;
87 return EOK;
88}
89
90int exfat_directory_close(exfat_directory_t *di)
91{
92 int rc = EOK;
93
94 if (di->b) {
95 rc = block_put(di->b);
96 di->b = NULL;
97 }
98
99 return rc;
100}
101
102static int exfat_directory_block_load(exfat_directory_t *di)
103{
104 uint32_t i;
105 int rc = EOK;
106
107 i = (di->pos * sizeof(exfat_dentry_t)) / BPS(di->bs);
108 if (di->nodep && (i >= di->blocks))
109 return ENOENT;
110
111 if (di->b && di->bnum != i) {
112 rc = block_put(di->b);
113 di->b = NULL;
114 }
115 if (!di->b) {
116 if (di->nodep) {
117 rc = exfat_block_get(&di->b, di->bs, di->nodep, i,
118 BLOCK_FLAGS_NONE);
119 } else {
120 rc = exfat_block_get_by_clst(&di->b, di->bs,
121 di->service_id, di->fragmented, di->firstc, NULL, i,
122 BLOCK_FLAGS_NONE);
123 }
124 if (rc != EOK) {
125 di->b = NULL;
126 return rc;
127 }
128 di->bnum = i;
129 }
130 return rc;
131}
132
133int exfat_directory_next(exfat_directory_t *di)
134{
135 int rc;
136
137 di->pos += 1;
138 rc = exfat_directory_block_load(di);
139 if (rc != EOK)
140 di->pos -= 1;
141
142 return rc;
143}
144
145int exfat_directory_prev(exfat_directory_t *di)
146{
147 int rc = EOK;
148
149 if (di->pos > 0) {
150 di->pos -= 1;
151 rc = exfat_directory_block_load(di);
152 } else
153 return ENOENT;
154
155 if (rc != EOK)
156 di->pos += 1;
157
158 return rc;
159}
160
161int exfat_directory_seek(exfat_directory_t *di, aoff64_t pos)
162{
163 aoff64_t _pos = di->pos;
164 int rc;
165
166 di->pos = pos;
167 rc = exfat_directory_block_load(di);
168 if (rc != EOK)
169 di->pos = _pos;
170
171 return rc;
172}
173
174int exfat_directory_get(exfat_directory_t *di, exfat_dentry_t **d)
175{
176 int rc;
177
178 rc = exfat_directory_block_load(di);
179 if (rc == EOK) {
180 aoff64_t o = di->pos % (BPS(di->bs) / sizeof(exfat_dentry_t));
181 *d = ((exfat_dentry_t *)di->b->data) + o;
182 }
183
184 return rc;
185}
186
187int exfat_directory_find(exfat_directory_t *di, exfat_dentry_clsf_t type,
188 exfat_dentry_t **d)
189{
190 do {
191 if (exfat_directory_get(di, d) == EOK) {
192 if (exfat_classify_dentry(*d) == type)
193 return EOK;
194 } else
195 return ENOENT;
196 } while (exfat_directory_next(di) == EOK);
197
198 return ENOENT;
199}
200
201int
202exfat_directory_find_continue(exfat_directory_t *di, exfat_dentry_clsf_t type,
203 exfat_dentry_t **d)
204{
205 int rc;
206 rc = exfat_directory_next(di);
207 if (rc != EOK)
208 return rc;
209 return exfat_directory_find(di, type, d);
210}
211
212
213int exfat_directory_read_file(exfat_directory_t *di, char *name, size_t size,
214 exfat_file_dentry_t *df, exfat_stream_dentry_t *ds)
215{
216 uint16_t wname[EXFAT_FILENAME_LEN + 1];
217 exfat_dentry_t *d = NULL;
218 int rc, i;
219 size_t offset = 0;
220 aoff64_t start_pos = 0;
221
222 rc = exfat_directory_find(di, EXFAT_DENTRY_FILE, &d);
223 if (rc != EOK)
224 return rc;
225 start_pos = di->pos;
226 *df = d->file;
227
228 rc = exfat_directory_next(di);
229 if (rc != EOK)
230 return rc;
231 rc = exfat_directory_get(di, &d);
232 if (rc != EOK)
233 return rc;
234 if (exfat_classify_dentry(d) != EXFAT_DENTRY_STREAM)
235 return ENOENT;
236 *ds = d->stream;
237
238 if (ds->name_size > size)
239 return EOVERFLOW;
240
241 for (i = 0; i < df->count - 1; i++) {
242 rc = exfat_directory_next(di);
243 if (rc != EOK)
244 return rc;
245 rc = exfat_directory_get(di, &d);
246 if (rc != EOK)
247 return rc;
248 if (exfat_classify_dentry(d) != EXFAT_DENTRY_NAME)
249 return ENOENT;
250 exfat_dentry_get_name(&d->name, ds->name_size, wname, &offset);
251 }
252 rc = utf16_to_str(name, size, wname);
253 if (rc != EOK)
254 return rc;
255
256 exfat_directory_seek(di, start_pos);
257 return EOK;
258}
259
260static uint16_t exfat_directory_set_checksum(const uint8_t *bytes, size_t count)
261{
262 uint16_t checksum = 0;
263 size_t idx;
264
265 for (idx = 0; idx < count; idx++) {
266 if (idx == 2 || idx == 3)
267 continue;
268 checksum = ((checksum << 15) | (checksum >> 1)) + (uint16_t)bytes[idx];
269 }
270 return checksum;
271}
272
273int exfat_directory_sync_file(exfat_directory_t *di, exfat_file_dentry_t *df,
274 exfat_stream_dentry_t *ds)
275{
276 int rc, i, count;
277 exfat_dentry_t *array = NULL, *de;
278 aoff64_t pos = di->pos;
279
280 rc = exfat_directory_get(di, &de);
281 if (rc != EOK)
282 return rc;
283 count = de->file.count + 1;
284 array = (exfat_dentry_t *) malloc(count * sizeof(exfat_dentry_t));
285 if (!array)
286 return ENOMEM;
287 for (i = 0; i < count; i++) {
288 rc = exfat_directory_get(di, &de);
289 if (rc != EOK) {
290 free(array);
291 return rc;
292 }
293 array[i] = *de;
294 rc = exfat_directory_next(di);
295 if (rc != EOK) {
296 free(array);
297 return rc;
298 }
299 }
300 rc = exfat_directory_seek(di, pos);
301 if (rc != EOK) {
302 free(array);
303 return rc;
304 }
305
306 /* Sync */
307 array[0].file.attr = host2uint16_t_le(df->attr);
308 array[1].stream.firstc = host2uint32_t_le(ds->firstc);
309 array[1].stream.flags = ds->flags;
310 array[1].stream.valid_data_size = host2uint64_t_le(ds->valid_data_size);
311 array[1].stream.data_size = host2uint64_t_le(ds->data_size);
312 array[0].file.checksum = host2uint16_t_le(exfat_directory_set_checksum((uint8_t *)array,
313 count * sizeof(exfat_dentry_t)));
314
315 /* Store */
316 for (i = 0; i < count; i++) {
317 rc = exfat_directory_get(di, &de);
318 if (rc != EOK) {
319 free(array);
320 return rc;
321 }
322 *de = array[i];
323 di->b->dirty = true;
324 rc = exfat_directory_next(di);
325 if (rc != EOK) {
326 free(array);
327 return rc;
328 }
329 }
330 free(array);
331
332 return EOK;
333}
334
335int exfat_directory_write_file(exfat_directory_t *di, const char *name)
336{
337 fs_node_t *fn;
338 exfat_node_t *uctablep;
339 uint16_t *uctable;
340 exfat_dentry_t df, ds, *de;
341 uint16_t wname[EXFAT_FILENAME_LEN + 1];
342 int rc, i;
343 size_t uctable_chars, j;
344 aoff64_t pos;
345
346 rc = str_to_utf16(wname, EXFAT_FILENAME_LEN, name);
347 if (rc != EOK)
348 return rc;
349 rc = exfat_uctable_get(&fn, di->service_id);
350 if (rc != EOK)
351 return rc;
352 uctablep = EXFAT_NODE(fn);
353
354 uctable_chars = ALIGN_DOWN(uctablep->size, sizeof(uint16_t)) / sizeof(uint16_t);
355 uctable = (uint16_t *) malloc(uctable_chars * sizeof(uint16_t));
356 rc = exfat_read_uctable(di->bs, uctablep, (uint8_t *)uctable);
357 if (rc != EOK) {
358 (void) exfat_node_put(fn);
359 free(uctable);
360 return rc;
361 }
362
363 /* Fill stream entry */
364 ds.type = EXFAT_TYPE_STREAM;
365 ds.stream.flags = 0;
366 ds.stream.valid_data_size = 0;
367 ds.stream.data_size = 0;
368 ds.stream.name_size = utf16_length(wname);
369 ds.stream.hash = host2uint16_t_le(exfat_name_hash(wname, uctable,
370 uctable_chars));
371
372 /* Fill file entry */
373 df.type = EXFAT_TYPE_FILE;
374 df.file.attr = 0;
375 df.file.count = ROUND_UP(ds.stream.name_size, EXFAT_NAME_PART_LEN) /
376 EXFAT_NAME_PART_LEN + 1;
377 df.file.checksum = 0;
378
379 free(uctable);
380 rc = exfat_node_put(fn);
381 if (rc != EOK)
382 return rc;
383
384 /* Looking for set of free entries */
385 rc = exfat_directory_lookup_free(di, df.file.count + 1);
386 if (rc != EOK)
387 return rc;
388 pos = di->pos;
389
390 /* Write file entry */
391 rc = exfat_directory_get(di, &de);
392 if (rc != EOK)
393 return rc;
394 *de = df;
395 di->b->dirty = true;
396 rc = exfat_directory_next(di);
397 if (rc != EOK)
398 return rc;
399
400 /* Write stream entry */
401 rc = exfat_directory_get(di, &de);
402 if (rc != EOK)
403 return rc;
404 *de = ds;
405 di->b->dirty = true;
406
407 /* Write file name */
408 size_t chars = EXFAT_NAME_PART_LEN;
409 uint16_t *sname = wname;
410
411 for (i = 0; i < ds.stream.name_size; i++)
412 wname[i] = host2uint16_t_le(wname[i]);
413
414 for (i = 0; i < df.file.count - 1; i++) {
415 rc = exfat_directory_next(di);
416 if (rc != EOK)
417 return rc;
418
419 if (i == df.file.count - 2)
420 chars = ds.stream.name_size - EXFAT_NAME_PART_LEN*(df.file.count - 2);
421 rc = exfat_directory_get(di, &de);
422 if (rc != EOK)
423 return rc;
424 de->type = EXFAT_TYPE_NAME;
425 /* test */
426 for (j = 0; j < chars; j++) {
427 de->name.name[j] = *sname;
428 sname++;
429 }
430
431 di->b->dirty = true;
432 }
433
434 return exfat_directory_seek(di, pos);
435}
436
437int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos)
438{
439 int rc, count;
440 exfat_dentry_t *de;
441
442 di->pos = pos;
443
444 rc = exfat_directory_get(di, &de);
445 if (rc != EOK)
446 return rc;
447 count = de->file.count + 1;
448
449 while (count) {
450 rc = exfat_directory_get(di, &de);
451 if (rc != EOK)
452 return rc;
453 de->type &= ~EXFAT_TYPE_USED;
454 di->b->dirty = true;
455
456 rc = exfat_directory_next(di);
457 if (rc != EOK)
458 return rc;
459 count--;
460 }
461 return EOK;
462}
463
464int exfat_directory_expand(exfat_directory_t *di)
465{
466 int rc;
467
468 if (!di->nodep)
469 return ENOSPC;
470
471 rc = exfat_node_expand(di->nodep->idx->service_id, di->nodep, 1);
472 if (rc != EOK)
473 return rc;
474
475 di->fragmented = di->nodep->fragmented;
476 di->nodep->size += BPC(di->bs);
477 di->nodep->dirty = true; /* need to sync node */
478 di->blocks = di->nodep->size / BPS(di->bs);
479
480 return EOK;
481}
482
483int exfat_directory_lookup_free(exfat_directory_t *di, size_t count)
484{
485 int rc;
486 exfat_dentry_t *d;
487 size_t found;
488 aoff64_t pos;
489
490 rc = exfat_directory_seek(di, 0);
491 if (rc != EOK)
492 return rc;
493
494 do {
495 found = 0;
496 pos = 0;
497 do {
498 if (exfat_directory_get(di, &d) == EOK) {
499 switch (exfat_classify_dentry(d)) {
500 case EXFAT_DENTRY_LAST:
501 case EXFAT_DENTRY_FREE:
502 if (found == 0)
503 pos = di->pos;
504 found++;
505 if (found == count) {
506 exfat_directory_seek(di, pos);
507 return EOK;
508 }
509 break;
510 default:
511 found = 0;
512 break;
513 }
514 }
515 } while (exfat_directory_next(di) == EOK);
516 } while (exfat_directory_expand(di) == EOK);
517 return ENOSPC;
518}
519
520
521/**
522 * @}
523 */
Note: See TracBrowser for help on using the repository browser.