source: mainline/uspace/srv/fs/exfat/exfat_directory.c@ 375ab5e

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 375ab5e was 375ab5e, checked in by Jakub Jermar <jakub@…>, 14 years ago

Merge from lp:~romanenko-oleg/helenos/fat.

  • Property mode set to 100644
File size: 11.2 KB
RevLine 
[4dd9395]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
[bca3eac]38#include "exfat.h"
[4dd9395]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>
[d8df2fb]45#include <malloc.h>
[4dd9395]46#include <str.h>
[bca3eac]47#include <align.h>
[4dd9395]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;
[f061de75]58 di->fragmented = false;
59 di->firstc = 0;
[4dd9395]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;
[375ab5e]68 di->service_id = nodep->idx->service_id;
[f061de75]69 di->fragmented = nodep->fragmented;
70 di->firstc = nodep->firstc;
[4dd9395]71
[375ab5e]72 di->bs = block_bb_get(di->service_id);
[bca3eac]73/* di->blocks = nodep->size / BPS(di->bs); */
74 di->blocks = ROUND_UP(nodep->size, BPS(di->bs))/BPS(di->bs);
[f061de75]75 return EOK;
76}
77
78int exfat_directory_open_parent(exfat_directory_t *di,
[375ab5e]79 service_id_t service_id, exfat_cluster_t firstc, bool fragmented)
[f061de75]80{
81 exfat_directory_init(di);
[375ab5e]82 di->service_id = service_id;
[f061de75]83 di->fragmented = fragmented;
84 di->firstc = firstc;
[375ab5e]85 di->bs = block_bb_get(service_id);
[f061de75]86 di->blocks = 0;
[4dd9395]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);
[f061de75]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) {
[4dd9395]115 rc = exfat_block_get(&di->b, di->bs, di->nodep, i, BLOCK_FLAGS_NONE);
[f061de75]116 } else {
[375ab5e]117 rc = exfat_block_get_by_clst(&di->b, di->bs, di->service_id,
[f061de75]118 di->fragmented, di->firstc, NULL, i, BLOCK_FLAGS_NONE);
[4dd9395]119 }
[f061de75]120 if (rc != EOK) {
121 di->b = NULL;
122 return rc;
123 }
124 di->bnum = i;
[4dd9395]125 }
[f061de75]126 return EOK;
[4dd9395]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
[27678a0]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}
[4dd9395]205
[73b1218]206
[f5f1e1c]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)
[73b1218]209{
210 uint16_t wname[EXFAT_FILENAME_LEN+1];
211 exfat_dentry_t *d = NULL;
212 int rc, i;
213 size_t offset = 0;
[963ea42]214 aoff64_t start_pos = 0;
[73b1218]215
216 rc = exfat_directory_find(di, EXFAT_DENTRY_FILE, &d);
217 if (rc != EOK)
218 return rc;
[963ea42]219 start_pos = di->pos;
[73b1218]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;
[f5f1e1c]231
232 if (ds->name_size > size)
[73b1218]233 return EOVERFLOW;
[f5f1e1c]234
[73b1218]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 }
[f5f1e1c]246 rc = utf16_to_str(name, size, wname);
[73b1218]247 if (rc != EOK)
248 return rc;
249
[963ea42]250 exfat_directory_seek(di, start_pos);
[73b1218]251 return EOK;
252}
253
[d8df2fb]254static uint16_t exfat_directory_set_checksum(const uint8_t *bytes, size_t count)
[151a4e2]255{
[d8df2fb]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;
[c56c4576]285 array[i] = *de;
[d8df2fb]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 */
[c56c4576]299 array[0].file.attr = host2uint16_t_le(df->attr);
300 array[1].stream.firstc = host2uint32_t_le(ds->firstc);
[d8df2fb]301 array[1].stream.flags = ds->flags;
[c56c4576]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);
[bca3eac]304 array[0].file.checksum = host2uint16_t_le(exfat_directory_set_checksum((uint8_t *)array,
305 count*sizeof(exfat_dentry_t)));
[d8df2fb]306
307 /* Store */
308 for (i=0; i<count; i++) {
309 rc = exfat_directory_get(di, &de);
310 if (rc != EOK)
311 return rc;
[c56c4576]312 *de = array[i];
[d8df2fb]313 di->b->dirty = true;
314 rc = exfat_directory_next(di);
315 if (rc!=EOK) {
316 free(array);
317 return rc;
318 }
319 }
[c56c4576]320 free(array);
[d8df2fb]321
[151a4e2]322 return EOK;
323}
324
[bca3eac]325int exfat_directory_write_file(exfat_directory_t *di, const char *name)
326{
327 fs_node_t *fn;
328 exfat_node_t *uctablep;
329 uint16_t *uctable;
330 exfat_dentry_t df, ds, *de;
331 uint16_t wname[EXFAT_FILENAME_LEN+1];
332 int rc, i;
[c56c4576]333 size_t uctable_chars, j;
[bca3eac]334 aoff64_t pos;
335
336 rc = str_to_utf16(wname, EXFAT_FILENAME_LEN, name);
337 if (rc != EOK)
338 return rc;
[375ab5e]339 rc = exfat_uctable_get(&fn, di->service_id);
[bca3eac]340 if (rc != EOK)
341 return rc;
342 uctablep = EXFAT_NODE(fn);
343
344 uctable_chars = ALIGN_DOWN(uctablep->size, sizeof(uint16_t)) / sizeof(uint16_t);
345 uctable = (uint16_t *) malloc(uctable_chars * sizeof(uint16_t));
346 rc = exfat_read_uctable(di->bs, uctablep, (uint8_t *)uctable);
347 if (rc != EOK) {
348 (void) exfat_node_put(fn);
349 free(uctable);
350 return rc;
351 }
352
353 /* Fill stream entry */
354 ds.type = EXFAT_TYPE_STREAM;
355 ds.stream.flags = 0;
356 ds.stream.valid_data_size = 0;
357 ds.stream.data_size = 0;
358 ds.stream.name_size = utf16_length(wname);
359 ds.stream.hash = host2uint16_t_le(exfat_name_hash(wname, uctable,
360 uctable_chars));
361
362 /* Fill file entry */
363 df.type = EXFAT_TYPE_FILE;
364 df.file.attr = 0;
365 df.file.count = ROUND_UP(ds.stream.name_size, EXFAT_NAME_PART_LEN) /
366 EXFAT_NAME_PART_LEN + 1;
367 df.file.checksum = 0;
368
369 free(uctable);
370 rc = exfat_node_put(fn);
371 if (rc != EOK)
372 return rc;
373
374 /* Looking for set of free entries */
375 rc = exfat_directory_lookup_free(di, df.file.count+1);
376 if (rc != EOK)
377 return rc;
378 pos = di->pos;
379
380 /* Write file entry */
381 rc = exfat_directory_get(di, &de);
382 if (rc != EOK)
383 return rc;
[c56c4576]384 *de = df;
[bca3eac]385 di->b->dirty = true;
386 rc = exfat_directory_next(di);
387 if (rc != EOK)
388 return rc;
389
390 /* Write stream entry */
391 rc = exfat_directory_get(di, &de);
392 if (rc != EOK)
393 return rc;
[c56c4576]394 *de = ds;
[bca3eac]395 di->b->dirty = true;
396
397 /* Write file name */
398 size_t chars = EXFAT_NAME_PART_LEN;
399 uint16_t *sname = wname;
400
401 for (i=0; i<ds.stream.name_size; i++)
402 wname[i] = host2uint16_t_le(wname[i]);
403
404 for (i=0; i < df.file.count-1; i++) {
405 rc = exfat_directory_next(di);
406 if (rc != EOK)
407 return rc;
408
409 if (i == df.file.count-2)
410 chars = ds.stream.name_size - EXFAT_NAME_PART_LEN*(df.file.count-2);
411 rc = exfat_directory_get(di, &de);
412 if (rc != EOK)
413 return rc;
414 de->type = EXFAT_TYPE_NAME;
[c56c4576]415 /* test */
416 for (j=0; j<chars; j++){
417 de->name.name[j] = *sname;
418 sname++;
419 }
420
[bca3eac]421 di->b->dirty = true;
422 sname += chars;
423 }
424
425 return exfat_directory_seek(di, pos);
426}
427
[7f0c08c]428int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos)
429{
[4ba3535]430 int rc, count;
431 exfat_dentry_t *de;
432
433 rc = exfat_directory_get(di, &de);
434 if (rc != EOK)
435 return rc;
436 count = de->file.count+1;
437
438 while (count) {
439 rc = exfat_directory_get(di, &de);
440 if (rc != EOK)
441 return rc;
442 de->type &= (~EXFAT_TYPE_USED);
443 di->b->dirty = true;
444
445 rc = exfat_directory_next(di);
446 if (rc!=EOK)
447 return rc;
448 count--;
449 }
[7f0c08c]450 return EOK;
451}
452
[7d78d163]453int exfat_directory_expand(exfat_directory_t *di)
454{
455 int rc;
456
457 if (!di->nodep)
458 return ENOSPC;
459
[375ab5e]460 rc = exfat_node_expand(di->nodep->idx->service_id, di->nodep, 1);
[7d78d163]461 if (rc != EOK)
462 return rc;
463
464 di->fragmented = di->nodep->fragmented;
465 di->nodep->size += BPC(di->bs);
466 di->nodep->dirty = true; /* need to sync node */
467 di->blocks = di->nodep->size / BPS(di->bs);
468
469 return EOK;
470}
471
472int exfat_directory_lookup_free(exfat_directory_t *di, size_t count)
473{
474 int rc;
475 exfat_dentry_t *d;
476 size_t found;
477 aoff64_t pos;
478
479 rc = exfat_directory_seek(di, 0);
480 if (rc != EOK)
481 return rc;
482
483 do {
484 found = 0;
485 pos = 0;
486 do {
487 if (exfat_directory_get(di, &d) == EOK) {
488 switch (exfat_classify_dentry(d)) {
489 case EXFAT_DENTRY_LAST:
490 case EXFAT_DENTRY_FREE:
491 if (found==0) pos = di->pos;
492 found++;
493 if (found == count) {
494 exfat_directory_seek(di, pos);
495 return EOK;
496 }
497 break;
498 default:
499 found = 0;
500 break;
501 }
502 }
503 } while (exfat_directory_next(di) == EOK);
504 } while (exfat_directory_expand(di) == EOK);
505 return ENOSPC;
506}
507
[73b1218]508
[4dd9395]509/**
510 * @}
511 */
Note: See TracBrowser for help on using the repository browser.