source: mainline/uspace/srv/fs/fat/fat_directory.c@ 92b638c

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

When a FAT filesystem is mounted with the 'nolfn' mount option, besides
writing new dentries in the 8.3 format, also read dentries only in the
8.3 format. Note that some other aspects (such as complete cleanup of
the LFN dentries on SFN erase) of LFN are still active even with
'nolfn'.

  • Property mode set to 100644
File size: 11.9 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 fat_directory.c
35 * @brief Functions that work with FAT directory.
36 */
37
38#include "fat_directory.h"
39#include "fat_fat.h"
40#include <libblock.h>
41#include <errno.h>
42#include <byteorder.h>
43#include <mem.h>
44#include <str.h>
45#include <align.h>
46#include <stdio.h>
47
48int fat_directory_open(fat_node_t *nodep, fat_directory_t *di)
49{
50 di->b = NULL;
51 di->nodep = nodep;
52 if (di->nodep->type != FAT_DIRECTORY)
53 return EINVAL;
54
55 di->bs = block_bb_get(di->nodep->idx->service_id);
56 di->blocks = ROUND_UP(nodep->size, BPS(di->bs)) / BPS(di->bs);
57 di->pos = 0;
58 di->bnum = 0;
59 di->last = false;
60
61 return EOK;
62}
63
64int fat_directory_close(fat_directory_t *di)
65{
66 int rc = EOK;
67
68 if (di->b)
69 rc = block_put(di->b);
70
71 return rc;
72}
73
74static int fat_directory_block_load(fat_directory_t *di)
75{
76 uint32_t i;
77 int rc;
78
79 i = (di->pos * sizeof(fat_dentry_t)) / BPS(di->bs);
80 if (i < di->blocks) {
81 if (di->b && di->bnum != i) {
82 block_put(di->b);
83 di->b = NULL;
84 }
85 if (!di->b) {
86 rc = fat_block_get(&di->b, di->bs, di->nodep, i,
87 BLOCK_FLAGS_NONE);
88 if (rc != EOK) {
89 di->b = NULL;
90 return rc;
91 }
92 di->bnum = i;
93 }
94 return EOK;
95 }
96
97 return ENOENT;
98}
99
100int fat_directory_next(fat_directory_t *di)
101{
102 int rc;
103
104 di->pos += 1;
105 rc = fat_directory_block_load(di);
106 if (rc != EOK)
107 di->pos -= 1;
108
109 return rc;
110}
111
112int fat_directory_prev(fat_directory_t *di)
113{
114 int rc = EOK;
115
116 if (di->pos > 0) {
117 di->pos -= 1;
118 rc = fat_directory_block_load(di);
119 } else
120 return ENOENT;
121
122 if (rc != EOK)
123 di->pos += 1;
124
125 return rc;
126}
127
128int fat_directory_seek(fat_directory_t *di, aoff64_t pos)
129{
130 aoff64_t _pos = di->pos;
131 int rc;
132
133 di->pos = pos;
134 rc = fat_directory_block_load(di);
135 if (rc != EOK)
136 di->pos = _pos;
137
138 return rc;
139}
140
141int fat_directory_get(fat_directory_t *di, fat_dentry_t **d)
142{
143 int rc;
144
145 rc = fat_directory_block_load(di);
146 if (rc == EOK) {
147 aoff64_t o = di->pos % (BPS(di->bs) / sizeof(fat_dentry_t));
148 *d = ((fat_dentry_t *)di->b->data) + o;
149 }
150
151 return rc;
152}
153
154int fat_directory_read(fat_directory_t *di, char *name, fat_dentry_t **de)
155{
156 fat_dentry_t *d = NULL;
157 uint16_t wname[FAT_LFN_NAME_SIZE];
158 size_t lfn_offset, lfn_size;
159 bool long_entry = false;
160 int long_entry_count = 0;
161 uint8_t checksum = 0;
162 int rc;
163
164 void *data;
165 fat_instance_t *instance;
166
167 rc = fs_instance_get(di->nodep->idx->service_id, &data);
168 assert(rc == EOK);
169 instance = (fat_instance_t *) data;
170
171 do {
172 rc = fat_directory_get(di, &d);
173 if (rc != EOK)
174 return rc;
175
176 switch (fat_classify_dentry(d)) {
177 case FAT_DENTRY_LAST:
178 long_entry_count = 0;
179 long_entry = false;
180 return ENOENT;
181 case FAT_DENTRY_LFN:
182 if (long_entry) {
183 /* We found long entry */
184 long_entry_count--;
185 if ((FAT_LFN_ORDER(d) == long_entry_count) &&
186 (checksum == FAT_LFN_CHKSUM(d))) {
187 /* Right order! */
188 fat_lfn_get_entry(d, wname,
189 &lfn_offset);
190 } else {
191 /*
192 * Something wrong with order.
193 * Skip this long entries set.
194 */
195 long_entry_count = 0;
196 long_entry = false;
197 }
198 } else if (FAT_IS_LFN(d) && instance->lfn_enabled) {
199 /* We found Last long entry! */
200 if (FAT_LFN_COUNT(d) <= FAT_LFN_MAX_COUNT) {
201 long_entry = true;
202 long_entry_count = FAT_LFN_COUNT(d);
203 lfn_size = (FAT_LFN_ENTRY_SIZE *
204 (FAT_LFN_COUNT(d) - 1)) +
205 fat_lfn_size(d);
206 lfn_offset = lfn_size;
207 fat_lfn_get_entry(d, wname,
208 &lfn_offset);
209 checksum = FAT_LFN_CHKSUM(d);
210 }
211 }
212 break;
213 case FAT_DENTRY_VALID:
214 if (long_entry &&
215 (checksum == fat_dentry_chksum(d->name))) {
216 wname[lfn_size] = '\0';
217 if (utf16_to_str(name, FAT_LFN_NAME_SIZE,
218 wname) != EOK)
219 fat_dentry_name_get(d, name);
220 } else
221 fat_dentry_name_get(d, name);
222
223 *de = d;
224 return EOK;
225 default:
226 case FAT_DENTRY_SKIP:
227 case FAT_DENTRY_FREE:
228 long_entry_count = 0;
229 long_entry = false;
230 break;
231 }
232 } while (fat_directory_next(di) == EOK);
233
234 return ENOENT;
235}
236
237int fat_directory_erase(fat_directory_t *di)
238{
239 int rc;
240 fat_dentry_t *d;
241 bool flag = false;
242 uint8_t checksum;
243
244 rc = fat_directory_get(di, &d);
245 if (rc != EOK)
246 return rc;
247 checksum = fat_dentry_chksum(d->name);
248
249 d->name[0] = FAT_DENTRY_ERASED;
250 di->b->dirty = true;
251
252 while (!flag && fat_directory_prev(di) == EOK) {
253 if (fat_directory_get(di, &d) == EOK &&
254 fat_classify_dentry(d) == FAT_DENTRY_LFN &&
255 checksum == FAT_LFN_CHKSUM(d)) {
256 if (FAT_IS_LFN(d))
257 flag = true;
258 memset(d, 0, sizeof(fat_dentry_t));
259 d->name[0] = FAT_DENTRY_ERASED;
260 di->b->dirty = true;
261 } else
262 break;
263 }
264
265 return EOK;
266}
267
268int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de)
269{
270 int rc;
271 void *data;
272 fat_instance_t *instance;
273
274 rc = fs_instance_get(di->nodep->idx->service_id, &data);
275 assert(rc == EOK);
276 instance = (fat_instance_t *) data;
277
278 if (fat_valid_short_name(name)) {
279 /*
280 * NAME could be directly stored in dentry without creating
281 * LFN.
282 */
283 fat_dentry_name_set(de, name);
284 if (fat_directory_is_sfn_exist(di, de))
285 return EEXIST;
286 rc = fat_directory_lookup_free(di, 1);
287 if (rc != EOK)
288 return rc;
289 rc = fat_directory_write_dentry(di, de);
290 return rc;
291 } else if (instance->lfn_enabled && fat_valid_name(name)) {
292 /* We should create long entries to store name */
293 int long_entry_count;
294 uint8_t checksum;
295 uint16_t wname[FAT_LFN_NAME_SIZE];
296 size_t lfn_size, lfn_offset;
297
298 rc = str_to_utf16(wname, FAT_LFN_NAME_SIZE, name);
299 if (rc != EOK)
300 return rc;
301
302 lfn_size = utf16_length(wname);
303 long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE;
304 if (lfn_size % FAT_LFN_ENTRY_SIZE)
305 long_entry_count++;
306 rc = fat_directory_lookup_free(di, long_entry_count + 1);
307 if (rc != EOK)
308 return rc;
309 aoff64_t start_pos = di->pos;
310
311 /* Write Short entry */
312 rc = fat_directory_create_sfn(di, de, name);
313 if (rc != EOK)
314 return rc;
315 checksum = fat_dentry_chksum(de->name);
316
317 rc = fat_directory_seek(di, start_pos + long_entry_count);
318 if (rc != EOK)
319 return rc;
320 rc = fat_directory_write_dentry(di, de);
321 if (rc != EOK)
322 return rc;
323
324 /* Write Long entry by parts */
325 lfn_offset = 0;
326 fat_dentry_t *d;
327 size_t idx = 0;
328 do {
329 rc = fat_directory_prev(di);
330 if (rc != EOK)
331 return rc;
332 rc = fat_directory_get(di, &d);
333 if (rc != EOK)
334 return rc;
335 fat_lfn_set_entry(wname, &lfn_offset, lfn_size + 1, d);
336 FAT_LFN_CHKSUM(d) = checksum;
337 FAT_LFN_ORDER(d) = ++idx;
338 di->b->dirty = true;
339 } while (lfn_offset < lfn_size);
340 FAT_LFN_ORDER(d) |= FAT_LFN_LAST;
341
342 rc = fat_directory_seek(di, start_pos + long_entry_count);
343 return rc;
344 }
345
346 return ENOTSUP;
347}
348
349int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de,
350 const char *lname)
351{
352 char name[FAT_NAME_LEN + 1];
353 char ext[FAT_EXT_LEN + 1];
354 char number[FAT_NAME_LEN + 1];
355 memset(name, FAT_PAD, FAT_NAME_LEN);
356 memset(ext, FAT_PAD, FAT_EXT_LEN);
357 memset(number, FAT_PAD, FAT_NAME_LEN);
358
359 size_t name_len = str_size(lname);
360 char *pdot = str_rchr(lname, '.');
361 ext[FAT_EXT_LEN] = '\0';
362 if (pdot) {
363 pdot++;
364 str_to_ascii(ext, pdot, FAT_EXT_LEN, FAT_SFN_CHAR);
365 name_len = (pdot - lname - 1);
366 }
367 if (name_len > FAT_NAME_LEN)
368 name_len = FAT_NAME_LEN;
369 str_to_ascii(name, lname, name_len, FAT_SFN_CHAR);
370
371 unsigned idx;
372 for (idx = 1; idx <= FAT_MAX_SFN; idx++) {
373 snprintf(number, sizeof(number), "%u", idx);
374
375 /* Fill de->name with FAT_PAD */
376 memset(de->name, FAT_PAD, FAT_NAME_LEN + FAT_EXT_LEN);
377 /* Copy ext */
378 memcpy(de->ext, ext, str_size(ext));
379 /* Copy name */
380 memcpy(de->name, name, str_size(name));
381
382 /* Copy number */
383 size_t offset;
384 if (str_size(name)+str_size(number) + 1 > FAT_NAME_LEN)
385 offset = FAT_NAME_LEN - str_size(number) - 1;
386 else
387 offset = str_size(name);
388 de->name[offset] = '~';
389 offset++;
390 memcpy(de->name + offset, number, str_size(number));
391
392 if (!fat_directory_is_sfn_exist(di, de))
393 return EOK;
394 }
395
396 return ERANGE;
397}
398
399int fat_directory_write_dentry(fat_directory_t *di, fat_dentry_t *de)
400{
401 fat_dentry_t *d;
402 int rc;
403
404 rc = fat_directory_get(di, &d);
405 if (rc != EOK)
406 return rc;
407 memcpy(d, de, sizeof(fat_dentry_t));
408 di->b->dirty = true;
409
410 return EOK;
411}
412
413int fat_directory_expand(fat_directory_t *di)
414{
415 int rc;
416 fat_cluster_t mcl, lcl;
417
418 if (!FAT_IS_FAT32(di->bs) && di->nodep->firstc == FAT_CLST_ROOT) {
419 /* Can't grow the root directory on FAT12/16. */
420 return ENOSPC;
421 }
422 rc = fat_alloc_clusters(di->bs, di->nodep->idx->service_id, 1, &mcl,
423 &lcl);
424 if (rc != EOK)
425 return rc;
426 rc = fat_zero_cluster(di->bs, di->nodep->idx->service_id, mcl);
427 if (rc != EOK) {
428 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
429 mcl);
430 return rc;
431 }
432 rc = fat_append_clusters(di->bs, di->nodep, mcl, lcl);
433 if (rc != EOK) {
434 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
435 mcl);
436 return rc;
437 }
438 di->nodep->size += BPS(di->bs) * SPC(di->bs);
439 di->nodep->dirty = true; /* need to sync node */
440 di->blocks = di->nodep->size / BPS(di->bs);
441
442 return EOK;
443}
444
445int fat_directory_lookup_free(fat_directory_t *di, size_t count)
446{
447 fat_dentry_t *d;
448 size_t found;
449 aoff64_t pos;
450 int rc;
451
452 do {
453 found = 0;
454 pos = 0;
455 fat_directory_seek(di, 0);
456 do {
457 rc = fat_directory_get(di, &d);
458 if (rc != EOK)
459 return rc;
460
461 switch (fat_classify_dentry(d)) {
462 case FAT_DENTRY_LAST:
463 case FAT_DENTRY_FREE:
464 if (found == 0)
465 pos = di->pos;
466 found++;
467 if (found == count) {
468 fat_directory_seek(di, pos);
469 return EOK;
470 }
471 break;
472 case FAT_DENTRY_VALID:
473 case FAT_DENTRY_LFN:
474 case FAT_DENTRY_SKIP:
475 default:
476 found = 0;
477 break;
478 }
479 } while (fat_directory_next(di) == EOK);
480 } while (fat_directory_expand(di) == EOK);
481
482 return ENOSPC;
483}
484
485int fat_directory_lookup_name(fat_directory_t *di, const char *name,
486 fat_dentry_t **de)
487{
488 char entry[FAT_LFN_NAME_SIZE];
489
490 fat_directory_seek(di, 0);
491 while (fat_directory_read(di, entry, de) == EOK) {
492 if (fat_dentry_namecmp(entry, name) == 0) {
493 return EOK;
494 } else {
495 if (fat_directory_next(di) != EOK)
496 break;
497 }
498 }
499
500 return ENOENT;
501}
502
503bool fat_directory_is_sfn_exist(fat_directory_t *di, fat_dentry_t *de)
504{
505 fat_dentry_t *d;
506 int rc;
507
508 fat_directory_seek(di, 0);
509 do {
510 rc = fat_directory_get(di, &d);
511 if (rc != EOK)
512 return false;
513
514 switch (fat_classify_dentry(d)) {
515 case FAT_DENTRY_LAST:
516 return false;
517 case FAT_DENTRY_VALID:
518 if (bcmp(de->name, d->name,
519 FAT_NAME_LEN + FAT_EXT_LEN)==0)
520 return true;
521 break;
522 default:
523 case FAT_DENTRY_LFN:
524 case FAT_DENTRY_SKIP:
525 case FAT_DENTRY_FREE:
526 break;
527 }
528 } while (fat_directory_next(di) == EOK);
529
530 return false;
531}
532
533/**
534 * @}
535 */
Note: See TracBrowser for help on using the repository browser.