4 \ The contents of this file are subject to the terms of the
5 \ Common Development and Distribution License (the "License").
6 \ You may not use this file except in compliance with the License.
8 \ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 \ or http://www.opensolaris.org/os/licensing.
10 \ See the License for the specific language governing permissions
11 \ and limitations under the License.
13 \ When distributing Covered Code, include this CDDL HEADER in each
14 \ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 \ If applicable, add the following below this CDDL HEADER, with the
16 \ fields enclosed by brackets "[]" replaced with your own identifying
17 \ information: Portions Copyright [yyyy] [name of copyright owner]
22 \ Copyright 2010 Sun Microsystems, Inc. All rights reserved.
23 \ Use is subject to license terms.
25 \ Copyright 2015 Toomas Soome <tsoome@me.com>
28 purpose: ZFS file system support package
29 copyright: Copyright 2010 Sun Microsystems, Inc. All Rights Reserved
31 " /packages" get-package push-package
34 fs-pkg$ device-name diag-cr?
36 0 instance value temp-space
40 \ fcode is still 32b on 64b sparc-v9, so
41 \ we need to override some arithmetic ops
42 \ stack ops and logical ops (dup, and, etc) are 64b
43 : xcmp ( x1 x2 -- -1|0|1 )
44 xlsplit rot xlsplit ( x2.lo x2.hi x1.lo x1.hi )
45 rot 2dup u< if ( x2.lo x1.lo x1.hi x2.hi )
47 else u> if ( x2.lo x1.lo )
49 else swap 2dup u< if ( x1.lo x2.lo )
55 then then then then ( -1|0|1 )
57 : x< ( x1 x2 -- <? ) xcmp -1 = ;
58 : x> ( x1 x2 -- >? ) xcmp 1 = ;
59 \ : x= ( x1 x2 -- =? ) xcmp 0= ;
60 : x<> ( x1 x2 -- <>? ) xcmp 0<> ;
61 : x0= ( x -- 0=? ) xlsplit 0= swap 0= and ;
63 /buf-len instance buffer: numbuf
66 numbuf /buf-len + swap ( adr u )
68 d# 10 /mod swap ( adr u' rem )
69 ascii 0 + ( adr u' c )
70 rot 1- tuck c! ( u adr' )
71 swap dup 0= ( adr u done? )
73 dup numbuf - /buf-len swap - ( adr len )
77 /buf-len instance buffer: bootprop-buf
78 : bootprop$ ( -- prop$ ) bootprop-buf cscount ;
82 \ kernel/os/compress.c has a definitive theory of operation comment
83 \ on lzjb, but here's the reader's digest version:
85 \ repeated phrases are replaced by referenced to the original
87 \ y a d d a _ y a d d a _ y a d d a , _ b l a h _ b l a h _ b l a h
89 \ y a d d a _ 6 11 , _ b l a h 5 10
90 \ where 6 11 means memmove(ptr, ptr - 6, 11)
92 \ data is separated from metadata with embedded copymap entries
94 \ 0x40 y a d d a _ 6 11 , 0x20 _ b l a h 5 10
95 \ the copymap has a set bit for copy refercences
96 \ and a clear bit for bytes to be copied directly
98 \ the reference marks are encoded with match-bits and match-min
100 \ byte[0] = ((mlen - MATCH_MIN) << (NBBY - MATCH_BITS) | (off >> NBBY)
101 \ byte[1] = (uint8_t)off
104 : pow2 ( n -- 2**n ) 1 swap lshift ;
106 \ assume MATCH_BITS=6 and MATCH_MIN=3
109 8 mbits - constant mshift
110 d# 16 mbits - pow2 1- constant mmask
112 : decode-src ( src -- mlen off )
113 dup c@ swap 1+ c@ ( c[0] c[1] )
114 over mshift rshift mmin + ( c[0] c[1] mlen )
115 -rot swap bwjoin mmask and ( mlen off )
118 \ equivalent of memmove(dst, dst - off, len)
119 \ src points to a copy reference to be decoded
120 : mcopy ( dend dst src -- dend dst' )
121 decode-src ( dend dst mlen off )
122 2 pick swap - >r ( dent dst mlen r: cpy )
124 1- dup 0>= ( dend dst mlen' any? r: cpy )
125 2over > and ( dend dst mlen !done? r : cpy )
126 while ( dend dst mlen r: cpy )
127 swap r> dup 1+ >r c@ ( dend mlen dst c r: cpy' )
128 over c! 1+ swap ( dend dst' mlen r: cpy )
129 repeat ( dend dst' mlen r: cpy )
130 r> 2drop ( dend dst )
134 : lzjb ( src dst len -- )
135 over + swap ( src dend dst )
136 rot >r ( dend dst r: src )
138 \ setup mask so 1st while iteration fills map
139 0 7 pow2 2swap ( map mask dend dst r: src )
142 2swap 1 lshift ( dend dst map mask' r: src )
146 2drop ( dend dst r: src )
147 r> dup 1+ >r c@ 1 ( dend dst map' mask' r: src' )
148 then ( dend dst map mask r: src' )
150 \ if (map & mask) we hit a copy reference
151 \ else just copy 1 byte
152 2swap 2over and if ( map mask dend dst r: src )
153 r> dup 2+ >r ( map mask dend dst src r: src' )
154 mcopy ( map mask dend dst' r: src )
156 r> dup 1+ >r c@ ( map mask dend dst c r: src' )
157 over c! 1+ ( map mask dend dst' r: src )
159 repeat ( map mask dend dst r: src )
160 2drop 2drop r> drop ( )
163 \ decode lz4 buffer header, returns src addr and len
164 : lz4_sbuf ( addr -- s_addr s_len )
165 dup C@ 8 lshift swap 1+ ( byte0 addr++ )
166 dup C@ ( byte0 addr byte1 )
167 rot ( addr byte1 byte0 )
168 or d# 16 lshift swap 1+ ( d addr++ )
170 dup C@ 8 lshift ( d addr byte2 )
171 swap 1+ ( d byte2 addr++ )
172 dup C@ swap 1+ ( d byte2 byte3 addr++ )
173 -rot ( d s_addr byte2 byte3 )
180 8 constant COPYLENGTH
181 5 constant LASTLITERALS
183 d# 15 constant ML_MASK \ (1<<ML_BITS)-1
184 4 constant RUN_BITS \ 8 - ML_BITS
185 d# 15 constant RUN_MASK \ (1<<RUN_BITS)-1
187 \ A32(d) = A32(s); d+=4; s+=4
188 : lz4_copystep ( dest source -- dest' source')
191 swap 4 + ( dest+4 source+4 )
194 \ do { LZ4_COPYPACKET(s, d) } while (d < e);
195 : lz4_copy ( e d s -- e d' s' )
198 lz4_copystep ( e d s )
204 \ lz4 decompress translation from C code
205 \ could use some factorisation
206 : lz4 ( src dest len -- )
207 swap dup >r swap \ save original dest to return stack.
209 lz4_sbuf ( dest len s_buf s_len )
210 over + ( dest len s_buf s_end )
211 2swap ( s_buf s_end dest len )
212 over + ( s_buf s_end dest dest_end )
213 2swap ( dest dest_end s_buf s_end )
217 swap dup C@ ( dest dest_end s_end s_buf token )
218 swap CHAR+ swap ( dest dest_end s_end s_buf++ token )
219 dup ML_BITS rshift ( dest dest_end s_end s_buf token length )
220 >r rot rot r> ( dest dest_end token s_end s_buf length )
222 d# 255 begin ( dest dest_end token s_end s_buf length s )
223 swap ( dest dest_end token s_end s_buf s length )
224 >r >r ( ... R: length s )
225 2dup > ( dest dest_end token s_end s_buf flag )
226 r@ d# 255 = and ( dest dest_end token s_end s_buf flag R: length s )
227 r> swap r> swap ( dest dest_end token s_end s_buf s length flag )
228 >r swap r> ( dest dest_end token s_end s_buf length s flag )
230 drop >r ( dest dest_end token s_end s_buf R: length )
231 dup c@ swap CHAR+ ( dest dest_end token s_end s s_buf++ )
232 swap ( dest dest_end token s_end s_buf s )
233 dup ( dest dest_end token s_end s_buf s s )
234 r> + swap ( dest dest_end token s_end s_buf length s )
236 drop ( dest dest_end token s_end s_buf length )
239 -rot ( dest dest_end token length s_end s_buf )
240 swap >r >r ( dest dest_end token length R: s_end s_buf )
241 swap >r ( dest dest_end length R: s_end s_buf token )
242 rot ( dest_end length dest )
243 2dup + ( dest_end length dest cpy )
245 2dup > if ( dest > cpy )
249 3 pick COPYLENGTH - over < ( dest_end length dest cpy flag )
250 3 pick ( dest_end length dest cpy flag length )
251 r> ( dest_end length dest cpy flag length token )
252 r> ( dest_end length dest cpy flag length token s_buf R: s_end )
253 rot ( dest_end length dest cpy flag token s_buf length )
254 over + ( dest_end length dest cpy flag token s_buf length+s_buf )
255 r@ COPYLENGTH - > ( dest_end length dest cpy flag token s_buf flag )
256 swap >r ( dest_end length dest cpy flag token flag R: s_end s_buf )
257 swap >r ( dest_end length dest cpy flag flag R: s_end s_buf token )
258 or if ( dest_end length dest cpy R: s_end s_buf token )
260 3 pick over swap > if
261 " lz4 write beyond buffer end" die ( write beyond the dest end )
262 then ( dest_end length dest cpy )
264 2 pick ( dest_end length dest cpy length )
265 r> r> swap ( dest_end length dest cpy length s_buf token R: s_end )
266 r> ( dest_end length dest cpy length s_buf token s_end )
267 swap >r >r ( dest_end length dest cpy length s_buf R: token s_end )
269 swap over + ( dest_end length dest cpy s_buf s_buf+length )
270 r@ > if ( dest_end length dest cpy s_buf R: token s_end )
271 " lz4 read beyond source" die \ read beyond source buffer
274 nip ( dest_end length dest s_buf R: token s_end )
275 >r ( dest_end length dest R: token s_end s_buf )
276 over r@ ( dest_end length dest length s_buf )
277 -rot move ( dest_end length )
280 " lz4 format violation" die \ LZ4 format violation
283 r> drop \ drop original dest
288 swap ( dest_end length cpy dest R: s_end s_buf token )
289 r> r> swap >r ( dest_end length cpy dest s_buf R: s_end token )
291 lz4_copy ( dest_end length cpy dest s_buf)
293 -rot ( dest_end length s_buf cpy dest )
294 over - ( dest_end length s_buf cpy dest-cpy )
295 rot ( dest_end length cpy dest-cpy s_buf )
296 swap - ( dest_end length cpy s_buf )
298 dup C@ swap ( dest_end length cpy b s_buf )
299 dup 1+ C@ 8 lshift ( dest_end length cpy b s_buf w )
300 rot or ( dest_end length cpy s_buf w )
301 2 pick swap - ( dest_end length cpy s_buf ref )
302 swap 2 + ( dest_end length cpy ref s_buf+2 )
303 \ note: cpy is also dest, remember to save it
304 -rot ( dest_end length s_buf cpy ref )
305 dup ( dest_end length s_buf cpy ref ref )
307 \ now we need original dest
308 r> r> swap r@ ( dest_end length s_buf cpy ref ref s_end token dest )
311 " lz4 reference outside buffer" die \ reference outside dest buffer
312 then ( dest_end length s_buf op ref )
314 2swap ( dest_end op ref length s_buf )
315 swap ( dest_end op ref s_buf length R: dest s_end token )
318 drop r> ML_MASK and ( dest_end op ref s_buf length R: dest s_end )
319 dup ML_MASK = if ( dest_end op ref s_buf length R: dest s_end )
322 rot ( dest_end op ref length flag s_buf )
323 dup r@ < ( dest_end op ref length flag s_buf flag )
324 rot and ( dest_end op ref length s_buf flag )
326 dup c@ ( dest_end op ref length s_buf s )
327 swap 1+ ( dest_end op ref length s s_buf++ )
328 -rot ( dest_end op ref s_buf length s )
329 swap over + swap ( dest_end op ref s_buf length+s s )
333 then ( dest_end op ref s_buf length R: dest s_end )
335 2swap ( dest_end s_buf length op ref )
337 \ copy repeated sequence
338 2dup - STEPSIZE < if ( dest_end s_buf length op ref )
339 \ 4 times *op++ = *ref++;
340 dup c@ >r ( dest_end s_buf length op ref R: C )
341 CHAR+ swap ( dest_end s_buf length ref++ op )
342 dup r> swap c! CHAR+ swap ( dest_end s_buf length op ref )
343 dup c@ >r ( dest_end s_buf length op ref R: C )
344 CHAR+ swap ( dest_end s_buf length ref++ op )
345 dup r> swap c! CHAR+ swap ( dest_end s_buf length op ref )
346 dup c@ >r ( dest_end s_buf length op ref R: C )
347 CHAR+ swap ( dest_end s_buf length ref++ op )
348 dup r> swap c! CHAR+ swap ( dest_end s_buf length op ref )
349 dup c@ >r ( dest_end s_buf length op ref R: C )
350 CHAR+ swap ( dest_end s_buf length ref++ op )
351 dup r> swap c! CHAR+ swap ( dest_end s_buf length op ref )
352 2dup - ( dest_end s_buf length op ref op-ref )
360 2dup swap 4 move ( dest_end s_buf length op ref )
362 swap ( dest_end s_buf length op ref )
364 lz4_copystep ( dest_end s_buf length op ref )
366 -rot ( dest_end s_buf ref length op )
367 swap over ( dest_end s_buf ref op length op )
368 + STEPSIZE 4 - - ( dest_end s_buf ref op cpy R: dest s_end )
370 \ if cpy > oend - COPYLENGTH
371 4 pick COPYLENGTH - ( dest_end s_buf ref op cpy oend-COPYLENGTH )
372 2dup > if ( dest_end s_buf ref op cpy oend-COPYLENGTH )
373 swap ( dest_end s_buf ref op oend-COPYLENGTH cpy )
376 " lz4 write outside buffer" die \ write outside of dest buffer
377 then ( dest_end s_buf ref op oend-COPYLENGTH cpy )
379 >r ( dest_end s_buf ref op oend-COPYLENGTH R: dest s_end cpy )
380 -rot swap ( dest_end s_buf oend-COPYLENGTH op ref )
381 lz4_copy ( dest_end s_buf oend-COPYLENGTH op ref )
382 rot drop swap r> ( dest_end s_buf ref op cpy )
386 >r ( dest_end s_buf ref op R: cpy )
387 over ( dest_end s_buf ref op ref )
388 c@ ( dest_end s_buf ref op C )
389 over c! ( dest_end s_buf ref op )
390 >r 1+ r> 1+ r> ( dest_end s_buf ref++ op++ cpy )
393 nip ( dest_end s_buf ref op )
395 \ op == dest_end we are done, cleanup
396 r> r> 2drop 2drop 2drop
399 ( dest_end s_buf ref op R: dest s_end )
400 nip ( dest_end s_buf op )
402 drop ( dest_end s_buf ref op cpy R: dest s_end)
403 -rot ( dest_end s_buf cpy ref op )
404 swap ( dest_end s_buf cpy op ref )
406 2drop ( dest_end s_buf op )
409 -rot r> ( op dest_end s_buf s_end R: dest )
418 \ ZFS block (SPA) routines
423 3 constant lzjb-comp#
424 d# 15 constant lz4-comp#
426 h# 2.0000 constant /max-bsize
427 d# 512 constant /disk-block
428 d# 128 constant /blkp
430 alias /gang-block /disk-block
432 \ the ending checksum is larger than 1 byte, but that
433 \ doesn't affect the math here
435 /blkp / constant #blks/gang
437 : blk_offset ( bp -- n ) h# 8 + x@ -1 h# 7fff.ffff lxjoin and ;
438 : blk_gang ( bp -- n ) h# 8 + x@ xlsplit nip d# 31 rshift ;
439 : blk_etype ( bp -- n ) h# 32 + c@ ;
440 : blk_comp ( bp -- n ) h# 33 + c@ h# 7f and ;
441 : blk_embedded? ( bp -- flag ) h# 33 + c@ h# 80 and h# 80 = ;
442 : blk_psize ( bp -- n ) h# 34 + w@ ;
443 : blk_lsize ( bp -- n ) h# 36 + w@ ;
444 : blk_birth ( bp -- n ) h# 50 + x@ ;
446 : blke_psize ( bp -- n ) h# 34 + c@ 1 rshift h# 7f and 1+ ;
447 : blke_lsize ( bp -- n ) h# 34 + l@ h# 1ff.ffff and 1+ ;
449 0 instance value dev-ih
450 0 instance value blk-space
451 0 instance value gang-space
453 : foff>doff ( fs-off -- disk-off ) /disk-block * h# 40.0000 + ;
454 : fsz>dsz ( fs-size -- disk-size ) 1+ /disk-block * ;
456 : bp-dsize ( bp -- dsize )
464 : bp-lsize ( bp -- lsize )
472 : (read-dva) ( adr len dva -- )
473 blk_offset foff>doff dev-ih read-disk
476 : gang-read ( adr len bp gb-adr -- ) tokenizer[ reveal ]tokenizer
479 tuck /gang-block rot (read-dva) ( adr len gb-adr )
481 \ loop through indirected bp's
482 dup /blkp #blks/gang * ( adr len gb-adr bp-list bp-list-len )
483 bounds do ( adr len gb-adr )
484 i blk_offset x0= ?leave
486 \ calc subordinate read len
487 over i bp-dsize min ( adr len gb-adr sub-len )
488 2swap swap ( gb-adr sub-len len adr )
490 \ nested gang block - recurse with new gang block area
492 2swap ( len adr gb-adr sub-len )
493 3dup swap /gang-block + ( len adr gb-adr sub-len adr sub-len gb-adr' )
494 i swap gang-read ( len adr gb-adr sub-len )
495 2swap ( gb-adr sub-len len adr )
497 3dup nip swap ( gb-adr sub-len len adr adr sub-len )
498 i (read-dva) ( gb-adr sub-len len adr )
499 then ( gb-adr sub-len len adr )
501 \ adjust adr,len and check if done
502 -rot over - ( gb-adr adr sub-len len' )
503 -rot + swap ( gb-adr adr' len' )
505 rot ( adr' len' gb-adr )
510 : read-dva ( adr len dva -- )
518 : read-embedded ( adr len bp -- )
519 \ loop over buf len, w in comment is octet count
520 \ note, we dont increment bp, but use index value of w
521 \ so we can skip the non-payload octets
522 swap 0 0 ( adr bp len 0 0 )
523 rot 0 do ( adr bp 0 0 )
524 I 8 mod 0= if ( adr bp w x )
526 2dup ( adr bp w bp w )
527 xa+ ( adr bp w bp+w*8 )
528 x@ swap ( adr bp x w )
529 1+ dup 6 = if 1+ else \ skip 6th word
530 dup h# a = if 1+ then \ skip 10th word
536 swap dup ( w bp adr x x )
538 xlsplit ( w bp adr x x.lo x.hi )
539 drop ( w bp adr x x.lo )
541 xlsplit ( w bp adr x x.lo x.hi )
542 nip ( w bp adr x x.hi )
544 I 4 mod 8 * rshift h# ff and ( w bp adr x c )
546 swap over ( w bp x adr c adr )
547 I + c! ( w bp x adr )
549 \ now we need to fix the stack for next pass
550 \ need to get ( adr bp w x )
551 swap 2swap ( adr x w bp )
558 \ block read that check for holes, gangs, compression, etc
559 : read-bp ( adr len bp -- )
561 dup x@ x0= ( addr len bp flag0 )
562 swap dup 8 + x@ x0= ( addr len flag0 bp flag1 )
563 rot ( addr len bp flag1 flag0 )
569 dup blk_comp no-comp# = if
573 \ read into blk-space. read is either from embedded area or disk
575 dup blk-space over bp-dsize ( adr len bp bp blk-adr rd-len )
576 rot read-embedded ( adr len bp )
578 dup blk-space over bp-dsize ( adr len bp bp blk-adr rd-len )
579 rot read-dva ( adr len bp )
582 \ set up the stack for decompress
583 blk_comp >r ( adr len R: alg )
584 blk-space -rot r> ( blk-adr adr len alg )
587 lzjb-comp# of lzjb endof
588 lz4-comp# of lz4 endof
589 def-comp# of lz4 endof \ isn't this writer only?
591 " : unknown compression algorithm, only lzjb and lz4 are supported"
600 h# 1.c000 constant /nvpairs
601 h# 4000 constant nvpairs-off
607 \ array of xdr packed nvpairs
608 \ 4B encoded nvpair size
609 \ 4B decoded nvpair size
610 \ 4B name string size
613 \ 4B # of data elements
617 d# 12 constant /nvhead
619 : >nvsize ( nv -- size ) l@ ;
620 : >nvname ( nv -- name$ )
621 /l 2* + dup /l + swap l@
623 : >nvdata ( nv -- data )
627 \ convert nvdata to 64b int or string
628 : nvdata>x ( nvdata -- x )
630 dup /l + l@ swap l@ ( x.lo x.hi )
633 alias nvdata>$ >nvname
635 : nv-lookup ( nv name$ -- nvdata false | true )
636 rot /nvhead + ( name$ nvpair )
637 begin dup >nvsize while
638 dup >r >nvname ( name$ nvname$ r: nvpair )
639 2over $= if ( name$ r: nvpair )
640 2drop r> >nvdata ( nvdata )
641 false exit ( nvdata found )
642 then ( name$ r: nvpair )
643 r> dup >nvsize + ( name$ nvpair' )
645 3drop true ( not-found )
649 temp-space /nvpairs nvpairs-off ( adr len off )
651 temp-space " txg" nv-lookup if
653 then nvdata>x ( txg )
655 " detached mirror" die
657 temp-space " name" nv-lookup if
658 " no name nvpair" die
659 then nvdata>$ ( pool$ )
660 bootprop-buf swap move ( )
665 \ ZFS ueber-block routines
668 d# 1024 constant /uber-block
669 d# 128 constant #ub/label
670 #ub/label /uber-block * constant /ub-ring
671 h# 2.0000 constant ubring-off
673 : ub_magic ( ub -- n ) x@ ;
674 : ub_txg ( ub -- n ) h# 10 + x@ ;
675 : ub_timestamp ( ub -- n ) h# 20 + x@ ;
676 : ub_rootbp ( ub -- p ) h# 28 + ;
678 0 instance value uber-block
680 : ub-cmp ( ub1 ub2 -- best-ub )
682 \ ub1 wins if ub2 isn't valid
683 dup ub_magic h# 00bab10c x<> if
687 \ if ub1 is 0, ub2 wins by default
688 over 0= if nip exit then ( ub2 )
690 \ 2 valid ubs, compare transaction groups
691 over ub_txg over ub_txg ( ub1 ub2 txg1 txg2 )
693 2drop nip exit ( ub2 )
694 then ( ub1 ub2 txg1 txg2 )
695 x> if drop exit then ( ub1 )
697 \ same txg, check timestamps
698 over ub_timestamp over ub_timestamp x> if
705 \ find best uber-block in ring, and copy it to uber-block
707 temp-space /ub-ring ubring-off ( adr len off )
709 0 temp-space /ub-ring ( null-ub adr len )
714 \ make sure we found a valid ub
715 dup 0= if " no ub found" die then
717 uber-block /uber-block move ( )
722 \ ZFS dnode (DMU) routines
725 d# 44 constant ot-sa#
727 d# 512 constant /dnode
729 : dn_indblkshift ( dn -- n ) h# 1 + c@ ;
730 : dn_nlevels ( dn -- n ) h# 2 + c@ ;
731 : dn_bonustype ( dn -- n ) h# 4 + c@ ;
732 : dn_datablkszsec ( dn -- n ) h# 8 + w@ ;
733 : dn_bonuslen ( dn -- n ) h# a + w@ ;
734 : dn_blkptr ( dn -- p ) h# 40 + ;
735 : dn_bonus ( dn -- p ) h# c0 + ;
736 : dn_spill ( dn -- p ) h# 180 + ;
738 0 instance value dnode
742 \ ind-cache is a 1 block indirect block cache from dnode ic-dn
744 \ ic-bp and ic-bplim point into the ic-dn's block ptr array,
745 \ either in dn_blkptr or in ind-cache ic-bp is the ic-blk#'th
746 \ block ptr, and ic-bplim is limit of the current bp array
748 \ the assumption is that reads will be sequential, so we can
749 \ just increment ic-bp
751 0 instance value ind-cache
752 0 instance value ic-dn
753 0 instance value ic-blk#
754 0 instance value ic-bp
755 0 instance value ic-bplim
757 : dn-bsize ( dn -- bsize ) dn_datablkszsec /disk-block * ;
758 : dn-indsize ( dn -- indsize ) dn_indblkshift pow2 ;
759 : dn-indmask ( dn -- mask ) dn-indsize 1- ;
761 \ recursively climb the block tree from the leaf to the root
762 : blk@lvl>bp ( dn blk# lvl -- bp ) tokenizer[ reveal ]tokenizer
763 >r /blkp * over dn_nlevels ( dn bp-off #lvls r: lvl )
765 \ at top, just add dn_blkptr
766 r@ = if ( dn bp-off r: lvl )
767 swap dn_blkptr + ( bp r: lvl )
769 then ( dn bp-off r: lvl )
771 \ shift bp-off down and find parent indir blk
772 2dup over dn_indblkshift rshift ( dn bp-off dn blk# r: lvl )
773 r> 1+ blk@lvl>bp ( dn bp-off bp )
775 \ read parent indir blk and index
776 rot tuck dn-indsize ( bp-off dn bp len )
777 ind-cache swap rot read-bp ( bp-off dn )
778 dn-indmask and ( bp-off' )
782 \ return end of current bp array
783 : bplim ( dn bp -- bp-lim )
784 over dn_nlevels 1 = if
785 drop dn_blkptr ( bp0 )
786 3 /blkp * + ( bplim )
788 1+ swap dn-indsize ( bp+1 indsz )
793 \ return the lblk#'th block ptr from dnode
794 : lblk#>bp ( dn blk# -- bp )
795 2dup ( dn blk# dn blk# )
796 ic-blk# <> swap ic-dn <> or ( dn blk# cache-miss? )
797 ic-bp ic-bplim = ( dn blk# cache-miss? cache-empty? )
799 2dup 1 blk@lvl>bp ( dn blk# bp )
800 dup to ic-bp ( dn blk# bp )
801 swap to ic-blk# ( dn bp )
802 2dup bplim to ic-bplim ( dn bp )
805 ic-blk# 1+ to ic-blk#
806 ic-bp dup /blkp + to ic-bp ( bp )
811 \ ZFS attribute (ZAP) routines
819 d# 24 constant /lf-chunk
820 d# 21 constant /lf-arr
821 h# ffff constant chain-end#
823 h# 100 constant /lf-buf
824 /lf-buf instance buffer: leaf-value
825 /lf-buf instance buffer: leaf-name
827 : +le ( len off -- n ) + w@ ;
828 : le_next ( le -- n ) h# 2 +le ;
829 : le_name_chunk ( le -- n ) h# 4 +le ;
830 : le_name_length ( le -- n ) h# 6 +le ;
831 : le_value_chunk ( le -- n ) h# 8 +le ;
832 : le_value_length ( le -- n ) h# a +le ;
834 : la_array ( la -- adr ) 1+ ;
835 : la_next ( la -- n ) h# 16 + w@ ;
837 0 instance value zap-space
839 \ setup leaf hash bounds
840 : >leaf-hash ( dn lh -- hash-adr /hash )
841 /lf-chunk 2* + ( dn hash-adr )
842 \ size = (bsize / 32) * 2
843 swap dn-bsize 4 rshift ( hash-adr /hash )
845 : >leaf-chunks ( lf -- ch0 ) >leaf-hash + ;
847 \ convert chunk # to leaf chunk
848 : ch#>lc ( dn ch# -- lc )
849 /lf-chunk * ( dn lc-off )
850 swap zap-space >leaf-chunks ( lc-off ch0 )
854 \ assemble chunk chain into single buffer
855 : get-chunk-data ( dn ch# adr -- )
856 dup >r /lf-buf erase ( dn ch# r: adr )
858 2dup ch#>lc nip ( dn la r: adr )
859 dup la_array ( dn la la-arr r: adr )
860 r@ /lf-arr move ( dn la r: adr )
861 r> /lf-arr + >r ( dn la r: adr' )
862 la_next dup chain-end# = ( dn la-ch# end? r: adr )
866 \ get leaf entry's name
867 : entry-name$ ( dn le -- name$ )
868 2dup le_name_chunk ( dn le dn la-ch# )
869 leaf-name get-chunk-data ( dn le )
870 nip le_name_length 1- ( len )
871 leaf-name swap ( name$ )
874 \ return entry value as int
875 : entry-int-val ( dn le -- n )
876 le_value_chunk ( dn la-ch# )
877 leaf-value get-chunk-data ( )
883 \ get leaf entry's value as string
884 : entry-val$ ( dn le -- val$ )
885 2dup le_value_chunk ( dn le dn la-ch# )
886 leaf-value get-chunk-data ( dn le )
887 nip le_value_length ( len )
888 leaf-value swap ( name$ )
893 : entry-apply ( xt dn le -- xt dn false | ??? true )
894 over >r ( xt dn le r: dn )
895 rot dup >r execute if ( ??? r: xt dn )
896 r> r> 2drop true ( ??? true )
898 r> r> false ( xt dn false )
902 \ apply xt to every entry in chain
903 : chain-apply ( xt dn ch# -- xt dn false | ??? true )
905 2dup ch#>lc nip ( xt dn le )
906 dup >r entry-apply if ( ??? r: le )
907 r> drop true exit ( ??? found )
909 r> le_next ( xt dn ch# )
910 dup chain-end# = ( xt dn ch# end? )
912 false ( xt dn false )
915 \ apply xt to every entry in leaf
916 : leaf-apply ( xt dn blk# -- xt dn false | ??? true )
918 \ read zap leaf into zap-space
919 2dup lblk#>bp ( xt dn blk# bp )
920 nip over dn-bsize zap-space ( xt dn bp len adr )
921 swap rot read-bp ( xt dn )
923 \ call chunk-look for every valid chunk list
924 dup zap-space >leaf-hash ( xt dn hash-adr /hash )
926 i w@ dup chain-end# <> if ( xt dn ch# )
927 chain-apply if ( ??? )
928 unloop true exit ( ??? found )
930 else drop then ( xt dn )
932 false ( xt dn not-found )
935 \ apply xt to every entry in fzap
936 : fzap-apply ( xt dn fz -- ??? not-found? )
938 \ blk# 1 is always the 1st leaf
939 >r 1 leaf-apply if ( ??? r: fz )
940 r> drop true exit ( ??? found )
943 \ call leaf-apply on every non-duplicate hash entry
944 \ embedded hash is in 2nd half of fzap block
945 over dn-bsize tuck + ( xt dn bsize hash-eadr )
946 swap 2dup 2/ - ( xt dn hash-eadr bsize hash-adr )
948 i x@ dup 1 <> if ( xt dn blk# )
949 leaf-apply if ( ??? )
950 unloop true exit ( ??? found )
952 else drop then ( xt dn )
954 2drop false ( not-found )
957 : mze_value ( uz -- n ) x@ ;
958 : mze_name ( uz -- p ) h# e + ;
960 : uzap-name$ ( uz -- name$ ) mze_name cscount ;
962 \ apply xt to each entry in micro-zap
963 : uzap-apply ( xt uz len -- ??? not-found? )
965 i swap dup >r ( uz xt r: xt )
966 execute if ( ??? r: xt )
968 unloop true exit ( ??? found )
971 drop false ( not-found )
975 : fz-nmlook ( prop$ dn le -- prop$ false | prop$ dn le true )
976 2dup entry-name$ ( prop$ dn le name$ )
977 2rot 2swap ( dn le prop$ name$ )
978 2over $= if ( dn le prop$ )
979 2swap true ( prop$ dn le true )
981 2swap 2drop false ( prop$ false )
982 then ( prop$ false | prop$ dn le true )
986 : uz-nmlook ( prop$ uz -- prop$ false | prop$ uz true )
987 dup >r uzap-name$ ( prop$ name$ r: uz )
988 2over $= if ( prop$ r: uz )
989 r> true ( prop$ uz true )
991 r> drop false ( prop$ false )
992 then ( prop$ false | prop$ uz true )
995 : zap-type ( zp -- n ) h# 7 + c@ ;
996 : >uzap-ent ( adr -- ent ) h# 40 + ;
998 \ read zap block into temp-space
999 : get-zap ( dn -- zp )
1000 dup 0 lblk#>bp ( dn bp )
1001 swap dn-bsize ( bp len )
1002 temp-space swap ( bp adr len )
1007 \ find prop in zap dnode
1008 : zap-lookup ( dn prop$ -- [ n ] not-found? )
1009 rot dup get-zap ( prop$ dn zp )
1012 >uzap-ent swap dn-bsize ( prop$ uz len )
1013 ['] uz-nmlook -rot ( prop$ xt uz len )
1014 uzap-apply if ( prop$ uz )
1015 mze_value -rot 2drop ( n )
1018 2drop true ( !found )
1019 then ( [ n ] not-found? )
1022 ['] fz-nmlook -rot ( prop$ xt dn fz )
1023 fzap-apply if ( prop$ dn le )
1024 entry-int-val ( prop$ n )
1025 -rot 2drop false ( n found )
1027 2drop true ( !found )
1028 then ( [ n ] not-found? )
1030 3drop 2drop true ( !found )
1031 endcase ( [ n ] not-found? )
1035 : zap-lookup-str ( dn prop$ -- [ val$ ] not-found? )
1036 rot dup get-zap ( prop$ dn zp )
1037 dup zap-type fzap# <> if ( prop$ dn zp )
1038 2drop 2drop true exit ( !found )
1039 then ( prop$ dn zp )
1040 ['] fz-nmlook -rot ( prop$ xt dn fz )
1041 fzap-apply if ( prop$ dn le )
1042 entry-val$ 2swap 2drop false ( val$ found )
1044 2drop true ( !found )
1045 then ( [ val$ ] not-found? )
1049 : fz-print ( dn le -- false )
1050 entry-name$ type cr false
1053 : uz-print ( uz -- false )
1054 uzap-name$ type cr false
1057 : zap-print ( dn -- )
1058 dup get-zap ( dn zp )
1061 >uzap-ent swap dn-bsize ( uz len )
1062 ['] uz-print -rot ( xt uz len )
1063 uzap-apply ( false )
1066 ['] fz-print -rot ( xt dn fz )
1067 fzap-apply ( false )
1069 3drop false ( false )
1076 \ ZFS object set (DSL) routines
1079 1 constant pool-dir#
1081 : dd_head_dataset_obj ( dd -- n ) h# 8 + x@ ;
1082 : dd_child_dir_zapobj ( dd -- n ) h# 20 + x@ ;
1084 : ds_snapnames_zapobj ( ds -- n ) h# 20 + x@ ;
1085 : ds_bp ( ds -- p ) h# 80 + ;
1087 0 instance value mos-dn
1088 0 instance value obj-dir
1089 0 instance value root-dsl
1090 0 instance value fs-dn
1092 \ dn-cache contains dc-dn's contents at dc-blk#
1093 \ dc-dn will be either mos-dn or fs-dn
1094 0 instance value dn-cache
1095 0 instance value dc-dn
1096 0 instance value dc-blk#
1098 alias >dsl-dir dn_bonus
1099 alias >dsl-ds dn_bonus
1101 : #dn/blk ( dn -- n ) dn-bsize /dnode / ;
1103 \ read block into dn-cache
1104 : get-dnblk ( dn blk# -- )
1105 lblk#>bp dn-cache swap ( adr bp )
1106 dup bp-lsize swap read-bp ( )
1109 \ read obj# from objset dir dn into dnode
1110 : get-dnode ( dn obj# -- )
1113 2dup swap #dn/blk /mod ( dn obj# off# blk# )
1114 swap >r nip ( dn blk# r: off# )
1115 2dup dc-blk# <> ( dn blk# dn !blk-hit? r: off# )
1116 swap dc-dn <> or if ( dn blk# r: off# )
1117 \ cache miss, fill from dir
1121 then ( dn blk# r: off# )
1124 2drop r> /dnode * ( off )
1125 dn-cache + ( dn-adr )
1126 dnode /dnode move ( )
1129 \ read meta object set from uber-block
1131 mos-dn uber-block ub_rootbp ( adr bp )
1132 dup bp-lsize swap read-bp
1135 : get-mos-dnode ( obj# -- )
1136 mos-dn swap get-dnode
1140 : get-root-dsl ( -- )
1146 pool-dir# get-mos-dnode
1147 dnode obj-dir /dnode move
1150 obj-dir " root_dataset" zap-lookup if
1151 " no root_dataset" die
1154 dnode root-dsl /dnode move
1157 \ find snapshot of given dataset
1158 : snap-look ( snap$ ds-obj# -- [ss-obj# ] not-found? )
1159 get-mos-dnode dnode >dsl-ds ( snap$ ds )
1160 ds_snapnames_zapobj get-mos-dnode ( snap$ )
1161 dnode -rot zap-lookup ( [ss-obj# ] not-found? )
1164 \ dsl dir to dataset
1165 : dir>ds ( dn -- obj# ) >dsl-dir dd_head_dataset_obj ;
1167 \ look thru the dsl hierarchy for path
1168 \ this looks almost exactly like a FS directory lookup
1169 : dsl-lookup ( path$ -- [ ds-obj# ] not-found? )
1170 root-dsl >r ( path$ r: root-dn )
1172 ascii / left-parse-string ( path$ file$ r: dn )
1175 \ get child dir zap dnode
1176 r> >dsl-dir dd_child_dir_zapobj ( path$ file$ obj# )
1177 get-mos-dnode ( path$ file$ )
1179 \ check for snapshot names
1180 ascii @ left-parse-string ( path$ snap$ file$ )
1183 dnode -rot zap-lookup if ( path$ snap$ )
1185 2drop 2drop true exit ( not-found )
1186 then ( path$ snap$ obj# )
1187 get-mos-dnode ( path$ snap$ )
1189 \ lookup any snapshot name
1191 \ must be last path component
1192 2swap nip if ( snap$ )
1193 2drop true exit ( not-found )
1195 dnode dir>ds snap-look if ( )
1196 true exit ( not-found )
1198 false exit ( obj# found )
1199 else 2drop then ( path$ )
1201 dnode >r ( path$ r: dn )
1202 repeat ( path$ file$ r: dn)
1203 2drop 2drop r> drop ( )
1205 \ found it, return dataset obj#
1206 dnode dir>ds ( ds-obj# )
1207 false ( ds-obj# found )
1210 \ get objset from dataset
1211 : get-objset ( adr dn -- )
1212 >dsl-ds ds_bp dup bp-lsize swap read-bp
1217 \ ZFS file-system (ZPL) routines
1220 1 constant master-node#
1222 0 instance value bootfs-obj#
1223 0 instance value root-obj#
1224 0 instance value current-obj#
1225 0 instance value search-obj#
1227 instance defer fsize ( dn -- size )
1228 instance defer mode ( dn -- mode )
1229 instance defer parent ( dn -- obj# )
1230 instance defer readlink ( dst dn -- )
1233 \ routines when bonus pool contains a znode
1235 d# 264 constant /znode
1236 d# 56 constant /zn-slink
1238 : zp_mode ( zn -- n ) h# 48 + x@ ;
1239 : zp_size ( zn -- n ) h# 50 + x@ ;
1240 : zp_parent ( zn -- n ) h# 58 + x@ ;
1242 alias >znode dn_bonus
1244 : zn-fsize ( dn -- n ) >znode zp_size ;
1245 : zn-mode ( dn -- n ) >znode zp_mode ;
1246 : zn-parent ( dn -- n ) >znode zp_parent ;
1248 \ copy symlink target to dst
1249 : zn-readlink ( dst dn -- )
1250 dup zn-fsize tuck /zn-slink > if ( dst size dn )
1251 \ contents in 1st block
1252 temp-space over dn-bsize ( dst size dn t-adr bsize )
1253 rot 0 lblk#>bp read-bp ( dst size )
1254 temp-space ( dst size src )
1255 else ( dst size dn )
1257 >znode /znode + ( dst size src )
1258 then ( dst size src )
1263 \ routines when bonus pool contains sa's
1266 \ SA header size when link is in dn_bonus
1267 d# 16 constant /sahdr-link
1269 : sa_props ( sa -- n ) h# 4 + w@ ;
1271 : sa-hdrsz ( sa -- sz ) sa_props h# 7 >> ;
1275 : >sadata ( dn -- adr ) >sa dup sa-hdrsz + ;
1276 : sa-mode ( dn -- n ) >sadata x@ ;
1277 : sa-fsize ( dn -- n ) >sadata h# 8 + x@ ;
1278 : sa-parent ( dn -- n ) >sadata h# 28 + x@ ;
1280 \ copy symlink target to dst
1281 : sa-readlink ( dst dn -- )
1282 dup >sa sa-hdrsz /sahdr-link <> if
1283 \ contents in 1st attr of dn_spill
1284 temp-space over dn_spill ( dst dn t-adr bp )
1285 dup bp-lsize swap read-bp ( dst dn )
1286 sa-fsize ( dst size )
1287 temp-space dup sa-hdrsz + ( dst size src )
1289 \ content in bonus buf
1290 dup dn_bonus over dn_bonuslen + ( dst dn ebonus )
1291 swap sa-fsize tuck - ( dst size src )
1292 then ( dst size src )
1297 \ setup attr routines for dn
1298 : set-attr ( dn -- )
1299 dn_bonustype ot-sa# = if
1300 ['] sa-fsize to fsize
1302 ['] sa-parent to parent
1303 ['] sa-readlink to readlink
1305 ['] zn-fsize to fsize
1307 ['] zn-parent to parent
1308 ['] zn-readlink to readlink
1312 : ftype ( dn -- type ) mode h# f000 and ;
1313 : dir? ( dn -- flag ) ftype h# 4000 = ;
1314 : symlink? ( dn -- flag ) ftype h# a000 = ;
1316 \ read obj# from fs objset
1317 : get-fs-dnode ( obj# -- )
1319 fs-dn swap get-dnode ( )
1322 \ get root-obj# from dataset
1323 : get-rootobj# ( ds-obj# -- fsroot-obj# )
1326 fs-dn dnode get-objset
1328 \ get root obj# from master node
1329 master-node# get-fs-dnode
1330 dnode " ROOT" zap-lookup if
1332 then ( fsroot-obj# )
1335 : prop>rootobj# ( -- )
1336 obj-dir " pool_props" zap-lookup if
1337 " no pool_props" die
1340 dnode " bootfs" zap-lookup if
1343 get-rootobj# ( fsroot-obj# )
1346 : fs>rootobj# ( fs$ -- root-obj# not-found? )
1349 ascii / left-parse-string 2drop
1353 true exit ( not-found )
1356 get-rootobj# ( fsroot-obj# )
1357 false ( fsroot-obj# found )
1360 \ lookup file is current directory
1361 : dirlook ( file$ dn -- not-found? )
1362 \ . and .. are magic
1363 -rot 2dup " ." $= if ( dn file$ )
1364 3drop false exit ( found )
1368 2drop parent ( obj# )
1371 current-obj# to search-obj#
1373 true exit ( not-found )
1381 /buf-len instance buffer: fpath-buf
1382 /buf-len instance buffer: tpath-buf
1384 : tpath-buf$ ( -- path$ ) tpath-buf cscount ;
1385 : fpath-buf$ ( -- path$ ) fpath-buf cscount ;
1387 \ modify tail to account for symlink
1388 : follow-symlink ( tail$ -- tail$' )
1390 tpath-buf /buf-len erase
1391 tpath-buf dnode readlink
1393 \ append current path
1395 " /" tpath-buf$ $append ( tail$ )
1396 tpath-buf$ $append ( )
1400 fpath-buf /buf-len erase
1401 tpath-buf$ fpath-buf swap move
1402 fpath-buf$ ( path$ )
1404 \ get directory that starts changed path
1405 over c@ ascii / = if ( path$ )
1406 str++ root-obj# ( path$' obj# )
1408 search-obj# ( path$ obj# )
1410 get-fs-dnode ( path$ )
1414 \ open dnode at path
1415 : lookup ( path$ -- not-found? )
1417 \ get directory that starts path
1418 over c@ ascii / = if
1419 str++ root-obj# ( path$' obj# )
1421 current-obj# ( path$ obj# )
1423 get-fs-dnode ( path$ )
1426 \ lookup each path component
1428 ascii / left-parse-string ( path$ file$ )
1431 2drop true exit ( not-found )
1432 then ( path$ file$ )
1433 dnode dirlook if ( path$ )
1434 2drop true exit ( not-found )
1437 follow-symlink ( path$' )
1439 repeat ( path$ file$ )
1440 2drop 2drop false ( found )
1444 \ ZFS volume (ZVOL) routines
1446 1 constant zvol-data#
1447 2 constant zvol-prop#
1449 0 instance value zv-dn
1451 : get-zvol ( zvol$ -- not-found? )
1453 drop true exit ( failed )
1458 zv-dn dnode get-objset
1462 \ get zvol data dnode
1464 zv-dn zvol-data# get-dnode
1467 : zvol-size ( -- size )
1468 zv-dn zvol-prop# get-dnode
1469 dnode " size" zap-lookup if
1476 \ ZFS installation routines
1479 \ ZFS file interface
1485 constant /file-record
1487 d# 10 constant #opens
1488 #opens /file-record * constant /file-records
1490 /file-records instance buffer: file-records
1492 -1 instance value current-fd
1494 : fd>record ( fd -- rec ) /file-record * file-records + ;
1495 : file-offset@ ( -- off ) current-fd fd>record >offset x@ ;
1496 : file-offset! ( off -- ) current-fd fd>record >offset x! ;
1497 : file-dnode ( -- dn ) current-fd fd>record >dnode ;
1498 : file-size ( -- size ) current-fd fd>record >fsize x@ ;
1499 : file-bsize ( -- bsize ) file-dnode dn-bsize ;
1502 : get-slot ( -- fd false | true )
1504 i fd>record >busy x@ 0= if
1510 : free-slot ( fd -- )
1511 0 swap fd>record >busy x!
1514 \ init fd to offset 0 and copy dnode
1515 : init-fd ( fsize fd -- )
1516 fd>record ( fsize rec )
1518 dup >dnode dnode swap /dnode move
1519 dup >fsize rot swap x! ( rec )
1520 >offset 0 swap x! ( )
1524 : set-fd ( fd -- error? )
1525 dup fd>record >busy x@ 0= if ( fd )
1526 drop true exit ( failed )
1528 to current-fd false ( succeeded )
1531 \ read next fs block
1532 : file-bread ( adr -- )
1533 file-bsize ( adr len )
1534 file-offset@ over / ( adr len blk# )
1535 file-dnode swap lblk#>bp ( adr len bp )
1539 \ advance file io stack by n
1540 : fio+ ( # adr len n -- #+n adr+n len-n )
1541 dup file-offset@ + file-offset!
1542 dup >r - -rot ( len' # adr r: n )
1543 r@ + -rot ( adr' len' # r: n )
1544 r> + -rot ( #' adr' len' )
1551 /disk-block 6 * + ( size )
1552 \ ugh - sg proms can't free 512k allocations
1553 \ that aren't a multiple of 512k in size
1554 h# 8.0000 roundup ( size' )
1558 : allocate-buffers ( -- )
1559 alloc-size h# a0.0000 vmem-alloc dup 0= if
1562 dup to temp-space /max-bsize + ( adr )
1563 dup to dn-cache /max-bsize + ( adr )
1564 dup to blk-space /max-bsize + ( adr )
1565 dup to ind-cache /max-bsize + ( adr )
1566 dup to zap-space /max-bsize + ( adr )
1567 dup to uber-block /uber-block + ( adr )
1568 dup to mos-dn /dnode + ( adr )
1569 dup to obj-dir /dnode + ( adr )
1570 dup to root-dsl /dnode + ( adr )
1571 dup to fs-dn /dnode + ( adr )
1572 dup to zv-dn /dnode + ( adr )
1573 dup to dnode /dnode + ( adr )
1576 \ zero instance buffers
1577 file-records /file-records erase
1578 bootprop-buf /buf-len erase
1581 : release-buffers ( -- )
1582 temp-space alloc-size mem-free
1588 my-args dev-open dup 0= if
1599 : open-fs ( fs$ -- okay? )
1603 to root-obj# true ( succeeded )
1613 : open-file ( path$ -- fd true | false )
1615 \ open default fs if no open-fs
1617 prop>rootobj# to root-obj#
1621 2drop false exit ( failed )
1622 then -rot ( fd path$ )
1625 drop false exit ( failed )
1628 dnode fsize over init-fd
1629 true ( fd succeeded )
1632 : open-volume ( vol$ -- okay? )
1634 2drop false exit ( failed )
1635 then -rot ( fd vol$ )
1638 drop false exit ( failed )
1641 zvol-size over ( fd size fd )
1642 zvol-data init-fd ( fd )
1643 true ( fd succeeded )
1646 : close-file ( fd -- )
1650 : size-file ( fd -- size )
1651 set-fd if 0 else file-size then
1654 : seek-file ( off fd -- off true | false )
1656 drop false exit ( failed )
1659 dup file-size x> if ( off )
1660 drop false exit ( failed )
1662 dup file-offset! true ( off succeeded )
1665 : read-file ( adr len fd -- #read )
1666 set-fd if ( adr len )
1670 \ adjust len if reading past eof
1671 dup file-offset@ + file-size x> if
1672 dup file-offset@ + file-size - -
1674 dup 0= if nip exit then
1676 0 -rot ( #read adr len )
1678 \ initial partial block
1679 file-offset@ file-bsize mod ?dup if ( #read adr len off )
1680 temp-space file-bread
1681 2dup file-bsize swap - min ( #read adr len off cpy-len )
1682 2over drop -rot ( #read adr len adr off cpy-len )
1683 >r temp-space + swap ( #read adr len cpy-src adr r: cpy-len )
1684 r@ move r> fio+ ( #read' adr' len' )
1685 then ( #read adr len )
1687 dup file-bsize / 0 ?do ( #read adr len )
1689 file-bsize fio+ ( #read' adr' len' )
1690 loop ( #read adr len )
1692 \ final partial block
1693 dup if ( #read adr len )
1694 temp-space file-bread
1695 2dup temp-space -rot move ( #read adr len )
1696 dup fio+ ( #read' adr' 0 )
1697 then 2drop ( #read )
1700 : cinfo-file ( fd -- bsize fsize comp? )
1704 file-bsize file-size ( bsize fsize )
1705 \ zfs does internal compression
1706 0 ( bsize fsize comp? )
1710 \ read ramdisk fcode at rd-offset
1711 : get-rd ( adr len -- )
1712 rd-offset dev-ih read-disk
1716 " /" bootprop$ $append
1717 bootfs-obj# (xu.) bootprop$ $append
1718 bootprop$ encode-string " zfs-bootfs" ( propval propname )
1724 current-obj# -rot ( obj# dir$ )
1727 ." no such dir" cr exit
1729 dnode dir? 0= if ( obj# )
1731 ." not a dir" cr exit
1736 current-obj# get-fs-dnode