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