\ \ CDDL HEADER START \ \ The contents of this file are subject to the terms of the \ Common Development and Distribution License (the "License"). \ You may not use this file except in compliance with the License. \ \ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE \ or http://www.opensolaris.org/os/licensing. \ See the License for the specific language governing permissions \ and limitations under the License. \ \ When distributing Covered Code, include this CDDL HEADER in each \ file and include the License file at usr/src/OPENSOLARIS.LICENSE. \ If applicable, add the following below this CDDL HEADER, with the \ fields enclosed by brackets "[]" replaced with your own identifying \ information: Portions Copyright [yyyy] [name of copyright owner] \ \ CDDL HEADER END \ \ \ Copyright 2009 Sun Microsystems, Inc. All rights reserved. \ Use is subject to license terms. \ purpose: UFS file system support package copyright: Copyright 2009 Sun Microsystems, Inc. All Rights Reserved headers " /packages" get-package push-package new-device fs-pkg$ device-name diag-cr? \ \ UFS low-level block routines \ h# 2000 constant /max-bsize d# 512 constant /disk-block 0 instance value dev-ih 0 instance value temp-block : blk>byte ( block# -- byte# ) /disk-block * ; : read-disk-blocks ( adr len dev-block# -- ) blk>byte dev-ih read-disk ; \ \ UFS superblock routines \ d# 512 constant /super-block d# 16 constant super-block# 0 instance value super-block : +sb ( index -- value ) super-block swap la+ l@ ; : iblkno ( -- n ) d# 04 +sb ; : cgoffset ( -- n ) d# 06 +sb ; : cgmask ( -- n ) d# 07 +sb ; : bsize ( -- n ) d# 12 +sb ; : fragshift ( -- n ) d# 24 +sb ; : fsbtodbc ( -- n ) d# 25 +sb ; : inopb ( -- n ) d# 30 +sb ; : ipg ( -- n ) d# 46 +sb ; : fpg ( -- n ) d# 47 +sb ; : /frag ( -- fragsize ) bsize fragshift rshift ; : get-super-block ( -- ) super-block /super-block super-block# read-disk-blocks ; : cgstart ( cg -- block# ) dup cgmask invert and cgoffset * swap fpg * + ; : cgimin ( cg -- block# ) cgstart iblkno + ; : blkstofrags ( #blocks -- #frags ) fragshift lshift ; : lblkno ( byte-off -- lblk# ) bsize / ; : blkoff ( byte-off -- blk-off ) bsize mod ; : fsbtodb ( fs-blk# -- dev-blk# ) fsbtodbc lshift ; : read-fs-blocks ( adr len fs-blk# -- ) fsbtodb read-disk-blocks ; \ \ UFS inode routines \ h# 80 constant /inode 0 instance value inode 0 instance value iptr : itoo ( i# -- offset ) inopb mod ; : itog ( i# -- group ) ipg / ; : itod ( i# -- block# ) dup itog cgimin swap ipg mod inopb / blkstofrags + ; : +i ( n -- adr ) iptr + ; : ftype ( -- n ) 0 +i w@ h# f000 and ; : dir? ( -- flag ) ftype h# 4000 = ; : symlink? ( -- flag ) ftype h# a000 = ; : regular? ( -- flag ) ftype h# 8000 = ; : file-size ( -- n ) 8 +i x@ ; : direct0 ( -- adr ) d# 40 +i ; : indirect0 ( -- adr ) d# 88 +i ; : indirect1 ( -- adr ) d# 92 +i ; : indirect2 ( -- adr ) d# 96 +i ; : comp? ( -- flag ) d# 100 +i l@ 4 and 0<> ; 0 instance value current-file : iget ( i# -- ) dup temp-block bsize rot itod ( i# adr len blk# ) read-fs-blocks dup itoo /inode * temp-block + inode /inode move inode to iptr to current-file ( ) ; : l@++ ( ptr -- value ) dup @ l@ /l rot +! ; d# 12 constant #direct : #blk-addr/blk bsize /l / ; : #sgl-addr #blk-addr/blk ; : #dbl-addr #sgl-addr #blk-addr/blk * ; \ : #tri-addr #dbl-addr #blk-addr/blk * ; : >1-idx ( blk# -- idx ) #blk-addr/blk mod ; : >2-idx ( blk# -- idx ) #sgl-addr / >1-idx ; \ : >3-idx ( blk# -- idx ) #dbl-addr / >1-idx ; \ \ indirect block cache \ we assume reads will mostly be sequential, and only \ cache the current indirect block tree \ : get-indir ( fs-blk# var adr -- adr ) -rot dup >r @ over = if ( adr fs-blk# r: var ) r> 2drop exit ( adr ) then ( adr fs-blk# r: var ) 2dup bsize swap read-fs-blocks ( adr fs-blk# r: var ) r> ! ( adr ) ; 0 instance value indir0-adr instance variable cur-indir0 : get-indir0 ( fs-blk# -- adr ) cur-indir0 indir0-adr get-indir ; 0 instance value indir1-adr instance variable cur-indir1 : get-indir1 ( fs-blk# -- adr ) cur-indir1 indir1-adr get-indir ; \ \ blkptr and blklim point to an array of blk#s, \ whether in the inode direct block array or in \ an indirect block \ instance variable blkptr instance variable blklim : (bmap) ( lblk# -- ) dup #direct < if ( lblk# ) direct0 swap la+ blkptr ! ( ) direct0 #direct la+ blklim ! exit then ( lblk# ) #direct - ( lblk#' ) dup #sgl-addr < if indirect0 l@ get-indir0 ( lblk# adr ) tuck swap >1-idx la+ blkptr ! ( adr ) #blk-addr/blk la+ blklim ! exit then ( lblk# ) #sgl-addr - ( lblk#' ) dup #dbl-addr < if indirect1 l@ get-indir0 ( lblk# adr ) over >2-idx la+ l@ get-indir1 ( lblk# adr' ) tuck swap >1-idx la+ blkptr ! ( adr ) #blk-addr/blk la+ blklim ! ( ) exit then ( lblk# ) \ #dbl-addr - ( lblk#' ) \ dup #tri-addr < if \ indirect2 l@ get-indir0 ( lblk# adr ) \ over >3-idx la+ l@ get-indir1 ( lblk# adr' ) \ over >2-idx la+ l@ get-indir2 ( lblk# adr' ) \ tuck swap >1-idx la+ blkptr ! ( adr ) \ #blk-addr/blk la+ blklim ! ( ) \ exit \ then ( lblk# ) ." file too large" cr drop true ( failed ) ; 0 instance value cur-blk : bmap ( lblk# -- fs-blk# ) dup cur-blk <> blkptr @ blklim @ = or if ( lblk# ) dup (bmap) ( lblk# ) then ( lblk# ) 1+ to cur-blk ( ) blkptr l@++ ( fs-blk# ) ; : read-one-block ( adr block# -- ) bmap ?dup if bsize swap read-fs-blocks else bsize erase then ; : read-partial-block ( adr len off block# -- ) bmap ?dup if fsbtodb blk>byte + ( adr len byte# ) dev-ih read-disk else drop erase then ; \ \ UFS directory routines \ instance variable dir-blk instance variable totoff instance variable dirptr 0 instance value dir-buf : get-dirblk ( -- ) dir-buf bsize dir-blk @ bmap ( adr len fs-blk# ) read-fs-blocks ( ) 1 dir-blk +! ; 2 constant rootino : +d ( n -- adr ) dirptr @ + ; : dir-ino ( -- adr ) 0 +d l@ ; : reclen ( -- adr ) 4 +d w@ ; : namelen ( -- adr ) 6 +d w@ ; : dir-name ( -- adr ) 8 +d ; : dir-name$ ( -- file$ ) dir-name namelen ; \ \ UFS high-level routines \ \ After this point, the code should be independent of the disk format! 0 instance value search-dir : init-dent 0 totoff ! 0 dir-blk ! current-file to search-dir ; : get-dent ( -- end-of-dir? ) begin totoff @ file-size >= if true exit then totoff @ blkoff 0= if get-dirblk dir-buf dirptr ! else reclen dirptr +! then reclen totoff +! dir-ino 0<> until false ; : dirlook ( file$ -- not-found? ) init-dent begin get-dent 0= while ( file$ ) 2dup dir-name$ $= if ( file$ ) dir-ino iget ( file$ ) 2drop false exit ( found ) then ( file$ ) repeat 2drop true ( not-found ) ; h# 200 constant /fpath-buf /fpath-buf instance buffer: fpath-buf : clr-fpath-buf ( -- ) fpath-buf /fpath-buf erase ; : fpath-buf$ ( -- path$ ) fpath-buf cscount ; : follow-symlink ( tail$ -- tail$' ) clr-fpath-buf ( tail$ ) fpath-buf file-size 0 0 read-partial-block ( tail$ ) ?dup if ( tail$ ) " /" fpath-buf$ $append ( tail$ ) fpath-buf$ $append ( ) else drop then ( ) fpath-buf$ ( path$ ) over c@ ascii / = if ( path$ ) str++ rootino ( path$' i# ) else ( path$ ) search-dir ( path$ i# ) then ( path$ i# ) iget ( path$ ) ; : lookup ( path$ -- not-found? ) over c@ ascii / = if str++ rootino ( path$' i# ) else current-file ( path$ i# ) then ( path$ i# ) iget ( path$ ) begin ( path$ ) ascii / left-parse-string ( path$ file$ ) dup while dir? 0= if 2drop true exit then dirlook if 2drop true exit then ( path$ ) symlink? if follow-symlink ( path$' ) then ( path$ ) repeat ( path$ file$ ) 2drop 2drop false ( succeeded ) ; : i#>name ( i# -- name$ ) init-dent ( i# ) begin get-dent 0= while ( i# ) dup dir-ino = if ( i# ) drop dir-name$ exit ( name$ ) then ( i# ) repeat drop " ???" ( name$ ) ; \ \ UFS installation routines \ /max-bsize 4 * /super-block + /inode + constant alloc-size \ **** Allocate memory for necessary data structures : allocate-buffers ( -- ) alloc-size mem-alloc dup 0= if ." no memory" abort then ( adr ) dup to temp-block /max-bsize + ( adr ) dup to dir-buf /max-bsize + ( adr ) dup to indir0-adr /max-bsize + ( adr ) dup to indir1-adr /max-bsize + ( adr ) dup to super-block /super-block + ( adr ) to inode ( ) ; : release-buffers ( -- ) temp-block alloc-size mem-free ; \ UFS file interface struct /x field >busy /x field >offset /inode field >inode constant /file-record d# 10 constant #opens #opens /file-record * constant /file-records /file-records instance buffer: file-records -1 instance value current-fd : fd>record ( fd -- record ) /file-record * file-records + ; : file-offset@ ( -- off ) current-fd fd>record >offset x@ ; : file-offset! ( off -- ) current-fd fd>record >offset x! ; : get-slot ( -- fd false | true ) #opens 0 do i fd>record >busy x@ 0= if i false unloop exit then loop true ; : free-slot ( fd -- ) 0 swap fd>record >busy x! ; : init-fd ( fd -- ) fd>record ( rec ) dup >busy 1 swap x! dup >inode inode swap /inode move >offset 0 swap x! ; : set-fd ( fd -- error? ) dup fd>record dup >busy x@ 0= if ( fd rec ) 2drop true exit ( failed ) then >inode to iptr ( fd ) to current-fd false ( succeeded ) ; \ get current lblk# and offset within it : file-blk+off ( -- off block# ) file-offset@ dup blkoff swap lblkno ; \ advance file io stack by n : fio+ ( # adr len n -- #+n adr+n len-n ) dup file-offset@ + file-offset! dup >r - -rot ( len' # adr r: n ) r@ + -rot ( adr' len' # r: n ) r> + -rot ( #' adr' len' ) ; : (cwd) ( i# -- ) tokenizer[ reveal ]tokenizer dup rootino <> if \ open parent, find current name " .." lookup drop i#>name ( name$ ) \ recurse to print path components above current-file (cwd) ( name$ ) \ and print this component type ( ) else drop then ( ) \ slash is both root name and separator ." /" ; external : open ( -- okay? ) my-args dev-open dup 0= if ( 0 ) exit ( failed ) then to dev-ih allocate-buffers get-super-block file-records /file-records erase true ( succeeded ) ; : close ( -- ) dev-ih dev-close 0 to dev-ih release-buffers ; : open-file ( path$ -- fd true | false ) get-slot if 2drop false exit ( failed ) then -rot ( fd path$ ) lookup if ( fd ) drop false exit ( failed ) then dup init-fd true ( fd succeeded ) ; : close-file ( fd -- ) free-slot ( ) ; : size-file ( fd -- size ) set-fd if 0 else file-size then ; : seek-file ( off fd -- off true | false ) set-fd if ( off ) drop false exit ( failed ) then ( off ) dup file-size > if ( off ) drop false exit ( failed ) then ( off ) dup file-offset! true ( off succeeded ) ; : read-file ( adr len fd -- #read ) set-fd if ( adr len ) 2drop 0 exit ( 0 ) then ( adr len ) regular? 0= if 2drop 0 exit then \ adjust len if reading past eof dup file-offset@ + file-size > if dup file-offset@ + file-size - - then dup 0= if nip exit then 0 -rot ( #read adr len ) \ initial partial block file-offset@ blkoff ?dup if ( #read adr len off ) bsize swap - over min ( #read adr len len' ) 3dup nip file-blk+off ( #read adr len len' adr len' off lblk# ) read-partial-block ( #read adr len len ) fio+ ( #read' adr' len' ) then ( #read adr len ) dup lblkno 0 ?do ( #read adr len ) over file-blk+off nip ( #read adr len adr lblk# ) read-one-block ( #read adr len ) bsize fio+ ( #read' adr' len' ) loop ( #read adr len ) \ final partial block dup if ( #read adr len ) 2dup file-blk+off ( #read adr len adr len off lblk# ) read-partial-block ( #read adr len ) dup fio+ ( #read' adr' 0 ) then 2drop ( #read ) ; : cinfo-file ( fd -- bsize fsize comp? ) set-fd if 0 0 0 else bsize file-size comp? then ; \ read ramdisk fcode at rd-offset : get-rd ( adr len -- ) rd-offset dev-ih read-disk ; \ no additional props needed for ufs : bootprop ( -- ) false ; \ debug words headers : chdir ( dir$ -- ) current-file -rot ( i# dir$ ) lookup if ( i# ) to current-file ( ) ." no such dir" cr exit then ( i# ) dir? 0= if ( i# ) to current-file ( ) ." not a dir" cr exit then drop ( ) ; : dir ( -- ) current-file iget init-dent begin get-dent 0= while dir-name$ type cr repeat ; : cwd ( -- ) current-file ( i# ) dup (cwd) cr ( i# ) iget ( ) ; finish-device pop-package