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

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

Merge from lp:~romanenko-oleg/helenos/fat.

  • 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#include <align.h>
46
47int fat_directory_block_load(fat_directory_t *);
48
49
50int fat_directory_open(fat_node_t *nodep, fat_directory_t *di)
51{
52 di->b = NULL;
53 di->nodep = nodep;
54 if (di->nodep->type != FAT_DIRECTORY)
55 return EINVAL;
56
57 di->bs = block_bb_get(di->nodep->idx->service_id);
58 di->blocks = ROUND_UP(nodep->size, BPS(di->bs))/BPS(di->bs);
59 di->pos = 0;
60 di->bnum = 0;
61 di->last = false;
62 return EOK;
63}
64
65int fat_directory_close(fat_directory_t *di)
66{
67 int rc=EOK;
68
69 if (di->b)
70 rc = block_put(di->b);
71
72 return rc;
73}
74
75int fat_directory_block_load(fat_directory_t *di)
76{
77 uint32_t i;
78 int rc;
79
80 i = (di->pos * sizeof(fat_dentry_t)) / BPS(di->bs);
81 if (i < di->blocks) {
82 if (di->b && di->bnum != i) {
83 block_put(di->b);
84 di->b = NULL;
85 }
86 if (!di->b) {
87 rc = fat_block_get(&di->b, di->bs, di->nodep, i, 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 return ENOENT;
97}
98
99int fat_directory_next(fat_directory_t *di)
100{
101 int rc;
102
103 di->pos += 1;
104 rc = fat_directory_block_load(di);
105 if (rc!=EOK)
106 di->pos -= 1;
107
108 return rc;
109}
110
111int fat_directory_prev(fat_directory_t *di)
112{
113 int rc=EOK;
114
115 if (di->pos > 0) {
116 di->pos -= 1;
117 rc=fat_directory_block_load(di);
118 }
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
163 do {
164 if (fat_directory_get(di, &d) == EOK) {
165 switch (fat_classify_dentry(d)) {
166 case FAT_DENTRY_LAST:
167 long_entry_count = 0;
168 long_entry = false;
169 return ENOENT;
170 case FAT_DENTRY_LFN:
171 if (long_entry) {
172 /* We found long entry */
173 long_entry_count--;
174 if ((FAT_LFN_ORDER(d) == long_entry_count) &&
175 (checksum == FAT_LFN_CHKSUM(d))) {
176 /* Right order! */
177 fat_lfn_get_entry(d, wname, &lfn_offset);
178 } else {
179 /* Something wrong with order. Skip this long entries set */
180 long_entry_count = 0;
181 long_entry = false;
182 }
183 } else {
184 if (FAT_IS_LFN(d)) {
185 /* We found Last long entry! */
186 if (FAT_LFN_COUNT(d) <= FAT_LFN_MAX_COUNT) {
187 long_entry = true;
188 long_entry_count = FAT_LFN_COUNT(d);
189 lfn_size = (FAT_LFN_ENTRY_SIZE *
190 (FAT_LFN_COUNT(d) - 1)) + fat_lfn_size(d);
191 lfn_offset = lfn_size;
192 fat_lfn_get_entry(d, wname, &lfn_offset);
193 checksum = FAT_LFN_CHKSUM(d);
194 }
195 }
196 }
197 break;
198 case FAT_DENTRY_VALID:
199 if (long_entry &&
200 (checksum == fat_dentry_chksum(d->name))) {
201 wname[lfn_size] = '\0';
202 if (utf16_to_str(name, FAT_LFN_NAME_SIZE, wname) != EOK)
203 fat_dentry_name_get(d, name);
204 }
205 else
206 fat_dentry_name_get(d, name);
207
208 *de = d;
209 return EOK;
210 default:
211 case FAT_DENTRY_SKIP:
212 case FAT_DENTRY_FREE:
213 long_entry_count = 0;
214 long_entry = false;
215 break;
216 }
217 }
218 } while (fat_directory_next(di) == EOK);
219
220 return ENOENT;
221}
222
223int fat_directory_erase(fat_directory_t *di)
224{
225 int rc;
226 fat_dentry_t *d;
227 bool flag = false;
228 uint8_t checksum;
229
230 rc = fat_directory_get(di, &d);
231 if (rc != EOK)
232 return rc;
233 checksum = fat_dentry_chksum(d->name);
234
235 d->name[0] = FAT_DENTRY_ERASED;
236 di->b->dirty = true;
237
238 while (!flag && fat_directory_prev(di) == EOK) {
239 if (fat_directory_get(di, &d) == EOK &&
240 fat_classify_dentry(d) == FAT_DENTRY_LFN &&
241 checksum == FAT_LFN_CHKSUM(d)) {
242 if (FAT_IS_LFN(d))
243 flag = true;
244 memset(d, 0, sizeof(fat_dentry_t));
245 d->name[0] = FAT_DENTRY_ERASED;
246 di->b->dirty = true;
247 }
248 else
249 break;
250 }
251
252 return EOK;
253}
254
255int fat_directory_write(fat_directory_t *di, const char *name, fat_dentry_t *de)
256{
257 int rc;
258 bool enable_lfn = true; /* We can use this variable to switch off LFN support */
259
260 if (fat_valid_short_name(name)) {
261 /* NAME could be directly stored in dentry without creating LFN */
262 fat_dentry_name_set(de, name);
263 if (fat_directory_is_sfn_exist(di, de))
264 return EEXIST;
265 rc = fat_directory_lookup_free(di, 1);
266 if (rc != EOK)
267 return rc;
268 rc = fat_directory_write_dentry(di, de);
269 return rc;
270 } else if (enable_lfn && fat_valid_name(name)) {
271 /* We should create long entries to store name */
272 int long_entry_count;
273 uint8_t checksum;
274 uint16_t wname[FAT_LFN_NAME_SIZE];
275 size_t lfn_size, lfn_offset;
276
277 rc = str_to_utf16(wname, FAT_LFN_NAME_SIZE, name);
278 if (rc != EOK)
279 return rc;
280
281 lfn_size = utf16_length(wname);
282 long_entry_count = lfn_size / FAT_LFN_ENTRY_SIZE;
283 if (lfn_size % FAT_LFN_ENTRY_SIZE)
284 long_entry_count++;
285 rc = fat_directory_lookup_free(di, long_entry_count+1);
286 if (rc != EOK)
287 return rc;
288 aoff64_t start_pos = di->pos;
289
290 /* Write Short entry */
291 rc = fat_directory_create_sfn(di, de, name);
292 if (rc != EOK)
293 return rc;
294 checksum = fat_dentry_chksum(de->name);
295
296 rc = fat_directory_seek(di, start_pos+long_entry_count);
297 if (rc != EOK)
298 return rc;
299 rc = fat_directory_write_dentry(di, de);
300 if (rc != EOK)
301 return rc;
302
303 /* Write Long entry by parts */
304 lfn_offset = 0;
305 fat_dentry_t *d;
306 size_t idx = 0;
307 do {
308 rc = fat_directory_prev(di);
309 if (rc != EOK)
310 return rc;
311 rc = fat_directory_get(di, &d);
312 if (rc != EOK)
313 return rc;
314 fat_lfn_set_entry(wname, &lfn_offset, lfn_size+1, d);
315 FAT_LFN_CHKSUM(d) = checksum;
316 FAT_LFN_ORDER(d) = ++idx;
317 di->b->dirty = true;
318 } while (lfn_offset < lfn_size);
319 FAT_LFN_ORDER(d) |= FAT_LFN_LAST;
320
321 rc = fat_directory_seek(di, start_pos+long_entry_count);
322 return rc;
323 }
324
325 return ENOTSUP;
326}
327
328int fat_directory_create_sfn(fat_directory_t *di, fat_dentry_t *de, const char *lname)
329{
330 char name[FAT_NAME_LEN+1];
331 char ext[FAT_EXT_LEN+1];
332 char number[FAT_NAME_LEN+1];
333 memset(name, FAT_PAD, FAT_NAME_LEN);
334 memset(ext, FAT_PAD, FAT_EXT_LEN);
335 memset(number, FAT_PAD, FAT_NAME_LEN);
336
337 size_t name_len = str_size(lname);
338 char *pdot = str_rchr(lname, '.');
339 ext[FAT_EXT_LEN] = '\0';
340 if (pdot) {
341 pdot++;
342 str_to_ascii(ext, pdot, FAT_EXT_LEN, FAT_SFN_CHAR);
343 name_len = (pdot - lname - 1);
344 }
345 if (name_len > FAT_NAME_LEN)
346 name_len = FAT_NAME_LEN;
347 str_to_ascii(name, lname, name_len, FAT_SFN_CHAR);
348
349 size_t idx;
350 for (idx=1; idx <= FAT_MAX_SFN; idx++) {
351 if (size_t_str(idx, 10, number, FAT_NAME_LEN-2)!=EOK)
352 return EOVERFLOW;
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)
378{
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;
387 return EOK;
388}
389
390int fat_directory_expand(fat_directory_t *di)
391{
392 int rc;
393 fat_cluster_t mcl, lcl;
394
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 }
399 rc = fat_alloc_clusters(di->bs, di->nodep->idx->service_id, 1, &mcl, &lcl);
400 if (rc != EOK)
401 return rc;
402 rc = fat_zero_cluster(di->bs, di->nodep->idx->service_id, mcl);
403 if (rc != EOK) {
404 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id, mcl);
405 return rc;
406 }
407 rc = fat_append_clusters(di->bs, di->nodep, mcl, lcl);
408 if (rc != EOK) {
409 (void) fat_free_clusters(di->bs, di->nodep->idx->service_id, mcl);
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}
492
493/**
494 * @}
495 */
Note: See TracBrowser for help on using the repository browser.