Author: Virgil Dupras <email@example.com>
Date: Tue, 19 Jul 2022 22:17:57 -0400
Introduce the concept of "File/Directory ID" in the File API
4 files changed, 60 insertions(+), 6 deletions(-)
diff --git a/fs/doc/file.txt b/fs/doc/file.txt
@@ -21,12 +21,38 @@ fclose ( hdl -- )
On top of that, the File API also define global aliases in which the current
active filesystem will plug itself:
-fopen ( path -- hdl )
+fchild ( dirid name -- id )
+ Enumerate the contents of directory "dirid" and look for name "name". If
+ found, returns the ID of the child (either a file or directory), otherwise,
+ return 0. See below for the concept of "ID".
+fopen ( id -- hdl )
Open file at path and return a handle through which other file-related word
identify the target file. Once a file isn't used anymore, it should be
closed with fclose. Aborts on error.
-fnewfile ( path -- direntry )
- Create a new empty file at path. Errors out if path's parent is unreachabe or
- if path already exists.
+fnewfile ( dirid name -- id )
+ Create a new empty file named "name" in "dirid" and return the ID of the new
+ file. Aborts on error.
+## File ID design considerations
+When we refer to a file or directory ID, we refer to a single 32-bit number
+uniquely identifying the file or directory in the volume. From this number, we
+must be able to find the file or directory in the volume so that we can open it
+or enumerate it. The ID of the root directory is always zero.
+That IDs are 32-bit make their manipulation straightforward. One might fear that
+32-bit is not enough to identify all elements of big filesystems.
+On many filesystems, 32-bit is enough. For example, in ext4, inodes always fit
+32-bit, even in 64-bit mode.
+For FAT32, it's another matter. The straightforward way to derive an ID from an
+element is to return the offset of its DirEntry and divide it by 32. This means
+that this ID scheme could only address 64GB volumes.
+This would mean that the File API would be broken on a subset of filesystems.
+Does it mean that Dusk couldn't read them? No, only that file/dir enumeration
+would have to go through FS-specific tools. I think that this incovenience is
+worth it if it means an overall simpler API.
diff --git a/fs/fs/fat.fs b/fs/fs/fat.fs
@@ -10,6 +10,14 @@
\ For now, this FS only supports FAT16 and FAT12.
\ Like any filesystem in Dusk, path separator char is "/".
+\ A "direntry" is the address of a DirEntry structure in memory. These
+\ references are short-lived because they're an address in the FAT sector buffer
+\ which is very very often being overwritten.
+\ File and directory IDs in FAT are the offset, on disk, of their corresponding
$ffff const EOC
diff --git a/fs/fs/fatlo.fs b/fs/fs/fatlo.fs
@@ -94,6 +94,12 @@ here const )fnbuf
: upcase ( c -- c ) dup 'a' - 26 < if $df and then ;
: fnbufclr fnbuf( FNAMESZ SPC fill ;
+: fnbuf! ( name -- )
+ A>r fnbufclr c@+ >r >A fnbuf( begin ( dst )
+ Ac@+ dup '.' = if
+ 2drop fnbuf( 8 + else
+ upcase swap c!+ then ( dst+1 )
+ dup )fnbuf = if leave then next drop r>A ;
: _ ( -- direntry-or-0 )
fatbuf( begin ( a )
@@ -114,6 +120,18 @@ here const )fnbuf
1 FirstRootDirSecNum RootDirSectors then ( cluster sec cnt )
readsector ( cluster ) to bufcluster ;
+\ Get DirEntry address from FS ID "id"
+: getdirentry ( id -- direntry )
+ ?dup if
+ BPB_BytsPerSec /mod ( offset sec ) 1 readsector ( off ) fatbuf( +
+ else rootdirentry( then ;
+\ Get ID for direntry
+: getid ( direntry -- id ) fatbuf( - bufsec BPB_BytsPerSec * + ;
+: fatchild ( dirid name -- id )
+ fnbuf! getdirentry readdir findindir getid ;
\ Find the parent directory of "path", that is, go up directories in path until
\ the last element is reached, but don't look for that last element, return
\ directory's direntry instead. As this word returns, fnbuf( will be set with
@@ -171,8 +189,7 @@ here const )fnbuf
: FCUR_buf( ( fcur -- a ) 44 + ;
: FCUR_)buf ( fcur -- a ) FCUR_buf( ClusterSize + ;
: FCUR_bufpos ( fcur -- a ) dup FCUR_pos ClusterSize mod swap FCUR_buf( + ;
-: FCUR_dirent ( fcur -- dirent )
- 32 + @ BPB_BytsPerSec /mod ( offset sec ) 1 readsector ( off ) fatbuf( + ;
+: FCUR_dirent ( fcur -- dirent ) 32 + @ getdirentry ;
: FCUR_cluster0 ( fcur -- cl ) FCUR_dirent DIR_Cluster ;
create fcursors( FCursorSize FCURSORCNT * allot0
diff --git a/fs/tests/fs/fat.fs b/fs/tests/fs/fat.fs
@@ -34,4 +34,7 @@ S" /newfile" fatopen ( fc )
dup FCUR_cluster0 0 #eq \ no cluster allocated yet
dup S" 42" c@+ rot fatwritebuf 2 #eq ( fc ) fclose
f<< /newfile 42 #eq
+\ temporary tests until I tie everything together in the File API
+0 S" lib" fatchild S" str.fs" fatchild #