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

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since efa8ed93 was c4bbca8, checked in by Oleg Romanenko <romanenko.oleg@…>, 14 years ago

Add copyrights

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