r26082@plastic: rob | 2007-04-17 13:36:45 +1000
[cake.git] / workbench / fs / fat / direntry.c
blob838906873d6fb0b75a27e7e28c4c2e105b571318
1 /*
2 * fat.handler - FAT12/16/32 filesystem handler
4 * Copyright © 2006 Marek Szyprowski
5 * Copyright © 2007 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 <exec/types.h>
14 #include <dos/dos.h>
15 #include <proto/exec.h>
17 #include <string.h>
19 #include "cache.h"
21 #include "fat_fs.h"
22 #include "fat_protos.h"
24 #define RESET_DIRHANDLE(dh) \
25 do { \
26 RESET_HANDLE(&(dh->ioh)); \
27 dh->cur_index = 0xffffffff; \
28 } while(0);
30 LONG InitDirHandle(struct FSSuper *sb, ULONG cluster, struct DirHandle *dh) {
31 dh->ioh.sb = sb;
33 if (cluster == 0) {
34 dh->ioh.first_cluster = sb->rootdir_cluster;
35 dh->ioh.first_sector = sb->rootdir_sector;
37 else {
38 dh->ioh.first_cluster = cluster;
39 dh->ioh.first_sector = 0;
42 dh->ioh.block = NULL;
44 RESET_DIRHANDLE(dh);
46 D(bug("[fat] initialised dir handle, first cluster is %ld, first sector is %ld\n", dh->ioh.first_cluster, dh->ioh.first_sector));
48 return 0;
51 LONG ReleaseDirHandle(struct DirHandle *dh) {
52 RESET_DIRHANDLE(dh);
53 return 0;
56 LONG GetDirEntry(struct DirHandle *dh, ULONG index, struct DirEntry *de) {
57 LONG err = 0;
58 ULONG nread;
60 D(bug("[fat] looking for dir entry %ld in dir starting at cluster %ld\n", index, dh->ioh.first_cluster));
62 /* fat dirs are limited to 2^16 entries */
63 if (index >= 0x10000) {
64 D(bug("[fat] request for out-of-range index, returning not found\n"));
65 return ERROR_OBJECT_NOT_FOUND;
68 /* setup the return object */
69 de->sb = dh->ioh.sb;
70 de->cluster = dh->ioh.first_cluster;
71 de->index = index;
72 de->pos = index * sizeof(struct FATDirEntry);
74 /* get the data directly into the entry */
75 err = ReadFileChunk(&(dh->ioh), de->pos, sizeof(struct FATDirEntry), (UBYTE *) &(de->e.entry), &nread);
76 if (err != 0) {
77 D(bug("[fat] dir entry lookup failed\n"));
78 return err;
81 /* remember where we are for GetNextDirEntry() */
82 dh->cur_index = index;
84 /* done! */
85 return 0;
88 LONG GetNextDirEntry(struct DirHandle *dh, struct DirEntry *de) {
89 LONG err;
91 D(bug("[fat] looking for next entry after index %ld\n", dh->cur_index));
93 /* cur_index defaults to -1, so this will do the right thing even on a
94 * fresh dirhandle */
95 while ((err = GetDirEntry(dh, dh->cur_index + 1, de)) == 0) {
96 /* skip unused entries */
97 if (de->e.entry.name[0] == 0xe5) {
98 D(bug("[fat] entry %ld is empty, skipping it\n", dh->cur_index));
99 continue;
102 /* this flag will be set for both volume name entries and long
103 * filename entries. either way we want to skip them */
104 if (de->e.entry.attr & ATTR_VOLUME_ID) {
105 D(bug("[fat] entry %ld is a volume name or long filename, skipping it\n", dh->cur_index));
106 continue;
109 /* end of directory, there is no next entry */
110 if (de->e.entry.name[0] == 0x00) {
111 D(bug("[fat] entry %ld is end-of-directory marker, we're done\n", dh->cur_index));
112 RESET_DIRHANDLE(dh);
113 return ERROR_OBJECT_NOT_FOUND;
116 D(bug("[fat] returning entry %ld\n", dh->cur_index));
118 return 0;
121 return err;
124 LONG GetParentDir(struct DirHandle *dh, struct DirEntry *de) {
125 D(bug("[fat] getting parent for directory at cluster %ld\n", dh->ioh.first_cluster));
127 /* if we're already at the root, then we can't go any further */
128 if (dh->ioh.first_cluster == dh->ioh.sb->rootdir_cluster) {
129 D(bug("[fat] trying go up past the root, so entry not found\n"));
130 return ERROR_OBJECT_NOT_FOUND;
133 /* otherwise, the next cluster is held in the '..' entry, which is
134 * entry #1 */
135 GetDirEntry(dh, 1, de);
137 /* make sure its actually the parent dir entry */
138 if (!((de->e.entry.attr & ATTR_LONG_NAME_MASK) == ATTR_DIRECTORY) ||
139 strncmp((char *) de->e.entry.name, ".. ", 11) != 0) {
140 D(bug("[fat] entry index 1 does not have name '..', can't go up\n"));
141 return ERROR_OBJECT_NOT_FOUND;
144 /* take us up */
145 InitDirHandle(dh->ioh.sb, FIRST_FILE_CLUSTER(de), dh);
147 return 0;
150 LONG GetDirEntryByName(struct DirHandle *dh, STRPTR name, ULONG namelen, struct DirEntry *de) {
151 UBYTE buf[256];
152 ULONG buflen;
153 LONG err;
155 D(bug("[fat] looking for dir entry with name '%.*s'\n", namelen, name));
157 /* start at the start */
158 RESET_DIRHANDLE(dh);
160 /* loop through the entries until we find a match */
161 while ((err = GetNextDirEntry(dh, de)) == 0) {
162 /* compare with the short name first, since we already have it */
163 GetDirShortName(de, buf, &buflen);
164 if (namelen == buflen && strnicmp((char *) name, (char *) buf, buflen) == 0) {
165 D(bug("[fat] matched short name '%.*s' at entry %ld, returning\n", buflen, buf, dh->cur_index));
166 return 0;
169 /* no match, extract the long name and compare with that instead */
170 GetDirLongName(de, buf, &buflen);
171 if (namelen == buflen && strnicmp((char *) name, (char *) buf, buflen) == 0) {
172 D(bug("[fat] matched long name '%.*s' at entry %ld, returning\n", buflen, buf, dh->cur_index));
173 return 0;
177 return err;
180 LONG GetDirEntryByPath(struct DirHandle *dh, STRPTR path, ULONG pathlen, struct DirEntry *de) {
181 LONG err;
182 ULONG len;
184 D(bug("[fat] looking for entry with path '%.*s' from dir at cluster %ld\n", pathlen, path, dh->ioh.first_cluster));
186 /* get back to the start of the dir */
187 RESET_DIRHANDLE(dh);
189 /* eat up leading slashes */
190 while (pathlen >= 0 && path[0] == '/') {
191 D(bug("[fat] leading '/', moving up to eat it\n"));
193 if ((err = GetParentDir(dh, de)) != 0)
194 return err;
196 path++;
197 pathlen--;
199 /* if we ate the whole path, bail out now with the current entry
200 * pointing to its parent dir */
201 if (pathlen == 0) {
202 D(bug("[fat] explicit request for parent dir, returning it\n"));
203 return 0;
207 /* eat up trailing slashes */
208 while (pathlen > 0) {
209 if (path[pathlen-1] != '/')
210 break;
211 pathlen--;
214 D(bug("[fat] now looking for entry with path '%.*s' from dir at cluster %ld\n", pathlen, path, dh->ioh.first_cluster));
216 /* each time around the loop we find one dir/file in the full path */
217 while (pathlen > 0) {
219 /* zoom forward and find the first dir separator */
220 for (len = 0; len < pathlen && path[len] != '/'; len++);
222 D(bug("[fat] remaining path is '%.*s' (%d bytes), current chunk is '%.*s' (%d bytes)\n", pathlen, path, pathlen, len, path, len));
224 /* if the first character is a /, or they've asked for '..', then we
225 * have to go up a level */
226 if (len == 0) {
228 /* get the parent dir, and bail if we've gone past it (ie we are
229 * the root) */
230 if ((err = GetParentDir(dh, de)) != 0)
231 break;
234 /* otherwise, we want to search the current directory for this name */
235 else {
236 if ((err = GetDirEntryByName(dh, path, len, de)) != 0)
237 return ERROR_OBJECT_NOT_FOUND;
239 /* if the current chunk if all the name we have left, then we found it */
240 if (len == pathlen) {
241 D(bug("[fat] found the entry, returning it\n"));
242 return 0;
245 /* more to do, so this entry had better be a directory */
246 if (!(de->e.entry.attr & ATTR_DIRECTORY)) {
247 D(bug("[fat] '%.*s' is not a directory, so can't go any further\n", len, path));
248 return ERROR_OBJECT_WRONG_TYPE;
251 InitDirHandle(dh->ioh.sb, FIRST_FILE_CLUSTER(de), dh);
254 /* move up the buffer */
255 path += len;
256 pathlen -= len;
258 /* a / here is either the path seperator or the directory we just went
259 * up. either way, we have to ignore it */
260 if (pathlen > 0 && path[0] == '/') {
261 path++;
262 pathlen--;
266 D(bug("[fat] empty path supplied, so naturally not found\n"));
268 return ERROR_OBJECT_NOT_FOUND;
271 LONG UpdateDirEntry(struct DirEntry *de) {
272 struct DirHandle dh;
273 LONG err = 0;
274 ULONG nwritten;
276 D(bug("[fat] writing dir entry %ld in dir starting at cluster %ld\n", de->index, de->cluster));
278 InitDirHandle(glob->sb, de->cluster, &dh);
280 err = WriteFileChunk(&(dh.ioh), de->pos, sizeof(struct FATDirEntry), (UBYTE *) &(de->e.entry), &nwritten);
281 if (err != 0) {
282 D(bug("[fat] dir entry update failed\n"));
283 return err;
286 ReleaseDirHandle(&dh);
288 return 0;
293 #define sb glob->sb
295 LONG FillFIB (struct ExtFileLock *fl, struct FileInfoBlock *fib) {
296 struct DirHandle dh;
297 struct DirEntry de;
298 LONG result = 0;
300 D(bug("\tFilling FIB data.\n"));
302 if (fl->dir_cluster == 0xffffffff) {
303 D(bug("\t\ttype: root directory\n"));
304 fib->fib_DirEntryType = ST_ROOT;
306 else if (fl->attr & ATTR_DIRECTORY) {
307 D(bug("\t\ttype: directory\n"));
308 fib->fib_DirEntryType = ST_USERDIR;
310 else {
311 D(bug("\t\ttype: file\n"));
312 fib->fib_DirEntryType = ST_FILE;
315 D(bug("\t\tsize: %ld\n", fl->size));
317 fib->fib_Size = fl->size;
318 fib->fib_NumBlocks = ((fl->size + (sb->clustersize - 1)) >> sb->clustersize_bits) << sb->cluster_sectors_bits;
319 fib->fib_EntryType = fib->fib_DirEntryType;
320 fib->fib_DiskKey = 0xfffffffflu; //fl->entry;
322 if (fib->fib_DirEntryType == ST_ROOT)
323 CopyMem(&sb->volume.create_time, &fib->fib_Date, sizeof(struct DateStamp));
324 else {
325 InitDirHandle(sb, fl->dir_cluster, &dh);
326 GetDirEntry(&dh, fl->dir_entry, &de);
327 ConvertDate(de.e.entry.write_date, de.e.entry.write_time, &fib->fib_Date);
328 ReleaseDirHandle(&dh);
331 memcpy(fib->fib_FileName, fl->name, 108);
332 D(bug("\t\tname (len %ld) %.*s\n", fib->fib_FileName[0], fib->fib_FileName[0], &(fib->fib_FileName[1])));
334 fib->fib_Protection = 0;
335 if (fl->attr & ATTR_READ_ONLY) fib->fib_Protection |= (FIBF_DELETE | FIBF_WRITE);
336 if (fl->attr & ATTR_ARCHIVE) fib->fib_Protection |= FIBF_ARCHIVE;
338 fib->fib_Comment[0] = '\0';
340 return result;