$title$
140 | $if(subtitle)$ 141 |$subtitle$
142 | $endif$ 143 | $if(date)$ 144 | 147 | 148 | 149 | $else$ 150 | $if(author)$ 151 | 152 | $endif$ 153 | $endif$ 154 |$subtitle$
142 | $endif$ 143 | $if(date)$ 144 | 147 | 148 | 149 | $else$ 150 | $if(author)$ 151 | 152 | $endif$ 153 | $endif$ 154 |Click here for a printer friendly PDF version.
`{=html} 12 | 13 | # On-disk Format 14 | 15 | ## Vdev 16 | 17 | ZFS storage pools are essentially a collection of _virtual devices_, or _vdevs_, 18 | which are arranged in a tree 19 | with physical vdevs sitting at leaves. 20 | @fig:vdev illustrates such a tree representing 21 | a sample pool configuration containing two mirrors, 22 | each of which has two disks. 23 | 24 | ```{.figure} 25 | { 26 | "path" : "Figures/zfs_vdev", 27 | "caption" : "Vdev Tree", 28 | "label" : "vdev", 29 | "options" : "scale=1,center", 30 | "place" : "ht" 31 | } 32 | ``` 33 | 34 | ## Label and Uberblock 35 | 36 | Each physical vdev within a storage pool contains four identical copies of 256KB structure 37 | called a vdev _label_ (`vdev_label_t`). 38 | The vdev label 39 | contains information describing this physical vdev 40 | and all other vdevs which share a common top-level vdev as an ancestor. 41 | For example, 42 | in @fig:vdev, 43 | the vdev label on D3 44 | would contain information describing D3, D4, and M2. 45 | Any copy of the labels can be used to access and verify the pool. 46 | There are two labels at the front of the device 47 | and two labels at the back. 48 | @fig:label illustrates the physical layout of vdev labels. 49 | 50 | ```{.figure} 51 | { 52 | "path" : "Figures/zfs_label", 53 | "caption" : "From label to MOS", 54 | "label" : "label", 55 | "options" : "width=\\textwidth", 56 | "place" : "ht" 57 | } 58 | ``` 59 | 60 | An _uberblock_ (`uberblock_t`) contains information 61 | necessary to access the contents of the pool. 62 | Only one uberblock in the pool is _active_ at any time. 63 | The uberblock with the highest transaction group number 64 | and valid checksum is the active uberblock. 65 | @fig:ub illustrates the layout of an active uberblock, 66 | and how accessing the storage pool can start from it. 67 | 68 | ```{.figure} 69 | { 70 | "path" : "Figures/zfs_ub", 71 | "caption" : "From Uberblock to MOS", 72 | "label" : "ub", 73 | "options" : "width=\\textwidth", 74 | "place" : "ht" 75 | } 76 | ``` 77 | 78 | ## Block Pointer 79 | 80 | Data is transferred between disk and main memory in units called blocks. 81 | A block pointer (`blkptr_t`) is a 128 byte ZFS structure 82 | used to physically locate, verify, and describe blocks of data on disk. 83 | The layout of a normal blkptr is shown as @fig:blkptr. 84 | Note that, 85 | the size of a block is described by three different fields: 86 | _psize_, _lsize_, and _asize_. 87 | 88 | ```{.figure} 89 | { 90 | "path" : "Figures/zfs_blkptr", 91 | "caption" : "Block Pointer Layout", 92 | "label" : "blkptr", 93 | "options" : "scale=.8,center", 94 | "place" : "ht" 95 | } 96 | ``` 97 | 98 | * _lsize_: Logical size. 99 | The size of the data without compression, raidz or gang overhead. 100 | * _psize_: Physical size of the block on disk after compression 101 | * _asize_: Allocated size, 102 | total size of all blocks allocated to hold this data 103 | including any gang headers or raid-Z parity information 104 | 105 | Normally, 106 | block pointers point (via their DVAs) to a block which holds data. 107 | If the data that we need to store is very small, 108 | this is an inefficient use of space, 109 | Additionally, reading these small blocks tends to generate 110 | more random reads. 111 | Embedded-data Block Pointers was introduced. 112 | It allows small pieces of data 113 | (the "payload", upto 112 bytes) embedded in the block pointer, 114 | the block pointer doesn't point to anything then. 115 | The layout of an embedded block pointer is as @fig:embedded. 116 | 117 | ```{.figure} 118 | { 119 | "path" : "Figures/zfs_embedded_blkptr", 120 | "caption" : "Embedded Block Pointer Layout", 121 | "label" : "embedded", 122 | "options" : "scale=.8,center", 123 | "place" : "ht" 124 | } 125 | ``` 126 | 127 | ## DMU 128 | 129 | The _Data Management Unit_, 130 | or _DMU_ groups blocks into logical units 131 | called _objects_. 132 | Almost everything in ZFS is an object. 133 | Objects are defined by 512 bytes structures called _dnodes_ (`dnode_phys_t`). 134 | A dnode describes and organizes a collection of blocks making up an object, 135 | for example, 136 | `dn_type` determines the type of the object, 137 | `dn_blkptr` stores the block pointers for block addressing. 138 | A dnode has a limited number (`dn_nblkptr`) of block pointers to describe an object's data. 139 | For a dnode using the largest data block size (128KB) 140 | and containing the maximum number of block pointers (3), 141 | the largest object size it can represent is 384 KB. 142 | To allow larger objects, 143 | indirect blocks are introduced, 144 | the largest indirect block (128KB) can hold up to 1024 block pointers, 145 | so that 384MB object can be represented without the next level of indirection. 146 | The `dn_nlevel` field tells total levels of addressing. 147 | @fig:indirect illustrates a 3-levels indirect addressing. 148 | 149 | ```{.figure} 150 | { 151 | "path" : "Figures/zfs_indirect", 152 | "caption" : "Indirect block addressing", 153 | "label" : "indirect", 154 | "options" : "scale=1,center", 155 | "place" : "ht" 156 | } 157 | ``` 158 | 159 | Related objects can be further grouped by the DMU into _object sets_. 160 | ZFS allows users to create four kinds of object sets: 161 | _filesystems_, _clones_, _snapshots_, and _volumes_. 162 | 163 | ## DSL 164 | 165 | The _Dataset and Snapshot Layer_, or _DSL_ 166 | is for describing 167 | and managing relationships-between and properties-of object sets. 168 | 169 | Each object set is represented in the DSL as a _dataset_ (`dsl_dataset_phys_t`). 170 | Datasets are grouped into collections called _Dataset Dircectories_, 171 | which manages a related grouping of datasets 172 | and the properties associated with that grouping. 173 | A DSL directory always has exactly one _active_ dataset. 174 | All other datasets under the directory are related to the active dataset 175 | through _snapshots_, _clones_, or _child/parent dependencies_. 176 | 177 | ## MOS 178 | 179 | The DSL is implementd as an object set of type `DMU_OST_META`, 180 | which is often called the _Meta Object Set_, or _MOS_. 181 | There is a single distinguished object in the Meta Object Set, 182 | called the _object directory_. 183 | Object directory is always located in the $1^{st}$ element of the dnode array 184 | (index starts from $0$). 185 | All other objects can be located by traversing 186 | through a set of object references starting at this object. 187 | 188 | The object directory is implemented as a _ZAP_ object 189 | that is made up of name/value pairs. 190 | The object directory contains 191 | _root\_dataset_, _config_, _free\_bpobj_, 192 | and some other attribute pairs. 193 | 194 | @fig:mos illustrates a realistic layout of the MOS of a sample pool, 195 | from which a data set (e.g., file system) can be accessed. 196 | 197 | ```{.figure} 198 | { 199 | "path" : "Figures/zfs_mos", 200 | "caption" : "MOS", 201 | "label" : "mos", 202 | "options" : "width=\\textwidth", 203 | "place" : "ht" 204 | } 205 | ``` 206 | 207 | @fig:fs illustrates the path from a data set object to 208 | user data (contents of the sample file `/sbin/zdump`). 209 | 210 | ```{.figure} 211 | { 212 | "path" : "Figures/zfs_fs", 213 | "caption" : "From the data set to user data", 214 | "label" : "fs", 215 | "options" : "width=\\textwidth", 216 | "place" : "ht" 217 | } 218 | ``` 219 | 220 | ## ZAP 221 | 222 | The _ZFS Attributes Processors_, or _ZAPs_ are objects 223 | used to store attributes in the form of name-value pairs. 224 | ZAPs come in two forms: 225 | _micro ZAP_ for small number of attributes 226 | and _fat ZAP_ for large number of attributes. 227 | Both of them are arranged 228 | based on hash of the attribute's name. 229 | Directories in ZFS are implemented as ZAP objects. 230 | 231 | # On-Disk Data Walk (Or: Where's My Data) 232 | 233 | This part (title and content) was inspired by 234 | Max Bruning's great demostration -- 235 | [ZFS On-Disk Data Walk (Or: Where's My Data)](http://www.osdevcon.org/2008/files/osdevcon2008-max.pdf) 236 | , and even better, 237 | his 238 | [training material](https://www.yumpu.com/en/document/view/3947186/zfs-on-disk-data-walk-or-wheres-my-data-bruning-systems). 239 | He used the modified[^mod] `mdb`, `zdb`, 240 | and `dd` to read and dump ZFS data structure from physical devices 241 | to illustrate the ZFS on-disk layout, 242 | from vdev label to content of a file. 243 | 244 | There is not `mdb` equivalent on Linux, 245 | and I don't want to switch among tools from time to time, 246 | so that I wrote a simple tool to do all the things, 247 | reading (via `open` and `read` system calls), 248 | decompressing (by calling _liblz4_ functions) data from the physical vdev, 249 | and dumping the ZFS physical data structures as JSON format. 250 | It doesn't call any function of the ZFS libraries. 251 | A few helpers are still used 252 | because I was too lazy to write my own, 253 | perhaps I will remove all of them in the future. 254 | However the core functions 255 | such as `spa_xxxx`, `dmu_xxxx`, `dsl_xxxx`, `zio_xxxx`, are avoided. 256 | 257 | [^mod]: by himself `👍`{=html}`\lower.6ex\hbox{\includegraphics{Figures/1F44D}}`{=latex} 258 | 259 | ## Environment 260 | 261 | ```bash 262 | $ cat /etc/os-release 263 | NAME="Ubuntu" 264 | VERSION="20.04.2 LTS (Focal Fossa)" 265 | .... output omitted .... 266 | $ sudo apt install libzpool2linux libzfs2linux libzfslinux-dev 267 | $ sudo apt install libnvpair1linux libjson-c-dev liblz4-dev 268 | ``` 269 | 270 | ## Preparation 271 | 272 | #. Clone and build `zdump` tool. 273 | #. Create a new file system, with a very simple hierarchy. 274 | Note that, 275 | as the `zdump` tool only supports lz4, 276 | the default compression algorithm of ZFS on Linux, 277 | don't set the `compression` property for creating ZFS. 278 | 279 | ```bash 280 | $ sudo zfs create -V 4G dpool/zvol0 281 | $ sudo fdisk -l /dev/zd0 | head -1 282 | Disk /dev/zd0: 4 GiB, 4294967296 bytes, 8388608 sectors 283 | $ sudo dmsetup create zdisk0 --table '0 8388607 linear /dev/zd0 0' 284 | $ sudo mkdir /mnt/zwalk 285 | $ sudo zpool create -f -m /mnt/zwalk zwalk /dev/mapper/zdisk0 286 | $ sudo zfs list 287 | NAME USED AVAIL REFER MOUNTPOINT 288 | zwalk 840K 3.62G 192K /mnt/zwalk 289 | $ sudo mkdir /mnt/zwalk/{sbin,var,usr} 290 | $ sudo su -c 'printf "#!/bin/bash\n\necho Hello ZFS\n" >/mnt/zwalk/sbin/zdump' 291 | $ cat /mnt/zwalk/sbin/zdump 292 | #!/bin/bash 293 | 294 | echo Hello ZFS 295 | ``` 296 | 297 | To clean up after the walk: 298 | 299 | ```bash 300 | $ sudo zpool destroy zwalk 301 | $ sudo dmsetup remove zdisk0 302 | $ sudo zfs destroy dpool/zvol0 303 | ``` 304 | 305 | #. Add the user into `disk` group 306 | so that `sudo` is not needed to read the block device file. 307 | 308 | ```bash 309 | $ sudo usermod -aG disk $(whoami) 310 | ``` 311 | 312 | ## Walk the data. 313 | 314 | #. The first step is dumping the label and active uberblock. 315 | Note that the `offset`, `psize`, and `lsize` are hexadecimal. 316 | 317 | ```bash 318 | $ zdump --label /dev/mapper/zdisk0:0:40000/40000 319 | { 320 | "Vdev Label":{ 321 | "name":"zwalk", 322 | "version":5000, 323 | "uberblock":{ 324 | "magic":"0x0000000000bab10", 325 | "version":5000, 326 | "txg":10, 327 | "rootbp":{ 328 | "vdev":"0", 329 | "offset":"3801e000", 330 | "asize":"2000", 331 | "psize":"1000", 332 | "lsize":"1000", 333 | "compressed":"uncompressed" 334 | } 335 | } 336 | } 337 | } 338 | ``` 339 | 340 | #. Dump dnode of the MOS, 341 | using the `offset` ($3801e000$), 342 | `psize` ($1000$), and `lsize` ($1000$) we got from the previous step. 343 | 344 | ```bash 345 | $ zdump --mos /dev/mapper/zdisk0:"3801e000":1000/1000 346 | { 347 | "MOS":{ 348 | "os_type":"META", 349 | "dnonde":{ 350 | "dn_type":"DMU dnode", 351 | "dn_bonustype":0, 352 | "dn_indblkshift":17, 353 | "dn_nlevels":2, 354 | "dn_nblkptr":3, 355 | "dn_blkptr":[ 356 | { 357 | "vdev":"0", 358 | "offset":"4e000", 359 | "asize":"2000", 360 | "psize":"2000", 361 | "lsize":"20000", 362 | "compressed":"lz4" 363 | }, 364 | .... output omitted .... 365 | ] 366 | } 367 | } 368 | } 369 | ``` 370 | 371 | #. From output of the previous step, 372 | we notice that the MOS uses $2$-levels indirect addressing (`dn_nlevels` was $2$[^zvol]), 373 | so we need to find the $0^{th}$ level block pointer to access to the data block. 374 | The `lsize` of each block pointer is $16$K, 375 | that can contain $32$ dnodes. 376 | 377 | [^zvol]: If we create the ZVOL with `-s` option, 378 | there will only one level of block pointer. 379 | 380 | ```bash 381 | $ zdump --indirect-blkptr /dev/mapper/zdisk0:"4e000":20000/2000:2 382 | { 383 | "[L0]":[ 384 | { 385 | "vdev":"0", 386 | "offset":"28008000", 387 | "asize":"2000", 388 | "psize":"2000", 389 | "lsize":"4000", 390 | "compressed":"lz4" 391 | }, 392 | { 393 | "vdev":"0", 394 | "offset":"2802c000", 395 | "asize":"2000", 396 | "psize":"2000", 397 | "lsize":"4000", 398 | "compressed":"lz4" 399 | }, 400 | { 401 | "vdev":"0", 402 | "offset":"0", 403 | "asize":"0", 404 | "psize":"200", 405 | "lsize":"200", 406 | "compressed":"inherit" 407 | }, 408 | { 409 | "vdev":"0", 410 | "offset":"0", 411 | "asize":"0", 412 | "psize":"200", 413 | "lsize":"200", 414 | "compressed":"inherit" 415 | }, 416 | { 417 | "vdev":"0", 418 | "offset":"28006000", 419 | "asize":"2000", 420 | "psize":"2000", 421 | "lsize":"4000", 422 | "compressed":"lz4" 423 | }, 424 | .... output omitted .... 425 | ] 426 | } 427 | ``` 428 | 429 | #. Dump the MOS object directory, 430 | which is the $1^{st}$ object (the $0^{th}$ is not used) in the dnode array. 431 | The MOS is a fat ZAP object, 432 | whose entries will be dumped as well as its dnode. 433 | We will use the `root_dataset` object to move forward. 434 | 435 | ```bash 436 | $ zdump --mos-objdir /dev/mapper/zdisk0:"28008000":4000/2000 437 | { 438 | "dnode":{ 439 | "dn_type":"object directory", 440 | "dn_bonustype":0, 441 | "dn_indblkshift":17, 442 | "dn_nlevels":1, 443 | "dn_nblkptr":3, 444 | "dn_blkptr":[ 445 | { 446 | "vdev":"0", 447 | "offset":"10000", 448 | "asize":"2000", 449 | "psize":"2000", 450 | "lsize":"4000", 451 | "compressed":"lz4" 452 | }, 453 | { 454 | "vdev":"0", 455 | "offset":"12000", 456 | "asize":"2000", 457 | "psize":"2000", 458 | "lsize":"4000", 459 | "compressed":"lz4" 460 | }, 461 | { 462 | "vdev":"0", 463 | "offset":"0", 464 | "asize":"0", 465 | "psize":"200", 466 | "lsize":"200", 467 | "compressed":"inherit" 468 | } 469 | ] 470 | }, 471 | "FZAP":{ 472 | "zap_block_type":"ZBT_HEADER", 473 | "zap_magic":"0x00000002f52ab2a", 474 | "zap_num_entries":13, 475 | "zap_table_phys":{ 476 | "zt_blk":0 477 | } 478 | }, 479 | "FZAP leaf":{ 480 | "entries":[ 481 | { 482 | "name":"root_dataset", 483 | "value":32 484 | }, 485 | .... output omitted .... 486 | { 487 | "name":"config", 488 | "value":61 489 | }, 490 | .... output omitted .... 491 | ] 492 | } 493 | } 494 | ``` 495 | 496 | #. Dump the root data set. 497 | From the previous step, 498 | we knew that it's the $32^{nd}$ item in the dnode array, 499 | therefore, 500 | we seek it in the $1^{st}$ block (with the offset $2802c000$). 501 | `dsl_dir_phys_t` is stored 502 | in the `dn_bonus` field of dnode of the root data set object. 503 | We can see that the head data set object is the $54^{th}$ object, 504 | located in the same block as the root data set's. 505 | 506 | ```bash 507 | $ zdump --mos-rootds /dev/mapper/zdisk0:"2802c000":4000/2000:32 508 | { 509 | "dnode":{ 510 | "dn_type":"DSL directory", 511 | "dn_bonustype":12, 512 | "dn_indblkshift":17, 513 | "dn_nlevels":1, 514 | "dn_nblkptr":1, 515 | "dn_blkptr":[ 516 | { 517 | "vdev":"0", 518 | "offset":"0", 519 | "asize":"0", 520 | "psize":"200", 521 | "lsize":"200", 522 | "compressed":"inherit" 523 | } 524 | ] 525 | }, 526 | "DSL":{ 527 | "dd_creation_time":"....", 528 | "dd_child_dir_zapobj":34, 529 | "dd_head_dataset_obj":54 530 | } 531 | } 532 | ``` 533 | 534 | #. Dump the childmap and head data set. 535 | The later will be used to move forward to ZPL. 536 | 537 | ```bash 538 | $ zdump --mos-childmap /dev/mapper/zdisk0:"2802c000":4000/2000:34 539 | { 540 | "Embedded Block Pointer":{ 541 | "type":0, 542 | "psize":78, 543 | "lsize":512, 544 | "compressed":"lz4", 545 | "Micro ZAP":[ 546 | { 547 | "mze_name":"$MOS", 548 | "mze_value":35 549 | }, 550 | { 551 | "mze_name":"$FREE", 552 | "mze_value":38 553 | }, 554 | { 555 | "mze_name":"$ORIGIN", 556 | "mze_value":42 557 | } 558 | ] 559 | } 560 | } 561 | $ zdump --mos-headds /dev/mapper/zdisk0:"2802c000":4000/2000:54 562 | { 563 | "Head data set":{ 564 | "ds_dir_obj":32, 565 | "ds_creation_time":"Tue May 18 08:51:28", 566 | "ds_create_txg":1, 567 | "ds_bp":{ 568 | "vdev":"0", 569 | "offset":"8028000", 570 | "asize":"2000", 571 | "psize":"1000", 572 | "lsize":"1000", 573 | "compressed":"uncompressed" 574 | } 575 | }, 576 | "Object Set":{ 577 | "os_type":"ZPL", 578 | "dnonde":{ 579 | "dn_type":"DMU dnode", 580 | "dn_bonustype":0, 581 | "dn_indblkshift":17, 582 | "dn_nlevels":6, 583 | "dn_nblkptr":3, 584 | "dn_blkptr":[ 585 | { 586 | "vdev":"0", 587 | "offset":"8024000", 588 | "asize":"2000", 589 | "psize":"2000", 590 | "lsize":"20000", 591 | "compressed":"lz4" 592 | }, 593 | .... output omitted .... 594 | ] 595 | } 596 | } 597 | } 598 | ``` 599 | 600 | #. Note that 6-levels indirect block pointer is used, 601 | we need to walk down to the L0 block pointer first. 602 | 603 | ```bash 604 | $ zdump --indirect-blkptr /dev/mapper/zdisk0:"8024000":20000/2000:6 605 | { 606 | "[L4]":[ 607 | { 608 | "vdev":"0", 609 | "offset":"3801c000", 610 | "asize":"2000", 611 | "psize":"2000", 612 | "lsize":"20000", 613 | "compressed":"lz4" 614 | }, 615 | .... output omitted .... 616 | ], 617 | "[L3]":[ 618 | { 619 | "vdev":"0", 620 | "offset":"2802a000", 621 | "asize":"2000", 622 | "psize":"2000", 623 | "lsize":"20000", 624 | "compressed":"lz4" 625 | }, 626 | .... output omitted .... 627 | ], 628 | "[L2]":[ 629 | { 630 | "vdev":"0", 631 | "offset":"8022000", 632 | "asize":"2000", 633 | "psize":"2000", 634 | "lsize":"20000", 635 | "compressed":"lz4" 636 | }, 637 | .... output omitted .... 638 | ], 639 | "[L1]":[ 640 | { 641 | "vdev":"0", 642 | "offset":"4c000", 643 | "asize":"2000", 644 | "psize":"2000", 645 | "lsize":"20000", 646 | "compressed":"lz4" 647 | }, 648 | .... output omitted .... 649 | ], 650 | "[L0]":[ 651 | { 652 | "vdev":"0", 653 | "offset":"46000", 654 | "asize":"2000", 655 | "psize":"2000", 656 | "lsize":"4000", 657 | "compressed":"lz4" 658 | }, 659 | { 660 | "vdev":"0", 661 | "offset":"4a000", 662 | "asize":"2000", 663 | "psize":"2000", 664 | "lsize":"4000", 665 | "compressed":"lz4" 666 | }, 667 | { 668 | "vdev":"0", 669 | "offset":"0", 670 | "asize":"0", 671 | "psize":"200", 672 | "lsize":"200", 673 | "compressed":"inherit" 674 | }, 675 | { 676 | "vdev":"0", 677 | "offset":"0", 678 | "asize":"0", 679 | "psize":"200", 680 | "lsize":"200", 681 | "compressed":"inherit" 682 | }, 683 | { 684 | "vdev":"0", 685 | "offset":"48000", 686 | "asize":"2000", 687 | "psize":"2000", 688 | "lsize":"4000", 689 | "compressed":"lz4" 690 | }, 691 | .... output omitted .... 692 | ] 693 | } 694 | ``` 695 | 696 | #. Dump the master node, 697 | which is a micro ZAP object and fixed in the $1^{st}$ dnode in the array. 698 | 699 | ```bash 700 | $ zdump --headds-masternode /dev/mapper/zdisk0:"46000":4000/2000 701 | { 702 | "dnode":{ 703 | "dn_type":"ZFS master node", 704 | "dn_bonustype":0, 705 | "dn_indblkshift":17, 706 | "dn_nlevels":1, 707 | "dn_nblkptr":3, 708 | "dn_blkptr":[ 709 | { 710 | "vdev":"0", 711 | "offset":"2000", 712 | "asize":"2000", 713 | "psize":"200", 714 | "lsize":"200", 715 | "compressed":"uncompressed" 716 | }, 717 | .... output omitted .... 718 | ] 719 | }, 720 | "Micro ZAP":[ 721 | .... output omitted .... 722 | { 723 | "mze_name":"ROOT", 724 | "mze_value":34 725 | } 726 | ] 727 | } 728 | ``` 729 | 730 | #. The ROOT object ("/") is the $34^{th}$ object, 731 | located in the $1^{st}$ block (offset $4a000$). 732 | In this simplest case, 733 | the ROOT's dnode contains _embedded block pointer_, 734 | it is a micro ZAP object. 735 | 736 | ```bash 737 | $ zdump --root-dir /dev/mapper/zdisk0:"4a000":4000/2000:34 738 | { 739 | "Micro ZAP":[ 740 | { 741 | "mze_name":"sbin", 742 | "mze_value":2 743 | }, 744 | { 745 | "mze_name":"var", 746 | "mze_value":3 747 | }, 748 | { 749 | "mze_name":"usr", 750 | "mze_value":4 751 | } 752 | ] 753 | } 754 | ``` 755 | 756 | #. Dump the `sbin` directory, 757 | the $2^{nd}$ object in the $0^{th}$ block, 758 | from the output of indirect block pointer dumping, 759 | we knew that the block is located at $46000$, 760 | the object contains embedded block pointer again. 761 | 762 | ```bash 763 | $ zdump --dir /dev/mapper/zdisk0:"46000":4000/2000:2 764 | { 765 | "Micro ZAP":[ 766 | { 767 | "mze_name":"zdump", 768 | "mze_value":128 769 | }, 770 | { 771 | "mze_name":"", 772 | "mze_value":0 773 | }, 774 | { 775 | "mze_name":"", 776 | "mze_value":0 777 | } 778 | ] 779 | } 780 | ``` 781 | 782 | #. The `/sbin/zdump` file is the$128^{th}$ object, 783 | located in the $4^{th}$ block (offset $48000$), 784 | let's dump it. 785 | 786 | ```bash 787 | $ zdump --text /dev/mapper/zdisk0:"48000":4000/2000:128 788 | #!/bin/bash 789 | 790 | echo Hello ZFS 791 | ``` 792 | 793 | # Recovering removed file 794 | 795 | \textcolor{red}{TBD} 796 | -------------------------------------------------------------------------------- /docs/zfs_internals.md.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahrens/zfsondisk/30ae2531352ab0d9d5ee9af82ce39a2cd45ccd79/docs/zfs_internals.md.pdf -------------------------------------------------------------------------------- /ondiskformatfinal.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ahrens/zfsondisk/30ae2531352ab0d9d5ee9af82ce39a2cd45ccd79/ondiskformatfinal.odt -------------------------------------------------------------------------------- /zwalk/src/Makefile: -------------------------------------------------------------------------------- 1 | CC = clang 2 | 3 | # FIXME: /usr/src/zfs/include shouldn't be needed! 4 | CFLAGS += -I/usr/include/libspl -I/usr/include/libzfs -I/usr/src/zfs-0.8.3/include 5 | 6 | CFLAGS += -D_FILE_OFFSET_BITS=64 \ 7 | -D_LARGEFILE64_SOURCE \ 8 | -Wall -g -O2 9 | 10 | LDFLAGS = -llz4 -ljson-c -lzpool -lnvpair 11 | 12 | all: zdump 13 | 14 | zdump: zdump.o 15 | $(CC) $(LDFLAGS) $^ -o $@ 16 | 17 | zdump.o: zdump.c 18 | $(CC) $(CFLAGS) -c $^ -o $@ 19 | 20 | clean: 21 | rm -f zdump.o zdump 22 | -------------------------------------------------------------------------------- /zwalk/src/TODO.md: -------------------------------------------------------------------------------- 1 | #. check nvlist_xxxx return value, 2 | otherwise, segfault if block is invalid 3 | #. zdump_dnode: check dn\_type and dn\_bonustype range, 4 | otherwise, zdump\_dnode crashes. 5 | #. Remove all dependencies on ZFS libraries, 6 | only data structure definition needed. 7 | #. support multiple blocks file. 8 | -------------------------------------------------------------------------------- /zwalk/src/zdump.c: -------------------------------------------------------------------------------- 1 | #include