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

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

Make FAT use instance data.

  • Property mode set to 100644
File size: 11.7 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 do {
165 rc = fat_directory_get(di, &d);
166 if (rc != EOK)
167 return rc;
168
169 switch (fat_classify_dentry(d)) {
170 case FAT_DENTRY_LAST:
171 long_entry_count = 0;
172 long_entry = false;
173 return ENOENT;
174 case FAT_DENTRY_LFN:
175 if (long_entry) {
176 /* We found long entry */
177 long_entry_count--;
178 if ((FAT_LFN_ORDER(d) == long_entry_count) &&
179 (checksum == FAT_LFN_CHKSUM(d))) {
180 /* Right order! */
181 fat_lfn_get_entry(d, wname,
182 &lfn_offset);
183 } else {
184 /*
185 * Something wrong with order.
186 * Skip this long entries set.
187 */
188 long_entry_count = 0;
189 long_entry = false;
190 }
191 } else if (FAT_IS_LFN(d)) {
192 /* We found Last long entry! */
193 if (FAT_LFN_COUNT(d) <= FAT_LFN_MAX_COUNT) {
194 long_entry = true;
195 long_entry_count = FAT_LFN_COUNT(d);
196 lfn_size = (FAT_LFN_ENTRY_SIZE *
197 (FAT_LFN_COUNT(d) - 1)) +
198 fat_lfn_size(d);
199 lfn_offset = lfn_size;
200 fat_lfn_get_entry(d, wname,
201 &lfn_offset);
202 checksum = FAT_LFN_CHKSUM(d);
203 }
204 }
205 break;
206 case FAT_DENTRY_VALID:
207 if (long_entry &&
208 (checksum == fat_dentry_chksum(d->name))) {
209 wname[lfn_size] = '\0';
210 if (utf16_to_str(name, FAT_LFN_NAME_SIZE,
211 wname) != EOK)
212 fat_dentry_name_get(d, name);
213 } else
214 fat_dentry_name_get(d, name);
215
216 *de = d;
217 return EOK;
218 default:
219 case FAT_DENTRY_SKIP:
220 case FAT_DENTRY_FREE:
221 long_entry_count = 0;
222 long_entry = false;
223 break;
224 }
225 } while (fat_directory_next(di) == EOK);
226
227 return ENOENT;
228}
229
230int fat_directory_erase(fat_directory_t *di)
231{
232 int rc;
233 fat_dentry_t *d;
234 bool flag = false;
235 uint8_t checksum;
236
237 rc = fat_directory_get(di, &d);
238 if (rc != EOK)
239 return rc;
240 checksum = fat_dentry_chksum(d->name);
241
242 d->name[0] = FAT_DENTRY_ERASED;
243 di->b->dirty = true;
244
245 while (!flag && fat_directory_prev(di) == EOK) {
246 if (fat_directory_get(di, &d) == EOK &&
247 fat_classify_dentry(d) == FAT_DENTRY_LFN &&
248 checksum == FAT_LFN_CHKSUM(d)) {
249 if (FAT_IS_LFN(d))
250 flag = true;
251 memset(d, 0, sizeof(fat_dentry_t));
252 d->name[0] = FAT_DENTRY_ERASED;
253 di->b->dirty = true;
254 } else
255 break;
256 }
257
258 return EOK;
259}
260
261int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de)
262{
263 int rc;
264 void *data;
265 fat_instance_t *instance;
266
267 rc = fs_instance_get(di->nodep->idx->service_id, &data);
268 assert(rc == EOK);
269 instance = (fat_instance_t *) data;
270
271 if (fat_valid_short_name(name)) {
272 /*
273 * NAME could be directly stored in dentry without creating
274 * LFN.
275 */
276 fat_dentry_name_set(de, name);
277 if (fat_directory_is_sfn_exist(di, de))
278 return EEXIST;
279 rc = fat_directory_lookup_free(di, 1);
280 if (rc != EOK)
281 return rc;
282 rc = fat_directory_write_dentry(di, de);
283 return rc;
284 } else if (instance->lfn_enabled && fat_valid_name(name)) {
285 /* We should create long entries to store name */
286 int long_entry_count;
287 uint8_t checksum;
288 uint16_t wname[FAT_LFN_NAME_SIZE];
289 size_t lfn_size, lfn_offset;
290
291 rc = str_to_utf16(wname, FAT_LFN_NAME_SIZE, name);
292 if (rc != EOK)
293 return rc;
294
295 lfn_size = utf16_length(wname);
296 long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE;
297 if (lfn_size % FAT_LFN_ENTRY_SIZE)
298 long_entry_count++;
299 rc = fat_directory_lookup_free(di, long_entry_count + 1);
300 if (rc != EOK)
301 return rc;
302 aoff64_t start_pos = di->pos;
303
304 /* Write Short entry */
305 rc = fat_directory_create_sfn(di, de, name);
306 if (rc != EOK)
307 return rc;
308 checksum = fat_dentry_chksum(de->name);
309
310 rc = fat_directory_seek(di, start_pos+long_entry_count);
311 if (rc != EOK)
312 return rc;
313 rc = fat_directory_write_dentry(di, de);
314 if (rc != EOK)
315 return rc;
316
317 /* Write Long entry by parts */
318 lfn_offset = 0;
319 fat_dentry_t *d;
320 size_t idx = 0;
321 do {
322 rc = fat_directory_prev(di);
323 if (rc != EOK)
324 return rc;
325 rc = fat_directory_get(di, &d);
326 if (rc != EOK)
327 return rc;
328 fat_lfn_set_entry(wname, &lfn_offset, lfn_size + 1, d);
329 FAT_LFN_CHKSUM(d) = checksum;
330 FAT_LFN_ORDER(d) = ++idx;
331 di->b->dirty = true;
332 } while (lfn_offset < lfn_size);
333 FAT_LFN_ORDER(d) |= FAT_LFN_LAST;
334
335 rc = fat_directory_seek(di, start_pos + long_entry_count);
336 return rc;
337 }
338
339 return ENOTSUP;
340}
341
342int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de,
343 const char *lname)
344{
345 char name[FAT_NAME_LEN + 1];
346 char ext[FAT_EXT_LEN + 1];
347 char number[FAT_NAME_LEN + 1];
348 memset(name, FAT_PAD, FAT_NAME_LEN);
349 memset(ext, FAT_PAD, FAT_EXT_LEN);
350 memset(number, FAT_PAD, FAT_NAME_LEN);
351
352 size_t name_len = str_size(lname);
353 char *pdot = str_rchr(lname, '.');
354 ext[FAT_EXT_LEN] = '\0';
355 if (pdot) {
356 pdot++;
357 str_to_ascii(ext, pdot, FAT_EXT_LEN, FAT_SFN_CHAR);
358 name_len = (pdot - lname - 1);
359 }
360 if (name_len > FAT_NAME_LEN)
361 name_len = FAT_NAME_LEN;
362 str_to_ascii(name, lname, name_len, FAT_SFN_CHAR);
363
364 unsigned idx;
365 for (idx = 1; idx <= FAT_MAX_SFN; idx++) {
366 snprintf(number, sizeof(number), "%u", idx);
367
368 /* Fill de->name with FAT_PAD */
369 memset(de->name, FAT_PAD, FAT_NAME_LEN + FAT_EXT_LEN);
370 /* Copy ext */
371 memcpy(de->ext, ext, str_size(ext));
372 /* Copy name */
373 memcpy(de->name, name, str_size(name));
374
375 /* Copy number */
376 size_t offset;
377 if (str_size(name)+str_size(number) + 1 > FAT_NAME_LEN)
378 offset = FAT_NAME_LEN - str_size(number) - 1;
379 else
380 offset = str_size(name);
381 de->name[offset] = '~';
382 offset++;
383 memcpy(de->name + offset, number, str_size(number));
384
385 if (!fat_directory_is_sfn_exist(di, de))
386 return EOK;
387 }
388
389 return ERANGE;
390}
391
392int fat_directory_write_dentry(fat_directory_t *di, fat_dentry_t *de)
393{
394 fat_dentry_t *d;
395 int rc;
396
397 rc = fat_directory_get(di, &d);
398 if (rc != EOK)
399 return rc;
400 memcpy(d, de, sizeof(fat_dentry_t));
401 di->b->dirty = true;
402
403 return EOK;
404}
405
406int fat_directory_expand(fat_directory_t *di)
407{
408 int rc;
409 fat_cluster_t mcl, lcl;
410
411 if (!FAT_IS_FAT32(di->bs) && di->nodep->firstc == FAT_CLST_ROOT) {
412 /* Can't grow the root directory on FAT12/16. */
413 return ENOSPC;
414 }
415 rc = fat_alloc_clusters(di->bs, di->nodep->idx->service_id, 1, &mcl,
416 &lcl);
417 if (rc != EOK)
418 return rc;
419 rc = fat_zero_cluster(di->bs, di->nodep->idx->service_id, mcl);
420 if (rc != EOK) {
421 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
422 mcl);
423 return rc;
424 }
425 rc = fat_append_clusters(di->bs, di->nodep, mcl, lcl);
426 if (rc != EOK) {
427 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id,
428 mcl);
429 return rc;
430 }
431 di->nodep->size += BPS(di->bs) * SPC(di->bs);
432 di->nodep->dirty = true; /* need to sync node */
433 di->blocks = di->nodep->size / BPS(di->bs);
434
435 return EOK;
436}
437
438int fat_directory_lookup_free(fat_directory_t *di, size_t count)
439{
440 fat_dentry_t *d;
441 size_t found;
442 aoff64_t pos;
443 int rc;
444
445 do {
446 found = 0;
447 pos = 0;
448 fat_directory_seek(di, 0);
449 do {
450 rc = fat_directory_get(di, &d);
451 if (rc != EOK)
452 return rc;
453
454 switch (fat_classify_dentry(d)) {
455 case FAT_DENTRY_LAST:
456 case FAT_DENTRY_FREE:
457 if (found == 0)
458 pos = di->pos;
459 found++;
460 if (found == count) {
461 fat_directory_seek(di, pos);
462 return EOK;
463 }
464 break;
465 case FAT_DENTRY_VALID:
466 case FAT_DENTRY_LFN:
467 case FAT_DENTRY_SKIP:
468 default:
469 found = 0;
470 break;
471 }
472 } while (fat_directory_next(di) == EOK);
473 } while (fat_directory_expand(di) == EOK);
474
475 return ENOSPC;
476}
477
478int fat_directory_lookup_name(fat_directory_t *di, const char *name,
479 fat_dentry_t **de)
480{
481 char entry[FAT_LFN_NAME_SIZE];
482
483 fat_directory_seek(di, 0);
484 while (fat_directory_read(di, entry, de) == EOK) {
485 if (fat_dentry_namecmp(entry, name) == 0) {
486 return EOK;
487 } else {
488 if (fat_directory_next(di) != EOK)
489 break;
490 }
491 }
492
493 return ENOENT;
494}
495
496bool fat_directory_is_sfn_exist(fat_directory_t *di, fat_dentry_t *de)
497{
498 fat_dentry_t *d;
499 int rc;
500
501 fat_directory_seek(di, 0);
502 do {
503 rc = fat_directory_get(di, &d);
504 if (rc != EOK)
505 return false;
506
507 switch (fat_classify_dentry(d)) {
508 case FAT_DENTRY_LAST:
509 return false;
510 case FAT_DENTRY_VALID:
511 if (bcmp(de->name, d->name,
512 FAT_NAME_LEN + FAT_EXT_LEN)==0)
513 return true;
514 break;
515 default:
516 case FAT_DENTRY_LFN:
517 case FAT_DENTRY_SKIP:
518 case FAT_DENTRY_FREE:
519 break;
520 }
521 } while (fat_directory_next(di) == EOK);
522
523 return false;
524}
525
526/**
527 * @}
528 */
Note: See TracBrowser for help on using the repository browser.