281 lines
8.8 KiB
Forth
281 lines
8.8 KiB
Forth
\ *****************************************************************************
|
|
\ * Copyright (c) 2004, 2011 IBM Corporation
|
|
\ * All rights reserved.
|
|
\ * This program and the accompanying materials
|
|
\ * are made available under the terms of the BSD License
|
|
\ * which accompanies this distribution, and is available at
|
|
\ * http://www.opensource.org/licenses/bsd-license.php
|
|
\ *
|
|
\ * Contributors:
|
|
\ * IBM Corporation - initial implementation
|
|
\ ****************************************************************************/
|
|
s" ext2-files" device-name
|
|
|
|
INSTANCE VARIABLE first-block
|
|
INSTANCE VARIABLE inode-size
|
|
INSTANCE VARIABLE block-size
|
|
INSTANCE VARIABLE inodes/group
|
|
|
|
INSTANCE VARIABLE blocks-per-group
|
|
INSTANCE VARIABLE group-descriptors
|
|
INSTANCE VARIABLE desc-size
|
|
|
|
: seek s" seek" $call-parent ;
|
|
: read s" read" $call-parent ;
|
|
|
|
INSTANCE VARIABLE data
|
|
INSTANCE VARIABLE #data
|
|
INSTANCE VARIABLE indirect-block
|
|
INSTANCE VARIABLE dindirect-block
|
|
|
|
: free-data
|
|
data @ ?dup IF #data @ free-mem 0 data ! THEN ;
|
|
: read-data ( offset size -- )
|
|
free-data dup #data ! alloc-mem data !
|
|
xlsplit seek -2 and ABORT" ext2-files read-data: seek failed"
|
|
data @ #data @ read #data @ <> ABORT" ext2-files read-data: read failed" ;
|
|
|
|
: read-block ( block# -- )
|
|
block-size @ * block-size @ read-data ;
|
|
|
|
INSTANCE VARIABLE inode
|
|
INSTANCE VARIABLE file-len
|
|
INSTANCE VARIABLE blocks \ data from disk blocks
|
|
INSTANCE VARIABLE #blocks
|
|
INSTANCE VARIABLE ^blocks \ current pointer in blocks
|
|
INSTANCE VARIABLE #blocks-left
|
|
: blocks-read ( n -- ) dup negate #blocks-left +! 4 * ^blocks +! ;
|
|
: read-indirect-blocks ( indirect-block# -- )
|
|
read-block data @ data off
|
|
dup #blocks-left @ 4 * block-size @ min dup >r ^blocks @ swap move
|
|
r> 2 rshift blocks-read block-size @ free-mem ;
|
|
|
|
: read-double-indirect-blocks ( double-indirect-block# -- )
|
|
\ Resolve one level of indirection and call read-indirect-block
|
|
read-block data @ indirect-block ! data off
|
|
BEGIN
|
|
indirect-block @ l@-le dup 0 <>
|
|
WHILE
|
|
read-indirect-blocks
|
|
4 indirect-block +! \ point to next indirect block
|
|
REPEAT
|
|
drop \ drop 0, the invalid block number
|
|
;
|
|
|
|
: read-triple-indirect-blocks ( triple-indirect-block# -- )
|
|
\ Resolve one level of indirection and call double-indirect-block
|
|
read-block data @ dindirect-block ! data off
|
|
BEGIN
|
|
dindirect-block @ l@-le dup 0 <>
|
|
WHILE
|
|
read-double-indirect-blocks
|
|
4 dindirect-block +! \ point to next double indirect block
|
|
REPEAT
|
|
drop \ drop 0, the invalid block number
|
|
;
|
|
|
|
: inode-i-block ( inode -- block ) 28 + ;
|
|
80000 CONSTANT EXT4_EXTENTS_FL
|
|
: inode-i-flags ( inode -- i_flags ) 20 + l@-le ;
|
|
F30A CONSTANT EXT4_EH_MAGIC
|
|
: extent-tree-entries ( iblock -- entries ) C + ;
|
|
|
|
STRUCT
|
|
2 field ext4-eh>magic
|
|
2 field ext4-eh>entries
|
|
2 field ext4-eh>max
|
|
2 field ext4-eh>depth
|
|
4 field ext4-eh>generation
|
|
CONSTANT /ext4-eh
|
|
|
|
STRUCT
|
|
4 field ext4-ee>block
|
|
2 field ext4-ee>len
|
|
2 field ext4-ee>start_hi
|
|
4 field ext4-ee>start_lo
|
|
CONSTANT /ext4-ee
|
|
|
|
: ext4-ee-start ( entries -- ee-start )
|
|
dup ext4-ee>start_hi w@-le 32 lshift
|
|
swap
|
|
ext4-ee>start_lo l@-le or
|
|
;
|
|
|
|
: expand-blocks ( start len -- )
|
|
bounds
|
|
?DO
|
|
i ^blocks @ l!-le
|
|
1 blocks-read
|
|
1 +LOOP
|
|
;
|
|
|
|
\ [0x28..0x34] ext4_extent_header
|
|
\ [0x34..0x64] ext4_extent_idx[eh_entries if eh_depth > 0] (not supported)
|
|
\ ext4_extent[eh_entries if eh_depth == 0]
|
|
: read-extent-tree ( inode -- )
|
|
inode-i-block
|
|
dup ext4-eh>magic w@-le EXT4_EH_MAGIC <> IF ." BAD extent tree magic" cr EXIT THEN
|
|
dup ext4-eh>depth w@-le 0 <> IF ." Root inode is not lead, not supported" cr EXIT THEN
|
|
\ depth=0 means it is a leaf and entries are ext4_extent[eh_entries]
|
|
dup ext4-eh>entries w@-le
|
|
>r
|
|
/ext4-eh +
|
|
r>
|
|
0
|
|
DO
|
|
dup ext4-ee-start
|
|
over ext4-ee>len w@-le ( ext4_extent^ start len )
|
|
expand-blocks
|
|
/ext4-ee +
|
|
LOOP
|
|
drop
|
|
;
|
|
|
|
\ Reads block numbers into blocks
|
|
: read-block#s ( -- )
|
|
blocks @ ?dup IF #blocks @ 4 * free-mem THEN \ free blocks if allocated
|
|
inode @ 4 + l@-le file-len ! \ *file-len = i_size_lo
|
|
file-len @ block-size @ // #blocks ! \ *#blocks = roundup(file-len/block-size)
|
|
#blocks @ 4 * alloc-mem blocks ! \ *blocks = allocmem(*#blocks)
|
|
blocks @ ^blocks ! #blocks @ #blocks-left !
|
|
inode @ inode-i-flags EXT4_EXTENTS_FL and IF inode @ read-extent-tree EXIT THEN
|
|
#blocks-left @ c min \ # direct blocks
|
|
inode @ inode-i-block over 4 * ^blocks @ swap move blocks-read
|
|
#blocks-left @ IF inode @ 58 + l@-le read-indirect-blocks THEN
|
|
#blocks-left @ IF inode @ 5c + l@-le read-double-indirect-blocks THEN
|
|
#blocks-left @ IF inode @ 60 + l@-le read-triple-indirect-blocks THEN
|
|
;
|
|
|
|
: read-inode-table ( groupdesc -- table )
|
|
dup 8 + l@-le \ reads bg_inode_table_lo
|
|
desc-size @ 20 > IF
|
|
over 28 + l@-le \ reads bg_inode_table_hi
|
|
20 lshift or
|
|
THEN
|
|
nip
|
|
;
|
|
|
|
: read-inode ( inode# -- )
|
|
1- inodes/group @ u/mod
|
|
desc-size @ * group-descriptors @ +
|
|
read-inode-table
|
|
block-size @ * \ # in group, inode table
|
|
swap inode-size @ * + xlsplit seek drop inode @ inode-size @ read drop
|
|
;
|
|
|
|
: .rwx ( bits last-char-if-special special? -- )
|
|
rot dup 4 and IF ." r" ELSE ." -" THEN
|
|
dup 2 and IF ." w" ELSE ." -" THEN
|
|
swap IF 1 and 0= IF upc THEN emit ELSE
|
|
1 and IF ." x" ELSE ." -" THEN drop THEN ;
|
|
CREATE mode-chars 10 allot s" ?pc?d?b?-?l?s???" mode-chars swap move
|
|
: .mode ( mode -- )
|
|
dup c rshift f and mode-chars + c@ emit
|
|
dup 6 rshift 7 and over 800 and 73 swap .rwx
|
|
dup 3 rshift 7 and over 400 and 73 swap .rwx
|
|
dup 7 and swap 200 and 74 swap .rwx ;
|
|
: .inode ( -- )
|
|
base @ >r decimal
|
|
inode @ w@-le .mode \ file mode
|
|
inode @ 1a + w@-le 5 .r \ link count
|
|
inode @ 02 + w@-le 9 .r \ uid
|
|
inode @ 18 + w@-le 9 .r \ gid
|
|
inode @ 04 + l@-le 9 .r \ size
|
|
r> base ! ;
|
|
|
|
80 CONSTANT EXT4_INCOMPAT_64BIT
|
|
: super-feature-incompat ( data -- flags ) 60 + l@-le ;
|
|
: super-desc-size ( data -- size ) FE + w@-le ;
|
|
: super-feature-incompat-64bit ( data -- true|false )
|
|
super-feature-incompat EXT4_INCOMPAT_64BIT and 0<>
|
|
;
|
|
|
|
: do-super ( -- )
|
|
400 400 read-data
|
|
data @ 14 + l@-le first-block !
|
|
400 data @ 18 + l@-le lshift block-size !
|
|
data @ 28 + l@-le inodes/group !
|
|
\ Check revision level... in revision 0, the inode size is always 128
|
|
data @ 4c + l@-le 0= IF
|
|
80 inode-size !
|
|
ELSE
|
|
data @ 58 + w@-le inode-size !
|
|
THEN
|
|
data @ 20 + l@-le blocks-per-group !
|
|
data @ super-feature-incompat-64bit IF
|
|
data @ super-desc-size desc-size !
|
|
ELSE
|
|
20 desc-size !
|
|
THEN
|
|
|
|
\ Read the group descriptor table:
|
|
first-block @ 1+ block-size @ *
|
|
blocks-per-group @
|
|
read-data
|
|
data @ group-descriptors !
|
|
|
|
\ We keep the group-descriptor memory area, so clear data pointer:
|
|
data off
|
|
;
|
|
|
|
INSTANCE VARIABLE current-pos
|
|
|
|
: read ( adr len -- actual )
|
|
file-len @ current-pos @ - min \ can't go past end of file
|
|
current-pos @ block-size @ u/mod 4 * blocks @ + l@-le read-block
|
|
block-size @ over - rot min >r ( adr off r: len )
|
|
data @ + swap r@ move r> dup current-pos +! ;
|
|
: read ( adr len -- actual )
|
|
( check if a file is selected, first )
|
|
dup >r BEGIN dup WHILE 2dup read dup 0= ABORT" ext2-files: read failed"
|
|
/string REPEAT 2drop r> ;
|
|
: seek ( lo hi -- status )
|
|
lxjoin dup file-len @ > IF drop true EXIT THEN current-pos ! false ;
|
|
: load ( adr -- len )
|
|
file-len @ read dup file-len @ <> ABORT" ext2-files: failed loading file" ;
|
|
|
|
: .name ( adr -- ) dup 8 + swap 6 + c@ type ;
|
|
: read-dir ( inode# -- adr )
|
|
read-inode read-block#s file-len @ alloc-mem
|
|
0 0 seek ABORT" ext2-files read-dir: seek failed"
|
|
dup file-len @ read file-len @ <> ABORT" ext2-files read-dir: read failed"
|
|
;
|
|
|
|
: .dir ( inode# -- )
|
|
read-dir dup BEGIN 2dup file-len @ - > over l@-le tuck and WHILE
|
|
cr dup 8 0.r space read-inode .inode space space dup .name
|
|
dup 4 + w@-le + REPEAT 2drop file-len @ free-mem
|
|
;
|
|
|
|
: (find-file) ( adr name len -- inode#|0 )
|
|
2>r dup BEGIN 2dup file-len @ - > over l@-le and WHILE
|
|
dup 8 + over 6 + c@ 2r@ str= IF 2r> 2drop nip l@-le EXIT THEN
|
|
dup 4 + w@-le + REPEAT 2drop 2r> 2drop 0
|
|
;
|
|
|
|
: find-file ( inode# name len -- inode#|0 )
|
|
2>r read-dir dup 2r> (find-file) swap file-len @ free-mem
|
|
;
|
|
|
|
: find-path ( inode# name len -- inode#|0 )
|
|
dup 0= IF 3drop 0 ." empty name " EXIT THEN
|
|
over c@ [char] \ = IF 1 /string ." slash " RECURSE EXIT THEN
|
|
[char] \ split 2>r find-file ?dup 0= IF
|
|
2r> 2drop false ." not found " EXIT THEN
|
|
r@ 0<> IF 2r> ." more... " RECURSE EXIT THEN
|
|
2r> 2drop ." got it " ;
|
|
|
|
: close
|
|
inode @ inode-size @ free-mem
|
|
group-descriptors @ blocks-per-group @ free-mem
|
|
free-data
|
|
blocks @ ?dup IF #blocks @ 4 * free-mem THEN
|
|
;
|
|
|
|
: open
|
|
0 data ! 0 blocks ! 0 #blocks !
|
|
do-super
|
|
inode-size @ alloc-mem inode !
|
|
my-args nip 0= IF 0 0 ELSE
|
|
2 my-args find-path ?dup 0= IF close false EXIT THEN THEN
|
|
read-inode read-block#s 0 0 seek 0= ;
|