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

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

Remove str_reverse() and size_t_str().

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