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

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

Cstyle.

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