diff --git a/boot/freeldr/bootsect/CMakeLists.txt b/boot/freeldr/bootsect/CMakeLists.txt index f76ac7a95bb..ddcbf968018 100644 --- a/boot/freeldr/bootsect/CMakeLists.txt +++ b/boot/freeldr/bootsect/CMakeLists.txt @@ -6,6 +6,7 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") CreateBootSectorTarget(fat ${CMAKE_CURRENT_SOURCE_DIR}/fat.S ${CMAKE_CURRENT_BINARY_DIR}/fat.bin 7c00) CreateBootSectorTarget(fat32 ${CMAKE_CURRENT_SOURCE_DIR}/fat32.S ${CMAKE_CURRENT_BINARY_DIR}/fat32.bin 7c00) + CreateBootSectorTarget(btrfsvbr ${CMAKE_CURRENT_SOURCE_DIR}/btrfs.S ${CMAKE_CURRENT_BINARY_DIR}/btrfs.bin 7c00) ## New versions using FATY.S (experimental) # add_definitions(-DFAT12) @@ -24,6 +25,7 @@ if(ARCH STREQUAL "i386" OR ARCH STREQUAL "amd64") add_cd_file(TARGET dosmbr DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/dosmbr.bin FOR bootcd regtest) add_cd_file(TARGET ext2 DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/ext2.bin FOR bootcd regtest) + add_cd_file(TARGET btrfsvbr DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/btrfs.bin FOR bootcd regtest) add_cd_file(TARGET fat DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/fat.bin FOR bootcd regtest) add_cd_file(TARGET fat32 DESTINATION loader NO_CAB FILE ${CMAKE_CURRENT_BINARY_DIR}/fat32.bin FOR bootcd regtest) add_cd_file(TARGET isoboot DESTINATION loader NO_CAB NOT_IN_HYBRIDCD FILE ${CMAKE_CURRENT_BINARY_DIR}/isoboot.bin FOR all hybridcd) diff --git a/boot/freeldr/bootsect/btrfs.S b/boot/freeldr/bootsect/btrfs.S new file mode 100644 index 00000000000..7e1ffa16ea3 --- /dev/null +++ b/boot/freeldr/bootsect/btrfs.S @@ -0,0 +1,773 @@ +/* + * 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 +#include + +.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 diff --git a/boot/freeldr/freeldr/CMakeLists.txt b/boot/freeldr/freeldr/CMakeLists.txt index 63d985e920f..762b53e3963 100644 --- a/boot/freeldr/freeldr/CMakeLists.txt +++ b/boot/freeldr/freeldr/CMakeLists.txt @@ -37,6 +37,7 @@ list(APPEND FREELDR_BOOTLIB_COMMON_SOURCE lib/peloader.c lib/comm/rs232.c ## add KD support + lib/fs/btrfs.c lib/fs/ext2.c lib/fs/fat.c lib/fs/fs.c diff --git a/boot/freeldr/freeldr/arch/i386/hwdisk.c b/boot/freeldr/freeldr/arch/i386/hwdisk.c index f20ee62ce61..af86f62382e 100644 --- a/boot/freeldr/freeldr/arch/i386/hwdisk.c +++ b/boot/freeldr/freeldr/arch/i386/hwdisk.c @@ -188,7 +188,7 @@ DiskSeek(ULONG FileId, LARGE_INTEGER* Position, SEEKMODE SeekMode) if (Position->LowPart & (Context->SectorSize - 1)) return EINVAL; - Context->SectorNumber = (ULONG)(Position->QuadPart / Context->SectorSize); + Context->SectorNumber = Position->QuadPart / Context->SectorSize; return ESUCCESS; } diff --git a/boot/freeldr/freeldr/include/freeldr.h b/boot/freeldr/freeldr/include/freeldr.h index bb339cc8527..b62d4e56003 100644 --- a/boot/freeldr/freeldr/include/freeldr.h +++ b/boot/freeldr/freeldr/include/freeldr.h @@ -89,6 +89,7 @@ #include #include #include +#include /* UI support */ #include diff --git a/boot/freeldr/freeldr/include/fs/btrfs.h b/boot/freeldr/freeldr/include/fs/btrfs.h new file mode 100644 index 00000000000..b8fddc773c3 --- /dev/null +++ b/boot/freeldr/freeldr/include/fs/btrfs.h @@ -0,0 +1,418 @@ +/* + * 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 +#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 + +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 + +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); diff --git a/boot/freeldr/freeldr/include/fs/crc32c.h b/boot/freeldr/freeldr/include/fs/crc32c.h new file mode 100644 index 00000000000..2dc31000ae7 --- /dev/null +++ b/boot/freeldr/freeldr/include/fs/crc32c.h @@ -0,0 +1,52 @@ +/* + * Copied from Linux kernel crypto/crc32c.c + * Copyright (c) 2004 Cisco Systems, Inc. + * Copyright (c) 2008 Herbert Xu + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the Free + * Software Foundation; either version 2 of the License, or (at your option) + * any later version. + * + */ + +#pragma once + +/* + * This is the CRC-32C table + * Generated with: + * width = 32 bits + * poly = 0x1EDC6F41 + * reflect input bytes = true + * reflect output bytes = true + */ + +static u32 crc32c_table[256]; + +/* + * Steps through buffer one byte at at time, calculates reflected + * crc using table. + */ + +static inline u32 crc32c_le(u32 crc, const char *data, size_t length) +{ + while (length--) + crc = crc32c_table[(u8)(crc ^ *data++)] ^ (crc >> 8); + + return crc; +} + +static inline void btrfs_init_crc32c(void) +{ + int i, j; + u32 v; + const u32 poly = 0x82F63B78; /* Bit-reflected CRC32C polynomial */ + + for (i = 0; i < 256; i++) { + v = i; + for (j = 0; j < 8; j++) { + v = (v >> 1) ^ ((v & 1) ? poly : 0); + } + crc32c_table[i] = v; + } +} diff --git a/boot/freeldr/freeldr/lib/fs/btrfs.c b/boot/freeldr/freeldr/lib/fs/btrfs.c new file mode 100644 index 00000000000..7c3aa5c4ce1 --- /dev/null +++ b/boot/freeldr/freeldr/lib/fs/btrfs.c @@ -0,0 +1,1283 @@ +/* + * 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 +#include + +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; + } +} diff --git a/boot/freeldr/freeldr/lib/fs/fs.c b/boot/freeldr/freeldr/lib/fs/fs.c index 74958d04276..b1de1603fd5 100644 --- a/boot/freeldr/freeldr/lib/fs/fs.c +++ b/boot/freeldr/freeldr/lib/fs/fs.c @@ -152,6 +152,8 @@ ARC_STATUS ArcOpen(CHAR* Path, OPENMODE OpenMode, ULONG* FileId) if (!FileData[DeviceId].FileFuncTable) #endif FileData[DeviceId].FileFuncTable = FatMount(DeviceId); + if (!FileData[DeviceId].FileFuncTable) + FileData[DeviceId].FileFuncTable = BtrFsMount(DeviceId); #ifndef _M_ARM if (!FileData[DeviceId].FileFuncTable) FileData[DeviceId].FileFuncTable = NtfsMount(DeviceId); diff --git a/boot/freeldr/freeldr/ntldr/winldr.c b/boot/freeldr/freeldr/ntldr/winldr.c index aaf1d9da562..b11015600a9 100644 --- a/boot/freeldr/freeldr/ntldr/winldr.c +++ b/boot/freeldr/freeldr/ntldr/winldr.c @@ -488,15 +488,15 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion, /* Initialize SystemRoot\System32 path */ strcpy(DirPath, BootPath); - strcat(DirPath, "SYSTEM32\\"); + strcat(DirPath, "system32\\"); // // TODO: Parse also the separate INI values "Kernel=" and "Hal=" // /* Default KERNEL and HAL file names */ - strcpy(KernelFileName, "NTOSKRNL.EXE"); - strcpy(HalFileName , "HAL.DLL"); + strcpy(KernelFileName, "ntoskrnl.exe"); + strcpy(HalFileName , "hal.dll"); /* Find any /KERNEL= or /HAL= switch in the boot options */ Options = BootOptions; @@ -544,10 +544,10 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion, TRACE("Kernel file = '%s' ; HAL file = '%s'\n", KernelFileName, HalFileName); /* Load the Kernel */ - LoadModule(LoaderBlock, DirPath, KernelFileName, "NTOSKRNL.EXE", LoaderSystemCode, KernelDTE, 30); + LoadModule(LoaderBlock, DirPath, KernelFileName, "ntoskrnl.exe", LoaderSystemCode, KernelDTE, 30); /* Load the HAL */ - LoadModule(LoaderBlock, DirPath, HalFileName, "HAL.DLL", LoaderHalCode, &HalDTE, 45); + LoadModule(LoaderBlock, DirPath, HalFileName, "hal.dll", LoaderHalCode, &HalDTE, 45); /* Load the Kernel Debugger Transport DLL */ if (OperatingSystemVersion > _WIN32_WINNT_WIN2K) @@ -630,7 +630,7 @@ LoadWindowsCore(IN USHORT OperatingSystemVersion, * Load the transport DLL. Override the base DLL name of the * loaded transport DLL to the default "KDCOM.DLL" name. */ - LoadModule(LoaderBlock, DirPath, KdTransportDllName, "KDCOM.DLL", LoaderSystemCode, &KdComDTE, 60); + LoadModule(LoaderBlock, DirPath, KdTransportDllName, "kdcom.dll", LoaderSystemCode, &KdComDTE, 60); } } diff --git a/boot/freeldr/freeldr/ntldr/wlregistry.c b/boot/freeldr/freeldr/ntldr/wlregistry.c index bf194027cf0..40e297e9cbb 100644 --- a/boot/freeldr/freeldr/ntldr/wlregistry.c +++ b/boot/freeldr/freeldr/ntldr/wlregistry.c @@ -126,7 +126,7 @@ BOOLEAN WinLdrInitSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, // FIXME: For now we only try system strcpy(SearchPath, DirectoryPath); - strcat(SearchPath, "SYSTEM32\\CONFIG\\"); + strcat(SearchPath, "system32\\config\\"); Success = WinLdrLoadSystemHive(LoaderBlock, SearchPath, "SYSTEM"); // Fail if failed... @@ -173,7 +173,7 @@ BOOLEAN WinLdrScanSystemHive(IN OUT PLOADER_PARAMETER_BLOCK LoaderBlock, // Load NLS data strcpy(SearchPath, DirectoryPath); - strcat(SearchPath, "SYSTEM32\\"); + strcat(SearchPath, "system32\\"); Success = WinLdrLoadNLSData(LoaderBlock, SearchPath, AnsiName, OemName, LangName); TRACE("NLS data loading %s\n", Success ? "successful" : "failed");