Pass 'glob' explicitly to nearly every function. Now builds again with
[AROS.git] / rom / filesys / fat / direntry.c
blobdedbc49aae6e4a01d829f02643f960058ac1cc81
1 /*
2 * fat-handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007-2015 The AROS Development Team
7 * This program is free software; you can redistribute it and/or modify it
8 * under the same terms as AROS itself.
10 * $Id$
13 #include <aros/macros.h>
14 #include <exec/types.h>
15 #include <dos/dos.h>
16 #include <proto/exec.h>
17 #include <proto/dos.h>
19 #include <string.h>
21 #include "cache.h"
23 #include "fat_fs.h"
24 #include "fat_protos.h"
26 #define DEBUG DEBUG_DIRENTRY
27 #include "debug.h"
29 LONG InitDirHandle(struct FSSuper *sb, ULONG cluster, struct DirHandle *dh,
30 BOOL reuse, struct Globals *glob)
32 /* dh may or may not be initialised when this is called. if it is, then it
33 * probably has a valid cache block that we need to free, but we wouldn't
34 * know. we test the superblock pointer to figure out if it's valid or
35 * not */
36 if (reuse && (dh->ioh.sb == sb))
38 D(bug("[fat] reusing directory handle\n"));
39 if (dh->ioh.block != NULL)
41 Cache_FreeBlock(sb->cache, dh->ioh.block);
42 dh->ioh.block = NULL;
45 else
47 dh->ioh.sb = sb;
48 dh->ioh.block = NULL;
51 if (cluster == 0)
53 dh->ioh.first_cluster = sb->rootdir_cluster;
54 dh->ioh.first_sector = sb->rootdir_sector;
56 else
58 dh->ioh.first_cluster = cluster;
59 dh->ioh.first_sector = 0;
62 RESET_DIRHANDLE(dh);
63 dh->ioh.cur_sector = dh->ioh.first_sector;
64 dh->ioh.sector_offset = 0;
66 D(bug("[fat] initialised dir handle, first cluster is %ld,"
67 " first sector is %ld\n", dh->ioh.first_cluster,
68 dh->ioh.first_sector));
70 return 0;
73 LONG ReleaseDirHandle(struct DirHandle *dh, struct Globals *glob)
75 D(bug("[fat] releasing dir handle (cluster %ld)\n",
76 dh->ioh.first_cluster));
77 RESET_DIRHANDLE(dh);
79 return 0;
82 LONG GetDirEntry(struct DirHandle *dh, ULONG index, struct DirEntry *de,
83 struct Globals *glob)
85 LONG err = 0;
86 ULONG nread;
88 D(bug("[fat] looking for dir entry %ld in dir starting at cluster %ld\n",
89 index, dh->ioh.first_cluster));
91 /* fat dirs are limited to 2^16 entries */
92 if (index >= 0x10000)
94 D(bug("[fat] request for out-of-range index, returning not found\n"));
95 return ERROR_OBJECT_NOT_FOUND;
98 /* set up the return object */
99 de->sb = dh->ioh.sb;
100 de->cluster = dh->ioh.first_cluster;
101 de->index = index;
102 de->pos = index * sizeof(struct FATDirEntry);
104 /* get the data directly into the entry */
105 err = ReadFileChunk(&(dh->ioh), de->pos, sizeof(struct FATDirEntry),
106 (UBYTE *) &(de->e.entry), &nread);
107 if (err != 0)
109 D(bug("[fat] dir entry lookup failed\n"));
110 return err;
113 /* remember where we are for GetNextDirEntry() */
114 dh->cur_index = index;
116 /* done! */
117 return 0;
120 LONG GetNextDirEntry(struct DirHandle *dh, struct DirEntry *de,
121 struct Globals *glob)
123 LONG err;
125 D(bug("[fat] looking for next entry after index %ld\n", dh->cur_index));
127 /* cur_index defaults to -1, so this will do the right thing even on a
128 * fresh dirhandle */
129 while ((err = GetDirEntry(dh, dh->cur_index + 1, de, glob)) == 0)
131 /* end of directory, there is no next entry */
132 if (de->e.entry.name[0] == 0x00)
134 D(bug("[fat] entry %ld is end-of-directory marker, we're done\n",
135 dh->cur_index));
136 RESET_DIRHANDLE(dh);
137 return ERROR_OBJECT_NOT_FOUND;
140 /* skip unused entries */
141 if (de->e.entry.name[0] == 0xe5)
143 D(bug("[fat] entry %ld is empty, skipping it\n", dh->cur_index));
144 continue;
147 /* this flag will be set for both volume name entries and long
148 * filename entries. either way we want to skip them */
149 if (de->e.entry.attr & ATTR_VOLUME_ID)
151 D(bug("[fat] entry %ld is a volume name or long filename,"
152 " skipping it\n", dh->cur_index));
153 continue;
156 /* ignore the . and .. entries */
157 if (de->e.entry.name[0] == '.' && ((de->index == 0
158 && strncmp((char *)de->e.entry.name, ". ",
159 FAT_MAX_SHORT_NAME) == 0)
160 || (de->index == 1
161 && strncmp((char *)de->e.entry.name, ".. ",
162 FAT_MAX_SHORT_NAME) == 0)))
164 D(bug("[fat] skipping . or .. entry\n"));
165 continue;
168 D(bug("[fat] returning entry %ld\n", dh->cur_index));
170 return 0;
173 return err;
176 LONG GetParentDir(struct DirHandle *dh, struct DirEntry *de,
177 struct Globals *glob)
179 LONG err = 0;
180 ULONG cluster;
182 D(bug("[fat] getting parent for directory at cluster %ld\n",
183 dh->ioh.first_cluster));
185 /* if we're already at the root, then we can't go any further */
186 if (dh->ioh.first_cluster == dh->ioh.sb->rootdir_cluster)
188 D(bug("[fat] trying to go up past the root, so entry not found\n"));
189 return ERROR_OBJECT_NOT_FOUND;
192 /* otherwise, the next cluster is held in the '..' entry, which is
193 * entry #1 */
194 GetDirEntry(dh, 1, de, glob);
196 /* make sure it's actually the parent dir entry */
197 if (((de->e.entry.attr & ATTR_DIRECTORY) == 0) ||
198 strncmp((char *)de->e.entry.name, ".. ", FAT_MAX_SHORT_NAME)
199 != 0)
201 D(bug("[fat] entry index 1 does not have name '..', can't go up\n"));
202 D(bug("[fat] actual name: '%.*s', attrs: 0x%x\n", FAT_MAX_SHORT_NAME,
203 de->e.entry.name, de->e.entry.attr));
204 return ERROR_OBJECT_NOT_FOUND;
207 /* take us up */
208 InitDirHandle(dh->ioh.sb, FIRST_FILE_CLUSTER(de), dh, TRUE, glob);
210 /* get handle on grandparent dir so we can find entry with parent's
211 * name */
212 if (dh->ioh.first_cluster != dh->ioh.sb->rootdir_cluster)
214 D(bug("[fat] getting grandparent, first cluster is %ld,"
215 " root cluster is %ld\n", dh->ioh.first_cluster,
216 dh->ioh.sb->rootdir_cluster));
217 cluster = dh->ioh.first_cluster;
218 GetDirEntry(dh, 1, de, glob);
219 InitDirHandle(dh->ioh.sb, FIRST_FILE_CLUSTER(de), dh, TRUE, glob);
221 err = GetDirEntryByCluster(dh, cluster, de, glob);
224 return err;
227 LONG GetDirEntryByCluster(struct DirHandle *dh, ULONG cluster,
228 struct DirEntry *de, struct Globals *glob)
230 LONG err;
232 D(bug("[fat] looking for dir entry with first cluster %lu\n", cluster));
234 /* start at the start */
235 RESET_DIRHANDLE(dh);
237 /* loop through the entries until we find a match */
238 while ((err = GetNextDirEntry(dh, de, glob)) == 0)
240 if (de->e.entry.first_cluster_hi == (cluster >> 16)
241 && de->e.entry.first_cluster_lo == (cluster & 0xffff))
243 D(bug("[fat] matched starting cluster at entry %ld, returning\n",
244 dh->cur_index));
245 return 0;
249 D(bug("[fat] dir entry with first cluster %lu not found\n", cluster));
250 return err;
253 LONG GetDirEntryByName(struct DirHandle *dh, STRPTR name, ULONG namelen,
254 struct DirEntry *de, struct Globals *glob)
256 UBYTE buf[256];
257 ULONG buflen;
258 LONG err;
260 D(bug("[fat] looking for dir entry with name '%s'\n", name));
262 /* start at the start */
263 RESET_DIRHANDLE(dh);
265 /* loop through the entries until we find a match */
266 while ((err = GetNextDirEntry(dh, de, glob)) == 0)
268 /* compare with the short name first, since we already have it */
269 GetDirEntryShortName(de, buf, &buflen, glob);
270 if (namelen == buflen
271 && strnicmp((char *)name, (char *)buf, buflen) == 0)
273 D(bug("[fat] matched short name '%s' at entry %ld, returning\n",
274 buf, dh->cur_index));
275 return 0;
278 /* no match, extract the long name and compare with that instead */
279 GetDirEntryLongName(de, buf, &buflen);
280 if (namelen == buflen
281 && strnicmp((char *)name, (char *)buf, buflen) == 0)
283 D(bug("[fat] matched long name '%s' at entry %ld, returning\n",
284 buf, dh->cur_index));
285 return 0;
289 return err;
292 LONG GetDirEntryByPath(struct DirHandle *dh, STRPTR path, ULONG pathlen,
293 struct DirEntry *de, struct Globals *glob)
295 LONG err;
296 ULONG len, i;
299 bug("[fat] looking for entry with path '");
300 RawPutChars(path, pathlen);
301 bug("' from dir at cluster %ld\n", dh->ioh.first_cluster);
304 /* get back to the start of the dir */
305 RESET_DIRHANDLE(dh);
307 /* if it starts with a volume specifier (or just a :), remove it and get
308 * us back to the root dir */
309 for (i = 0; i < pathlen; i++)
310 if (path[i] == ':')
312 D(bug(
313 "[fat] path has volume specifier, moving to the root dir\n"));
315 pathlen -= (i + 1);
316 path = &path[i + 1];
318 InitDirHandle(dh->ioh.sb, 0, dh, TRUE, glob);
320 /* If we were called with simply ":" as the name we will return
321 immediately after this, so we prepare a fictional direntry for
322 such a case.
323 Note that we fill only fields which are actually used in our handler */
324 de->cluster = 0;
325 de->index = -1; /* WARNING! Dummy index */
326 de->e.entry.attr = ATTR_DIRECTORY;
327 de->e.entry.first_cluster_hi = 0;
328 de->e.entry.first_cluster_lo = 0;
330 break;
334 bug("[fat] now looking for entry with path '");
335 RawPutChars(path, pathlen);
336 bug("' from dir at cluster %ld\n", dh->ioh.first_cluster);
339 /* each time around the loop we find one dir/file in the full path */
340 while (pathlen > 0)
343 /* zoom forward and find the first dir separator */
344 for (len = 0; len < pathlen && path[len] != '/'; len++);
347 bug("[fat] remaining path is '");
348 RawPutChars(path, pathlen);
349 bug("' (%d bytes), current chunk is '", pathlen);
350 RawPutChars(path, len); bug("' (%d bytes)\n", len);
353 /* if the first character is a /, then we have to go up a level */
354 if (len == 0)
357 /* get the parent dir, and bale if we've gone past it (i.e. we are
358 * the root) */
359 if ((err = GetParentDir(dh, de, glob)) != 0)
360 return err;
363 /* otherwise, we want to search the current directory for this name */
364 else
366 if ((err = GetDirEntryByName(dh, path, len, de, glob)) != 0)
367 return ERROR_OBJECT_NOT_FOUND;
370 /* move up the buffer */
371 path += len;
372 pathlen -= len;
374 /* a / here is either the path separator or the directory we just went
375 * up. either way, we have to ignore it */
376 if (pathlen > 0 && path[0] == '/')
378 path++;
379 pathlen--;
382 if (pathlen > 0)
384 /* more to do, so this entry had better be a directory */
385 if (!(de->e.entry.attr & ATTR_DIRECTORY))
387 D(bug("[fat] '%.*s' is not a directory,"
388 " so can't go any further\n", len, path));
389 return ERROR_OBJECT_WRONG_TYPE;
392 InitDirHandle(dh->ioh.sb, FIRST_FILE_CLUSTER(de), dh, TRUE,
393 glob);
397 /* nothing left, so we've found it */
398 D(bug("[fat] found the entry, returning it\n"));
399 return 0;
402 LONG UpdateDirEntry(struct DirEntry *de, struct Globals *glob)
404 struct DirHandle dh;
405 LONG err = 0;
406 ULONG nwritten;
408 D(bug("[fat] writing dir entry %ld in dir starting at cluster %ld\n",
409 de->index, de->cluster));
411 InitDirHandle(glob->sb, de->cluster, &dh, FALSE, glob);
413 err =
414 WriteFileChunk(&(dh.ioh), de->pos, sizeof(struct FATDirEntry),
415 (UBYTE *) &(de->e.entry), &nwritten);
416 if (err != 0)
418 D(bug("[fat] dir entry update failed\n"));
419 ReleaseDirHandle(&dh, glob);
420 return err;
423 ReleaseDirHandle(&dh, glob);
425 return 0;
428 LONG AllocDirEntry(struct DirHandle *dh, ULONG gap, struct DirEntry *de,
429 struct Globals *glob)
431 ULONG nwant;
432 LONG err;
433 ULONG nfound;
434 BOOL clusteradded = FALSE;
436 /* find out how many entries we need */
437 nwant = gap + 1;
439 D(bug("[fat] need to find room for %ld contiguous entries\n", nwant));
441 /* get back to the start of the dir */
442 RESET_DIRHANDLE(dh);
444 /* search the directory until we find a large enough gap */
445 nfound = 0;
446 de->index = -1;
447 while (nfound < nwant)
449 err = GetDirEntry(dh, de->index + 1, de, glob);
451 /* if we can't get the entry, then we ran off the end, so there's no
452 * space left */
453 if (err == ERROR_OBJECT_NOT_FOUND)
455 D(bug("[fat] ran off the end of the directory, no space left\n"));
456 return ERROR_NO_FREE_STORE;
459 /* return any other error direct */
460 if (err != 0)
461 return err;
463 /* if it's unused, make a note */
464 if (de->e.entry.name[0] == 0xe5)
466 nfound++;
467 continue;
470 /* if we hit end-of-directory, then we can shortcut it */
471 if (de->e.entry.name[0] == 0x00)
473 ULONG last;
475 if (de->index + nwant >= 0x10000)
477 D(bug("[fat] hit end-of-directory marker,"
478 " but there's not enough room left after it\n"));
479 return ERROR_NO_FREE_STORE;
482 D(bug("[fat] found end-of-directory marker,"
483 " making space after it\n"));
485 last = de->index + nwant;
488 if (GetDirEntry(dh, de->index + 1, de, glob) != 0)
489 clusteradded = TRUE;
490 de->e.entry.name[0] = 0x00;
491 UpdateDirEntry(de, glob);
493 while (de->index != last);
495 D(bug("[fat] new end-of-directory is entry %ld\n", de->index));
497 /* clear all remaining entries in any new cluster added */
498 if (clusteradded)
499 while (GetDirEntry(dh, de->index + 1, de, glob) == 0)
501 memset(&de->e.entry, 0, sizeof(struct FATDirEntry));
502 UpdateDirEntry(de, glob);
505 /* get the previous entry; this is the base (short name) entry */
506 GetDirEntry(dh, last - 1, de, glob);
508 break;
511 /* anything else is an in-use entry, so reset our count */
512 nfound = 0;
515 D(bug("[fat] found a gap, base (short name) entry is %ld\n",
516 de->index));
517 return 0;
520 LONG CreateDirEntry(struct DirHandle *dh, STRPTR name, ULONG namelen,
521 UBYTE attr, ULONG cluster, struct DirEntry *de, struct Globals *glob)
523 ULONG gap;
524 LONG err;
527 bug("[fat] creating dir entry (name '");
528 RawPutChars(name, namelen);
529 bug("' attr 0x%02x cluster %ld)\n", attr, cluster);
532 /* find out how many extra entries we need for the long name */
533 gap = NumLongNameEntries(name, namelen);
535 /* search for a suitable unused entry */
536 err = AllocDirEntry(dh, gap, de, glob);
537 if (err != 0)
538 return err;
540 /* build the entry */
541 FillDirEntry(de, attr, cluster, glob);
543 SetDirEntryName(de, name, namelen);
545 if ((err = UpdateDirEntry(de, glob)) != 0)
547 D(bug(
548 "[fat] couldn't update base directory entry, creation failed\n"));
549 return err;
552 D(bug("[fat] created dir entry %ld\n", de->index));
554 return 0;
557 void FillDirEntry(struct DirEntry *de, UBYTE attr, ULONG cluster,
558 struct Globals *glob)
560 struct DateStamp ds;
562 de->e.entry.attr = attr;
563 de->e.entry.nt_res = 0;
565 DateStamp(&ds);
566 ConvertDOSDate(&ds, &(de->e.entry.create_date),
567 &(de->e.entry.create_time), glob);
568 de->e.entry.write_date = de->e.entry.create_date;
569 de->e.entry.write_time = de->e.entry.create_time;
570 de->e.entry.last_access_date = de->e.entry.create_date;
571 de->e.entry.create_time_tenth = ds.ds_Tick % (TICKS_PER_SECOND * 2)
572 / (TICKS_PER_SECOND / 10);
574 de->e.entry.first_cluster_lo = cluster & 0xffff;
575 de->e.entry.first_cluster_hi = cluster >> 16;
577 de->e.entry.file_size = 0;
580 LONG DeleteDirEntry(struct DirEntry *de, struct Globals *glob)
582 struct DirHandle dh;
583 UBYTE checksum;
584 ULONG order;
585 LONG err;
587 InitDirHandle(glob->sb, de->cluster, &dh, FALSE, glob);
589 /* calculate the short name checksum before we trample on the name */
590 CALC_SHORT_NAME_CHECKSUM(de->e.entry.name, checksum);
592 D(bug("[fat] short name checksum is 0x%02x\n", checksum));
594 /* mark the short entry free */
595 de->e.entry.name[0] = 0xe5;
596 UpdateDirEntry(de, glob);
598 D(bug("[fat] deleted short name entry\n"));
600 /* now we loop over the previous entries, looking for matching long name
601 * entries and killing them */
602 order = 1;
603 while ((err = GetDirEntry(&dh, de->index - 1, de, glob)) == 0)
606 /* see if this is a matching long name entry. if it's not, we're done */
607 if (!((de->e.entry.attr & ATTR_LONG_NAME_MASK) == ATTR_LONG_NAME) ||
608 (de->e.long_entry.order & ~0x40) != order ||
609 de->e.long_entry.checksum != checksum)
611 break;
613 /* kill it */
614 de->e.entry.name[0] = 0xe5;
615 UpdateDirEntry(de, glob);
617 order++;
620 D(bug("[fat] deleted %ld long name entries\n", order - 1));
622 ReleaseDirHandle(&dh, glob);
624 return err;
627 LONG FillFIB(struct ExtFileLock *fl, struct FileInfoBlock *fib,
628 struct Globals *glob)
630 struct FSSuper *sb = glob->sb;
631 struct GlobalLock *gl = (fl != NULL ? fl->gl : &sb->info->root_lock);
632 struct DirHandle dh;
633 struct DirEntry de;
634 LONG result = 0;
635 int len;
637 D(bug("\tFilling FIB data.\n"));
639 if (gl == &sb->info->root_lock)
641 D(bug("\t\ttype: root directory\n"));
642 fib->fib_DirEntryType = ST_ROOT;
644 else if (gl->attr & ATTR_DIRECTORY)
646 D(bug("\t\ttype: directory\n"));
647 fib->fib_DirEntryType = ST_USERDIR;
649 else
651 D(bug("\t\ttype: file\n"));
652 fib->fib_DirEntryType = ST_FILE;
655 D(bug("\t\tsize: %ld\n", gl->size));
657 fib->fib_Size = gl->size;
658 fib->fib_NumBlocks = ((gl->size + (sb->clustersize - 1))
659 >> sb->clustersize_bits) << sb->cluster_sectors_bits;
660 fib->fib_EntryType = fib->fib_DirEntryType;
661 fib->fib_DiskKey = 0xfffffffflu; //fl->entry;
663 if (fib->fib_DirEntryType == ST_ROOT)
664 CopyMem(&sb->volume.create_time, &fib->fib_Date,
665 sizeof(struct DateStamp));
666 else
668 InitDirHandle(sb, gl->dir_cluster, &dh, FALSE, glob);
669 GetDirEntry(&dh, gl->dir_entry, &de, glob);
670 ConvertFATDate(de.e.entry.write_date, de.e.entry.write_time,
671 &fib->fib_Date, glob);
672 ReleaseDirHandle(&dh, glob);
675 len = gl->name[0] <= 106 ? gl->name[0] : 106;
676 CopyMem(gl->name, fib->fib_FileName, len + 1);
677 fib->fib_FileName[len + 1] = '\0';
678 D(bug("\t\tname (len %ld) %s\n", len, fib->fib_FileName + 1));
680 fib->fib_Protection = 0;
681 if (gl->attr & ATTR_READ_ONLY)
682 fib->fib_Protection |= (FIBF_DELETE | FIBF_WRITE);
683 if (gl->attr & ATTR_ARCHIVE)
684 fib->fib_Protection |= FIBF_ARCHIVE;
686 fib->fib_Comment[0] = 0;
687 #if DEBUG_NAMES
688 CopyMem(gl->shortname, fib->fib_Comment, gl->shortname[0] + 1);
689 #endif
691 return result;