--- /dev/null
+/*
+ * PROJECT: FreeLoader
+ * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: BTRFS volume boot sector for FreeLoader
+ * COPYRIGHT: Copyright 2018 Victor Perevertkin (victor@perevertkin.ru)
+ */
+
+#include <asm.inc>
+#include <freeldr/include/arch/pc/x86common.h>
+
+.code16
+
+CHUNK_MAP_OFFSET = HEX(8200) // max - 256 chunks (chunk is 24 bytes)
+DATA_BUFFER_SEGMENT = HEX(9a0) // here we will store FS structures read from disk
+
+FIRST_SUPERBLOCK_OFFSET = HEX(80) //in sectors
+
+ROOT_TREE_OBJECTID = 1
+EXTENT_TREE_OBJECTID = 2
+CHUNK_TREE_OBJECTID = 3
+DEV_TREE_OBJECTID = 4
+FS_TREE_OBJECTID = 5
+FIRST_CHUNK_TREE_OBJECTID = 256
+
+DIR_ITEM_KEY = 84
+EXTENT_DATA_KEY = 108
+ROOT_ITEM_KEY = 132
+EXTENT_ITEM_KEY = 168
+CHUNK_ITEM_KEY = 228
+
+BTRFS_FT_REG_FILE = 1
+
+KEY_SIZE = 17
+MAX_CHUNK_ITEMS = 255
+
+start:
+ jmp short main
+ nop
+// globals
+ChunkMapSize:
+ .byte 0
+BootDrive:
+ .byte 0
+PartitionStartLBA:
+ .quad HEX(3f) // default value. Setup tool have to overwrite it upon installation
+
+TreeRootAddress:
+ .quad 0
+ChunkRootAddress:
+ .quad 0
+FsRootAddress:
+ .quad 0
+
+NodeSize:
+ .long 0
+LeafSize:
+ .long 0
+StripeSize:
+ .long 0
+SysChunkSize:
+ .long 0
+
+TreeRootLevel:
+ .byte 0
+ChunkRootLevel:
+ .byte 0
+FsRootLevel:
+ .byte 0
+
+main:
+ xor eax, eax // Setup segment registers
+ mov ds, ax // Make DS correct
+ mov es, ax // Make ES correct
+ mov ss, ax // Make SS correct
+ mov sp, HEX(7c00) // Setup a stack
+ mov bp, sp
+
+ mov byte ptr [BootDrive], dl
+
+// Now check if this computer supports extended reads. This boot sector will not work without it
+CheckInt13hExtensions:
+ mov ah, HEX(41) // AH = 41h
+ mov bx, HEX(55aa) // BX = 55AAh
+ int HEX(13) // IBM/MS INT 13 Extensions - INSTALLATION CHECK
+ jc PrintDiskError // CF set on error (extensions not supported)
+ cmp bx, HEX(aa55) // BX = AA55h if installed
+ jne PrintDiskError
+ test cl, 1 // si = API subset support bitmap
+ jz PrintDiskError // Bit 0, extended disk access functions (AH=42h-44h,47h,48h) supported
+
+LoadExtraBootCode:
+ // First we have to load our extra boot code into into memory at [0000:7e00h]
+ xor edx, edx
+ mov eax, 1 // read from the second sector - the first was already read
+ mov cx, 2
+ mov bx, HEX(7e00) // Read sector to [0000:7e00h]
+ call ReadSectors
+
+ mov ax, DATA_BUFFER_SEGMENT
+ mov es, ax
+
+// reading superblock
+ xor edx, edx
+ mov eax, FIRST_SUPERBLOCK_OFFSET
+ mov cx, 8 // superblock is 0x1000 bytes - 8 sectors
+ xor bx, bx
+ call ReadSectors
+
+ push es // swapping segments for memory operations with superblock
+ push ds // ds must be DATA_BUFFER_SEGMENT
+ pop es
+ pop ds
+
+ mov si, HEX(40)
+ lea di, [btrfsSignature]
+ mov cx, 4 // magic string size (words)
+ repe cmpsw
+ je SignatureOk
+
+ mov si, wrongSignatureError
+ call PrintCustomError
+
+SignatureOk:
+// signature is ok - reading superblock data
+ add si, 8 // logical address of the root tree root
+ lea di, [TreeRootAddress]
+ mov cx, 8 // read both root tree root and chunk tree root
+ rep movsw
+// read sizes
+ add si, HEX(34) // now pointing to nodesize field
+ lea di, [NodeSize] // read all 4 values
+ mov cx, 8
+ rep movsw
+// read both levels
+ add si, 34
+ movsw // di is on the right place
+
+ push es
+ push ds
+ pop es
+ pop ds
+
+// read sys chunk array
+ mov ax, HEX(32b) // sys_chunk_start
+ReadSysChunk:
+ mov bx, ax
+ add bx, KEY_SIZE
+ push bx // start of the chunk
+ call InsertChunk
+
+ pop si
+ mov bx, word ptr es:[si+44] // number of stripes
+ shl bx, 5 // one stripe entry is 32 bytes
+ lea si, [si+bx+48] // 48 - size of the chunk
+ mov ax, si // save for next iteration
+ sub si, HEX(32b)
+ cmp si, word ptr [SysChunkSize] // the size cannot be more than 0xFFFF here so use word ptr
+ jb ReadSysChunk
+
+ jmp SetTreeRoots
+
+
+// Reads logical sectors from disk
+// INPUT:
+// - ES:[BX] address at which the data will be stored
+// - EDX:EAX logical sector number from which to read
+// - CX number of sectors to read
+// OUTPUT:
+// - 512*CX bytes of memory at ES:[BX]
+// LOCALS:
+// - [bp-2] - LBASectorsRead
+ReadSectors:
+ push bp
+ mov bp, sp
+ sub sp, 2
+ push es // we will spoil es register here
+
+ add eax, dword ptr [PartitionStartLBA]
+ adc edx, dword ptr [PartitionStartLBA+4]
+ReadSectors_loop:
+ pushad // Save logical sector number & sector count
+
+ cmp cx, 8 // Maximum sectors per call is 0x7F, but VirtualBox correctly works with only 8
+ jbe ReadSectorsSetupDiskAddressPacket // If we are reading less than 9 sectors then just do the read
+ mov cx, 8 // Otherwise read only 8 sectors on this loop iteration
+
+ReadSectorsSetupDiskAddressPacket:
+ mov word ptr [bp-2], cx
+ push edx
+ push eax // Put 64-bit logical block address on stack
+ push es // Put transfer segment on stack
+ push bx // Put transfer offset on stack
+ push cx // Set transfer count
+ push 16 // Set size of packet to 10h
+ mov si, sp // Setup disk address packet on stack
+
+ mov dl, byte ptr [BootDrive] // Drive number
+ mov ah, HEX(42) // Int 13h, AH = 42h - Extended Read
+ int HEX(13) // Call BIOS
+ jc PrintDiskError // If the read failed then abort
+
+ add sp, 16 // Remove disk address packet from stack
+
+ popad // Restore sector count & logical sector number
+
+ push bx
+ movzx ebx, word ptr [bp-2]
+ add eax, ebx // Increment sector to read
+ adc edx, 0
+ shl ebx, 5 // Multiplying by 512=2^9 here.
+ // Shifting only by 5, because it goes to segment
+ // (segment will be shifter by another 4 when converted to real addr)
+ mov si, es
+ add si, bx // Setup read buffer for next sector
+ mov es, si
+ pop bx
+
+ sub cx, word ptr [bp-2]
+ jnz ReadSectors_loop // Read next sector
+
+ pop es
+ leave
+ ret
+
+// Displays a disk error message
+// And reboots
+PrintDiskError:
+ mov si, msgDiskError // Bad boot disk message
+PrintCustomError:
+ call PutChars // Display it
+
+Reboot:
+ lea si, [msgAnyKey] // Press any key message
+ call PutChars // Display it
+ xor ax,ax
+ int HEX(16) // Wait for a keypress
+ int HEX(19) // Reboot
+
+PutChars:
+ lodsb
+ or al,al
+ jz short Done
+ call PutCharsCallBios
+ jmp short PutChars
+PutCharsCallBios:
+ mov ah, HEX(0e)
+ mov bx, HEX(07)
+ int HEX(10)
+ ret
+Done:
+ mov al, HEX(0d)
+ call PutCharsCallBios
+ mov al, HEX(0a)
+ call PutCharsCallBios
+ ret
+
+
+msgDiskError:
+ .asciz "Disk error"
+msgAnyKey:
+ .asciz "Press any key to restart"
+
+.org 510
+ .word HEX(aa55) // BootSector signature
+
+SetTreeRoots:
+ // converting sizes to sectors. We dont need this sizes in bytes
+ shr dword ptr [NodeSize], 9
+ shr dword ptr [LeafSize], 9 // leafsize is deprecated
+
+ // finding FS_TREE root
+
+ // put the key on stack
+ xor eax, eax
+ push eax
+ push eax
+ dec sp
+ mov byte ptr [esp], ROOT_ITEM_KEY
+ push eax
+ push 0
+ push FS_TREE_OBJECTID
+
+ mov eax, dword ptr [TreeRootAddress]
+ mov edx, dword ptr [TreeRootAddress+4]
+ mov cl, byte ptr [TreeRootLevel]
+
+ call SearchTree
+ add sp, 17 // setting stack back
+
+ // bx - pointer to ROOT_ITEM
+ mov al, byte ptr es:[bx+238] // moving level
+ mov byte ptr [FsRootLevel], al
+ cld
+
+ mov eax, dword ptr es:[bx+176]
+ mov dword ptr [FsRootAddress], eax
+ mov eax, dword ptr es:[bx+176+4]
+ mov dword ptr [FsRootAddress+4], eax
+
+ // now we need to find DIR_ITEM_KEY with freeldr.sys hash
+ xor eax, eax
+ push eax
+ push dword ptr [filenameCrc]
+ dec sp
+ mov byte ptr [esp], DIR_ITEM_KEY
+ push eax
+ push 0
+ push 256 // root dir objectid
+
+ mov eax, dword ptr [FsRootAddress]
+ mov edx, dword ptr [FsRootAddress+4]
+ mov cl, byte ptr [FsRootLevel]
+
+ call SearchTree
+ add sp, 17 // setting stack back
+
+ // parsing DIR_ITEM
+ // bx - item addr
+ // cx - item length
+ mov ax, cx
+
+ push ds
+ push es
+ pop ds
+ pop es
+ParseDirItem_loop:
+ cmp byte ptr [bx+29], BTRFS_FT_REG_FILE // checking dir_item type
+ jne ParseDirItem_loop_2
+ cmp word ptr [bx+27], 11 // checking name length
+ jne ParseDirItem_loop_2
+ lea si, [bx+30]
+ lea di, [freeldrFilename]
+ mov cx, 11
+ repe cmpsb
+ je FreeLdrFound
+
+ParseDirItem_loop_2:
+ mov dx, 30
+ add dx, word ptr [bx+27]
+ cmp dx, ax
+ jae FreeLdrNotFound
+ add bx, dx
+ jmp ParseDirItem_loop
+
+FreeLdrNotFound:
+ lea si, [notFoundError]
+ call PrintCustomError
+
+FreeLdrFound:
+ // freeldr objectid is the first qword of DIR_ITEM structure
+ xor eax, eax
+ push eax
+ push eax
+ dec sp
+ mov byte ptr [esp], EXTENT_DATA_KEY
+ push dword ptr [bx+4]
+ push dword ptr [bx]
+
+ push es
+ pop ds
+
+ mov eax, dword ptr [FsRootAddress]
+ mov edx, dword ptr [FsRootAddress+4]
+ mov cl, byte ptr [FsRootLevel]
+
+ call SearchTree
+ add sp, 17
+
+ // here we have an EXTENT_ITEM with freeldr.sys
+ mov eax, dword ptr es:[bx+29]
+ shr eax, 9 // getting the number of clusters
+ mov cx, ax
+ push cx
+
+ lea di, [bx+21]
+ call ConvertAddress
+ pop cx
+
+ xor bx, bx
+ mov ds, bx
+ mov es, bx
+ mov bx, FREELDR_BASE
+ call ReadSectors
+
+ mov dl, byte ptr [BootDrive] // Load boot drive into DL
+ //mov dh, 0 // Load boot partition into DH (not needed, FreeLbr detects it itself)
+
+ /* Transfer execution to the bootloader */
+ ljmp16 0, FREELDR_BASE
+
+
+// Insert chunk into chunk map (located at DS:[CHUNK_MAP_OFFSET])
+// INPUT:
+// - ES:[AX] chunk key address
+// - ES:[BX] chunk item address
+// TODO: add max items checking
+InsertChunk:
+ push bx
+ push ax
+ push es
+
+ xor ecx, ecx // index
+InsertChunk_loop:
+ std // numbers are little-engian, going right-to-left
+ mov si, CHUNK_MAP_OFFSET
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8] // shift by 24 bytes
+
+ inc cx
+ cmp cl, byte ptr [ChunkMapSize]
+ ja InsertChunk_insert
+
+ lea si, [si+4] // set to the high dword of the 8-byte number
+ lea di, [eax+KEY_SIZE-4] // set to high dword of key's offset field (offset=logical addr)
+
+ cmpsd
+ jb InsertChunk_loop
+ cmpsd
+ jb InsertChunk_loop
+ lea si, [si+4] // set back to the beginning of key
+
+ // numbers are greater - need to insert Here
+ // shifting all to right by one element
+InsertChunk_insert:
+ push si // here we will store new chunk
+ dec cx // because we increased it before comparison
+
+ mov dx, ds
+ mov es, dx
+
+ movzx eax, byte ptr [ChunkMapSize] // number of elements
+
+ mov si, CHUNK_MAP_OFFSET
+ lea si, [esi+eax*8]
+ lea si, [esi+eax*8]
+ lea si, [esi+eax*8-4] // setting to the high dword of the last element
+
+ mov di, si
+ add di, 20 // last byte of the last + 1 element (-4 bytes)
+
+ sub ax, cx
+ mov bx, 6
+ mul bx // 24/4 because of dwords
+ mov cx, ax // number of elements to shift
+ rep movsd
+
+ // finally we can write the element
+ cld
+ pop di // here we will store new chunk
+
+ pop ds // key-to-insert address segment
+ pop si // key-to-insert address
+ add si, 9 // move to 'offset' field
+ movsd
+ movsd // logical address loaded!
+
+ // time for stripes
+ pop si // chunk item address, length is the first field
+ movsd
+ movsd
+
+ add si, 48 // move to offset of the first stripe (we read only first one)
+ movsd
+ movsd
+
+ push es // swapping segments back to original
+ push ds
+ pop es
+ pop ds
+
+ inc byte ptr [ChunkMapSize]
+ ret
+
+
+// Searches a key in a BTRFS tree
+// INPUT:
+// - [bp+4] BTRFS key to find
+// - EDX:EAX logical address of root header
+// - CL leaf node level
+// OUTPUT:
+// - ES:[SI] pointer to found item's key
+// - ES:[BX] pointer to found item
+// - ECX item length
+// LOCALS:
+// - [bp-8] - block number/current node offset
+// - [bp-9] - current level
+SearchTree:
+ push bp
+ mov bp, sp
+ sub sp, 9 // set stack frame
+
+ mov dword ptr [bp-4], edx
+ mov dword ptr [bp-8], eax
+ mov byte ptr [bp-9], cl
+
+SearchTree_readHeader:
+ push ss
+ pop es
+
+ lea di, [bp-8]
+ call ConvertAddress
+ // LBA is in edx:eax now
+ mov bx, DATA_BUFFER_SEGMENT
+ mov es, bx
+ xor bx, bx
+
+ mov cl, byte ptr [bp-9]
+ test cl, cl
+ jz SearchTree_readLeaf
+// read node
+ mov cx, word ptr [NodeSize] // word btr because we cannot read more than 65536 bytes yet
+ call ReadSectors // reading node to the memory
+
+ // every node begins with header
+ //mov ax, word ptr [DATA_BUFFER_OFFSET + HEX(60)] // number of items in this leaf
+ mov cx, -1 // index
+ // finding the key
+SearchTree_findLoop_1:
+ inc cx
+ cmp word ptr es:[HEX(60)], cx
+ je SearchTree_findLoop_1_equal // we are at the end - taking the last element
+
+ lea si, [bp+4] // key to find
+ mov di, HEX(65) // first key in leaf
+ mov ax, 33
+ call CompareKeys
+ jb SearchTree_findLoop_1
+ je SearchTree_findLoop_1_equal
+ sub di, 33 // setting to previous element
+
+ // we are here if key is equal or greater
+ // (si points to the start of the key)
+SearchTree_findLoop_1_equal:
+ push ds
+ push es
+ pop ds
+ pop es
+
+ lea si, [di+17]
+ lea di, [bp-8]
+ movsd
+ movsd
+
+ push es
+ pop ds
+
+ dec byte ptr [bp-9] // decrement level
+ jmp SearchTree_readHeader
+
+SearchTree_readLeaf:
+ mov cx, word ptr [LeafSize]
+ call ReadSectors
+
+ // every node begins with header
+ //mov ax, word ptr [DATA_BUFFER_OFFSET + HEX(60)] // number of items in this leaf
+ mov cx, -1 // index
+ // finding the key
+SearchTree_findLoop_2:
+ inc cx
+ cmp word ptr es:[HEX(60)], cx
+ je SearchTree_foundEqual
+
+ lea si, [bp+4] // key to find
+ mov di, HEX(65) // first key in leaf
+ mov ax, 25
+ call CompareKeys
+ jb SearchTree_findLoop_2
+ je SearchTree_foundEqual
+
+ // set pointer to previous element if greater
+ sub di, 25
+
+SearchTree_foundEqual:
+ // found equal or greater key
+ mov bx, word ptr es:[di+17] // data offset relative to end of header
+ mov cx, word ptr es:[di+21] // data size
+ add bx, HEX(65) // end of header
+ mov si, di
+
+ leave
+ ret
+
+SearchTree_notFound:
+ xor ecx, ecx // return ecx=0 if nothing found
+
+ leave
+ ret
+
+
+// Converts logical address into physical LBA addr using chunk map
+// INPUT:
+// - ES:[DI] pointer to logical addr
+// OUTPUT:
+// - EDX:EAX target LBA
+// - sets ZF on failure
+ConvertAddress:
+ // NOTE: SearchTree will overwrite data buffer area and our logical addr will be erased!
+ // so putting it on stack
+ push dword ptr es:[di+4]
+ push dword ptr es:[di]
+
+ mov bl, 1 // indicates first try. On second try BL must be set to 0
+ConvertAddress_secondTry:
+ push ds
+ pop es
+ xor ecx, ecx // set index to 0
+ConvertAddress_loop:
+ cmp cl, byte ptr [ChunkMapSize]
+ jae ConvertAddress_checkInclusion // greater chunk is not found in chunk map - checking the last one
+
+ std // numbers are little-engian, going right-to-left
+ mov si, CHUNK_MAP_OFFSET
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8] // shift by 24 bytes
+
+ lea di, [esp+4] // set to the second dword the 8-byte number
+ lea si, [si+4]
+ inc cl
+
+ cmpsd
+ jb ConvertAddress_loop
+ cmpsd
+ jb ConvertAddress_loop
+
+ConvertAddress_checkInclusion:
+ dec cl
+ cld
+ // found chunk map item, checking inclusion with length
+ mov si, CHUNK_MAP_OFFSET
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8]
+ lea si, [esi+ecx*8] // shift by 24 bytes
+
+ // logical_addr + length
+ mov eax, dword ptr [si] // low dword of address
+ mov edx, dword ptr [si+4] // high dword of address
+ add eax, dword ptr [si+8] // low dword of length
+ adc edx, dword ptr [si+12] // high dword of length
+ // edx:eax is the end of the chunk
+
+ // (logical_addr + length) - addr_to_find
+ cmp edx, dword ptr [esp+4]
+ ja ConvertAddress_found
+ cmp eax, dword ptr [esp]
+ ja ConvertAddress_found // address is greater than end of the chunk
+ test bl, bl
+ jnz ConvertAddress_notFound
+ ret 8
+
+ConvertAddress_found:
+ // found chunk. Calculating the address
+ // addr_to_find - logical_addr
+ pop eax // low dword of addr_to_find
+ pop edx // high dword of addr_to_find
+ sub eax, dword ptr [si]
+ sbb edx, dword ptr [si+4]
+ // (addr_to_find - logical_addr) + physical_addr
+ add eax, dword ptr [si+16]
+ adc edx, dword ptr [si+20]
+
+ // edx:eax is physical address. Converting to LBA (shifting by 9 bits)
+ shrd eax, edx, 9
+ shr edx, 9
+ inc bl // clears ZF (bl is 0 or 1 here)
+ ret
+
+ConvertAddress_notFound:
+ // offset is alredy on stack
+ //push dword ptr [esp+4]
+ //push dword ptr [esp]
+ dec sp
+ mov byte ptr [esp], CHUNK_ITEM_KEY
+ data32 push 0
+ push 0
+ push FIRST_CHUNK_TREE_OBJECTID
+
+ mov eax, dword ptr [ChunkRootAddress]
+ mov edx, dword ptr [ChunkRootAddress+4]
+ mov cl, byte ptr [ChunkRootLevel]
+
+ call SearchTree
+ add sp, 9 // setting stack back
+
+ mov ax, si // ES:[SI] - found key pointer
+ call InsertChunk
+
+ mov bl, 0 // indicates second try
+ jmp ConvertAddress_secondTry
+
+
+// Compare key (key1) with key in array identified by base and index (key2)
+// INPUT:
+// - DS:[SI] key1 pointer
+// - ES:[DI] start of the array
+// - AX size of one key in array
+// - CX key index
+// OUTPUT:
+// - DS:[SI] key1 pointer
+// - ES:[DI] key2 pointer
+// - sets flags according to comparison
+CompareKeys:
+ //xchg si, di
+
+ mul cx
+ add di, ax
+ push ds
+ push si
+ push es
+ push di
+
+ // must be replaced for proper flags after cmps
+ push ds
+ push es
+ pop ds
+ pop es
+ xchg si, di
+
+ lea si, [si+4] // key in array
+ lea di, [di+4] // searchable key
+ std
+
+ // comparing objectid
+ cmpsd
+ jne CompareKeys_end
+ cmpsd
+ jne CompareKeys_end
+ // comparing type
+ lea si, [si+12]
+ lea di, [di+12]
+
+ cmpsb
+ jne CompareKeys_end
+ // comparing offset
+ lea si, [si+1+1+4]
+ lea di, [di+1+1+4]
+
+ cmpsd
+ jne CompareKeys_end
+ cmpsd
+CompareKeys_end:
+ cld
+ pop di
+ pop es
+ pop si
+ pop ds
+ ret
+
+
+
+btrfsSignature:
+ .ascii "_BHRfS_M"
+
+//.org 1022
+// .word HEX(aa55)
+
+
+wrongSignatureError:
+ .asciz "BTRFS read error"
+MaxItemsError:
+ .asciz "Max items error"
+filenameCrc:
+ .long HEX(68cba33d) // computed hashsum of "freeldr.sys"
+freeldrFilename:
+ .ascii "freeldr.sys"
+notFoundError:
+ .asciz "NFE"
+.org 1534
+ .word HEX(aa55)
+.endcode16
+
+END
--- /dev/null
+/*
+ * PROJECT: FreeLoader
+ * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: BTRFS support for FreeLoader
+ * COPYRIGHT: Copyright 2018 Victor Perevertkin (victor@perevertkin.ru)
+ */
+
+/* Structures were taken from u-boot, https://github.com/u-boot/u-boot/tree/master/fs/btrfs */
+
+#pragma once
+
+typedef UCHAR u8;
+typedef USHORT u16;
+typedef ULONG32 u32;
+typedef ULONG64 u64;
+/* type that store on disk, but it is same as cpu type for i386 arch */
+typedef u8 __u8;
+typedef u16 __u16;
+typedef u32 __u32;
+typedef u64 __u64;
+
+#include <fs/crc32c.h>
+#define btrfs_crc32c(name, len) crc32c_le((u32)~1, name, len)
+
+#define BTRFS_SUPER_INFO_OFFSET (64 * 1024)
+#define BTRFS_SUPER_INFO_SIZE 4096
+#define BTRFS_MAX_LEAF_SIZE 4096
+#define BTRFS_BLOCK_SHIFT 12
+#define BTRFS_BLOCK_SIZE (1 << BTRFS_BLOCK_SHIFT)
+
+#define BTRFS_SUPER_MIRROR_MAX 3
+#define BTRFS_SUPER_MIRROR_SHIFT 12
+#define BTRFS_CSUM_SIZE 32
+#define BTRFS_FSID_SIZE 16
+#define BTRFS_LABEL_SIZE 256
+#define BTRFS_SYSTEM_CHUNK_ARRAY_SIZE 2048
+#define BTRFS_UUID_SIZE 16
+
+#define BTRFS_VOL_NAME_MAX 255
+#define BTRFS_NAME_MAX 255
+
+#define BTRFS_MAGIC "_BHRfS_M"
+#define BTRFS_MAGIC_L 8
+#define BTRFS_MAGIC_N 0x4d5f53665248425fULL
+
+#define BTRFS_SUPER_FLAG_METADUMP (1ULL << 33)
+
+#define BTRFS_DEV_ITEM_KEY 216
+#define BTRFS_CHUNK_ITEM_KEY 228
+#define BTRFS_ROOT_REF_KEY 156
+#define BTRFS_ROOT_ITEM_KEY 132
+#define BTRFS_EXTENT_DATA_KEY 108
+#define BTRFS_DIR_ITEM_KEY 84
+#define BTRFS_DIR_INDEX_KEY 96
+#define BTRFS_INODE_ITEM_KEY 1
+#define BTRFS_INODE_REF_KEY 12
+
+#define BTRFS_EXTENT_TREE_OBJECTID 2ULL
+#define BTRFS_FS_TREE_OBJECTID 5ULL
+
+#define BTRFS_FIRST_FREE_OBJECTID 256ULL
+#define BTRFS_LAST_FREE_OBJECTID -256ULL
+#define BTRFS_FIRST_CHUNK_TREE_OBJECTID 256ULL
+
+#define BTRFS_FILE_EXTENT_INLINE 0
+#define BTRFS_FILE_EXTENT_REG 1
+#define BTRFS_FILE_EXTENT_PREALLOC 2
+
+#define BTRFS_MAX_LEVEL 8
+#define BTRFS_MAX_CHUNK_ENTRIES 256
+
+#define BTRFS_DEV_ITEMS_OBJECTID 1ULL
+
+#define BTRFS_FT_REG_FILE 1
+#define BTRFS_FT_DIR 2
+#define BTRFS_FT_SYMLINK 7
+#define BTRFS_FT_XATTR 8
+#define BTRFS_FT_MAX 9
+
+#define BTRFS_COMPRESS_NONE 0
+#define BTRFS_COMPRESS_ZLIB 1
+#define BTRFS_COMPRESS_LZO 2
+
+#define ROOT_DIR_WORD 0x002f
+
+#include <pshpack1.h>
+
+struct btrfs_disk_key {
+ __u64 objectid;
+ __u8 type;
+ __u64 offset;
+};
+
+struct btrfs_header {
+ /* these first four must match the super block */
+ __u8 csum[BTRFS_CSUM_SIZE];
+ __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __u64 bytenr; /* which block this node is supposed to live in */
+ __u64 flags;
+
+ /* allowed to be different from the super from here on down */
+ __u8 chunk_tree_uuid[BTRFS_UUID_SIZE];
+ __u64 generation;
+ __u64 owner;
+ __u32 nritems;
+ __u8 level;
+};
+
+struct btrfs_item {
+ struct btrfs_disk_key key;
+ __u32 offset;
+ __u32 size;
+};
+
+struct btrfs_leaf {
+ struct btrfs_header header;
+ struct btrfs_item items[];
+};
+
+struct btrfs_key_ptr {
+ struct btrfs_disk_key key;
+ __u64 blockptr;
+ __u64 generation;
+};
+
+struct btrfs_node {
+ struct btrfs_header header;
+ struct btrfs_key_ptr ptrs[];
+};
+
+struct btrfs_dev_item {
+ /* the internal btrfs device id */
+ __u64 devid;
+
+ /* size of the device */
+ __u64 total_bytes;
+
+ /* bytes used */
+ __u64 bytes_used;
+
+ /* optimal io alignment for this device */
+ __u32 io_align;
+
+ /* optimal io width for this device */
+ __u32 io_width;
+
+ /* minimal io size for this device */
+ __u32 sector_size;
+
+ /* type and info about this device */
+ __u64 type;
+
+ /* expected generation for this device */
+ __u64 generation;
+
+ /*
+ * starting byte of this partition on the device,
+ * to allow for stripe alignment in the future
+ */
+ __u64 start_offset;
+
+ /* grouping information for allocation decisions */
+ __u32 dev_group;
+
+ /* seek speed 0-100 where 100 is fastest */
+ __u8 seek_speed;
+
+ /* bandwidth 0-100 where 100 is fastest */
+ __u8 bandwidth;
+
+ /* btrfs generated uuid for this device */
+ __u8 uuid[BTRFS_UUID_SIZE];
+
+ /* uuid of FS who owns this device */
+ __u8 fsid[BTRFS_UUID_SIZE];
+};
+
+struct btrfs_stripe {
+ __u64 devid;
+ __u64 offset;
+ __u8 dev_uuid[BTRFS_UUID_SIZE];
+};
+
+struct btrfs_chunk {
+ /* size of this chunk in bytes */
+ __u64 length;
+
+ /* objectid of the root referencing this chunk */
+ __u64 owner;
+
+ __u64 stripe_len;
+ __u64 type;
+
+ /* optimal io alignment for this chunk */
+ __u32 io_align;
+
+ /* optimal io width for this chunk */
+ __u32 io_width;
+
+ /* minimal io size for this chunk */
+ __u32 sector_size;
+
+ /* 2^16 stripes is quite a lot, a second limit is the size of a single
+ * item in the btree
+ */
+ __u16 num_stripes;
+
+ /* sub stripes only matter for raid10 */
+ __u16 sub_stripes;
+ struct btrfs_stripe stripe;
+ /* additional stripes go here */
+};
+
+struct btrfs_inode_ref {
+ __u64 index;
+ __u16 name_len;
+ /* name goes here */
+};
+
+struct btrfs_timespec {
+ __u64 sec;
+ __u32 nsec;
+};
+
+struct btrfs_inode_item {
+ /* nfs style generation number */
+ __u64 generation;
+ /* transid that last touched this inode */
+ __u64 transid;
+ __u64 size;
+ __u64 nbytes;
+ __u64 block_group;
+ __u32 nlink;
+ __u32 uid;
+ __u32 gid;
+ __u32 mode;
+ __u64 rdev;
+ __u64 flags;
+
+ /* modification sequence number for NFS */
+ __u64 sequence;
+
+ /*
+ * a little future expansion, for more than this we can
+ * just grow the inode item and version it
+ */
+ __u64 reserved[4];
+ struct btrfs_timespec atime;
+ struct btrfs_timespec ctime;
+ struct btrfs_timespec mtime;
+ struct btrfs_timespec otime;
+};
+
+
+struct btrfs_dir_item {
+ struct btrfs_disk_key location;
+ __u64 transid;
+ __u16 data_len;
+ __u16 name_len;
+ __u8 type;
+};
+
+struct btrfs_root_item {
+ struct btrfs_inode_item inode;
+ __u64 generation;
+ __u64 root_dirid;
+ __u64 bytenr;
+ __u64 byte_limit;
+ __u64 bytes_used;
+ __u64 last_snapshot;
+ __u64 flags;
+ __u32 refs;
+ struct btrfs_disk_key drop_progress;
+ __u8 drop_level;
+ __u8 level;
+};
+
+struct btrfs_root_ref {
+ __u64 dirid;
+ __u64 sequence;
+ __u16 name_len;
+};
+
+struct btrfs_file_extent_item {
+ /*
+ * transaction id that created this extent
+ */
+ __u64 generation;
+ /*
+ * max number of bytes to hold this extent in ram
+ * when we split a compressed extent we can't know how big
+ * each of the resulting pieces will be. So, this is
+ * an upper limit on the size of the extent in ram instead of
+ * an exact limit.
+ */
+ __u64 ram_bytes;
+
+ /*
+ * 32 bits for the various ways we might encode the data,
+ * including compression and encryption. If any of these
+ * are set to something a given disk format doesn't understand
+ * it is treated like an incompat flag for reading and writing,
+ * but not for stat.
+ */
+ __u8 compression;
+ __u8 encryption;
+ __u16 other_encoding; /* spare for later use */
+
+ /* are we inline data or a real extent? */
+ __u8 type;
+
+ /*
+ * disk space consumed by the extent, checksum blocks are included
+ * in these numbers
+ *
+ * At this offset in the structure, the inline extent data start.
+ */
+ __u64 disk_bytenr;
+ __u64 disk_num_bytes;
+ /*
+ * the logical offset in file blocks (no csums)
+ * this extent record is for. This allows a file extent to point
+ * into the middle of an existing extent on disk, sharing it
+ * between two snapshots (useful if some bytes in the middle of the
+ * extent have changed
+ */
+ __u64 offset;
+ /*
+ * the logical number of file blocks (no csums included). This
+ * always reflects the size uncompressed and without encoding.
+ */
+ __u64 num_bytes;
+
+};
+
+struct btrfs_super_block {
+ __u8 csum[BTRFS_CSUM_SIZE];
+ /* the first 4 fields must match struct btrfs_header */
+ __u8 fsid[BTRFS_FSID_SIZE]; /* FS specific uuid */
+ __u64 bytenr; /* this block number */
+ __u64 flags;
+
+ /* allowed to be different from the btrfs_header from here own down */
+ __u64 magic;
+ __u64 generation;
+ __u64 root;
+ __u64 chunk_root;
+ __u64 log_root;
+
+ /* this will help find the new super based on the log root */
+ __u64 log_root_transid;
+ __u64 total_bytes;
+ __u64 bytes_used;
+ __u64 root_dir_objectid;
+ __u64 num_devices;
+ __u32 sectorsize;
+ __u32 nodesize;
+ __u32 __unused_leafsize;
+ __u32 stripesize;
+ __u32 sys_chunk_array_size;
+ __u64 chunk_root_generation;
+ __u64 compat_flags;
+ __u64 compat_ro_flags;
+ __u64 incompat_flags;
+ __u16 csum_type;
+ __u8 root_level;
+ __u8 chunk_root_level;
+ __u8 log_root_level;
+ struct btrfs_dev_item dev_item;
+
+ char label[BTRFS_LABEL_SIZE];
+
+ __u64 cache_generation;
+ __u64 uuid_tree_generation;
+
+ /* future expansion */
+ __u64 reserved[30];
+ __u8 sys_chunk_array[BTRFS_SYSTEM_CHUNK_ARRAY_SIZE];
+};
+
+#include <poppack.h>
+
+union tree_buf {
+ struct btrfs_header header;
+ struct btrfs_node node;
+ struct btrfs_leaf leaf;
+};
+
+/* remember how we get to a node/leaf */
+struct btrfs_path {
+ u64 offsets[BTRFS_MAX_LEVEL];
+ int itemsnr[BTRFS_MAX_LEVEL];
+ int slots[BTRFS_MAX_LEVEL];
+ /* remember whole last leaf */
+ union tree_buf *tree_buf;
+};
+
+/* store logical offset to physical offset mapping */
+struct btrfs_chunk_map_item {
+ u64 logical;
+ u64 length;
+ u64 devid;
+ u64 physical;
+};
+
+struct btrfs_chunk_map {
+ struct btrfs_chunk_map_item *map;
+ u32 map_length;
+ u32 cur_length;
+};
+
+typedef struct {
+ u64 inr;
+ u64 position;
+ struct btrfs_inode_item inode;
+} btrfs_file_info, * pbtrfs_file_info;
+
+const DEVVTBL* BtrFsMount(ULONG DeviceId);
--- /dev/null
+/*
+ * PROJECT: FreeLoader
+ * LICENSE: GPL-2.0+ (https://spdx.org/licenses/GPL-2.0+)
+ * PURPOSE: BTRFS support for FreeLoader
+ * COPYRIGHT: Copyright 2018 Victor Perevertkin (victor@perevertkin.ru)
+ */
+
+/* Some code was taken from u-boot, https://github.com/u-boot/u-boot/tree/master/fs/btrfs */
+
+#include <freeldr.h>
+#include <debug.h>
+
+DBG_DEFAULT_CHANNEL(FILESYSTEM);
+
+#define TAG_BTRFS_INFO 'IftB'
+#define TAG_BTRFS_CHUNK_MAP 'CftB'
+#define TAG_BTRFS_NODE 'NftB'
+#define TAG_BTRFS_FILE 'FftB'
+#define TAG_BTRFS_LINK 'LftB'
+
+#define INVALID_INODE ((ULONGLONG)-1)
+
+struct BTRFS_INFO {
+ ULONG DeviceId;
+ struct btrfs_super_block SuperBlock;
+ struct btrfs_chunk_map ChunkMap;
+ struct btrfs_root_item FsRoot;
+ struct btrfs_root_item ExtentRoot;
+};
+
+struct BTRFS_INFO *BtrFsInfo;
+
+/* compare function used for bin_search */
+typedef int (*cmp_func)(const void *ptr1, const void *ptr2);
+
+/* simple but useful bin search, used for chunk search and btree search */
+static int bin_search(void *ptr, int item_size, void *cmp_item, cmp_func func,
+ int min, int max, int *slot)
+{
+ int low = min;
+ int high = max;
+ int mid;
+ int ret;
+ unsigned long offset;
+ UCHAR *item;
+
+ while (low < high)
+ {
+ mid = (low + high) / 2;
+ offset = mid * item_size;
+
+ item = (UCHAR *) ptr + offset;
+ ret = func((void *) item, cmp_item);
+
+ if (ret < 0)
+ {
+ low = mid + 1;
+ }
+ else if (ret > 0)
+ {
+ high = mid;
+ }
+ else
+ {
+ *slot = mid;
+ return 0;
+ }
+ }
+ *slot = low;
+ return 1;
+}
+
+static int btrfs_comp_chunk_map(struct btrfs_chunk_map_item *m1,
+ struct btrfs_chunk_map_item *m2)
+{
+ if (m1->logical > m2->logical)
+ return 1;
+ if (m1->logical < m2->logical)
+ return -1;
+ return 0;
+}
+
+/* insert a new chunk mapping item */
+static void insert_chunk_item(struct btrfs_chunk_map_item *item)
+{
+ struct btrfs_chunk_map *chunk_map = &BtrFsInfo->ChunkMap;
+ int ret;
+ int slot;
+ int i;
+
+ if (chunk_map->map == NULL)
+ {
+ /* first item */
+ chunk_map->map_length = BTRFS_MAX_CHUNK_ENTRIES;
+ chunk_map->map = FrLdrTempAlloc(chunk_map->map_length * sizeof(chunk_map->map[0]), TAG_BTRFS_CHUNK_MAP);
+ chunk_map->map[0] = *item;
+ chunk_map->cur_length = 1;
+ return;
+ }
+ ret = bin_search(chunk_map->map, sizeof(*item), item,
+ (cmp_func) btrfs_comp_chunk_map, 0,
+ chunk_map->cur_length, &slot);
+ if (ret == 0)/* already in map */
+ return;
+
+ if (chunk_map->cur_length == BTRFS_MAX_CHUNK_ENTRIES)
+ {
+ /* should be impossible */
+ TRACE("too many chunk items\n");
+ return;
+ }
+
+ for (i = chunk_map->cur_length; i > slot; i--)
+ chunk_map->map[i] = chunk_map->map[i - 1];
+
+ chunk_map->map[slot] = *item;
+ chunk_map->cur_length++;
+}
+
+static inline void insert_map(const struct btrfs_disk_key *key, struct btrfs_chunk *chunk)
+{
+ struct btrfs_stripe *stripe = &chunk->stripe;
+ struct btrfs_stripe *stripe_end = stripe + chunk->num_stripes;
+ struct btrfs_chunk_map_item item;
+
+ item.logical = key->offset;
+ item.length = chunk->length;
+ for (; stripe < stripe_end; stripe++)
+ {
+ TRACE("stripe: %p\n", stripe);
+ item.devid = stripe->devid;
+ item.physical = stripe->offset;
+ TRACE("inserting chunk log: %llx len: %llx devid: %llx phys: %llx\n", item.logical, item.length, item.devid,
+ item.physical);
+ insert_chunk_item(&item);
+ }
+
+#if 0
+ struct btrfs_chunk_map *chunk_map = &BtrFsInfo->ChunkMap;
+ struct btrfs_chunk_map_item *itm;
+ int i;
+
+ TRACE("insert finished. Printing chunk map:\n------------------------------\n");
+
+ for (i = 0; i < chunk_map->cur_length; i++)
+ {
+ itm = &chunk_map->map[i];
+ TRACE("%llx..%llx -> %llx..%llx, devid: %llu\n",
+ itm->logical,
+ itm->logical + itm->length,
+ itm->physical,
+ itm->physical + itm->length,
+ itm->devid
+ );
+ }
+#endif
+}
+
+static inline unsigned long btrfs_chunk_item_size(int num_stripes)
+{
+ return sizeof(struct btrfs_chunk) +
+ sizeof(struct btrfs_stripe) * (num_stripes - 1);
+}
+
+static inline void init_path(struct btrfs_path *path)
+{
+ memset(path, 0, sizeof(*path));
+ path->tree_buf = FrLdrTempAlloc(BtrFsInfo->SuperBlock.nodesize, TAG_BTRFS_NODE);
+}
+
+static inline void free_path(struct btrfs_path *path)
+{
+ if (path->tree_buf) FrLdrTempFree(path->tree_buf, TAG_BTRFS_NODE);
+}
+
+static inline struct btrfs_item *path_current_item(struct btrfs_path *path)
+{
+ return &path->tree_buf->leaf.items[path->slots[0]];
+}
+
+static inline UCHAR *path_current_data(struct btrfs_path *path)
+{
+ return (UCHAR *) path->tree_buf->leaf.items + path_current_item(path)->offset;
+}
+
+static inline const struct btrfs_disk_key *path_current_disk_key(struct btrfs_path *path)
+{
+ return (const struct btrfs_disk_key *) &path_current_item(path)->key;
+}
+
+
+static int btrfs_comp_keys(const struct btrfs_disk_key *k1,
+ const struct btrfs_disk_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ if (k1->offset > k2->offset)
+ return 1;
+ if (k1->offset < k2->offset)
+ return -1;
+ return 0;
+}
+
+/* compare keys but ignore offset, is useful to enumerate all same kind keys */
+static int btrfs_comp_keys_type(const struct btrfs_disk_key *k1,
+ const struct btrfs_disk_key *k2)
+{
+ if (k1->objectid > k2->objectid)
+ return 1;
+ if (k1->objectid < k2->objectid)
+ return -1;
+ if (k1->type > k2->type)
+ return 1;
+ if (k1->type < k2->type)
+ return -1;
+ return 0;
+}
+
+/*
+ * from sys_chunk_array or chunk_tree, we can convert a logical address to
+ * a physical address we can not support multi device case yet
+ */
+static u64 logical_physical(u64 logical)
+{
+ struct btrfs_chunk_map *chunk_map = &BtrFsInfo->ChunkMap;
+ struct btrfs_chunk_map_item item;
+ int slot, ret;
+
+ item.logical = logical;
+ ret = bin_search(chunk_map->map, sizeof(chunk_map->map[0]), &item,
+ (cmp_func) btrfs_comp_chunk_map, 0,
+ chunk_map->cur_length, &slot);
+ if (ret == 0)
+ slot++;
+ else if (slot == 0)
+ return -1;
+ if (logical >= chunk_map->map[slot - 1].logical + chunk_map->map[slot - 1].length)
+ return -1;
+
+ TRACE("Address translation: 0x%llx -> 0x%llx\n", logical,
+ chunk_map->map[slot - 1].physical + logical - chunk_map->map[slot - 1].logical);
+
+ return chunk_map->map[slot - 1].physical + logical - chunk_map->map[slot - 1].logical;
+}
+
+static BOOLEAN disk_read(u64 physical, void *dest, u32 count)
+{
+ LARGE_INTEGER Position;
+ ULONG Count;
+ ARC_STATUS Status;
+
+ if (!dest)
+ return FALSE;
+
+ Position.QuadPart = physical;
+ Status = ArcSeek(BtrFsInfo->DeviceId, &Position, SeekAbsolute);
+ if (Status != ESUCCESS)
+ {
+ ERR("ArcSeek returned status %lu\n", Status);
+ return FALSE;
+ }
+
+ Status = ArcRead(BtrFsInfo->DeviceId, dest, count, &Count);
+ if (Status != ESUCCESS || Count != count)
+ {
+ ERR("ArcRead returned status %lu\n", Status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOLEAN _BtrFsSearchTree(u64 loffset, u8 level, struct btrfs_disk_key *key,
+ struct btrfs_path *path)
+{
+ struct btrfs_super_block *sb = &BtrFsInfo->SuperBlock;
+ union tree_buf *tree_buf = path->tree_buf;
+ int slot, ret, lvl;
+ u64 physical, logical = loffset;
+ TRACE("BtrFsSearchTree called: offset: 0x%llx, level: %u (%llu %u %llu)\n",
+ loffset, level, key->objectid, key->type, key->offset);
+
+ if (!tree_buf)
+ {
+ ERR("Path struct is not allocated\n");
+ return FALSE;
+ }
+
+ for (lvl = level; lvl >= 0; lvl--)
+ {
+ physical = logical_physical(logical);
+
+ if (!disk_read(physical, &tree_buf->header, sb->nodesize))
+ {
+ ERR("Error when reading tree node, loffset: 0x%llx, poffset: 0x%llx, level: %u\n", logical, physical, lvl);
+ return FALSE;
+ }
+
+
+ if (tree_buf->header.level != lvl)
+ {
+ ERR("Error when searching in tree: expected lvl=%u but got %u\n", lvl, tree_buf->header.level);
+ return FALSE;
+ }
+
+ TRACE("BtrFsSearchTree loop, level %u, loffset: 0x%llx\n", lvl, logical);
+
+ if (lvl)
+ {
+ ret = bin_search(tree_buf->node.ptrs,
+ sizeof(struct btrfs_key_ptr),
+ key, (cmp_func) btrfs_comp_keys,
+ path->slots[lvl],
+ tree_buf->header.nritems, &slot);
+ TRACE("Inner node, min=%lu max=%lu\n", path->slots[0], tree_buf->header.nritems);
+ if (ret && slot > path->slots[lvl])
+ --slot;
+ }
+ else
+ {
+ ret = bin_search(tree_buf->leaf.items,
+ sizeof(struct btrfs_item),
+ key, (cmp_func) btrfs_comp_keys,
+ path->slots[0],
+ tree_buf->header.nritems, &slot);
+ TRACE("Leaf node, min=%lu max=%lu\n", path->slots[0], tree_buf->header.nritems);
+ if (slot == tree_buf->header.nritems)
+ --slot;
+ }
+
+ path->itemsnr[lvl] = tree_buf->header.nritems;
+ path->offsets[lvl] = logical;
+ path->slots[lvl] = slot;
+
+ logical = tree_buf->node.ptrs[slot].blockptr;
+ }
+
+ TRACE("Found slot no=%lu\n", slot);
+
+ TRACE("BtrFsSearchTree found item (%llu %u %llu) offset: %lu, size: %lu, returning %lu\n",
+ path_current_disk_key(path)->objectid, path_current_disk_key(path)->type, path_current_disk_key(path)->offset,
+ path_current_item(path)->offset, path_current_item(path)->size, !ret);
+
+ return !ret;
+}
+
+static inline BOOLEAN
+BtrFsSearchTree(const struct btrfs_root_item *root, struct btrfs_disk_key *key, struct btrfs_path *path)
+{
+ return _BtrFsSearchTree(root->bytenr, root->level, key, path);
+}
+
+static inline BOOLEAN
+BtrFsSearchTreeType(const struct btrfs_root_item *root, u64 objectid, u8 type, struct btrfs_path *path)
+{
+ struct btrfs_disk_key key;
+
+ key.objectid = objectid;
+ key.type = type;
+ key.offset = 0;
+
+ _BtrFsSearchTree(root->bytenr, root->level, &key, path);
+
+ if (path_current_disk_key(path)->objectid && !btrfs_comp_keys_type(&key, path_current_disk_key(path)))
+ return TRUE;
+ else
+ return FALSE;
+}
+
+/* return 0 if slot found */
+static int next_slot(struct btrfs_disk_key *key,
+ struct btrfs_path *path)
+{
+ int slot, level = 1;
+
+ if (!path->itemsnr[0])
+ return 1;
+ slot = path->slots[0] + 1;
+ if (slot >= path->itemsnr[0])
+ {
+ /* jumping to next leaf */
+ while (level < BTRFS_MAX_LEVEL)
+ {
+ if (!path->itemsnr[level]) /* no more nodes */
+ return 1;
+ slot = path->slots[level] + 1;
+ if (slot >= path->itemsnr[level])
+ {
+ level++;
+ continue;;
+ }
+ path->slots[level] = slot;
+ path->slots[level - 1] = 0; /* reset low level slots info */
+ path->itemsnr[level - 1] = 0;
+ path->offsets[level - 1] = 0;
+ _BtrFsSearchTree(path->offsets[level], level, key, path);
+ break;
+ }
+ if (level == BTRFS_MAX_LEVEL)
+ return 1;
+ goto out;
+ }
+ path->slots[0] = slot;
+
+ out:
+ if (path_current_disk_key(path)->objectid && !btrfs_comp_keys_type(key, path_current_disk_key(path)))
+ return 0;
+ else
+ return 1;
+}
+
+static int prev_slot(struct btrfs_disk_key *key,
+ struct btrfs_path *path)
+{
+ if (!path->slots[0])
+ return 1;
+ --path->slots[0];
+ if (path_current_disk_key(path)->objectid && !btrfs_comp_keys_type(key, path_current_disk_key(path)))
+ return 0;
+ else
+ return 1;
+}
+
+/*
+ * read chunk_array in super block
+ */
+static void btrfs_read_sys_chunk_array()
+{
+ struct btrfs_super_block *sb = &BtrFsInfo->SuperBlock;
+ struct btrfs_disk_key *key;
+ struct btrfs_chunk *chunk;
+ u16 cur;
+
+ /* read chunk array in superblock */
+ TRACE("reading chunk array\n-----------------------------\n");
+ cur = 0;
+ while (cur < sb->sys_chunk_array_size)
+ {
+ key = (struct btrfs_disk_key *) (sb->sys_chunk_array + cur);
+ TRACE("chunk key objectid: %llx, offset: %llx, type: %u\n", key->objectid, key->offset, key->type);
+ cur += sizeof(*key);
+ chunk = (struct btrfs_chunk *) (sb->sys_chunk_array + cur);
+ TRACE("chunk length: %llx\n", chunk->length);
+ TRACE("chunk owner: %llu\n", chunk->owner);
+ TRACE("chunk stripe_len: %llx\n", chunk->stripe_len);
+ TRACE("chunk type: %llu\n", chunk->type);
+ TRACE("chunk io_align: %u\n", chunk->io_align);
+ TRACE("chunk io_width: %u\n", chunk->io_width);
+ TRACE("chunk sector_size: %u\n", chunk->sector_size);
+ TRACE("chunk num_stripes: %u\n", chunk->num_stripes);
+ TRACE("chunk sub_stripes: %u\n", chunk->sub_stripes);
+
+ cur += btrfs_chunk_item_size(chunk->num_stripes);
+ TRACE("read_sys_chunk_array() cur=%d\n", cur);
+ insert_map((const struct btrfs_disk_key *) key, chunk);
+ }
+}
+
+
+/*
+ * read chunk items from chunk_tree and insert them to chunk map
+ * */
+static void btrfs_read_chunk_tree()
+{
+ struct btrfs_super_block *sb = &BtrFsInfo->SuperBlock;
+ struct btrfs_disk_key ignore_key;
+ struct btrfs_disk_key search_key;
+ struct btrfs_chunk *chunk;
+ struct btrfs_path path;
+
+ if (!(sb->flags & BTRFS_SUPER_FLAG_METADUMP))
+ {
+ if (sb->num_devices > 1)
+ TRACE("warning: only support single device btrfs\n");
+
+ ignore_key.objectid = BTRFS_DEV_ITEMS_OBJECTID;
+ ignore_key.type = BTRFS_DEV_ITEM_KEY;
+
+ /* read chunk from chunk_tree */
+ search_key.objectid = BTRFS_FIRST_CHUNK_TREE_OBJECTID;
+ search_key.type = BTRFS_CHUNK_ITEM_KEY;
+ search_key.offset = 0;
+ init_path(&path);
+ _BtrFsSearchTree(sb->chunk_root, sb->chunk_root_level, &search_key, &path);
+ do
+ {
+ /* skip information about underlying block
+ * devices.
+ */
+ if (!btrfs_comp_keys_type(&ignore_key, path_current_disk_key(&path)))
+ continue;
+ if (btrfs_comp_keys_type(&search_key, path_current_disk_key(&path)))
+ break;
+
+ chunk = (struct btrfs_chunk *) (path_current_data(&path));
+ insert_map(path_current_disk_key(&path), chunk);
+ } while (!next_slot(&search_key, &path));
+ free_path(&path);
+ }
+}
+
+////////////////////////////////////////
+///////////// DIR ITEM
+////////////////////////////////////////
+
+static BOOLEAN verify_dir_item(struct btrfs_dir_item *item, u32 start, u32 total)
+{
+ u16 max_len = BTRFS_NAME_MAX;
+ u32 end;
+
+ if (item->type >= BTRFS_FT_MAX)
+ {
+ ERR("Invalid dir item type: %i\n", item->type);
+ return TRUE;
+ }
+
+ if (item->type == BTRFS_FT_XATTR)
+ max_len = 255; /* XATTR_NAME_MAX */
+
+ end = start + sizeof(*item) + item->name_len;
+ if (item->name_len > max_len || end > total)
+ {
+ ERR("Invalid dir item name len: %u\n", item->name_len);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+
+static struct btrfs_dir_item *BtrFsMatchDirItemName(struct btrfs_path *path, const char *name, int name_len)
+{
+ struct btrfs_dir_item *item = (struct btrfs_dir_item *) path_current_data(path);
+ u32 cur = 0, this_len;
+ const char *name_ptr;
+
+ while (cur < path_current_item(path)->size)
+ {
+ this_len = sizeof(*item) + item->name_len + item->data_len;
+ name_ptr = (const char *) item + sizeof(*item);
+
+ if (verify_dir_item(item, cur, this_len))
+ return NULL;
+ if (item->name_len == name_len && !memcmp(name_ptr, name, name_len))
+ return item;
+
+ cur += this_len;
+ item = (struct btrfs_dir_item *) ((u8 *) item + this_len);
+ }
+
+ return NULL;
+}
+
+static BOOLEAN BtrFsLookupDirItem(const struct btrfs_root_item *root, u64 dir, const char *name, int name_len,
+ struct btrfs_dir_item *item)
+{
+ struct btrfs_path path;
+ struct btrfs_disk_key key;
+ struct btrfs_dir_item *res = NULL;
+
+ key.objectid = dir;
+ key.type = BTRFS_DIR_ITEM_KEY;
+ key.offset = btrfs_crc32c(name, name_len);
+ init_path(&path);
+
+ if (!BtrFsSearchTree(root, &key, &path))
+ {
+ free_path(&path);
+ return FALSE;
+ }
+
+ res = BtrFsMatchDirItemName(&path, name, name_len);
+ if (res)
+ *item = *res;
+ free_path(&path);
+
+ return res != NULL;
+}
+
+static BOOLEAN BtrFsLookupDirItemI(const struct btrfs_root_item *root, u64 dir_haystack, const char *name, int name_len,
+ struct btrfs_dir_item *ret_item)
+{
+ struct btrfs_path path;
+ struct btrfs_disk_key key;
+ struct btrfs_dir_item *item;
+ char *name_buf;
+ BOOLEAN result = FALSE;
+
+ key.objectid = dir_haystack;
+ key.type = BTRFS_DIR_INDEX_KEY;
+ key.offset = 0;
+ init_path(&path);
+
+ BtrFsSearchTree(root, &key, &path);
+
+ if (btrfs_comp_keys_type(&key, path_current_disk_key(&path)))
+ goto cleanup;
+
+ do
+ {
+ item = (struct btrfs_dir_item *) path_current_data(&path);
+ // TRACE("slot: %ld, KEY (%llu %u %llu) %.*s\n",
+ // path.slots[0], path.item.key.objectid, path.item.key.type,
+ // path.item.key.offset, item->name_len, (char *)item + sizeof(*item));
+
+ if (verify_dir_item(item, 0, sizeof(*item) + item->name_len))
+ continue;
+ if (item->type == BTRFS_FT_XATTR)
+ continue;
+
+ name_buf = (char *) item + sizeof(*item);
+ TRACE("Compare names %.*s and %.*s\n", name_len, name, item->name_len, name_buf);
+
+ if (_strnicmp(name, name_buf, name_len) == 0)
+ {
+ *ret_item = *item;
+ result = TRUE;
+ goto cleanup;
+ }
+
+ } while (!next_slot(&key, &path));
+
+ cleanup:
+ free_path(&path);
+ return result;
+}
+
+////////////////////////////////////////
+///////////// EXTENTS
+////////////////////////////////////////
+
+static u64 btrfs_read_extent_inline(struct btrfs_path *path,
+ struct btrfs_file_extent_item *extent, u64 offset,
+ u64 size, char *out)
+{
+ u32 dlen;
+ const char *cbuf;
+ const int data_off = offsetof(
+ struct btrfs_file_extent_item, disk_bytenr);
+
+ cbuf = (const char *) extent + data_off;
+ dlen = extent->ram_bytes;
+
+ TRACE("read_extent_inline offset=%llu size=%llu gener=%llu\n", offset, size, extent->generation);
+
+ if (offset > dlen)
+ {
+ ERR("Tried to read offset (%llu) beyond extent length (%lu)\n", offset, dlen);
+ return INVALID_INODE;
+ }
+
+ if (size > dlen - offset)
+ size = dlen - offset;
+
+ if (extent->compression == BTRFS_COMPRESS_NONE)
+ {
+ TRACE("read_extent_inline %lu, \n", data_off);
+ memcpy(out, cbuf + offset, size);
+ return size;
+ }
+
+ ERR("No compression supported right now\n");
+ return INVALID_INODE;
+}
+
+static u64 btrfs_read_extent_reg(struct btrfs_path *path, struct btrfs_file_extent_item *extent,
+ u64 offset, u64 size, char *out)
+{
+ u64 physical, dlen;
+ char *temp_out;
+ dlen = extent->num_bytes;
+
+ if (offset > dlen)
+ {
+ ERR("Tried to read offset (%llu) beyond extent length (%lu)\n", offset, dlen);
+ return -1ULL;
+ }
+
+ if (size > dlen - offset)
+ size = dlen - offset;
+
+ physical = logical_physical(extent->disk_bytenr);
+ if (physical == -1ULL)
+ {
+ ERR("Unable to convert logical address to physical: %llu\n", extent->disk_bytenr);
+ return -1ULL;
+ }
+
+ if (extent->compression == BTRFS_COMPRESS_NONE)
+ {
+ physical += extent->offset + offset;
+ if (physical & (512 - 1))
+ {
+ /* If somebody tried to do unaligned access */
+ physical -= offset;
+ temp_out = FrLdrTempAlloc(size + offset, TAG_BTRFS_FILE);
+
+ if (!disk_read(physical, temp_out, size + offset))
+ {
+ FrLdrTempFree(temp_out, TAG_BTRFS_FILE);
+ return -1ULL;
+ }
+
+ memcpy(out, temp_out + offset, size);
+ FrLdrTempFree(temp_out, TAG_BTRFS_FILE);
+ } else
+ {
+ if (!disk_read(physical, out, size))
+ return -1ULL;
+ }
+
+ return size;
+ }
+
+ ERR("No compression supported right now\n");
+ return -1ULL;
+}
+
+static u64 btrfs_file_read(const struct btrfs_root_item *root, u64 inr, u64 offset, u64 size, char *buf)
+{
+ struct btrfs_path path;
+ struct btrfs_disk_key key;
+ struct btrfs_file_extent_item *extent;
+ int res = 0;
+ u64 rd, seek_pointer = (u64) -1ULL, offset_in_extent;
+ BOOLEAN find_res;
+
+ TRACE("btrfs_file_read inr=%llu offset=%llu size=%llu\n", inr, offset, size);
+
+ key.objectid = inr;
+ key.type = BTRFS_EXTENT_DATA_KEY;
+ key.offset = offset;
+ init_path(&path);
+
+ find_res = BtrFsSearchTree(root, &key, &path);
+
+ /* if we found greater key, switch to the previous one */
+ if (!find_res && btrfs_comp_keys(&key, path_current_disk_key(&path)) < 0)
+ {
+ if (prev_slot(&key, &path))
+ goto out;
+
+ } else if (btrfs_comp_keys_type(&key, path_current_disk_key(&path)))
+ {
+ goto out;
+ }
+
+ seek_pointer = offset;
+
+ do
+ {
+ TRACE("Current extent: (%llu %u %llu) \n",
+ path_current_disk_key(&path)->objectid,
+ path_current_disk_key(&path)->type,
+ path_current_disk_key(&path)->offset);
+
+ extent = (struct btrfs_file_extent_item *) path_current_data(&path);
+
+ offset_in_extent = seek_pointer;
+ /* check if we need clean extent offset when switching to the next extent */
+ if ((seek_pointer) >= path_current_disk_key(&path)->offset)
+ offset_in_extent -= path_current_disk_key(&path)->offset;
+
+ if (extent->type == BTRFS_FILE_EXTENT_INLINE)
+ {
+ rd = btrfs_read_extent_inline(&path, extent, offset_in_extent, size, buf);
+ }
+ else
+ {
+ rd = btrfs_read_extent_reg(&path, extent, offset_in_extent, size, buf);
+ }
+
+ if (rd == -1ULL)
+ {
+ ERR("Error while reading extent\n");
+ seek_pointer = (u64) -1ULL;
+ goto out;
+ }
+
+ buf += rd;
+ seek_pointer += rd;
+ size -= rd;
+ TRACE("file_read size=%llu rd=%llu seek_pointer=%llu\n", size, rd, seek_pointer);
+
+ if (!size)
+ break;
+ } while (!(res = next_slot(&key, &path)));
+
+ if (res)
+ {
+ seek_pointer = (u64) -1ULL;
+ goto out;
+ }
+
+ seek_pointer -= offset;
+ out:
+ free_path(&path);
+ return seek_pointer;
+}
+
+////////////////////////////////////////
+///////////// INODE
+////////////////////////////////////////
+
+
+static u64 btrfs_lookup_inode_ref(const struct btrfs_root_item *root, u64 inr,
+ struct btrfs_inode_ref *refp, char *name)
+{
+ struct btrfs_path path;
+ struct btrfs_inode_ref *ref;
+ u64 ret = -1ULL;
+ init_path(&path);
+
+ if (BtrFsSearchTreeType(root, inr, BTRFS_INODE_REF_KEY, &path))
+ {
+ ref = (struct btrfs_inode_ref *) path_current_data(&path);
+
+ if (refp)
+ *refp = *ref;
+ ret = path_current_disk_key(&path)->offset;
+ }
+
+ free_path(&path);
+ return ret;
+}
+
+static int btrfs_lookup_inode(const struct btrfs_root_item *root,
+ struct btrfs_disk_key *location,
+ struct btrfs_inode_item *item,
+ struct btrfs_root_item *new_root)
+{
+ const struct btrfs_root_item tmp_root = *root;
+ struct btrfs_path path;
+ int res = -1;
+
+// if (location->type == BTRFS_ROOT_ITEM_KEY) {
+// if (btrfs_find_root(location->objectid, &tmp_root, NULL))
+// return -1;
+//
+// location->objectid = tmp_root.root_dirid;
+// location->type = BTRFS_INODE_ITEM_KEY;
+// location->offset = 0;
+// }
+ init_path(&path);
+ TRACE("Searching inode (%llu %u %llu)\n", location->objectid, location->type, location->offset);
+
+ if (BtrFsSearchTree(&tmp_root, location, &path))
+ {
+ if (item)
+ *item = *((struct btrfs_inode_item *) path_current_data(&path));
+
+ if (new_root)
+ *new_root = tmp_root;
+
+ res = 0;
+ }
+
+ free_path(&path);
+ return res;
+}
+
+static BOOLEAN btrfs_readlink(const struct btrfs_root_item *root, u64 inr, char **target)
+{
+ struct btrfs_path path;
+ struct btrfs_file_extent_item *extent;
+ char *data_ptr;
+ BOOLEAN res = FALSE;
+
+ init_path(&path);
+
+ if (!BtrFsSearchTreeType(root, inr, BTRFS_EXTENT_DATA_KEY, &path))
+ goto out;
+
+ extent = (struct btrfs_file_extent_item *) path_current_data(&path);
+ if (extent->type != BTRFS_FILE_EXTENT_INLINE)
+ {
+ ERR("Extent for symlink %llu not of INLINE type\n", inr);
+ goto out;
+ }
+
+ if (extent->compression != BTRFS_COMPRESS_NONE)
+ {
+ ERR("Symlink %llu extent data compressed!\n", inr);
+ goto out;
+ }
+ else if (extent->encryption != 0)
+ {
+ ERR("Symlink %llu extent data encrypted!\n", inr);
+ goto out;
+ }
+ else if (extent->ram_bytes >= BtrFsInfo->SuperBlock.sectorsize)
+ {
+ ERR("Symlink %llu extent data too long (%llu)!\n", inr, extent->ram_bytes);
+ goto out;
+ }
+
+ data_ptr = (char *) extent + offsetof(
+ struct btrfs_file_extent_item, disk_bytenr);
+
+ *target = FrLdrTempAlloc(extent->ram_bytes + 1, TAG_BTRFS_LINK);
+ if (!*target)
+ {
+ ERR("Cannot allocate %llu bytes\n", extent->ram_bytes + 1);
+ goto out;
+ }
+
+ memcpy(*target, data_ptr, extent->ram_bytes);
+ (*target)[extent->ram_bytes] = '\0';
+
+ res = TRUE;
+
+ out:
+ free_path(&path);
+ return res;
+}
+
+/* inr must be a directory (for regular files with multiple hard links this
+ function returns only one of the parents of the file) */
+static u64 get_parent_inode(const struct btrfs_root_item *root, u64 inr,
+ struct btrfs_inode_item *inode_item)
+{
+ struct btrfs_disk_key key;
+ u64 res;
+
+ if (inr == BTRFS_FIRST_FREE_OBJECTID)
+ {
+// if (root->objectid != btrfs_info.fs_root.objectid) {
+// u64 parent;
+// struct btrfs_root_ref ref;
+//
+// parent = btrfs_lookup_root_ref(root->objectid, &ref,
+// NULL);
+// if (parent == -1ULL)
+// return -1ULL;
+//
+// if (btrfs_find_root(parent, root, NULL))
+// return -1ULL;
+//
+// inr = ref.dirid;
+// }
+
+ if (inode_item)
+ {
+ key.objectid = inr;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ if (btrfs_lookup_inode(root, &key, inode_item, NULL))
+ return INVALID_INODE;
+ }
+
+ return inr;
+ }
+
+ res = btrfs_lookup_inode_ref(root, inr, NULL, NULL);
+ if (res == INVALID_INODE)
+ return INVALID_INODE;
+
+ if (inode_item)
+ {
+ key.objectid = res;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ if (btrfs_lookup_inode(root, &key, inode_item, NULL))
+ return INVALID_INODE;
+ }
+
+ return res;
+}
+
+static inline int next_length(const char *path)
+{
+ int res = 0;
+ while (*path != '\0' && *path != '/' && *path != '\\' && res <= BTRFS_NAME_MAX)
+ ++res, ++path;
+ return res;
+}
+
+static inline const char *skip_current_directories(const char *cur)
+{
+ while (1)
+ {
+ if (cur[0] == '/' || cur[0] == '\\')
+ ++cur;
+ else if (cur[0] == '.' && (cur[1] == '/' || cur[1] == '\\'))
+ cur += 2;
+ else
+ break;
+ }
+
+ return cur;
+}
+
+static u64 btrfs_lookup_path(const struct btrfs_root_item *root, u64 inr, const char *path,
+ u8 *type_p, struct btrfs_inode_item *inode_item_p, int symlink_limit)
+{
+ struct btrfs_dir_item item;
+ struct btrfs_inode_item inode_item;
+ u8 type = BTRFS_FT_DIR;
+ int len, have_inode = 0;
+ const char *cur = path;
+ struct btrfs_disk_key key;
+ char *link_target = NULL;
+
+ if (*cur == '/' || *cur == '\\')
+ {
+ ++cur;
+ inr = root->root_dirid;
+ }
+
+ do
+ {
+ cur = skip_current_directories(cur);
+
+ len = next_length(cur);
+ if (len > BTRFS_NAME_MAX)
+ {
+ ERR("%s: Name too long at \"%.*s\"\n", BTRFS_NAME_MAX, cur);
+ return -1ULL;
+ }
+
+ if (len == 1 && cur[0] == '.')
+ break;
+
+ if (len == 2 && cur[0] == '.' && cur[1] == '.')
+ {
+ cur += 2;
+ inr = get_parent_inode(root, inr, &inode_item);
+ if (inr == INVALID_INODE)
+ return INVALID_INODE;
+
+ type = BTRFS_FT_DIR;
+ continue;
+ }
+
+ if (!*cur)
+ break;
+
+ if (!BtrFsLookupDirItem(root, inr, cur, len, &item))
+ {
+ TRACE("Try to find case-insensitive, path=%s inr=%llu s=%.*s\n", path, inr, len, cur);
+ if (!BtrFsLookupDirItemI(root, inr, cur, len, &item))
+ return INVALID_INODE;
+ }
+
+ type = item.type;
+ have_inode = 1;
+ if (btrfs_lookup_inode(root, &item.location, &inode_item, NULL))
+ return INVALID_INODE;
+
+ if (type == BTRFS_FT_SYMLINK && symlink_limit >= 0)
+ {
+ if (!symlink_limit)
+ {
+ TRACE("%s: Too much symlinks!\n");
+ return INVALID_INODE;
+ }
+
+ /* btrfs_readlink allocates link_target by itself */
+ if (!btrfs_readlink(root, item.location.objectid, &link_target))
+ return INVALID_INODE;
+
+ inr = btrfs_lookup_path(root, inr, link_target, &type, &inode_item, symlink_limit - 1);
+
+ FrLdrTempFree(link_target, TAG_BTRFS_LINK);
+
+ if (inr == INVALID_INODE)
+ return INVALID_INODE;
+ } else if (type != BTRFS_FT_DIR && cur[len])
+ {
+ TRACE("%s: \"%.*s\" not a directory\n", (int) (cur - path + len), path);
+ return INVALID_INODE;
+ } else
+ {
+ inr = item.location.objectid;
+ }
+
+ cur += len;
+ } while (*cur);
+
+ if (type_p)
+ *type_p = type;
+
+ if (inode_item_p)
+ {
+ if (!have_inode)
+ {
+ key.objectid = inr;
+ key.type = BTRFS_INODE_ITEM_KEY;
+ key.offset = 0;
+
+ if (btrfs_lookup_inode(root, &key, &inode_item, NULL))
+ return INVALID_INODE;
+ }
+
+ *inode_item_p = inode_item;
+ }
+
+ return inr;
+}
+
+
+ARC_STATUS BtrFsClose(ULONG FileId)
+{
+ pbtrfs_file_info phandle = FsGetDeviceSpecific(FileId);
+ TRACE("BtrFsClose %lu\n", FileId);
+
+ FrLdrTempFree(phandle, TAG_BTRFS_FILE);
+
+ return ESUCCESS;
+}
+
+ARC_STATUS BtrFsGetFileInformation(ULONG FileId, FILEINFORMATION *Information)
+{
+ pbtrfs_file_info phandle = FsGetDeviceSpecific(FileId);
+
+ TRACE("BtrFsGetFileInformation %lu\n", FileId);
+
+ RtlZeroMemory(Information, sizeof(FILEINFORMATION));
+ Information->EndingAddress.QuadPart = phandle->inode.size;
+ Information->CurrentAddress.QuadPart = phandle->position;
+
+ TRACE("BtrFsGetFileInformation() FileSize = %llu\n",
+ Information->EndingAddress.QuadPart);
+ TRACE("BtrFsGetFileInformation() FilePointer = %llu\n",
+ Information->CurrentAddress.QuadPart);
+
+ return ESUCCESS;
+}
+
+ARC_STATUS BtrFsOpen(CHAR *Path, OPENMODE OpenMode, ULONG *FileId)
+{
+ u64 inr;
+ u8 type;
+
+ btrfs_file_info temp_file_info;
+ pbtrfs_file_info phandle;
+
+ TRACE("BtrFsOpen %s\n", Path);
+
+ if (OpenMode != OpenReadOnly)
+ return EACCES;
+
+ inr = btrfs_lookup_path(&BtrFsInfo->FsRoot, BtrFsInfo->FsRoot.root_dirid, Path, &type, &temp_file_info.inode, 40);
+
+ if (inr == -1ULL)
+ {
+ TRACE("Cannot lookup file %s\n", Path);
+ return ENOENT;
+ }
+
+ if (type != BTRFS_FT_REG_FILE)
+ {
+ TRACE("Not a regular file: %s\n", Path);
+ return EISDIR;
+ }
+
+ TRACE("found inode inr=%llu size=%llu\n", inr, temp_file_info.inode.size);
+
+ temp_file_info.inr = inr;
+ temp_file_info.position = 0;
+
+ phandle = FrLdrTempAlloc(sizeof(btrfs_file_info), TAG_BTRFS_FILE);
+ if (!phandle)
+ return ENOMEM;
+
+ memcpy(phandle, &temp_file_info, sizeof(btrfs_file_info));
+
+ FsSetDeviceSpecific(*FileId, phandle);
+ return ESUCCESS;
+}
+
+ARC_STATUS BtrFsRead(ULONG FileId, VOID *Buffer, ULONG Size, ULONG *BytesRead)
+{
+ pbtrfs_file_info phandle = FsGetDeviceSpecific(FileId);
+ u64 rd;
+
+ TRACE("BtrFsRead %lu, size=%lu \n", FileId, Size);
+
+ if (!Size)
+ Size = phandle->inode.size;
+
+ if (Size > phandle->inode.size)
+ Size = phandle->inode.size;
+
+ rd = btrfs_file_read(&BtrFsInfo->FsRoot, phandle->inr, phandle->position, Size, Buffer);
+ if (rd == -1ULL)
+ {
+ TRACE("An error occured while reading file %lu\n", FileId);
+ return ENOENT;
+ }
+
+ *BytesRead = rd;
+ return ESUCCESS;
+}
+
+ARC_STATUS BtrFsSeek(ULONG FileId, LARGE_INTEGER *Position, SEEKMODE SeekMode)
+{
+ pbtrfs_file_info phandle = FsGetDeviceSpecific(FileId);
+
+ TRACE("BtrFsSeek %lu NewFilePointer = %llu\n", FileId, Position->QuadPart);
+
+ if (SeekMode != SeekAbsolute)
+ return EINVAL;
+ if (Position->QuadPart >= phandle->inode.size)
+ return EINVAL;
+
+ phandle->position = Position->QuadPart;
+ return ESUCCESS;
+}
+
+const DEVVTBL BtrFsFuncTable =
+{
+ BtrFsClose,
+ BtrFsGetFileInformation,
+ BtrFsOpen,
+ BtrFsRead,
+ BtrFsSeek,
+ L"btrfs",
+};
+
+const DEVVTBL *BtrFsMount(ULONG DeviceId)
+{
+ struct btrfs_path path;
+ struct btrfs_root_item fs_root_item;
+
+ TRACE("Enter BtrFsMount(), sizeof %d %d\n", sizeof(struct BTRFS_INFO), sizeof(struct btrfs_super_block));
+
+ BtrFsInfo = FrLdrTempAlloc(sizeof(struct BTRFS_INFO), TAG_BTRFS_INFO);
+ if (!BtrFsInfo)
+ return NULL;
+ RtlZeroMemory(BtrFsInfo, sizeof(struct BTRFS_INFO));
+
+ /* Read the SuperBlock */
+ if (!disk_read(BTRFS_SUPER_INFO_OFFSET, &BtrFsInfo->SuperBlock, sizeof(struct btrfs_super_block)))
+ {
+ FrLdrTempFree(BtrFsInfo, TAG_BTRFS_INFO);
+ return NULL;
+ }
+
+ /* Check if SuperBlock is valid. If yes, return Ext2 function table */
+ if (BtrFsInfo->SuperBlock.magic == BTRFS_MAGIC_N)
+ {
+ BtrFsInfo->DeviceId = DeviceId;
+ TRACE("BtrFsMount() superblock magic ok\n");
+
+ btrfs_init_crc32c();
+
+ btrfs_read_sys_chunk_array();
+ btrfs_read_chunk_tree();
+
+ /* setup roots */
+ fs_root_item.bytenr = BtrFsInfo->SuperBlock.root;
+ fs_root_item.level = BtrFsInfo->SuperBlock.root_level;
+
+ init_path(&path);
+ if (!BtrFsSearchTreeType(&fs_root_item, BTRFS_FS_TREE_OBJECTID, BTRFS_ROOT_ITEM_KEY, &path))
+ {
+ FrLdrTempFree(BtrFsInfo, TAG_BTRFS_INFO);
+ free_path(&path);
+ return NULL;
+ }
+
+ BtrFsInfo->FsRoot = *(struct btrfs_root_item *) path_current_data(&path);
+
+ free_path(&path);
+
+ TRACE("BtrFsMount success\n");
+
+ return &BtrFsFuncTable;
+ }
+ else
+ {
+ return NULL;
+ }
+}