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.
13 #include <exec/types.h>
15 #include <proto/exec.h>
22 #include "fat_protos.h"
24 #define RESET_DIRHANDLE(dh) \
26 RESET_HANDLE(&(dh->ioh)); \
27 dh->cur_index = 0xffffffff; \
30 LONG
InitDirHandle(struct FSSuper
*sb
, ULONG cluster
, struct DirHandle
*dh
) {
34 dh
->ioh
.first_cluster
= sb
->rootdir_cluster
;
35 dh
->ioh
.first_sector
= sb
->rootdir_sector
;
38 dh
->ioh
.first_cluster
= cluster
;
39 dh
->ioh
.first_sector
= 0;
46 D(bug("[fat] initialised dir handle, first cluster is %ld, first sector is %ld\n", dh
->ioh
.first_cluster
, dh
->ioh
.first_sector
));
51 LONG
ReleaseDirHandle(struct DirHandle
*dh
) {
56 LONG
GetDirEntry(struct DirHandle
*dh
, ULONG index
, struct DirEntry
*de
) {
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 */
70 de
->cluster
= dh
->ioh
.first_cluster
;
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
);
77 D(bug("[fat] dir entry lookup failed\n"));
81 /* remember where we are for GetNextDirEntry() */
82 dh
->cur_index
= index
;
88 LONG
GetNextDirEntry(struct DirHandle
*dh
, struct DirEntry
*de
) {
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
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
));
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
));
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
));
113 return ERROR_OBJECT_NOT_FOUND
;
116 D(bug("[fat] returning entry %ld\n", dh
->cur_index
));
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
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
;
145 InitDirHandle(dh
->ioh
.sb
, FIRST_FILE_CLUSTER(de
), dh
);
150 LONG
GetDirEntryByName(struct DirHandle
*dh
, STRPTR name
, ULONG namelen
, struct DirEntry
*de
) {
155 D(bug("[fat] looking for dir entry with name '%.*s'\n", namelen
, name
));
157 /* start at the start */
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
));
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
));
180 LONG
GetDirEntryByPath(struct DirHandle
*dh
, STRPTR path
, ULONG pathlen
, struct DirEntry
*de
) {
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 */
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)
199 /* if we ate the whole path, bail out now with the current entry
200 * pointing to its parent dir */
202 D(bug("[fat] explicit request for parent dir, returning it\n"));
207 /* eat up trailing slashes */
208 while (pathlen
> 0) {
209 if (path
[pathlen
-1] != '/')
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 */
228 /* get the parent dir, and bail if we've gone past it (ie we are
230 if ((err
= GetParentDir(dh
, de
)) != 0)
234 /* otherwise, we want to search the current directory for this name */
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"));
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 */
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] == '/') {
266 D(bug("[fat] empty path supplied, so naturally not found\n"));
268 return ERROR_OBJECT_NOT_FOUND
;
271 LONG
UpdateDirEntry(struct DirEntry
*de
) {
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
);
282 D(bug("[fat] dir entry update failed\n"));
286 ReleaseDirHandle(&dh
);
295 LONG
FillFIB (struct ExtFileLock
*fl
, struct FileInfoBlock
*fib
) {
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
;
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
));
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';