6041 SPARC boot should support LZ4
[unleashed.git] / usr / src / psm / stand / bootblks / zfs / common / zfs.fth
blob569a845f083176929464a02e37d8477ccb516ed7
2 \ CDDL HEADER START
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]
19 \ CDDL HEADER END
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
33 new-device
34    fs-pkg$  device-name  diag-cr?
36    0 instance value temp-space
39    \ 64b ops
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 )
46          2drop 2drop  -1         ( lt )
47       else  u>  if               ( x2.lo x1.lo )
48          2drop  1                ( gt )
49       else  swap 2dup u<  if     ( x1.lo x2.lo )
50          2drop  -1               ( lt )
51       else  u>  if               (  )
52          1                       ( gt )
53       else                       (  )
54          0                       ( eq )
55       then then then then        ( -1|0|1 )
56    ;
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
65    : (xu.)  ( u -- u$ )
66       numbuf /buf-len +  swap         ( adr u )
67       begin
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? )
72       until  drop                     ( adr )
73       dup  numbuf -  /buf-len swap -  ( adr len )
74    ;
76    \ pool name
77    /buf-len  instance buffer:  bootprop-buf
78    : bootprop$  ( -- prop$ )  bootprop-buf cscount  ;
80    \ decompression
81    \
82    \ uts/common/os/compress.c has a definitive theory of operation comment
83    \ on lzjb, but here's the reader's digest version:
84    \
85    \ repeated phrases are replaced by referenced to the original
86    \ e.g.,
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
88    \ becomes
89    \ y a d d a _ 6 11 , _ b l a h 5 10
90    \ where 6 11 means memmove(ptr, ptr - 6, 11)
91    \
92    \ data is separated from metadata with embedded copymap entries
93    \ every 8 items  e.g., 
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
97    \
98    \ the reference marks are encoded with match-bits and match-min
99    \ e.g.,
100    \ byte[0] = ((mlen - MATCH_MIN) << (NBBY - MATCH_BITS) | (off >> NBBY)
101    \ byte[1] = (uint8_t)off
102    \
104    : pow2  ( n -- 2**n )  1 swap lshift  ;
106    \ assume MATCH_BITS=6 and MATCH_MIN=3
107    6                       constant mbits
108    3                       constant mmin
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 )
116    ;
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 )
123       begin
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 )
131    ;
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 )
141       begin  2dup >  while
142          2swap  1 lshift            ( dend dst map mask'  r: src )
144          dup  8 pow2  =  if
145             \ fetch next copymap
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 )
155          else
156             r> dup 1+ >r  c@        ( map mask dend dst c  r: src' )
157             over c!  1+             ( map mask dend dst'  r: src )
158          then
159       repeat                        ( map mask dend dst  r: src )
160       2drop 2drop  r> drop          (  )
161    ;
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 )
174       or                                ( d s_addr d' )
175       rot                               ( s_addr d' d )
176       or                                ( s_addr s_len )
177     ;
179     4           constant STEPSIZE
180     8           constant COPYLENGTH
181     5           constant LASTLITERALS
182     4           constant ML_BITS
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')
189       2dup swap 4 move
190       swap 4 +
191       swap 4 +          ( dest+4 source+4 )
192     ;
194     \ do { LZ4_COPYPACKET(s, d) } while (d < e);
195     : lz4_copy ( e d s -- e d' s' )
196       begin                     ( e d s )
197         lz4_copystep
198         lz4_copystep            ( e d s )
199         over                    ( e d s d )
200         3 pick < 0=
201       until
202     ;
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.
208       rot                       ( dest len src )
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 )
215       \ main loop
216       begin 2dup < while
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 )
221          dup RUN_MASK = if
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 )
229            while
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 )
235            repeat
236            drop                 ( dest dest_end token s_end s_buf length )
237          then
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 )
246             " lz4 overflow" die
247          then
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
272            then
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 )
279            r> + r> r> drop < if
280              " lz4 format violation" die                \ LZ4 format violation
281            then
283            r> drop              \ drop original dest
284            drop
285            exit                 \ parsing done
286          then
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 )
309          -rot swap >r >r
310          < if
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 )
317          \ get matchlength
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 )
320            -1           \ flag to top
321            begin
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 )
325            while
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 )
330              d# 255 =
331            repeat
332            swap
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 )
353            case
354              1 of 3 endof
355              2 of 2 endof
356              3 of 3 endof
357                0
358            endcase
359            -                    \ ref -= dec
360            2dup swap 4 move     ( dest_end s_buf length op ref )
361            swap STEPSIZE 4 - +
362            swap                 ( dest_end s_buf length op ref )
363         else
364            lz4_copystep         ( dest_end s_buf length op ref )
365         then
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 )
375           5 pick over < if
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 )
383           begin
384             2dup <
385           while
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 )
391           repeat
393           nip                   ( dest_end s_buf ref op )
394           dup 4 pick = if
395             \ op == dest_end  we are done, cleanup
396             r> r> 2drop 2drop 2drop
397             exit
398           then
399                                 ( dest_end s_buf ref op R: dest s_end )
400           nip                   ( dest_end s_buf op )
401         else
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 )
405           lz4_copy
406           2drop                 ( dest_end s_buf op )
407        then
409        -rot r>                  ( op dest_end s_buf s_end R: dest )
410      repeat
412      r> drop
413      2drop
414      2drop
415    ;
417    \
418    \    ZFS block (SPA) routines
419    \
421    1           constant  def-comp#
422    2           constant  no-comp#
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
434    /gang-block 1-
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_comp      ( bp -- n )  h# 33 +  c@  ;
440    : blk_psize     ( bp -- n )  h# 34 +  w@  ;
441    : blk_lsize     ( bp -- n )  h# 36 +  w@  ;
442    : blk_birth     ( bp -- n )  h# 50 +  x@  ;
444    0 instance value dev-ih
445    0 instance value blk-space
446    0 instance value gang-space
448    : foff>doff  ( fs-off -- disk-off )    /disk-block *  h# 40.0000 +  ;
449    : fsz>dsz    ( fs-size -- disk-size )  1+  /disk-block *  ;
451    : bp-dsize  ( bp -- dsize )  blk_psize fsz>dsz  ;
452    : bp-lsize  ( bp -- lsize )  blk_lsize fsz>dsz  ;
454    : (read-dva)  ( adr len dva -- )
455       blk_offset foff>doff  dev-ih  read-disk
456    ;
458    : gang-read  ( adr len bp gb-adr -- )    tokenizer[ reveal ]tokenizer
460       \ read gang block
461       tuck  /gang-block rot  (read-dva)   ( adr len gb-adr )
463       \ loop through indirected bp's
464       dup  /blkp #blks/gang *             ( adr len gb-adr bp-list bp-list-len )
465       bounds  do                          ( adr len gb-adr )
466          i blk_offset x0=  ?leave
468          \ calc subordinate read len
469          over  i bp-dsize  min            ( adr len gb-adr sub-len )
470          2swap swap                       ( gb-adr sub-len len adr )
472          \ nested gang block - recurse with new gang block area
473          i blk_gang  if
474             2swap                         ( len adr gb-adr sub-len )
475             3dup  swap  /gang-block +     ( len adr gb-adr sub-len adr sub-len gb-adr' )
476             i swap  gang-read             ( len adr gb-adr sub-len )
477             2swap                         ( gb-adr sub-len len adr )
478          else
479             3dup  nip  swap               ( gb-adr sub-len len adr adr sub-len )
480             i (read-dva)                  ( gb-adr sub-len len adr )
481          then                             ( gb-adr sub-len len adr )
483          \ adjust adr,len and check if done
484          -rot  over -                     ( gb-adr adr sub-len len' )
485          -rot  +  swap                    ( gb-adr adr' len' )
486          dup 0=  ?leave
487          rot                              ( adr' len' gb-adr )
488       /blkp  +loop
489       3drop                               (  )
490    ;
492    : read-dva  ( adr len dva -- )
493       dup  blk_gang  if
494          gang-space  gang-read
495       else
496          (read-dva)
497       then
498    ;
500    \ block read that check for holes, gangs, compression, etc
501    : read-bp  ( adr len bp -- )
502       \ sparse block?
503       dup  blk_birth x0=  if
504          drop  erase  exit               (  )
505       then
507       \ no compression?
508       dup blk_comp  no-comp#  =  if
509          read-dva  exit                  (  )
510       then
512       \ lzjb?
513       dup blk_comp  lzjb-comp#  =  if
514          \ read into blk-space and de-compress
515          blk-space  over bp-dsize           ( adr len bp blk-adr rd-len )
516          rot  read-dva                      ( adr len )
517          blk-space -rot  lzjb               (  )
518          exit
519       then
521       dup blk_comp  dup lz4-comp#  <>   ( adr len bp comp lz4? )
522       swap  def-comp#  <>  and  if       ( adr len bp )
523          dup hex . ." BP: "
524          blk_comp decimal .
525          ." : bug, unknown compression algorithm: "
526          " only lzjb and lz4 supported"  die
527       then
529       \ read into blk-space and de-compress
530       blk-space  over bp-dsize           ( adr len bp blk-adr rd-len )
531       rot  read-dva                      ( adr len )
532       blk-space -rot  lz4                (  )
533    ;
535    \
536    \    ZFS vdev routines
537    \
539    h# 1.c000  constant /nvpairs
540    h# 4000    constant nvpairs-off
542    \
543    \ xdr packed nvlist
544    \
545    \  12B header
546    \  array of xdr packed nvpairs
547    \     4B encoded nvpair size
548    \     4B decoded nvpair size
549    \     4B name string size
550    \     name string
551    \     4B data type
552    \     4B # of data elements
553    \     data
554    \  8B of 0
555    \
556    d# 12      constant /nvhead
558    : >nvsize  ( nv -- size )  l@  ;
559    : >nvname  ( nv -- name$ )
560       /l 2* +  dup /l +  swap l@
561    ;
562    : >nvdata  ( nv -- data )
563       >nvname +  /l roundup
564    ;
566    \ convert nvdata to 64b int or string
567    : nvdata>x  ( nvdata -- x )
568       /l 2* +                   ( ptr )
569       dup /l + l@  swap l@      ( x.lo x.hi )
570       lxjoin                    ( x )
571    ;
572    alias nvdata>$ >nvname
574    : nv-lookup  ( nv name$ -- nvdata false  |  true )
575       rot /nvhead +               ( name$ nvpair )
576       begin  dup >nvsize  while
577          dup >r  >nvname          ( name$ nvname$  r: nvpair )
578          2over $=  if             ( name$  r: nvpair )
579             2drop  r> >nvdata     ( nvdata )
580             false exit            ( nvdata found )
581          then                     ( name$  r: nvpair )
582          r>  dup >nvsize  +       ( name$ nvpair' )
583       repeat
584       3drop  true                 ( not-found )
585    ;
587    : scan-vdev  ( -- )
588       temp-space /nvpairs nvpairs-off    ( adr len off )
589       dev-ih  read-disk                  (  )
590       temp-space " txg"  nv-lookup  if
591          " no txg nvpair"  die
592       then  nvdata>x                     ( txg )
593       x0=  if
594          " detached mirror"  die
595       then                               (  )
596       temp-space " name"  nv-lookup  if
597          " no name nvpair"  die
598       then  nvdata>$                     ( pool$ )
599       bootprop-buf swap  move            (  )
600    ;
603    \
604    \    ZFS ueber-block routines
605    \
607    d# 1024                  constant /uber-block
608    d# 128                   constant #ub/label
609    #ub/label /uber-block *  constant /ub-ring
610    h# 2.0000                constant ubring-off
612    : ub_magic      ( ub -- n )          x@  ;
613    : ub_txg        ( ub -- n )  h# 10 + x@  ;
614    : ub_timestamp  ( ub -- n )  h# 20 + x@  ;
615    : ub_rootbp     ( ub -- p )  h# 28 +     ;
617    0 instance value uber-block
619    : ub-cmp  ( ub1 ub2 -- best-ub )
621       \ ub1 wins if ub2 isn't valid
622       dup  ub_magic h# 00bab10c  x<>  if
623          drop  exit                  ( ub1 )
624       then
626       \ if ub1 is 0, ub2 wins by default
627       over 0=  if  nip  exit  then   ( ub2 )
629       \ 2 valid ubs, compare transaction groups
630       over ub_txg  over ub_txg       ( ub1 ub2 txg1 txg2 )
631       2dup x<  if
632          2drop nip  exit             ( ub2 )
633       then                           ( ub1 ub2 txg1 txg2 )
634       x>  if  drop  exit  then       ( ub1 )
636       \ same txg, check timestamps
637       over ub_timestamp  over ub_timestamp  x>  if
638          nip                         ( ub2 )
639       else
640          drop                        ( ub1 )
641       then
642    ;
644    \ find best uber-block in ring, and copy it to uber-block
645    : get-ub  ( -- )
646       temp-space  /ub-ring ubring-off       ( adr len off )
647       dev-ih  read-disk                     (  )
648       0  temp-space /ub-ring                ( null-ub adr len )
649       bounds  do                            ( ub )
650          i ub-cmp                           ( best-ub )
651       /uber-block +loop
653       \ make sure we found a valid ub
654       dup 0=  if  " no ub found" die  then
656       uber-block /uber-block  move          (  )
657    ;
660    \
661    \    ZFS dnode (DMU) routines
662    \
664    d# 44  constant ot-sa#
666    d# 512 constant /dnode
668    : dn_indblkshift   ( dn -- n )  h#   1 +  c@  ;
669    : dn_nlevels       ( dn -- n )  h#   2 +  c@  ;
670    : dn_bonustype     ( dn -- n )  h#   4 +  c@  ;
671    : dn_datablkszsec  ( dn -- n )  h#   8 +  w@  ;
672    : dn_bonuslen      ( dn -- n )  h#   a +  w@  ;
673    : dn_blkptr        ( dn -- p )  h#  40 +      ;
674    : dn_bonus         ( dn -- p )  h#  c0 +      ;
675    : dn_spill         ( dn -- p )  h# 180 +      ;
677    0 instance value dnode
679    \ indirect cache
680    \
681    \ ind-cache is a 1 block indirect block cache from dnode ic-dn
682    \
683    \ ic-bp and ic-bplim point into the ic-dn's block ptr array,
684    \ either in dn_blkptr or in ind-cache   ic-bp is the ic-blk#'th
685    \ block ptr, and ic-bplim is limit of the current bp array
686    \
687    \ the assumption is that reads will be sequential, so we can
688    \ just increment ic-bp
689    \
690    0 instance value  ind-cache
691    0 instance value  ic-dn
692    0 instance value  ic-blk#
693    0 instance value  ic-bp
694    0 instance value  ic-bplim
696    : dn-bsize    ( dn -- bsize )    dn_datablkszsec /disk-block  *  ;
697    : dn-indsize  ( dn -- indsize )  dn_indblkshift  pow2  ;
698    : dn-indmask  ( dn -- mask )     dn-indsize 1-  ;
700    \ recursively climb the block tree from the leaf to the root
701    : blk@lvl>bp  ( dn blk# lvl -- bp )   tokenizer[ reveal ]tokenizer
702       >r  /blkp *  over dn_nlevels         ( dn bp-off #lvls  r: lvl )
704       \ at top, just add dn_blkptr
705       r@  =  if                            ( dn bp-off  r: lvl )
706          swap dn_blkptr  +                 ( bp  r: lvl )
707          r> drop  exit                     ( bp )
708       then                                 ( dn bp-off  r: lvl )
710       \ shift bp-off down and find parent indir blk
711       2dup over  dn_indblkshift  rshift    ( dn bp-off dn blk#  r: lvl )
712       r> 1+  blk@lvl>bp                    ( dn bp-off bp )
714       \ read parent indir blk and index
715       rot tuck dn-indsize                  ( bp-off dn bp len )
716       ind-cache swap rot  read-bp          ( bp-off dn )
717       dn-indmask  and                      ( bp-off' )
718       ind-cache +                          ( bp )
719    ;
721    \ return end of current bp array
722    : bplim ( dn bp -- bp-lim )
723       over dn_nlevels  1  =  if
724           drop dn_blkptr              ( bp0 )
725           3 /blkp *  +                ( bplim )
726       else
727           1+  swap dn-indsize         ( bp+1 indsz )
728           roundup                     ( bplim )
729       then
730    ;
732    \ return the lblk#'th block ptr from dnode
733    : lblk#>bp  ( dn blk# -- bp )
734       2dup                               ( dn blk# dn blk# )
735       ic-blk# <>  swap  ic-dn  <>  or    ( dn blk# cache-miss? )
736       ic-bp  ic-bplim  =                 ( dn blk# cache-miss? cache-empty? )
737       or  if                             ( dn blk# )
738          2dup  1 blk@lvl>bp              ( dn blk# bp )
739          dup         to ic-bp            ( dn blk# bp )
740          swap        to ic-blk#          ( dn bp )
741          2dup bplim  to ic-bplim         ( dn bp )
742          over        to ic-dn
743       then  2drop                        (  )
744       ic-blk# 1+          to ic-blk#
745       ic-bp dup  /blkp +  to ic-bp       ( bp )
746    ;
749    \
750    \    ZFS attribute (ZAP) routines
751    \
753    1        constant  fzap#
754    3        constant  uzap#
756    d# 64    constant  /uzap
758    d# 24    constant  /lf-chunk
759    d# 21    constant  /lf-arr
760    h# ffff  constant  chain-end#
762    h# 100   constant /lf-buf
763    /lf-buf  instance buffer: leaf-value
764    /lf-buf  instance buffer: leaf-name
766    : +le              ( len off -- n )  +  w@  ;
767    : le_next          ( le -- n )  h# 2 +le  ;
768    : le_name_chunk    ( le -- n )  h# 4 +le  ;
769    : le_name_length   ( le -- n )  h# 6 +le  ;
770    : le_value_chunk   ( le -- n )  h# 8 +le  ;
771    : le_value_length  ( le -- n )  h# a +le  ;
773    : la_array  ( la -- adr )  1+  ;
774    : la_next   ( la -- n )    h# 16 +  w@  ;
776    0 instance value zap-space
778    \ setup leaf hash bounds
779    : >leaf-hash  ( dn lh -- hash-adr /hash )
780       /lf-chunk 2*  +                 ( dn hash-adr ) 
781       \ size = (bsize / 32) * 2
782       swap dn-bsize  4 rshift         ( hash-adr /hash )
783    ;
784    : >leaf-chunks  ( lf -- ch0 )  >leaf-hash +  ;
786    \ convert chunk # to leaf chunk
787    : ch#>lc  ( dn ch# -- lc )
788       /lf-chunk *                     ( dn lc-off )
789       swap zap-space  >leaf-chunks    ( lc-off ch0 )
790       +                               ( lc )
791    ;
793    \ assemble chunk chain into single buffer
794    : get-chunk-data  ( dn ch# adr -- )
795       dup >r  /lf-buf  erase          ( dn ch#  r: adr )
796       begin
797          2dup  ch#>lc  nip            ( dn la  r: adr )
798          dup la_array                 ( dn la la-arr  r: adr )
799          r@  /lf-arr  move            ( dn la  r: adr )
800          r>  /lf-arr +  >r            ( dn la  r: adr' )
801          la_next  dup chain-end#  =   ( dn la-ch# end?  r: adr )
802       until  r> 3drop                 (  )
803    ;
805    \ get leaf entry's name
806    : entry-name$  ( dn le -- name$ )
807       2dup le_name_chunk              ( dn le dn la-ch# )
808       leaf-name  get-chunk-data       ( dn le )
809       nip  le_name_length 1-          ( len )
810       leaf-name swap                  ( name$ )
811    ;
813    \ return entry value as int
814    : entry-int-val  ( dn le -- n )
815       le_value_chunk                  ( dn la-ch# )
816       leaf-value  get-chunk-data      (  )
817       leaf-value x@                   ( n )
818    ;
821 [ifdef] strlookup
822    \ get leaf entry's value as string
823    : entry-val$  ( dn le -- val$ )
824       2dup le_value_chunk             ( dn le dn la-ch# )
825       leaf-value  get-chunk-data      ( dn le )
826       nip le_value_length             ( len )
827       leaf-value swap                 ( name$ )
828    ;
829 [then]
831    \ apply xt to entry
832    : entry-apply  ( xt dn le -- xt dn false  |  ??? true )
833       over >r                    ( xt dn le  r: dn )
834       rot  dup >r  execute  if   ( ???  r: xt dn )
835          r> r>  2drop  true      ( ??? true )
836       else                       (  )
837          r> r>  false            ( xt dn false )
838       then
839    ;
840          
841    \ apply xt to every entry in chain
842    : chain-apply  ( xt dn ch# -- xt dn false  |  ??? true )
843       begin
844          2dup  ch#>lc  nip               ( xt dn le )
845          dup >r  entry-apply  if         ( ???  r: le )
846             r> drop  true  exit          ( ??? found )
847          then                            ( xt dn  r: le )
848          r> le_next                      ( xt dn ch# )
849          dup chain-end#  =               ( xt dn ch# end? )
850       until  drop                        ( xt dn )
851       false                              ( xt dn false )
852    ;
854    \ apply xt to every entry in leaf
855    : leaf-apply  ( xt dn blk# -- xt dn false  |  ??? true )
857       \ read zap leaf into zap-space
858       2dup lblk#>bp                       ( xt dn blk# bp )
859       nip  over dn-bsize  zap-space       ( xt dn bp len adr )
860       swap rot  read-bp                   ( xt dn )
862      \ call chunk-look for every valid chunk list
863       dup zap-space  >leaf-hash           ( xt dn hash-adr /hash )
864       bounds  do                          ( xt dn )
865          i w@  dup chain-end#  <>  if     ( xt dn ch# )
866             chain-apply  if               ( ??? )
867                unloop  true  exit         ( ??? found )
868             then                          ( xt dn )
869          else  drop  then                 ( xt dn )
870       /w  +loop
871       false                               ( xt dn not-found )
872    ;
874    \ apply xt to every entry in fzap
875    : fzap-apply  ( xt dn fz -- ??? not-found? )
877       \ blk# 1 is always the 1st leaf
878       >r  1 leaf-apply  if              ( ???  r: fz )
879          r> drop  true  exit            ( ??? found )
880       then  r>                          ( xt dn fz )
882       \ call leaf-apply on every non-duplicate hash entry
883       \ embedded hash is in 2nd half of fzap block
884       over dn-bsize  tuck +             ( xt dn bsize hash-eadr )
885       swap 2dup  2/  -                  ( xt dn hash-eadr bsize hash-adr )
886       nip  do                           ( xt dn )
887          i x@  dup 1  <>  if            ( xt dn blk# )
888             leaf-apply  if              ( ??? )
889                unloop  true  exit       ( ??? found )
890             then                        ( xt dn )
891          else  drop  then               ( xt dn )
892       /x  +loop
893       2drop  false                      ( not-found )
894    ;
896    : mze_value  ( uz -- n )  x@  ;
897    : mze_name   ( uz -- p )  h# e +  ;
899    : uzap-name$  ( uz -- name$ )  mze_name  cscount  ;
901    \ apply xt to each entry in micro-zap
902    : uzap-apply ( xt uz len -- ??? not-found? )
903       bounds  do                      ( xt )
904          i swap  dup >r               ( uz xt  r: xt )
905          execute  if                  ( ???  r: xt )
906             r> drop                   ( ??? )
907             unloop true  exit         ( ??? found )
908          then  r>                     ( xt )
909       /uzap  +loop
910       drop  false                     ( not-found )
911    ;
913    \ match by name
914    : fz-nmlook  ( prop$ dn le -- prop$ false  |  prop$ dn le true )
915       2dup entry-name$        ( prop$ dn le name$ )
916       2rot 2swap              ( dn le prop$ name$ )
917       2over  $=  if           ( dn le prop$ )
918          2swap  true          ( prop$ dn le true )
919       else                    ( dn le prop$ )
920          2swap 2drop  false   ( prop$ false )
921       then                    ( prop$ false  |  prop$ dn le true )
922    ;
924    \ match by name
925    : uz-nmlook  ( prop$ uz -- prop$ false  |  prop$ uz true )
926       dup >r  uzap-name$      ( prop$ name$  r: uz )
927       2over  $=  if           ( prop$  r: uz )
928          r>  true             ( prop$ uz true )
929       else                    ( prop$  r: uz )
930          r> drop  false       ( prop$ false )
931       then                    ( prop$ false  |  prop$ uz true )
932    ;
934    : zap-type   ( zp -- n )     h#  7 + c@  ;
935    : >uzap-ent  ( adr -- ent )  h# 40 +  ;
937    \ read zap block into temp-space
938    : get-zap  ( dn -- zp )
939       dup  0 lblk#>bp    ( dn bp )
940       swap dn-bsize      ( bp len )
941       temp-space swap    ( bp adr len )
942       rot read-bp        (  )
943       temp-space         ( zp )
944    ;
946    \ find prop in zap dnode
947    : zap-lookup  ( dn prop$ -- [ n ] not-found? )
948       rot  dup get-zap                    ( prop$ dn zp )
949       dup zap-type  case
950          uzap#  of
951             >uzap-ent  swap dn-bsize      ( prop$ uz len )
952             ['] uz-nmlook  -rot           ( prop$ xt uz len )
953             uzap-apply  if                ( prop$ uz )
954                mze_value  -rot 2drop      ( n )
955                false                      ( n found )
956             else                          ( prop$ )
957                2drop  true                ( !found )
958             then                          ( [ n ] not-found? )
959          endof
960          fzap#  of
961             ['] fz-nmlook  -rot           ( prop$ xt dn fz )
962             fzap-apply  if                ( prop$ dn le )
963                entry-int-val              ( prop$ n )
964                -rot 2drop  false          ( n found )
965             else                          ( prop$ )
966                2drop  true                ( !found )
967             then                          ( [ n ] not-found? )
968          endof
969          3drop 2drop  true                ( !found )
970       endcase                             ( [ n ] not-found? )
971    ;
973 [ifdef] strlookup
974    : zap-lookup-str  ( dn prop$ -- [ val$ ] not-found? )
975       rot  dup get-zap                    ( prop$ dn zp )
976       dup zap-type  fzap#  <>  if         ( prop$ dn zp )
977          2drop 2drop  true  exit          ( !found )
978       then                                ( prop$ dn zp )
979       ['] fz-nmlook -rot                  ( prop$ xt dn fz )
980       fzap-apply  if                      ( prop$ dn le )
981          entry-val$  2swap 2drop  false   ( val$ found )
982       else                                ( prop$ )
983          2drop  true                      ( !found )
984       then                                ( [ val$ ] not-found? )
985    ;
986 [then]
988    : fz-print  ( dn le -- false )
989       entry-name$  type cr  false
990    ;
992    : uz-print  ( uz -- false )
993       uzap-name$  type cr  false
994    ;
996    : zap-print  ( dn -- )
997       dup get-zap                         ( dn zp )
998       dup zap-type  case
999          uzap#  of
1000             >uzap-ent  swap dn-bsize      ( uz len )
1001             ['] uz-print  -rot            ( xt uz len )
1002             uzap-apply                    ( false )
1003          endof
1004          fzap#  of
1005             ['] fz-print -rot             ( xt dn fz )
1006             fzap-apply                    ( false )
1007          endof
1008          3drop  false                     ( false )
1009       endcase                             ( false )
1010       drop                                (  )
1011    ;
1014    \
1015    \    ZFS object set (DSL) routines
1016    \
1018    1 constant pool-dir#
1020    : dd_head_dataset_obj  ( dd -- n )  h#  8 +  x@  ;
1021    : dd_child_dir_zapobj  ( dd -- n )  h# 20 +  x@  ;
1023    : ds_snapnames_zapobj  ( ds -- n )  h# 20 +  x@  ;
1024    : ds_bp                ( ds -- p )  h# 80 +      ;
1026    0 instance value mos-dn
1027    0 instance value obj-dir
1028    0 instance value root-dsl
1029    0 instance value fs-dn
1031    \ dn-cache contains dc-dn's contents at dc-blk#
1032    \ dc-dn will be either mos-dn or fs-dn
1033    0 instance value dn-cache
1034    0 instance value dc-dn
1035    0 instance value dc-blk#
1037    alias  >dsl-dir  dn_bonus
1038    alias  >dsl-ds   dn_bonus
1040    : #dn/blk  ( dn -- n )     dn-bsize /dnode  /  ;
1042    \ read block into dn-cache
1043    : get-dnblk  ( dn blk# -- )
1044       lblk#>bp  dn-cache swap         ( adr bp )
1045       dup bp-lsize swap  read-bp      (  )
1046    ;
1048    \ read obj# from objset dir dn into dnode
1049    : get-dnode  ( dn obj# -- )
1051       \ check dn-cache
1052       2dup  swap #dn/blk  /mod       ( dn obj# off# blk# )
1053       swap >r  nip                   ( dn blk#  r: off# )
1054       2dup  dc-blk#  <>              ( dn blk# dn !blk-hit?  r: off# )
1055       swap dc-dn  <>  or  if         ( dn blk#  r: off# )
1056          \ cache miss, fill from dir
1057          2dup  get-dnblk
1058          over  to dc-dn
1059          dup   to dc-blk#
1060       then                           ( dn blk#  r: off# )
1062       \ index and copy
1063       2drop r>  /dnode *             ( off )
1064       dn-cache +                     ( dn-adr )
1065       dnode  /dnode  move            (  )
1066    ;
1068    \ read meta object set from uber-block
1069    : get-mos  ( -- )
1070       mos-dn uber-block ub_rootbp    ( adr bp )
1071       dup bp-lsize swap read-bp
1072    ;
1074    : get-mos-dnode  ( obj# -- )
1075       mos-dn swap  get-dnode
1076    ;
1078    \ get root dataset
1079    : get-root-dsl  ( -- )
1081       \ read MOS
1082       get-mos
1084       \ read object dir
1085       pool-dir#  get-mos-dnode
1086       dnode obj-dir  /dnode  move
1088       \ read root dataset
1089       obj-dir " root_dataset"  zap-lookup  if
1090          " no root_dataset"  die
1091       then                                   ( obj# )
1092       get-mos-dnode                          (  )
1093       dnode root-dsl  /dnode  move
1094    ;
1096    \ find snapshot of given dataset
1097    : snap-look  ( snap$ ds-obj# -- [ss-obj# ] not-found? )
1098       get-mos-dnode  dnode >dsl-ds         ( snap$ ds )
1099       ds_snapnames_zapobj  get-mos-dnode   ( snap$ )
1100       dnode -rot  zap-lookup               ( [ss-obj# ] not-found? )
1101    ;
1103    \ dsl dir to dataset
1104    : dir>ds   ( dn -- obj# )  >dsl-dir dd_head_dataset_obj  ;
1106    \ look thru the dsl hierarchy for path
1107    \ this looks almost exactly like a FS directory lookup
1108    : dsl-lookup ( path$ -- [ ds-obj# ] not-found? )
1109       root-dsl >r                                 ( path$  r: root-dn )
1110       begin
1111          ascii /  left-parse-string               ( path$ file$  r: dn )
1112       dup  while
1114          \ get child dir zap dnode
1115          r>  >dsl-dir dd_child_dir_zapobj         ( path$ file$ obj# )
1116          get-mos-dnode                            ( path$ file$ )
1118          \ check for snapshot names
1119          ascii @  left-parse-string               ( path$ snap$ file$ )
1121          \ search it
1122          dnode -rot zap-lookup  if                ( path$ snap$ )
1123             \ not found
1124             2drop 2drop true  exit                ( not-found )
1125          then                                     ( path$ snap$ obj# )
1126          get-mos-dnode                            ( path$ snap$ )
1128          \ lookup any snapshot name
1129          dup  if
1130             \ must be last path component
1131             2swap  nip  if                        ( snap$ )
1132                2drop true  exit                   ( not-found )
1133             then
1134             dnode dir>ds  snap-look  if           (  )
1135                true  exit                         ( not-found )
1136             then                                  ( obj# )
1137             false  exit                           ( obj# found )
1138          else  2drop  then                        ( path$ )
1140          dnode >r                                 ( path$  r: dn )
1141       repeat                                      ( path$ file$  r: dn)
1142       2drop 2drop  r> drop                        (  )
1144       \ found it, return dataset obj#
1145       dnode  dir>ds                               ( ds-obj# )
1146       false                                       ( ds-obj# found )
1147    ;
1149    \ get objset from dataset
1150    : get-objset  ( adr dn -- )
1151       >dsl-ds ds_bp  dup bp-lsize swap  read-bp
1152    ;
1155    \
1156    \    ZFS file-system (ZPL) routines
1157    \
1159    1       constant master-node#
1161    0 instance value bootfs-obj#
1162    0 instance value root-obj#
1163    0 instance value current-obj#
1164    0 instance value search-obj#
1166    instance defer fsize         ( dn -- size )
1167    instance defer mode          ( dn -- mode )
1168    instance defer parent        ( dn -- obj# )
1169    instance defer readlink      ( dst dn -- )
1171    \
1172    \ routines when bonus pool contains a znode
1173    \
1174    d# 264  constant /znode
1175    d#  56  constant /zn-slink
1177    : zp_mode    ( zn -- n )  h# 48 +  x@  ;
1178    : zp_size    ( zn -- n )  h# 50 +  x@  ;
1179    : zp_parent  ( zn -- n )  h# 58 +  x@  ;
1181    alias  >znode  dn_bonus
1183    : zn-fsize     ( dn -- n )  >znode zp_size    ;
1184    : zn-mode      ( dn -- n )  >znode zp_mode    ;
1185    : zn-parent    ( dn -- n )  >znode zp_parent  ;
1187    \ copy symlink target to dst
1188    : zn-readlink  ( dst dn -- )
1189       dup zn-fsize  tuck /zn-slink  >  if ( dst size dn )
1190          \ contents in 1st block
1191          temp-space  over dn-bsize        ( dst size dn t-adr bsize )
1192          rot  0 lblk#>bp  read-bp         ( dst size )
1193          temp-space                       ( dst size src )
1194       else                                ( dst size dn )
1195          \ contents in dnode
1196          >znode  /znode +                 ( dst size src )
1197       then                                ( dst size src )
1198       -rot  move                          (  )
1199    ;
1201    \
1202    \ routines when bonus pool contains sa's
1203    \
1205    \ SA header size when link is in dn_bonus
1206    d# 16  constant  /sahdr-link
1208    : sa_props  ( sa -- n )   h# 4 +  w@  ;
1210    : sa-hdrsz  ( sa -- sz )  sa_props h# 7  >>  ;
1212    alias  >sa  dn_bonus
1214    : >sadata    ( dn -- adr )  >sa dup  sa-hdrsz  +  ;
1215    : sa-mode    ( dn -- n )    >sadata           x@  ;
1216    : sa-fsize   ( dn -- n )    >sadata  h#  8 +  x@  ;
1217    : sa-parent  ( dn -- n )    >sadata  h# 28 +  x@  ;
1219    \ copy symlink target to dst
1220    : sa-readlink  ( dst dn -- )
1221       dup  >sa sa-hdrsz  /sahdr-link  <>  if
1222          \ contents in 1st attr of dn_spill
1223          temp-space  over dn_spill           ( dst dn t-adr bp )
1224          dup bp-lsize  swap  read-bp         ( dst dn )
1225          sa-fsize                            ( dst size )
1226          temp-space dup sa-hdrsz  +          ( dst size src )
1227       else                                   ( dst dn )
1228          \ content in bonus buf
1229          dup dn_bonus  over  dn_bonuslen  +  ( dst dn ebonus )
1230          swap sa-fsize  tuck  -              ( dst size src )
1231       then                                   ( dst size src )
1232       -rot  move                             (  )
1233    ;
1236    \ setup attr routines for dn
1237    : set-attr  ( dn -- )
1238       dn_bonustype  ot-sa#  =  if
1239          ['] sa-fsize     to  fsize
1240          ['] sa-mode      to  mode
1241          ['] sa-parent    to  parent
1242          ['] sa-readlink  to  readlink
1243       else
1244          ['] zn-fsize     to  fsize
1245          ['] zn-mode      to  mode
1246          ['] zn-parent    to  parent
1247          ['] zn-readlink  to  readlink
1248       then
1249    ;
1251    : ftype     ( dn -- type )  mode   h# f000  and  ;
1252    : dir?      ( dn -- flag )  ftype  h# 4000  =  ;
1253    : symlink?  ( dn -- flag )  ftype  h# a000  =  ;
1255    \ read obj# from fs objset
1256    : get-fs-dnode  ( obj# -- )
1257       dup to current-obj#
1258       fs-dn swap  get-dnode    (  )
1259    ;
1261    \ get root-obj# from dataset
1262    : get-rootobj#  ( ds-obj# -- fsroot-obj# )
1263       dup to bootfs-obj#
1264       get-mos-dnode                   (  )
1265       fs-dn dnode  get-objset
1267       \ get root obj# from master node
1268       master-node#  get-fs-dnode
1269       dnode  " ROOT"  zap-lookup  if
1270          " no ROOT"  die
1271       then                             ( fsroot-obj# )
1272    ;
1274    : prop>rootobj#  ( -- )
1275       obj-dir " pool_props" zap-lookup  if
1276          " no pool_props"  die
1277       then                               ( prop-obj# )
1278       get-mos-dnode                      (  )
1279       dnode " bootfs" zap-lookup  if
1280          " no bootfs"  die
1281       then                               ( ds-obj# )
1282       get-rootobj#                       ( fsroot-obj# )
1283    ;
1285    : fs>rootobj#  ( fs$ -- root-obj# not-found? )
1287       \ skip pool name
1288       ascii /  left-parse-string  2drop
1290       \ lookup fs in dsl 
1291       dsl-lookup  if                   (  )
1292          true  exit                    ( not-found )
1293       then                             ( ds-obj# )
1295       get-rootobj#                     ( fsroot-obj# )
1296       false                            ( fsroot-obj# found )
1297    ;
1299    \ lookup file is current directory
1300    : dirlook  ( file$ dn -- not-found? )
1301       \ . and .. are magic
1302       -rot  2dup " ."  $=  if     ( dn file$ )
1303          3drop  false  exit       ( found )
1304       then
1306       2dup " .."  $=  if
1307          2drop  parent            ( obj# )
1308       else                        ( dn file$ )
1309          \ search dir
1310          current-obj# to search-obj#
1311          zap-lookup  if           (  )
1312             true  exit            ( not-found )
1313          then                     ( obj# )
1314       then                        ( obj# )
1315       get-fs-dnode
1316       dnode  set-attr
1317       false                       ( found )
1318    ;
1320    /buf-len  instance buffer: fpath-buf
1321    /buf-len  instance buffer: tpath-buf
1323    : tpath-buf$  ( -- path$ )  tpath-buf cscount  ;
1324    : fpath-buf$  ( -- path$ )  fpath-buf cscount  ;
1326    \ modify tail to account for symlink
1327    : follow-symlink  ( tail$ -- tail$' )
1328       \ read target
1329       tpath-buf /buf-len  erase
1330       tpath-buf dnode  readlink
1332       \ append current path
1333       ?dup  if                                  ( tail$ )
1334          " /" tpath-buf$  $append               ( tail$ )
1335          tpath-buf$  $append                    (  )
1336       else  drop  then                          (  )
1338       \ copy to fpath
1339       fpath-buf  /buf-len  erase
1340       tpath-buf$  fpath-buf  swap move
1341       fpath-buf$                                ( path$ )
1343       \ get directory that starts changed path
1344       over c@  ascii /  =  if                   ( path$ )
1345          str++  root-obj#                       ( path$' obj# )
1346       else                                      ( path$ )
1347          search-obj#                            ( path$ obj# )
1348       then                                      ( path$ obj# )
1349       get-fs-dnode                              ( path$ )
1350       dnode  set-attr
1351    ;
1353    \ open dnode at path
1354    : lookup  ( path$ -- not-found? )
1356       \ get directory that starts path
1357       over c@  ascii /  =  if
1358          str++  root-obj#                         ( path$' obj# )
1359       else
1360          current-obj#                             ( path$ obj# )
1361       then                                        ( path$ obj# )
1362       get-fs-dnode                                ( path$ )
1363       dnode  set-attr
1365       \ lookup each path component
1366       begin                                       ( path$ )
1367          ascii /  left-parse-string               ( path$ file$ )
1368       dup  while
1369          dnode dir?  0=  if
1370             2drop true  exit                      ( not-found )
1371          then                                     ( path$ file$ )
1372          dnode dirlook  if                        ( path$ )
1373             2drop true  exit                      ( not-found )
1374          then                                     ( path$ )
1375          dnode symlink?  if
1376             follow-symlink                        ( path$' )
1377          then                                     ( path$ )
1378       repeat                                      ( path$ file$ )
1379       2drop 2drop  false                          ( found )
1380    ;
1382    \
1383    \   ZFS volume (ZVOL) routines
1384    \
1385    1 constant  zvol-data#
1386    2 constant  zvol-prop#
1388    0 instance value zv-dn
1390    : get-zvol  ( zvol$ -- not-found? )
1391       dsl-lookup  if
1392          drop true  exit           ( failed )
1393       then                         ( ds-obj# )
1395       \ get zvol objset
1396       get-mos-dnode                (  )
1397       zv-dn dnode  get-objset
1398       false                        ( succeeded )
1399    ;
1401    \ get zvol data dnode
1402    : zvol-data  ( -- )
1403       zv-dn zvol-data#  get-dnode
1404    ;
1406    : zvol-size  ( -- size )
1407        zv-dn zvol-prop#   get-dnode
1408        dnode " size"  zap-lookup  if
1409           " no zvol size"  die
1410        then                            ( size )
1411    ;
1412        
1414    \
1415    \    ZFS installation routines
1416    \
1418    \ ZFS file interface
1419    struct
1420       /x     field >busy
1421       /x     field >offset
1422       /x     field >fsize
1423       /dnode field >dnode
1424    constant /file-record
1426    d# 10                  constant #opens
1427    #opens /file-record *  constant /file-records
1429    /file-records  instance buffer: file-records
1431    -1 instance value current-fd
1433    : fd>record     ( fd -- rec )  /file-record *  file-records +  ;
1434    : file-offset@  ( -- off )     current-fd fd>record >offset  x@  ;
1435    : file-offset!  ( off -- )     current-fd fd>record >offset  x!  ;
1436    : file-dnode    ( -- dn )      current-fd fd>record >dnode  ;
1437    : file-size     ( -- size )    current-fd fd>record >fsize  x@  ;
1438    : file-bsize    ( -- bsize )   file-dnode  dn-bsize  ;
1440    \ find free fd slot
1441    : get-slot  ( -- fd false | true )
1442       #opens 0  do
1443          i fd>record >busy x@  0=  if
1444             i false  unloop exit
1445          then
1446       loop  true
1447    ;
1449    : free-slot  ( fd -- )
1450       0 swap  fd>record >busy  x!
1451    ;
1453    \ init fd to offset 0 and copy dnode
1454    : init-fd  ( fsize fd -- )
1455       fd>record                ( fsize rec )
1456       dup  >busy  1 swap  x!
1457       dup  >dnode  dnode swap  /dnode  move
1458       dup  >fsize  rot swap  x!     ( rec )
1459       >offset  0 swap  x!      (  )
1460    ;
1462    \ make fd current
1463    : set-fd  ( fd -- error? )
1464       dup fd>record  >busy x@  0=  if   ( fd )
1465          drop true  exit                ( failed )
1466       then                              ( fd )
1467       to current-fd  false              ( succeeded )
1468    ;
1470    \ read next fs block
1471    : file-bread  ( adr -- )
1472       file-bsize                      ( adr len )
1473       file-offset@ over  /            ( adr len blk# )
1474       file-dnode swap  lblk#>bp       ( adr len bp )
1475       read-bp                         ( )
1476    ;
1478    \ advance file io stack by n
1479    : fio+  ( # adr len n -- #+n adr+n len-n )
1480       dup file-offset@ +  file-offset!
1481       dup >r  -  -rot   ( len' # adr  r: n )
1482       r@  +  -rot       ( adr' len' #  r: n )
1483       r>  +  -rot       ( #' adr' len' )
1484    ;
1487    /max-bsize    5 *
1488    /uber-block        +
1489    /dnode        6 *  +
1490    /disk-block   6 *  +    ( size )
1491    \ ugh - sg proms can't free 512k allocations
1492    \ that aren't a multiple of 512k in size
1493    h# 8.0000  roundup      ( size' )
1494    constant  alloc-size
1497    : allocate-buffers  ( -- )
1498       alloc-size h# a0.0000 vmem-alloc  dup 0=  if
1499          " no memory"  die
1500       then                                ( adr )
1501       dup to temp-space    /max-bsize  +  ( adr )
1502       dup to dn-cache      /max-bsize  +  ( adr )
1503       dup to blk-space     /max-bsize  +  ( adr )
1504       dup to ind-cache     /max-bsize  +  ( adr )
1505       dup to zap-space     /max-bsize  +  ( adr )
1506       dup to uber-block    /uber-block +  ( adr )
1507       dup to mos-dn        /dnode      +  ( adr )
1508       dup to obj-dir       /dnode      +  ( adr )
1509       dup to root-dsl      /dnode      +  ( adr )
1510       dup to fs-dn         /dnode      +  ( adr )
1511       dup to zv-dn         /dnode      +  ( adr )
1512       dup to dnode         /dnode      +  ( adr )
1513           to gang-space                   (  )
1515       \ zero instance buffers
1516       file-records /file-records  erase
1517       bootprop-buf /buf-len  erase
1518    ;
1520    : release-buffers  ( -- )
1521       temp-space  alloc-size  mem-free
1522    ;
1524    external
1526    : open ( -- okay? )
1527       my-args dev-open  dup 0=  if
1528          exit                       ( failed )
1529       then  to dev-ih
1531       allocate-buffers
1532       scan-vdev
1533       get-ub
1534       get-root-dsl
1535       true
1536    ;
1538    : open-fs  ( fs$ -- okay? )
1539       fs>rootobj#  if        (  )
1540          false               ( failed )
1541       else                   ( obj# )
1542          to root-obj#  true  ( succeeded )
1543       then                   ( okay? )
1544    ;
1546    : close  ( -- )
1547       dev-ih dev-close
1548       0 to dev-ih
1549       release-buffers
1550    ;
1552    : open-file  ( path$ -- fd true | false )
1554       \ open default fs if no open-fs
1555       root-obj# 0=  if
1556          prop>rootobj#  to root-obj#
1557       then
1559       get-slot  if
1560          2drop false  exit         ( failed )
1561       then  -rot                   ( fd path$ )
1563       lookup  if                   ( fd )
1564          drop false  exit          ( failed )
1565       then                         ( fd )
1567       dnode fsize  over init-fd
1568       true                         ( fd succeeded )
1569    ;
1571    : open-volume ( vol$ -- okay? )
1572       get-slot  if
1573          2drop false  exit         ( failed )
1574       then  -rot                   ( fd vol$ )
1576       get-zvol  if                 ( fd )
1577          drop false  exit          ( failed )
1578       then
1580       zvol-size over               ( fd size fd )
1581       zvol-data init-fd            ( fd )
1582       true                         ( fd succeeded )
1583    ;
1584       
1585    : close-file  ( fd -- )
1586       free-slot   (  )
1587    ;
1589    : size-file  ( fd -- size )
1590       set-fd  if  0  else  file-size  then
1591    ;
1593    : seek-file  ( off fd -- off true | false )
1594       set-fd  if                ( off )
1595          drop false  exit       ( failed )
1596       then                      ( off )
1598       dup file-size x>  if      ( off )
1599          drop false  exit       ( failed )
1600       then                      ( off )
1601       dup  file-offset!  true   ( off succeeded )
1602    ;
1604    : read-file  ( adr len fd -- #read )
1605       set-fd  if                   ( adr len )
1606          2drop 0  exit             ( 0 )
1607       then                         ( adr len )
1609       \ adjust len if reading past eof
1610       dup  file-offset@ +  file-size  x>  if
1611          dup  file-offset@ +  file-size -  -
1612       then
1613       dup 0=  if  nip exit  then
1615       0 -rot                              ( #read adr len )
1617       \ initial partial block
1618       file-offset@ file-bsize  mod  ?dup  if  ( #read adr len off )
1619          temp-space  file-bread
1620          2dup  file-bsize  swap -  min    ( #read adr len off cpy-len )
1621          2over drop -rot                  ( #read adr len adr off cpy-len )
1622          >r  temp-space +  swap           ( #read adr len cpy-src adr  r: cpy-len )
1623          r@  move  r> fio+                ( #read' adr' len' )
1624       then                                ( #read adr len )
1626       dup file-bsize /  0  ?do            ( #read adr len )
1627          over  file-bread
1628          file-bsize fio+                  ( #read' adr' len' )
1629       loop                                ( #read adr len )
1631       \ final partial block
1632       dup  if                             ( #read adr len )
1633          temp-space  file-bread
1634          2dup temp-space -rot  move       ( #read adr len )
1635          dup fio+                         ( #read' adr' 0 )
1636       then  2drop                         ( #read )
1637    ;
1639    : cinfo-file  ( fd -- bsize fsize comp? )
1640       set-fd  if
1641          0 0 0
1642       else
1643          file-bsize  file-size             ( bsize fsize )
1644          \ zfs does internal compression
1645          0                                 ( bsize fsize comp? )
1646       then
1647    ;
1649    \ read ramdisk fcode at rd-offset
1650    : get-rd   ( adr len -- )
1651       rd-offset dev-ih  read-disk
1652    ;
1654    : bootprop
1655       " /"  bootprop$  $append
1656       bootfs-obj# (xu.)  bootprop$  $append
1657       bootprop$  encode-string  " zfs-bootfs"   ( propval propname )
1658       true
1659    ;
1662    : chdir  ( dir$ -- )
1663       current-obj# -rot            ( obj# dir$ )
1664       lookup  if                   ( obj# )
1665          to current-obj#           (  )
1666          ." no such dir" cr  exit
1667       then                         ( obj# )
1668       dnode dir?  0=  if           ( obj# )
1669          to current-obj#           (  )
1670          ." not a dir" cr  exit
1671       then  drop                   (  )
1672    ;
1674    : dir  ( -- )
1675       current-obj# get-fs-dnode
1676       dnode zap-print
1677    ;
1679 finish-device
1680 pop-package