source: mainline/uspace/srv/fs/exfat/exfat_directory.c@ 842a2d2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 842a2d2 was c56c4576, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago

exFAT: fixes to support big endian. Tested on mips32 EB

  • Property mode set to 100644
File size: 12.2 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->devmap_handle = nodep->idx->devmap_handle;
69 di->fragmented = nodep->fragmented;
70 di->firstc = nodep->firstc;
71
72 di->bs = block_bb_get(di->devmap_handle);
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 devmap_handle_t devmap_handle, exfat_cluster_t firstc, bool fragmented)
80{
81 exfat_directory_init(di);
82 di->devmap_handle = devmap_handle;
83 di->fragmented = fragmented;
84 di->firstc = firstc;
85 di->bs = block_bb_get(devmap_handle);
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
97 return rc;
98}
99
100static int exfat_directory_block_load(exfat_directory_t *di)
101{
102 uint32_t i;
103 int rc;
104
105 i = (di->pos * sizeof(exfat_dentry_t)) / BPS(di->bs);
106 if (di->nodep && (i >= di->blocks))
107 return ENOENT;
108
109 if (di->b && di->bnum != i) {
110 block_put(di->b);
111 di->b = NULL;
112 }
113 if (!di->b) {
114 if (di->nodep) {
115 rc = exfat_block_get(&di->b, di->bs, di->nodep, i, BLOCK_FLAGS_NONE);
116 } else {
117 rc = exfat_block_get_by_clst(&di->b, di->bs, di->devmap_handle,
118 di->fragmented, di->firstc, NULL, i, BLOCK_FLAGS_NONE);
119 }
120 if (rc != EOK) {
121 di->b = NULL;
122 return rc;
123 }
124 di->bnum = i;
125 }
126 return EOK;
127}
128
129int exfat_directory_next(exfat_directory_t *di)
130{
131 int rc;
132
133 di->pos += 1;
134 rc = exfat_directory_block_load(di);
135 if (rc!=EOK)
136 di->pos -= 1;
137
138 return rc;
139}
140
141int exfat_directory_prev(exfat_directory_t *di)
142{
143 int rc=EOK;
144
145 if (di->pos > 0) {
146 di->pos -= 1;
147 rc=exfat_directory_block_load(di);
148 }
149 else
150 return ENOENT;
151
152 if (rc!=EOK)
153 di->pos += 1;
154
155 return rc;
156}
157
158int exfat_directory_seek(exfat_directory_t *di, aoff64_t pos)
159{
160 aoff64_t _pos = di->pos;
161 int rc;
162
163 di->pos = pos;
164 rc = exfat_directory_block_load(di);
165 if (rc!=EOK)
166 di->pos = _pos;
167
168 return rc;
169}
170
171int exfat_directory_get(exfat_directory_t *di, exfat_dentry_t **d)
172{
173 int rc;
174
175 rc = exfat_directory_block_load(di);
176 if (rc == EOK) {
177 aoff64_t o = di->pos % (BPS(di->bs) / sizeof(exfat_dentry_t));
178 *d = ((exfat_dentry_t *)di->b->data) + o;
179 }
180
181 return rc;
182}
183
184int exfat_directory_find(exfat_directory_t *di, exfat_dentry_clsf_t type, exfat_dentry_t **d)
185{
186 do {
187 if (exfat_directory_get(di, d) == EOK) {
188 if (exfat_classify_dentry(*d) == type)
189 return EOK;
190 } else
191 return ENOENT;
192 } while (exfat_directory_next(di) == EOK);
193
194 return ENOENT;
195}
196
197int exfat_directory_find_continue(exfat_directory_t *di, exfat_dentry_clsf_t type, exfat_dentry_t **d)
198{
199 int rc;
200 rc = exfat_directory_next(di);
201 if (rc != EOK)
202 return rc;
203 return exfat_directory_find(di, type, d);
204}
205
206
207int exfat_directory_read_file(exfat_directory_t *di, char *name, size_t size,
208 exfat_file_dentry_t *df, exfat_stream_dentry_t *ds)
209{
210 uint16_t wname[EXFAT_FILENAME_LEN+1];
211 exfat_dentry_t *d = NULL;
212 int rc, i;
213 size_t offset = 0;
214 aoff64_t start_pos = 0;
215
216 rc = exfat_directory_find(di, EXFAT_DENTRY_FILE, &d);
217 if (rc != EOK)
218 return rc;
219 start_pos = di->pos;
220 *df = d->file;
221
222 rc = exfat_directory_next(di);
223 if (rc != EOK)
224 return rc;
225 rc = exfat_directory_get(di, &d);
226 if (rc != EOK)
227 return rc;
228 if (exfat_classify_dentry(d) != EXFAT_DENTRY_STREAM)
229 return ENOENT;
230 *ds = d->stream;
231
232 if (ds->name_size > size)
233 return EOVERFLOW;
234
235 for (i=0; i<df->count-1; i++) {
236 rc = exfat_directory_next(di);
237 if (rc != EOK)
238 return rc;
239 rc = exfat_directory_get(di, &d);
240 if (rc != EOK)
241 return rc;
242 if (exfat_classify_dentry(d) != EXFAT_DENTRY_NAME)
243 return ENOENT;
244 exfat_dentry_get_name(&d->name, ds->name_size, wname, &offset);
245 }
246 rc = utf16_to_str(name, size, wname);
247 if (rc != EOK)
248 return rc;
249
250 exfat_directory_seek(di, start_pos);
251 return EOK;
252}
253
254static uint16_t exfat_directory_set_checksum(const uint8_t *bytes, size_t count)
255{
256 uint16_t checksum = 0;
257 size_t idx;
258
259 for (idx = 0; idx < count; idx++) {
260 if (idx == 2 || idx == 3)
261 continue;
262 checksum = ((checksum << 15) | (checksum >> 1)) + (uint16_t)bytes[idx];
263 }
264 return checksum;
265}
266
267int exfat_directory_sync_file(exfat_directory_t *di, exfat_file_dentry_t *df,
268 exfat_stream_dentry_t *ds)
269{
270 int rc, i, count;
271 exfat_dentry_t *array=NULL, *de;
272 aoff64_t pos = di->pos;
273
274 rc = exfat_directory_get(di, &de);
275 if (rc != EOK)
276 return rc;
277 count = de->file.count+1;
278 array = (exfat_dentry_t *) malloc(count*sizeof(exfat_dentry_t));
279 if (!array)
280 return ENOMEM;
281 for (i=0; i<count; i++) {
282 rc = exfat_directory_get(di, &de);
283 if (rc != EOK)
284 return rc;
285 array[i] = *de;
286 rc = exfat_directory_next(di);
287 if (rc!=EOK) {
288 free(array);
289 return rc;
290 }
291 }
292 rc = exfat_directory_seek(di, pos);
293 if (rc!=EOK) {
294 free(array);
295 return rc;
296 }
297
298 /* Sync */
299 array[0].file.attr = host2uint16_t_le(df->attr);
300 array[1].stream.firstc = host2uint32_t_le(ds->firstc);
301 array[1].stream.flags = ds->flags;
302 array[1].stream.valid_data_size = host2uint64_t_le(ds->valid_data_size);
303 array[1].stream.data_size = host2uint64_t_le(ds->data_size);
304 array[0].file.checksum = host2uint16_t_le(exfat_directory_set_checksum((uint8_t *)array,
305 count*sizeof(exfat_dentry_t)));
306
307 /* Store */
308 for (i=0; i<count; i++) {
309 rc = exfat_directory_get(di, &de);
310 if (rc != EOK)
311 return rc;
312 *de = array[i];
313 di->b->dirty = true;
314 rc = exfat_directory_next(di);
315 if (rc!=EOK) {
316 free(array);
317 return rc;
318 }
319 }
320 free(array);
321
322 return EOK;
323}
324
325int exfat_directory_print(exfat_directory_t *di)
326{
327 int rc;
328 exfat_dentry_t *de;
329 exfat_directory_seek(di, 0);
330 do
331 {
332 rc = exfat_directory_get(di, &de);
333 if (rc != EOK)
334 return rc;
335 switch (de->type) {
336 case EXFAT_TYPE_VOLLABEL:
337 printf("EXFAT_DENTRY_VOLLABEL\n"); break;
338 case EXFAT_TYPE_BITMAP:
339 printf("EXFAT_DENTRY_BITMAP\n"); break;
340 case EXFAT_TYPE_UCTABLE:
341 printf("EXFAT_DENTRY_UCTABLE\n"); break;
342 case EXFAT_TYPE_GUID:
343 printf("EXFAT_DENTRY_GUID\n"); break;
344 case EXFAT_TYPE_FILE:
345 printf("EXFAT_DENTRY_FILE\n"); break;
346 case EXFAT_TYPE_STREAM:
347 printf("EXFAT_DENTRY_STREAM\n"); break;
348 case EXFAT_TYPE_NAME:
349 printf("EXFAT_DENTRY_NAME\n"); break;
350 case EXFAT_TYPE_UNUSED:
351 printf("EXFAT_DENTRY_LAST\n");
352 return EOK;
353 default:
354 if (de->type & EXFAT_TYPE_USED)
355 printf("EXFAT_DENTRY_SKIP\n");
356 else
357 printf("EXFAT_DENTRY_FREE\n");
358 }
359 } while (exfat_directory_next(di) == EOK);
360 exfat_directory_seek(di, 0);
361 return EOK;
362}
363
364int exfat_directory_write_file(exfat_directory_t *di, const char *name)
365{
366 fs_node_t *fn;
367 exfat_node_t *uctablep;
368 uint16_t *uctable;
369 exfat_dentry_t df, ds, *de;
370 uint16_t wname[EXFAT_FILENAME_LEN+1];
371 int rc, i;
372 size_t uctable_chars, j;
373 aoff64_t pos;
374
375 rc = str_to_utf16(wname, EXFAT_FILENAME_LEN, name);
376 if (rc != EOK)
377 return rc;
378 rc = exfat_uctable_get(&fn, di->devmap_handle);
379 if (rc != EOK)
380 return rc;
381 uctablep = EXFAT_NODE(fn);
382
383 uctable_chars = ALIGN_DOWN(uctablep->size, sizeof(uint16_t)) / sizeof(uint16_t);
384 uctable = (uint16_t *) malloc(uctable_chars * sizeof(uint16_t));
385 rc = exfat_read_uctable(di->bs, uctablep, (uint8_t *)uctable);
386 if (rc != EOK) {
387 (void) exfat_node_put(fn);
388 free(uctable);
389 return rc;
390 }
391
392 /* Fill stream entry */
393 ds.type = EXFAT_TYPE_STREAM;
394 ds.stream.flags = 0;
395 ds.stream.valid_data_size = 0;
396 ds.stream.data_size = 0;
397 ds.stream.name_size = utf16_length(wname);
398 ds.stream.hash = host2uint16_t_le(exfat_name_hash(wname, uctable,
399 uctable_chars));
400
401 /* Fill file entry */
402 df.type = EXFAT_TYPE_FILE;
403 df.file.attr = 0;
404 df.file.count = ROUND_UP(ds.stream.name_size, EXFAT_NAME_PART_LEN) /
405 EXFAT_NAME_PART_LEN + 1;
406 df.file.checksum = 0;
407
408 free(uctable);
409 rc = exfat_node_put(fn);
410 if (rc != EOK)
411 return rc;
412
413 /* Looking for set of free entries */
414 rc = exfat_directory_lookup_free(di, df.file.count+1);
415 if (rc != EOK)
416 return rc;
417 pos = di->pos;
418
419 /* Write file entry */
420 rc = exfat_directory_get(di, &de);
421 if (rc != EOK)
422 return rc;
423 *de = df;
424 di->b->dirty = true;
425 rc = exfat_directory_next(di);
426 if (rc != EOK)
427 return rc;
428
429 /* Write stream entry */
430 rc = exfat_directory_get(di, &de);
431 if (rc != EOK)
432 return rc;
433 *de = ds;
434 di->b->dirty = true;
435
436 /* Write file name */
437 size_t chars = EXFAT_NAME_PART_LEN;
438 uint16_t *sname = wname;
439
440 for (i=0; i<ds.stream.name_size; i++)
441 wname[i] = host2uint16_t_le(wname[i]);
442
443 for (i=0; i < df.file.count-1; i++) {
444 rc = exfat_directory_next(di);
445 if (rc != EOK)
446 return rc;
447
448 if (i == df.file.count-2)
449 chars = ds.stream.name_size - EXFAT_NAME_PART_LEN*(df.file.count-2);
450 rc = exfat_directory_get(di, &de);
451 if (rc != EOK)
452 return rc;
453 de->type = EXFAT_TYPE_NAME;
454 /* test */
455 for (j=0; j<chars; j++){
456 de->name.name[j] = *sname;
457 sname++;
458 }
459
460 di->b->dirty = true;
461 sname += chars;
462 }
463
464 return exfat_directory_seek(di, pos);
465}
466
467int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos)
468{
469 int rc, count;
470 exfat_dentry_t *de;
471
472 rc = exfat_directory_get(di, &de);
473 if (rc != EOK)
474 return rc;
475 count = de->file.count+1;
476
477 while (count) {
478 rc = exfat_directory_get(di, &de);
479 if (rc != EOK)
480 return rc;
481 de->type &= (~EXFAT_TYPE_USED);
482 di->b->dirty = true;
483
484 rc = exfat_directory_next(di);
485 if (rc!=EOK)
486 return rc;
487 count--;
488 }
489 return EOK;
490}
491
492int exfat_directory_expand(exfat_directory_t *di)
493{
494 int rc;
495
496 if (!di->nodep)
497 return ENOSPC;
498
499 rc = exfat_node_expand(di->nodep->idx->devmap_handle, di->nodep, 1);
500 if (rc != EOK)
501 return rc;
502
503 di->fragmented = di->nodep->fragmented;
504 di->nodep->size += BPC(di->bs);
505 di->nodep->dirty = true; /* need to sync node */
506 di->blocks = di->nodep->size / BPS(di->bs);
507
508 return EOK;
509}
510
511int exfat_directory_lookup_free(exfat_directory_t *di, size_t count)
512{
513 int rc;
514 exfat_dentry_t *d;
515 size_t found;
516 aoff64_t pos;
517
518 rc = exfat_directory_seek(di, 0);
519 if (rc != EOK)
520 return rc;
521
522 do {
523 found = 0;
524 pos = 0;
525 do {
526 if (exfat_directory_get(di, &d) == EOK) {
527 switch (exfat_classify_dentry(d)) {
528 case EXFAT_DENTRY_LAST:
529 case EXFAT_DENTRY_FREE:
530 if (found==0) pos = di->pos;
531 found++;
532 if (found == count) {
533 exfat_directory_seek(di, pos);
534 return EOK;
535 }
536 break;
537 default:
538 found = 0;
539 break;
540 }
541 }
542 } while (exfat_directory_next(di) == EOK);
543 } while (exfat_directory_expand(di) == EOK);
544 return ENOSPC;
545}
546
547
548/**
549 * @}
550 */
Note: See TracBrowser for help on using the repository browser.