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>
18 #include "fat_protos.h"
20 #if defined(DEBUG_FULL) && DEBUG_FULL != 0
25 #include <aros/debug.h>
27 #define FREE_CLUSTER_CHAIN(sb,cl) \
30 while (cluster >= 0 && cluster < sb->eoc_mark) { \
31 ULONG next_cluster = GET_NEXT_CLUSTER(sb, cluster); \
32 SET_NEXT_CLUSTER(sb, cluster, 0); \
33 cluster = next_cluster; \
38 * this takes a full path and moves to the directory that would contain the
39 * last file in the path. ie calling with (dh, "foo/bar/baz", 11) will move to
40 * directory "foo/bar" under the dir specified by dh. dh will become a handle
41 * to the new dir. after the return name will be "baz" and namelen will be 3
43 static LONG
MoveToSubdir(struct DirHandle
*dh
, UBYTE
**pname
, ULONG
*pnamelen
) {
45 UBYTE
*name
= *pname
, *base
;
46 ULONG namelen
= *pnamelen
, baselen
;
49 /* we break the given name into two pieces - the name of the containing
50 * dir, and the name of the new dir to go within it. if the base ends up
51 * empty, then we just use the dirlock */
55 if (base
[baselen
-1] != '/')
60 if (base
[baselen
-1] == '/')
65 name
= &base
[baselen
];
67 D(bug("[fat] base is '%.*s', name is '%.*s'\n", baselen
, base
, namelen
, name
));
70 if ((err
= GetDirEntryByPath(dh
, base
, baselen
, &de
)) != 0) {
71 D(bug("[fat] base not found\n"));
75 if ((err
= InitDirHandle(dh
->ioh
.sb
, FIRST_FILE_CLUSTER(&de
), dh
)) != 0)
86 * obtains a lock on the named file under the given dir. this is the service
87 * routine for DOS Open() (ie FINDINPUT/FINDOUTPUT/FINDUPDATE) and as such may
88 * only return a lock on a file, never on a dir.
90 LONG
OpOpenFile(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, LONG action
, struct ExtFileLock
**filelock
) {
92 struct ExtFileLock
*lock
;
96 D(bug("[fat] opening file '%.*s' in dir at cluster %ld, action %s\n",
97 namelen
, name
, dirlock
!= NULL
? dirlock
->ioh
.first_cluster
: 0,
98 action
== ACTION_FINDINPUT
? "FINDINPUT" :
99 action
== ACTION_FINDOUTPUT
? "FINDOUTPUT" :
100 action
== ACTION_FINDUPDATE
? "FINDUPDATE" : "[unknown]"));
102 /* no filename means they're trying to open whatever dirlock is (which
103 * despite the name may not actually be a dir). since there's already an
104 * extant lock, its never going be possible to get an exclusive lock, so
105 * this will only work for FINDINPUT (read-only) */
107 D(bug("[fat] trying to copy passed dir lock\n"));
109 if (action
!= ACTION_FINDINPUT
) {
110 D(bug("[fat] can't copy lock for write (exclusive)\n"));
111 return ERROR_OBJECT_IN_USE
;
114 /* dirs can't be opened */
115 if (dirlock
== NULL
|| dirlock
->gl
->attr
& ATTR_DIRECTORY
) {
116 D(bug("[fat] dir lock is a directory, which can't be opened\n"));
117 return ERROR_OBJECT_WRONG_TYPE
;
120 /* its a file, just copy the lock */
121 return CopyLock(dirlock
, filelock
);
125 err
= LockFileByName(dirlock
, name
, namelen
, action
== ACTION_FINDINPUT
? SHARED_LOCK
: EXCLUSIVE_LOCK
, &lock
);
129 D(bug("[fat] found existing file\n"));
131 /* can't open directories */
132 if (lock
->gl
->attr
& ATTR_DIRECTORY
) {
133 D(bug("[fat] its a directory, can't open it\n"));
135 return ERROR_OBJECT_WRONG_TYPE
;
138 /* INPUT/UPDATE use the file as/is */
139 if (action
!= ACTION_FINDOUTPUT
) {
140 D(bug("[fat] returning the lock\n"));
145 /* whereas OUTPUT truncates it */
146 D(bug("[fat] handling FINDOUTPUT, so truncating the file\n"));
148 /* update the dir entry to make the file empty */
149 InitDirHandle(lock
->ioh
.sb
, lock
->gl
->dir_cluster
, &dh
);
150 GetDirEntry(&dh
, lock
->gl
->dir_entry
, &de
);
151 de
.e
.entry
.first_cluster_lo
= de
.e
.entry
.first_cluster_hi
= 0;
152 de
.e
.entry
.file_size
= 0;
155 D(bug("[fat] set first cluster and size to 0 in directory entry\n"));
157 /* free the clusters */
158 FREE_CLUSTER_CHAIN(lock
->ioh
.sb
, lock
->ioh
.first_cluster
);
159 lock
->gl
->first_cluster
= lock
->ioh
.first_cluster
= 0xffffffff;
160 RESET_HANDLE(&lock
->ioh
);
163 D(bug("[fat] file truncated, returning the lock\n"));
165 /* file is empty, go */
171 /* any error other than "not found" should be taken as-is */
172 if (err
!= ERROR_OBJECT_NOT_FOUND
)
175 /* not found. for INPUT or UPDATE, we bail out */
176 if (action
!= ACTION_FINDOUTPUT
) {
177 D(bug("[fat] file not found, and not creating it\n"));
178 return ERROR_OBJECT_NOT_FOUND
;
181 D(bug("[fat] trying to create '%.*s'\n", namelen
, name
));
183 /* otherwise its time to create the file. get a handle on the passed dir */
184 if ((err
= InitDirHandle(glob
->sb
, dirlock
!= NULL
? dirlock
->ioh
.first_cluster
: 0, &dh
)) != 0)
187 /* get down to the correct subdir */
188 if ((err
= MoveToSubdir(&dh
, &name
, &namelen
)) != 0) {
189 ReleaseDirHandle(&dh
);
193 /* create the entry */
194 if ((err
= CreateDirEntry(&dh
, name
, namelen
, 0, 0, &de
)) != 0) {
195 ReleaseDirHandle(&dh
);
199 /* lock the new file */
200 err
= LockFile(de
.cluster
, de
.index
, EXCLUSIVE_LOCK
, filelock
);
203 ReleaseDirHandle(&dh
);
206 D(bug("[fat] returning lock on new file\n"));
211 /* find the named file in the directory referenced by dirlock, and delete it.
212 * if the file is a directory, it will only be deleted if its empty */
213 LONG
OpDeleteFile(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
) {
215 struct ExtFileLock
*lock
;
221 D(bug("[fat] deleting file '%.*s' in directory at cluster %ld\n", namelen
, name
, dirlock
!= NULL
? dirlock
->ioh
.first_cluster
: 0));
223 /* obtain a lock on the file. we need an exclusive lock as we don't want
224 * to delete the file if its in use */
225 if ((err
= LockFileByName(dirlock
, name
, namelen
, EXCLUSIVE_LOCK
, &lock
)) != 0) {
226 D(bug("[fat] couldn't obtain exclusive lock on named file\n"));
230 /* if its a directory, we have to make sure its empty */
231 if (lock
->gl
->attr
& ATTR_DIRECTORY
) {
232 D(bug("[fat] file is a directory, making sure its empty\n"));
234 if ((err
= InitDirHandle(lock
->ioh
.sb
, lock
->ioh
.first_cluster
, &dh
)) != 0) {
239 /* loop over the entries, starting from entry 2 (the first real
240 * entry). skipping unused ones, we look for the end-of-directory
241 * marker. if we find it, the directory is empty. if we find a real
242 * name, its in use */
244 while ((err
= GetDirEntry(&dh
, de
.index
+1, &de
)) == 0) {
245 /* skip unused entries */
246 if (de
.e
.entry
.name
[0] == 0xe5)
249 /* end of directory, its empty */
250 if (de
.e
.entry
.name
[0] == 0x00)
253 /* otherwise the directory is still in use */
254 D(bug("[fat] directory still has files in it, won't delete it\n"));
256 ReleaseDirHandle(&dh
);
258 return ERROR_DIRECTORY_NOT_EMPTY
;
261 ReleaseDirHandle(&dh
);
264 /* open the containing directory */
265 if ((err
= InitDirHandle(lock
->ioh
.sb
, lock
->gl
->dir_cluster
, &dh
)) != 0) {
270 /* get the entry for the file */
271 GetDirEntry(&dh
, lock
->gl
->dir_entry
, &de
);
273 /* calculate the short name checksum before we trample on the name */
274 CALC_SHORT_NAME_CHECKSUM(de
.e
.entry
.name
, checksum
);
276 D(bug("[fat] short name checksum is 0x%02x\n", checksum
));
278 /* mark the short entry free */
279 de
.e
.entry
.name
[0] = 0xe5;
282 D(bug("[fat] deleted short name entry\n"));
284 /* now we loop over the previous entries, looking for matching long name
285 * entries and killing them */
287 while ((err
= GetDirEntry(&dh
, de
.index
-1, &de
)) == 0) {
289 /* see if this is a matching long name entry. if its not, we're done */
290 if (!((de
.e
.entry
.attr
& ATTR_LONG_NAME_MASK
) == ATTR_LONG_NAME
) ||
291 (de
.e
.long_entry
.order
& ~0x40) != order
||
292 de
.e
.long_entry
.checksum
!= checksum
)
297 de
.e
.entry
.name
[0] = 0xe5;
303 D(bug("[fat] deleted %ld long name entries\n", order
-1));
305 /* directory entries are free */
306 ReleaseDirHandle(&dh
);
308 /* now free the clusters the file was using */
309 FREE_CLUSTER_CHAIN(lock
->ioh
.sb
, lock
->ioh
.first_cluster
);
311 /* this lock is now completely meaningless */
314 D(bug("[fat] deleted '%.*s'\n", namelen
, name
));
319 LONG
OpCreateDir(struct ExtFileLock
*dirlock
, UBYTE
*name
, ULONG namelen
, struct ExtFileLock
**newdirlock
) {
322 struct DirHandle dh
, sdh
;
323 struct DirEntry de
, sde
;
325 D(bug("[fat] creating directory '%.*s' in directory at cluster %ld\n", namelen
, name
, dirlock
!= NULL
? dirlock
->ioh
.first_cluster
: 0));
327 /* get a handle on the passed dir */
328 if ((err
= InitDirHandle(glob
->sb
, dirlock
!= NULL
? dirlock
->ioh
.first_cluster
: 0, &dh
)) != 0)
331 /* get down to the correct subdir */
332 if ((err
= MoveToSubdir(&dh
, &name
, &namelen
)) != 0) {
333 ReleaseDirHandle(&dh
);
337 /* now see if the wanted name is in this dir. if it exists, then we do
339 if ((err
= GetDirEntryByName(&dh
, name
, namelen
, &de
)) == 0) {
340 D(bug("[fat] name exists, can't do anything\n"));
341 ReleaseDirHandle(&dh
);
342 return ERROR_OBJECT_EXISTS
;
345 /* find a free cluster to store the dir in */
346 if ((err
= FindFreeCluster(dh
.ioh
.sb
, &cluster
)) != 0) {
347 ReleaseDirHandle(&dh
);
352 SET_NEXT_CLUSTER(dh
.ioh
.sb
, cluster
, dh
.ioh
.sb
->eoc_mark
);
354 D(bug("[fat] allocated cluster %ld for directory\n", cluster
));
356 /* create the entry, pointing to the new cluster */
357 if ((err
= CreateDirEntry(&dh
, name
, namelen
, ATTR_DIRECTORY
, cluster
, &de
)) != 0) {
358 /* deallocate the cluster */
359 SET_NEXT_CLUSTER(dh
.ioh
.sb
, cluster
, 0);
361 ReleaseDirHandle(&dh
);
365 /* now get a handle on the new directory */
366 InitDirHandle(dh
.ioh
.sb
, cluster
, &sdh
);
368 /* create the dot entry. its a direct copy of the just-created entry, but
369 * with a different name */
370 GetDirEntry(&sdh
, 0, &sde
);
371 CopyMem(&de
.e
.entry
, &sde
.e
.entry
, sizeof(struct FATDirEntry
));
372 CopyMem(". ", &sde
.e
.entry
.name
, 11);
373 UpdateDirEntry(&sde
);
375 /* create the dot-dot entry. again, a copy, with the cluster pointer setup
376 * to point to the parent */
377 GetDirEntry(&sdh
, 1, &sde
);
378 CopyMem(&de
.e
.entry
, &sde
.e
.entry
, sizeof(struct FATDirEntry
));
379 CopyMem(".. ", &sde
.e
.entry
.name
, 11);
380 sde
.e
.entry
.first_cluster_lo
= dh
.ioh
.first_cluster
& 0xffff;
381 sde
.e
.entry
.first_cluster_hi
= dh
.ioh
.first_cluster
>> 16;
382 UpdateDirEntry(&sde
);
384 /* put an empty entry at the end to mark end of directory */
385 GetDirEntry(&sdh
, 2, &sde
);
386 memset(&sde
.e
.entry
, 0, sizeof(struct FATDirEntry
));
387 UpdateDirEntry(&sde
);
389 /* new dir created */
390 ReleaseDirHandle(&sdh
);
392 /* now obtain a lock on the new dir */
393 err
= LockFile(de
.cluster
, de
.index
, SHARED_LOCK
, newdirlock
);
396 ReleaseDirHandle(&dh
);
401 LONG
OpRead(struct ExtFileLock
*lock
, UBYTE
*data
, ULONG want
, ULONG
*read
) {
404 D(bug("[fat] request to read %ld bytes from file pos %ld\n", want
, lock
->pos
));
406 if (want
+ lock
->pos
> lock
->gl
->size
) {
407 want
= lock
->gl
->size
- lock
->pos
;
408 D(bug("[fat] full read would take us past end-of-file, adjusted want to %ld bytes\n", want
));
411 if ((err
= ReadFileChunk(&(lock
->ioh
), lock
->pos
, want
, data
, read
)) == 0) {
413 D(bug("[fat] read %ld bytes, new file pos is %ld\n", *read
, lock
->pos
));
419 LONG
OpWrite(struct ExtFileLock
*lock
, UBYTE
*data
, ULONG want
, ULONG
*written
) {
421 BOOL update_entry
= FALSE
;
425 D(bug("[fat] request to write %ld bytes to file pos %ld\n", want
, lock
->pos
));
427 /* if this is the first write, make a note as we'll have to store the
428 * first cluster in the directory entry later */
429 if (lock
->ioh
.first_cluster
== 0xffffffff)
432 if ((err
= WriteFileChunk(&(lock
->ioh
), lock
->pos
, want
, data
, written
)) == 0) {
433 lock
->pos
+= *written
;
434 if (lock
->pos
> lock
->gl
->size
) {
435 lock
->gl
->size
= lock
->pos
;
439 D(bug("[fat] wrote %ld bytes, new file pos is %ld, size is %ld\n", *written
, lock
->pos
, lock
->gl
->size
));
442 D(bug("[fat] updating dir entry, first cluster is %ld, size is %ld\n", lock
->ioh
.first_cluster
, lock
->gl
->size
));
444 lock
->gl
->first_cluster
= lock
->ioh
.first_cluster
;
446 InitDirHandle(lock
->ioh
.sb
, lock
->gl
->dir_cluster
, &dh
);
447 GetDirEntry(&dh
, lock
->gl
->dir_entry
, &de
);
449 de
.e
.entry
.file_size
= lock
->gl
->size
;
450 de
.e
.entry
.first_cluster_lo
= lock
->gl
->first_cluster
& 0xffff;
451 de
.e
.entry
.first_cluster_hi
= lock
->gl
->first_cluster
>> 16;
455 ReleaseDirHandle(&dh
);