[openfirmware] r1632 - ofw/fs/ext2fs

svn at openfirmware.info svn at openfirmware.info
Fri Dec 18 07:54:56 CET 2009


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




More information about the openfirmware mailing list