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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since d2c8533 was 44ecf89, checked in by Jiri Svoboda <jiri@…>, 12 years ago

Replace deprecated function bcmp() with memcmp().

  • Property mode set to 100644
File size: 11.9 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"
[f73b291]40#include <block.h>
[da2f8d10]41#include <errno.h>
42#include <byteorder.h>
[c6aca755]43#include <mem.h>
[4372b49]44#include <str.h>
[521550d]45#include <align.h>
[c065743]46#include <stdio.h>
[c6aca755]47
[da2f8d10]48int fat_directory_open(fat_node_t *nodep, fat_directory_t *di)
49{
[c6aca755]50 di->b = NULL;
[da2f8d10]51 di->nodep = nodep;
52 if (di->nodep->type != FAT_DIRECTORY)
53 return EINVAL;
54
[375ab5e]55 di->bs = block_bb_get(di->nodep->idx->service_id);
[5d95f02]56 di->blocks = ROUND_UP(nodep->size, BPS(di->bs)) / BPS(di->bs);
[da2f8d10]57 di->pos = 0;
58 di->bnum = 0;
59 di->last = false;
[5d95f02]60
[da2f8d10]61 return EOK;
62}
63
64int fat_directory_close(fat_directory_t *di)
65{
[5d95f02]66 int rc = EOK;
[da2f8d10]67
68 if (di->b)
69 rc = block_put(di->b);
70
71 return rc;
72}
73
[5d95f02]74static int 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) {
[5d95f02]86 rc = fat_block_get(&di->b, di->bs, di->nodep, i,
87 BLOCK_FLAGS_NONE);
[52ee8b7a]88 if (rc != EOK) {
[7a819535]89 di->b = NULL;
[da2f8d10]90 return rc;
[52ee8b7a]91 }
92 di->bnum = i;
[da2f8d10]93 }
94 return EOK;
95 }
[5d95f02]96
[da2f8d10]97 return ENOENT;
98}
99
[c6aca755]100int fat_directory_next(fat_directory_t *di)
101{
102 int rc;
103
104 di->pos += 1;
105 rc = fat_directory_block_load(di);
[5d95f02]106 if (rc != EOK)
[c6aca755]107 di->pos -= 1;
108
109 return rc;
110}
111
112int fat_directory_prev(fat_directory_t *di)
113{
[5d95f02]114 int rc = EOK;
[c6aca755]115
116 if (di->pos > 0) {
117 di->pos -= 1;
[5d95f02]118 rc = fat_directory_block_load(di);
119 } else
[c6aca755]120 return ENOENT;
121
[5d95f02]122 if (rc != EOK)
[c6aca755]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;
[5dfb1948]132
[c6aca755]133 di->pos = pos;
134 rc = fat_directory_block_load(di);
[5d95f02]135 if (rc != EOK)
[c6aca755]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
[da2f8d10]154int fat_directory_read(fat_directory_t *di, char *name, fat_dentry_t **de)
155{
156 fat_dentry_t *d = NULL;
[fcc3cd8]157 uint16_t wname[FAT_LFN_NAME_SIZE];
[298a6ce]158 size_t lfn_offset, lfn_size;
159 bool long_entry = false;
160 int long_entry_count = 0;
161 uint8_t checksum = 0;
[5d95f02]162 int rc;
[da2f8d10]163
[b6913b7]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
[c6aca755]171 do {
[5d95f02]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) &&
[b6913b7]186 (checksum == FAT_LFN_CHKSUM(d))) {
[5d95f02]187 /* Right order! */
188 fat_lfn_get_entry(d, wname,
189 &lfn_offset);
[c6aca755]190 } else {
[5d95f02]191 /*
192 * Something wrong with order.
193 * Skip this long entries set.
194 */
195 long_entry_count = 0;
196 long_entry = false;
[da2f8d10]197 }
[b6913b7]198 } else if (FAT_IS_LFN(d) && instance->lfn_enabled) {
[5d95f02]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);
[c6aca755]210 }
[5d95f02]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)
[da2f8d10]219 fat_dentry_name_get(d, name);
[5d95f02]220 } else
221 fat_dentry_name_get(d, name);
[c6aca755]222
[5d95f02]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;
[da2f8d10]231 }
[c6aca755]232 } while (fat_directory_next(di) == EOK);
233
[da2f8d10]234 return ENOENT;
235}
236
[c6aca755]237int fat_directory_erase(fat_directory_t *di)
238{
239 int rc;
240 fat_dentry_t *d;
241 bool flag = false;
[298a6ce]242 uint8_t checksum;
[c6aca755]243
244 rc = fat_directory_get(di, &d);
245 if (rc != EOK)
246 return rc;
[298a6ce]247 checksum = fat_dentry_chksum(d->name);
[c6aca755]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 &&
[5d95f02]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
[c6aca755]262 break;
263 }
264
265 return EOK;
266}
267
[5dfb1948]268int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de)
269{
270 int rc;
[32f623d9]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;
[298a6ce]277
[3e018e45]278 if (fat_valid_short_name(name)) {
[5d95f02]279 /*
280 * NAME could be directly stored in dentry without creating
281 * LFN.
282 */
[5dfb1948]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);
[3e018e45]290 return rc;
[32f623d9]291 } else if (instance->lfn_enabled && fat_valid_name(name)) {
[3e018e45]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);
[5dfb1948]299 if (rc != EOK)
300 return rc;
[3e018e45]301
302 lfn_size = utf16_length(wname);
[298a6ce]303 long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE;
304 if (lfn_size % FAT_LFN_ENTRY_SIZE)
305 long_entry_count++;
[32f623d9]306 rc = fat_directory_lookup_free(di, long_entry_count + 1);
[5dfb1948]307 if (rc != EOK)
308 return rc;
309 aoff64_t start_pos = di->pos;
310
311 /* Write Short entry */
[3e018e45]312 rc = fat_directory_create_sfn(di, de, name);
[5dfb1948]313 if (rc != EOK)
314 return rc;
[298a6ce]315 checksum = fat_dentry_chksum(de->name);
[5dfb1948]316
[b6913b7]317 rc = fat_directory_seek(di, start_pos + long_entry_count);
[5dfb1948]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 */
[298a6ce]325 lfn_offset = 0;
[5dfb1948]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;
[5d95f02]335 fat_lfn_set_entry(wname, &lfn_offset, lfn_size + 1, d);
[298a6ce]336 FAT_LFN_CHKSUM(d) = checksum;
[5dfb1948]337 FAT_LFN_ORDER(d) = ++idx;
338 di->b->dirty = true;
[298a6ce]339 } while (lfn_offset < lfn_size);
[5dfb1948]340 FAT_LFN_ORDER(d) |= FAT_LFN_LAST;
341
[32f623d9]342 rc = fat_directory_seek(di, start_pos + long_entry_count);
[3e018e45]343 return rc;
[5dfb1948]344 }
[3e018e45]345
346 return ENOTSUP;
[5dfb1948]347}
348
[5d95f02]349int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de,
350 const char *lname)
[5dfb1948]351{
[5d95f02]352 char name[FAT_NAME_LEN + 1];
353 char ext[FAT_EXT_LEN + 1];
354 char number[FAT_NAME_LEN + 1];
[5dfb1948]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
[3e018e45]359 size_t name_len = str_size(lname);
360 char *pdot = str_rchr(lname, '.');
[5dfb1948]361 ext[FAT_EXT_LEN] = '\0';
362 if (pdot) {
363 pdot++;
[3e018e45]364 str_to_ascii(ext, pdot, FAT_EXT_LEN, FAT_SFN_CHAR);
365 name_len = (pdot - lname - 1);
[5dfb1948]366 }
367 if (name_len > FAT_NAME_LEN)
368 name_len = FAT_NAME_LEN;
[3e018e45]369 str_to_ascii(name, lname, name_len, FAT_SFN_CHAR);
[5dfb1948]370
[c065743]371 unsigned idx;
[5d95f02]372 for (idx = 1; idx <= FAT_MAX_SFN; idx++) {
[c065743]373 snprintf(number, sizeof(number), "%u", idx);
[5dfb1948]374
375 /* Fill de->name with FAT_PAD */
[5d95f02]376 memset(de->name, FAT_PAD, FAT_NAME_LEN + FAT_EXT_LEN);
[5dfb1948]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;
[5d95f02]384 if (str_size(name)+str_size(number) + 1 > FAT_NAME_LEN)
385 offset = FAT_NAME_LEN - str_size(number) - 1;
[5dfb1948]386 else
387 offset = str_size(name);
388 de->name[offset] = '~';
389 offset++;
[5d95f02]390 memcpy(de->name + offset, number, str_size(number));
[5dfb1948]391
392 if (!fat_directory_is_sfn_exist(di, de))
393 return EOK;
394 }
[5d95f02]395
[5dfb1948]396 return ERANGE;
397}
398
399int fat_directory_write_dentry(fat_directory_t *di, fat_dentry_t *de)
[2d0d637]400{
[5dfb1948]401 fat_dentry_t *d;
402 int rc;
403
404 rc = fat_directory_get(di, &d);
[5d95f02]405 if (rc != EOK)
[5dfb1948]406 return rc;
407 memcpy(d, de, sizeof(fat_dentry_t));
408 di->b->dirty = true;
[5d95f02]409
[2d0d637]410 return EOK;
411}
412
[5dfb1948]413int fat_directory_expand(fat_directory_t *di)
414{
415 int rc;
416 fat_cluster_t mcl, lcl;
[2d0d637]417
[5dfb1948]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 }
[5d95f02]422 rc = fat_alloc_clusters(di->bs, di->nodep->idx->service_id, 1, &mcl,
423 &lcl);
[5dfb1948]424 if (rc != EOK)
425 return rc;
[375ab5e]426 rc = fat_zero_cluster(di->bs, di->nodep->idx->service_id, mcl);
[5dfb1948]427 if (rc != EOK) {
[5d95f02]428 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
429 mcl);
[5dfb1948]430 return rc;
431 }
432 rc = fat_append_clusters(di->bs, di->nodep, mcl, lcl);
433 if (rc != EOK) {
[5d95f02]434 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
435 mcl);
[5dfb1948]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;
[5d95f02]450 int rc;
[5dfb1948]451
452 do {
453 found = 0;
[5d95f02]454 pos = 0;
[5dfb1948]455 fat_directory_seek(di, 0);
456 do {
[5d95f02]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;
[5dfb1948]470 }
[5d95f02]471 break;
472 case FAT_DENTRY_VALID:
473 case FAT_DENTRY_LFN:
474 case FAT_DENTRY_SKIP:
475 default:
476 found = 0;
477 break;
[5dfb1948]478 }
479 } while (fat_directory_next(di) == EOK);
480 } while (fat_directory_expand(di) == EOK);
[5d95f02]481
[5dfb1948]482 return ENOSPC;
483}
484
[5d95f02]485int fat_directory_lookup_name(fat_directory_t *di, const char *name,
486 fat_dentry_t **de)
[5dfb1948]487{
488 char entry[FAT_LFN_NAME_SIZE];
[5d95f02]489
[5dfb1948]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 }
[5d95f02]499
[5dfb1948]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;
[5d95f02]506 int rc;
507
[5dfb1948]508 fat_directory_seek(di, 0);
509 do {
[5d95f02]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:
[44ecf89]518 if (memcmp(de->name, d->name,
519 FAT_NAME_LEN + FAT_EXT_LEN) == 0)
[5d95f02]520 return true;
521 break;
522 default:
523 case FAT_DENTRY_LFN:
524 case FAT_DENTRY_SKIP:
525 case FAT_DENTRY_FREE:
526 break;
[5dfb1948]527 }
528 } while (fat_directory_next(di) == EOK);
[5d95f02]529
[5dfb1948]530 return false;
531}
[da2f8d10]532
533/**
534 * @}
535 */
Note: See TracBrowser for help on using the repository browser.