Author: wmb
Date: 2009-12-18 07:54:56 +0100 (Fri, 18 Dec 2009)
New Revision: 1632
Modified:
ofw/fs/ext2fs/dir.fth
ofw/fs/ext2fs/inode.fth
ofw/fs/ext2fs/methods.fth
Log:
OLPC trac 6210 - improvements to ext2 filesystem driver, particularly
to implement $rename. In the process I checked several other functions
carefully.
Modified: ofw/fs/ext2fs/dir.fth
===================================================================
--- ofw/fs/ext2fs/dir.fth 2009-12-18 03:44:13 UTC (rev 1631)
+++ ofw/fs/ext2fs/dir.fth 2009-12-18 06:54:56 UTC (rev 1632)
@@ -10,6 +10,13 @@
variable diroff
variable totoff
+\ Information that we need about the working file/directory
+\ The working file changes at each level of a path search
+
+0 instance value wd-inum \ Inumber of directory to search
+0 instance value wf-inum \ Inumber of file or directory found
+0 instance value wf-type \ Type - 4 for directory, d# 10 for symlink, etc
+
: get-dirblk ( -- end? )
lblk# bsize * file-size >= if true exit then
lblk# >pblk# to dir-block#
@@ -28,14 +35,14 @@
: >reclen ( name-length -- record-length ) 8 + 4 round-up ;
: dirent-inode@ ( -- n ) dirent int@ ;
-: dirent-inode! ( n -- ) dirent int! ;
+: dirent-inode! ( n -- ) dirent int! update ;
: dirent-len@ ( -- n ) dirent la1+ short@ ;
-: dirent-len! ( n -- ) dirent la1+ short! ;
+: dirent-len! ( n -- ) dirent la1+ short! update ;
: dirent-nameadr ( -- adr ) dirent la1+ 2 wa+ ;
: dirent-namelen@ ( -- b ) dirent la1+ wa1+ c@ ;
-: dirent-namelen! ( b -- ) dirent la1+ wa1+ c! ;
+: dirent-namelen! ( b -- ) dirent la1+ wa1+ c! update ;
: dirent-type@ ( -- b ) dirent la1+ wa1+ ca1+ c@ ;
-: dirent-type! ( b -- ) dirent la1+ wa1+ ca1+ c! ;
+: dirent-type! ( b -- ) dirent la1+ wa1+ ca1+ c! update ;
: dirent-reclen ( -- n ) dirent-namelen@ >reclen ;
: lblk#++ ( -- ) lblk# 1+ to lblk# ;
@@ -44,7 +51,7 @@
diroff @ totoff @ lblk# inode#
;
: restore-dirent ( diroff totoff lblk# inode# -- )
- to inode# to lblk# totoff ! diroff !
+ set-inode to lblk# totoff ! diroff !
get-dirblk drop
;
@@ -59,35 +66,28 @@
false
;
-\ **** From directory, get handle of the file or subdir that it references
-\ For Unix, file handle is the inode #
-: file-handle ( -- i# ) dirent-inode@ ;
-
\ **** From directory, get name of file
: file-name ( -- adr len )
dirent la1+ wa1+ dup wa1+ ( len-adr name-adr )
swap c@ ( adr len )
;
-\
-\ high-level routines
-\
-\ After this point, the code should be independent of the disk format!
+: +link-count ( increment -- ) link-count + link-count! ;
-: init-inode ( mode inode# -- )
- inode >r ( mode r: inode-adr )
- r@ /inode erase ( mode r: inode-adr )
- r@ short! ( r: inode-adr )
- time&date >unix-seconds ( time r: inode-adr )
- dup r@ 2 la+ int! ( time r: inode-adr ) \ set access time
- dup r@ 3 la+ int! ( time r: inode-adr ) \ set creation time
- r@ 4 la+ int! ( r: inode-adr ) \ set modification time
- 1 r@ d# 13 wa+ short! ( r: inode-adr ) \ set links_count to 1
- update ( r: inode-adr )
- r> drop ( )
+: new-inode ( mode -- inode# )
+ alloc-inode set-inode
+ 0 +i /inode erase ( mode )
+ file-attr! ( )
+ time&date >unix-seconds ( time )
+ dup atime! ( time ) \ set access time
+ dup ctime! ( time ) \ set creation time
+ mtime! ( ) \ set modification time
+ 1 link-count! ( ) \ set links_count to 1
+ inode# ( inode# )
;
\ On entry:
+\ inode# refers to the directory block's inode
\ dir-block# is the physical block number of the first directory block
\ diroff @ is 0
\ On successful exit:
@@ -136,14 +136,46 @@
(last-dirent) drop
dirent-len@ dirent-reclen -
;
-: set-dirent ( name$ type rec-len file-type inode# -- )
- dirent int! ( name$ rec-len )
+0 constant unknown-type
+1 constant regular-type
+2 constant dir-type
+3 constant chrdev-type
+4 constant blkdev-type
+5 constant fifo-type
+6 constant sock-type
+7 constant symlink-type
+
+create dir-types
+ unknown-type c, \ 0
+ fifo-type c, \ 1
+ chrdev-type c, \ 2
+ unknown-type c, \ 3
+ dir-type c, \ 4
+ unknown-type c, \ 5
+ blkdev-type c, \ 6
+ unknown-type c, \ 7
+ regular-type c, \ 8
+ unknown-type c, \ 9
+ symlink-type c, \ 10
+ unknown-type c, \ 11
+ sock-type c, \ 12
+ unknown-type c, \ 13
+ unknown-type c, \ 14
+ unknown-type c, \ 15
+
+: inode>dir-type ( -- dir-type )
+ filetype d# 12 rshift ( index )
+ dir-types + c@
+;
+
+: fill-dirent ( name$ rec-len inode# -- )
+ dup set-inode 1 +link-count ( name$ rec-len inode# dir-type )
\ XXX this should be contingent upon EXT2_FEATURE_INCOMPAT_FILETYPE
- dirent-type! ( name$ )
- dirent-len! ( name$ )
- dup dirent-namelen! ( name$ )
- dirent-nameadr swap move ( )
- update
+ inode>dir-type dirent-type! ( name$ rec-len inode# )
+ dirent-inode! ( name$ rec-len )
+ dirent-len! ( name$ )
+ dup dirent-namelen! ( name$ )
+ dirent-nameadr swap move ( )
;
: to-previous-dirent ( -- )
@@ -157,8 +189,24 @@
diroff @ swap - totoff +! ( )
;
+\ Delete the currently selected inode. Does not affect the directory entry, if any.
+: idelete ( -- )
+ delete-blocks
+ \ clear #blks-held, link-count, etc.
+ 0 +i /inode 6 /l* /string erase
+
+ \ delete inode, and set its deletion time.
+ time&date >unix-seconds dtime!
+ inode# free-inode
+;
+
\ delete directory entry at diroff
-: del-dirent ( -- error? )
+: dirent-unlink ( -- )
+ dirent-inode@ set-inode -1 +link-count
+
+ \ Release the inode if it has no more links
+ link-count 0<= if idelete then
+
diroff @ if
\ Not first dirent in block; coalesce with previous
dirent-len@ ( deleted-len )
@@ -170,31 +218,40 @@
\ First dirent in block; zap its inode
0 dirent-inode!
then
- update
- false
;
-1 constant regular-type
-2 constant dir-type
-7 constant symlink-type
+\ The argument inode# means the inode to which the new directory entry
+\ will refer. The inode of the containing directory is in the *value*
+\ named inode#
-: ($create) ( name$ mode -- error? )
- >r ( name$ r: mode )
+: new-dirent ( name$ inode# -- error? )
+ >r ( name$ r: inode# )
\ check for room in the directory, and expand it if necessary
- dup >reclen no-dir-space? if ( name$ new-reclen r: mode )
+ dup >reclen no-dir-space? if ( name$ new-reclen r: inode# )
\ doesn't fit, allocate more room
- bsize ( name$ bsize r: mode )
- append-block
- lblk#++ get-dirblk drop
- then ( name$ rec-len r: mode )
+ bsize ( name$ bsize r: inode# )
+ append-block ( name$ bsize r: inode# )
+ lblk#++ get-dirblk if ( name$ bsize r: inode# )
+ r> 4drop ( )
+ true exit ( -- true )
+ then ( name$ bsize r: inode# )
+ then ( name$ rec-len r: inode# )
\ At this point dirent points to the place for the new dirent
- \ XXX handle symlinks!
- r@ h# f000 and h# 4000 = if dir-type else regular-type then ( name$ rec-len type r: mode )
- alloc-inode set-dirent false ( error? r: mode )
- r> dirent-inode@ init-inode
+ r> fill-dirent ( )
+ false ( error? )
;
+: ($create) ( name$ mode -- error? )
+ new-inode ( name$ inode# )
+
+ \ new-inode changed the value of inode#; we must restore it so
+ \ new-dirent can find info about the containing diretory
+ wd-inum set-inode ( name$ inode# )
+
+ new-dirent ( error? )
+;
+
: linkpath ( -- a )
file-acl if bsize 9 rshift else 0 then ( #acl-blocks )
#blks-held <> if \ long symbolic link path
@@ -204,15 +261,6 @@
then
;
-\ --
-
-\ Information that we need about the working file/directory
-\ The working file changes at each level of a path search
-
-0 instance value wd-inum \ Inumber of directory to search
-0 instance value wf-inum \ Inumber of file or directory found
-0 instance value wf-type \ Type - 4 for directory, d# 10 for symlink, etc
-
char \ instance value delimiter
defer $resolve-path
@@ -231,13 +279,9 @@
then ( name$ )
;
-: set-inode ( inode# -- )
- to inode#
- 0 to lblk#
-;
-
: first-dirent ( dir-inode# -- end? ) \ Adapted from (init-dir)
set-inode
+ 0 to lblk#
get-dirblk if true exit then
diroff off totoff off ( )
false ( )
@@ -273,7 +317,7 @@
;
: symlink-resolution$ ( inum -- data$ )
- to inode#
+ set-inode
linkpath dup cstrlen
;
@@ -352,59 +396,24 @@
\ Returns true if inode# refers to a directory that is empty
\ Side effect - changes dirent context
-: empty-dir? ( -- empty-dir? )
+: empty-dir? ( inode# -- empty-dir? )
+ set-inode
+
dir? 0= if false exit then
- file-handle first-dirent if false exit then \ Should be pointing to "." entry
+ inode# first-dirent if false exit then \ Should be pointing to "." entry
next-dirent if false exit then \ Should be point to ".." entry
next-dirent ( end? ) \ The rest should be empty
;
-\ Delete a file, given its inode. Does not affect the directory entry, if any.
-: idelete ( inode# -- )
- to inode#
- delete-blocks
- \ clear #blks-held, link-count, etc.
- inode# inode /inode 6 /l* /string erase
-
- \ delete inode, and set its deletion time.
- time&date >unix-seconds ( time )
- inode# inode 5 la+ int! update
- inode# free-inode
-;
-
-\ Delete the file at dirent
-: (delete-file) ( -- error? )
- file-handle 0= if true exit then
-
- inode# >r
- file-handle set-inode
- file? 0= if r> drop true exit then \ XXX handle symlinks
-
- \ is this the only link?
- link-count dup 2 u< if
- drop
- inode# idelete
- else
- 1- link-count!
- then
-
- r> to inode#
- \ delete directory entry
- del-dirent ( error? )
-;
-: (delete-files) ( -- ) \ from current directory. used by rmdir
- begin (delete-file) until
-;
-
external
\ directory information
: file-info ( id -- false | id' s m h d m y len attributes name$ true )
- inode# >r file-handle to inode# ( id )
- 1+ file-sec unix-seconds> file-size file-attr file-name true
- r> to inode#
+ inode# >r dirent-inode@ set-inode ( id )
+ 1+ mtime unix-seconds> file-size file-attr file-name true
+ r> set-inode
;
\ Deleted files at the beginning of a directory block have inode=0
@@ -412,7 +421,7 @@
dup if
begin
next-dirent 0= while
- file-handle if file-info exit then
+ dirent-inode@ if file-info exit then
repeat
drop false
Modified: ofw/fs/ext2fs/inode.fth
===================================================================
--- ofw/fs/ext2fs/inode.fth 2009-12-18 03:44:13 UTC (rev 1631)
+++ ofw/fs/ext2fs/inode.fth 2009-12-18 06:54:56 UTC (rev 1632)
@@ -4,6 +4,7 @@
decimal
0 instance value inode#
+: set-inode ( inode# -- ) to inode# ;
: ipb ( -- n ) bsize /inode / ;
: itob ( i# -- offset block# )
@@ -18,10 +19,22 @@
: ind ( n -- ) inode /inode dump ;
: +i ( n -- ) inode# inode + ;
: file-attr ( -- attributes ) 0 +i short@ ;
+: file-attr! ( attributes -- ) 0 +i short! update ;
+: uid ( -- uid ) 2 +i short@ ;
+: uid! ( uid -- ) 2 +i short! update ;
: filetype ( -- type ) file-attr h# f000 and ;
: file-size ( -- n ) 4 +i int@ ;
: file-size! ( n -- ) 4 +i int! update ;
-: file-sec ( -- seconds ) 16 +i int@ ; \ mtime -- is that what we want?
+: atime ( -- seconds ) 8 +i int@ ;
+: atime! ( seconds -- ) 8 +i int! update ;
+: ctime ( -- seconds ) 12 +i int@ ;
+: ctime! ( seconds -- ) 12 +i int! update ;
+: mtime ( -- seconds ) 16 +i int@ ;
+: mtime! ( seconds -- ) 16 +i int! update ;
+: dtime ( -- seconds ) 20 +i int@ ;
+: dtime! ( seconds -- ) 20 +i int! update ;
+: gid ( -- gid ) 24 +i short@ ;
+: gid! ( gid -- ) 24 +i short! update ;
: link-count ( -- n ) 26 +i short@ ;
: link-count! ( n -- ) 26 +i short! update ;
: #blks-held ( -- n ) 28 +i int@ ;
Modified: ofw/fs/ext2fs/methods.fth
===================================================================
--- ofw/fs/ext2fs/methods.fth 2009-12-18 03:44:13 UTC (rev 1631)
+++ ofw/fs/ext2fs/methods.fth 2009-12-18 06:54:56 UTC (rev 1632)
@@ -15,50 +15,97 @@
: $mkdir ( name$ -- error? )
o# 40777 ($create) if true exit then
- link-count 1+ link-count! \ The ".." entry is another link to parent
- file-handle to inode#
- link-count 1+ link-count! \ The "." entry is another link to the new directory
+ dirent-inode@ set-inode
- add-block ( block# )
+ add-block ( block# )
file-size h# 400 + file-size!
- dup direct0 int! update ( block# )
- block bsize erase update \ flush
- wd-inum >r
- inode# first-dirent if r> drop true exit then
- " ." d# 12 dir-type inode# set-dirent
- d# 12 diroff !
- " .." bsize d# 12 - dir-type r> set-dirent
- false ( error? )
+ dup direct0 int! update ( block# )
+ block bsize erase update \ flush ( )
+ inode# first-dirent if true exit then ( )
+ " ." bsize inode# fill-dirent ( )
+ " .." wd-inum new-dirent ( error? )
diroff off
;
: $delete ( name$ -- error? )
$find-file if true exit then ( )
-
- (delete-file)
+
+ wf-type dir-type = if true exit then
+
+ dirent-unlink
+ false
;
: $delete! $delete ; \ XXX should these be different?
+: $hardlink ( old-name$ new-name$ -- error? )
+ \ Save the current search context. The path part of the new name
+ \ has already been parsed out and resolved. Resolving old-name$ changes
+ \ the directory context, so we will need to restore the context for the
+ \ new name to create its dirent.
+ dirent-vars 2>r 2>r ( new-name$ r: 4xVars )
+
+ 2swap $find-file if ( new-name$ r: 4xVars )
+ 2drop 2r> 2r> 4drop true exit
+ then ( new-name$ r: 4xVars )
+
+ \ Hard links to directories mess up the filesystem tree
+ wf-type dir-type = if 2drop true exit then ( new-name$ r: 4xVars)
+
+ 2r> 2r> restore-dirent ( new-name$ )
+ wf-inum new-dirent ( error? )
+;
+
+: $rename ( old-name$ new-name$ -- error? )
+ \ If new-name$ is null, the destination is a directory (which has
+ \ already been located), so we set the destination filename to be
+ \ the same as the filename component of the old path.
+ dup 0= if ( old-path$ new-name$ )
+ 2drop ( old-path$ )
+ 2dup [char] \ right-split-string ( old-path$ name$ dir$ )
+ 2drop ( old-path$ new-name$ )
+ then ( old-path$ new-name$ )
+
+ 2over 2swap $hardlink if 2drop true exit then ( old-name$ )
+ $delete
+;
+
: $rmdir ( name$ -- error? )
$find-file if true exit then ( )
wf-type dir-type <> if true exit then ( )
+
\ Now the dirent is the one for the directory to delete and the
\ inode is for the parent directory
-
- dirent-vars 2>r 2>r \ save lookup state in parent directory
- file-handle set-inode \ Switch the inode to the directory to delete
- empty-dir? 0= if 2r> 2r> 4drop true exit then
+ dirent-inode@ >r ( r: dir-i# )
- \ The directory is empty so it's okay to remove it
- delete-blocks \ Release the data blocks used for dirent storage
- inode# free-inode \ Release the inode number
+ \ First verify that the directory to delete is empty
+ dirent-vars ( 4xVars r: dir-i# )
+ r@ empty-dir? 0= if r> drop 4drop true exit then
+ restore-dirent ( r: dir-i# )
+
+ \ Remove the dirent from the parent directory; the inode will not
+ \ be freed yet because its link count is still nonzero due to the
+ \ directory's "." entry.
+ dirent-unlink ( r: dir-i# )
+
+ \ First delete the ".." entry
+ " .." r@ $find-name if ( r: dir-i# )
+ ." Corrupt filesystem - directory does not have .. entry" cr
+ r> drop true exit
+ then ( r: dir-i# )
+ dirent-unlink ( r: dir-i# )
+
+ \ Then delete the "." entry
+ \ The link count should go to 0, freeing the directory blocks
+ " ." r@ $find-name if ( r: dir-i# )
+ ." Corrupt filesystem - directory does not have . entry" cr
+ r> drop true exit
+ then ( r: dir-i# )
+ dirent-unlink ( r: dir-i# )
+
+ r> drop ( )
- 2r> 2r> restore-dirent \ restore lookup state in parent directory
-
- del-dirent if true exit then \ Remove the dirent from the parent directory
- link-count 1- link-count! \ Decrement parent link count
false
;
@@ -76,7 +123,7 @@
drop bfbase @ bsize free-mem \ Registered with initbuf
modified? if
false to modified?
- time&date >unix-seconds file-handle inode 4 la+ int! update
+ time&date >unix-seconds dirent-inode@ set-inode ctime!
then
;
@@ -115,12 +162,12 @@
-rot $find-file if drop false exit then ( mode )
wf-type regular-type <> if drop false exit then ( mode )
- file-handle set-inode ( mode )
+ dirent-inode@ set-inode ( mode )
false to modified?
>r
bsize alloc-mem bsize initbuf
- file-handle r@ ['] ext2fsdflen ['] ext2fsdfalign ['] ext2fsfclose ['] ext2fsdfseek
+ dirent-inode@ r@ ['] ext2fsdflen ['] ext2fsdfalign ['] ext2fsfclose ['] ext2fsdfseek
r@ read = unknown-extensions? or if ['] nullwrite else ['] ext2fsfwrite then
r> write = if ['] nullread else ['] ext2fsfread then
true