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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since ccfbf71 was 3e018e45, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago
  1. Rewrite fat_directory_write to support:
  • using new fat_valid_name and fat_valid_short_name functions
  • flag for disabling/enabling LFN support on writing
  • using uint16_t instead of wchar_t for UTF16
  1. Rewrite fat_directory_create_sfn to support using uint16_t

instead of wchar_t for UTF16

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