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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since abb7491c 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
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
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->service_id,
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_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;
333 size_t uctable_chars, j;
334 aoff64_t pos;
335
336 rc = str_to_utf16(wname, EXFAT_FILENAME_LEN, name);
337 if (rc != EOK)
338 return rc;
339 rc = exfat_uctable_get(&fn, di->service_id);
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;
384 *de = df;
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;
394 *de = ds;
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;
415 /* test */
416 for (j=0; j<chars; j++){
417 de->name.name[j] = *sname;
418 sname++;
419 }
420
421 di->b->dirty = true;
422 sname += chars;
423 }
424
425 return exfat_directory_seek(di, pos);
426}
427
428int exfat_directory_erase_file(exfat_directory_t *di, aoff64_t pos)
429{
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 }
450 return EOK;
451}
452
453int exfat_directory_expand(exfat_directory_t *di)
454{
455 int rc;
456
457 if (!di->nodep)
458 return ENOSPC;
459
460 rc = exfat_node_expand(di->nodep->idx->service_id, di->nodep, 1);
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
508
509/**
510 * @}
511 */
Note: See TracBrowser for help on using the repository browser.