static NTSTATUS query_dir_item(fcb* fcb, ccb* ccb, void* buf, LONG* len, PIRP Irp, dir_entry* de, root* r) { PIO_STACK_LOCATION IrpSp; LONG needed; UINT64 inode; INODE_ITEM ii; NTSTATUS Status; ULONG atts = 0, ealen = 0; file_ref* fileref = ccb->fileref; IrpSp = IoGetCurrentIrpStackLocation(Irp); if (de->key.obj_type == TYPE_ROOT_ITEM) { // subvol LIST_ENTRY* le; r = NULL; le = fcb->Vcb->roots.Flink; while (le != &fcb->Vcb->roots) { root* subvol = CONTAINING_RECORD(le, root, list_entry); if (subvol->id == de->key.obj_id) { r = subvol; break; } le = le->Flink; } if (r && r->parent != fcb->subvol->id) r = NULL; inode = SUBVOL_ROOT_INODE; } else { inode = de->key.obj_id; } if (IrpSp->Parameters.QueryDirectory.FileInformationClass != FileNamesInformation) { // FIXME - object ID and reparse point classes too? switch (de->dir_entry_type) { case DirEntryType_File: { if (!r) { LARGE_INTEGER time; ii = fcb->Vcb->dummy_fcb->inode_item; atts = fcb->Vcb->dummy_fcb->atts; ealen = fcb->Vcb->dummy_fcb->ealen; KeQuerySystemTime(&time); win_time_to_unix(time, &ii.otime); ii.st_atime = ii.st_mtime = ii.st_ctime = ii.otime; } else { BOOL found = FALSE; if (de->dc && de->dc->fileref && de->dc->fileref->fcb) { ii = de->dc->fileref->fcb->inode_item; atts = de->dc->fileref->fcb->atts; ealen = de->dc->fileref->fcb->ealen; found = TRUE; } if (!found) { KEY searchkey; traverse_ptr tp; searchkey.obj_id = inode; searchkey.obj_type = TYPE_INODE_ITEM; searchkey.offset = 0xffffffffffffffff; Status = find_item(fcb->Vcb, r, &tp, &searchkey, FALSE, Irp); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); return Status; } if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { ERR("could not find inode item for inode %llx in root %llx\n", inode, r->id); return STATUS_INTERNAL_ERROR; } RtlZeroMemory(&ii, sizeof(INODE_ITEM)); if (tp.item->size > 0) RtlCopyMemory(&ii, tp.item->data, min(sizeof(INODE_ITEM), tp.item->size)); if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) { BOOL dotfile = de->name.Length > sizeof(WCHAR) && de->name.Buffer[0] == '.'; atts = get_file_attributes(fcb->Vcb, r, inode, de->type, dotfile, FALSE, Irp); } if (IrpSp->Parameters.QueryDirectory.FileInformationClass == FileBothDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileFullDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdBothDirectoryInformation || IrpSp->Parameters.QueryDirectory.FileInformationClass == FileIdFullDirectoryInformation) { ealen = get_ea_len(fcb->Vcb, r, inode, Irp); } } } break; } case DirEntryType_Self: ii = fcb->inode_item; r = fcb->subvol; inode = fcb->inode; atts = fcb->atts; ealen = fcb->ealen; break; case DirEntryType_Parent: if (fileref && fileref->parent) { ii = fileref->parent->fcb->inode_item; r = fileref->parent->fcb->subvol; inode = fileref->parent->fcb->inode; atts = fileref->parent->fcb->atts; ealen = fileref->parent->fcb->ealen; } else { ERR("no fileref\n"); return STATUS_INTERNAL_ERROR; } break; } if (atts == 0) atts = FILE_ATTRIBUTE_NORMAL; } switch (IrpSp->Parameters.QueryDirectory.FileInformationClass) { case FileBothDirectoryInformation: { FILE_BOTH_DIR_INFORMATION* fbdi = buf; TRACE("FileBothDirectoryInformation\n"); needed = sizeof(FILE_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } fbdi->NextEntryOffset = 0; fbdi->FileIndex = 0; fbdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime); fbdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime); fbdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime); fbdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); fbdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; if (de->type == BTRFS_TYPE_SYMLINK) fbdi->AllocationSize.QuadPart = 0; else if (atts & FILE_ATTRIBUTE_SPARSE_FILE) fbdi->AllocationSize.QuadPart = ii.st_blocks; else fbdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size); fbdi->FileAttributes = atts; fbdi->FileNameLength = de->name.Length; fbdi->EaSize = (r && atts & FILE_ATTRIBUTE_REPARSE_POINT) ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp) : ealen; fbdi->ShortNameLength = 0; RtlCopyMemory(fbdi->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileDirectoryInformation: { FILE_DIRECTORY_INFORMATION* fdi = buf; TRACE("FileDirectoryInformation\n"); needed = sizeof(FILE_DIRECTORY_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } fdi->NextEntryOffset = 0; fdi->FileIndex = 0; fdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime); fdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime); fdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime); fdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); fdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; if (de->type == BTRFS_TYPE_SYMLINK) fdi->AllocationSize.QuadPart = 0; else if (atts & FILE_ATTRIBUTE_SPARSE_FILE) fdi->AllocationSize.QuadPart = ii.st_blocks; else fdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size); fdi->FileAttributes = atts; fdi->FileNameLength = de->name.Length; RtlCopyMemory(fdi->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileFullDirectoryInformation: { FILE_FULL_DIR_INFORMATION* ffdi = buf; TRACE("FileFullDirectoryInformation\n"); needed = sizeof(FILE_FULL_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } ffdi->NextEntryOffset = 0; ffdi->FileIndex = 0; ffdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime); ffdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime); ffdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime); ffdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); ffdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; if (de->type == BTRFS_TYPE_SYMLINK) ffdi->AllocationSize.QuadPart = 0; else if (atts & FILE_ATTRIBUTE_SPARSE_FILE) ffdi->AllocationSize.QuadPart = ii.st_blocks; else ffdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size); ffdi->FileAttributes = atts; ffdi->FileNameLength = de->name.Length; ffdi->EaSize = (r && atts & FILE_ATTRIBUTE_REPARSE_POINT) ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp) : ealen; RtlCopyMemory(ffdi->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileIdBothDirectoryInformation: { FILE_ID_BOTH_DIR_INFORMATION* fibdi = buf; TRACE("FileIdBothDirectoryInformation\n"); needed = sizeof(FILE_ID_BOTH_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } fibdi->NextEntryOffset = 0; fibdi->FileIndex = 0; fibdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime); fibdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime); fibdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime); fibdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); fibdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; if (de->type == BTRFS_TYPE_SYMLINK) fibdi->AllocationSize.QuadPart = 0; else if (atts & FILE_ATTRIBUTE_SPARSE_FILE) fibdi->AllocationSize.QuadPart = ii.st_blocks; else fibdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size); fibdi->FileAttributes = atts; fibdi->FileNameLength = de->name.Length; fibdi->EaSize = (r && atts & FILE_ATTRIBUTE_REPARSE_POINT) ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp) : ealen; fibdi->ShortNameLength = 0; fibdi->FileId.QuadPart = r ? make_file_id(r, inode) : make_file_id(fcb->Vcb->dummy_fcb->subvol, fcb->Vcb->dummy_fcb->inode); RtlCopyMemory(fibdi->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileIdFullDirectoryInformation: { FILE_ID_FULL_DIR_INFORMATION* fifdi = buf; TRACE("FileIdFullDirectoryInformation\n"); needed = sizeof(FILE_ID_FULL_DIR_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } fifdi->NextEntryOffset = 0; fifdi->FileIndex = 0; fifdi->CreationTime.QuadPart = unix_time_to_win(&ii.otime); fifdi->LastAccessTime.QuadPart = unix_time_to_win(&ii.st_atime); fifdi->LastWriteTime.QuadPart = unix_time_to_win(&ii.st_mtime); fifdi->ChangeTime.QuadPart = unix_time_to_win(&ii.st_ctime); fifdi->EndOfFile.QuadPart = de->type == BTRFS_TYPE_SYMLINK ? 0 : ii.st_size; if (de->type == BTRFS_TYPE_SYMLINK) fifdi->AllocationSize.QuadPart = 0; else if (atts & FILE_ATTRIBUTE_SPARSE_FILE) fifdi->AllocationSize.QuadPart = ii.st_blocks; else fifdi->AllocationSize.QuadPart = sector_align(ii.st_size, fcb->Vcb->superblock.sector_size); fifdi->FileAttributes = atts; fifdi->FileNameLength = de->name.Length; fifdi->EaSize = (r && atts & FILE_ATTRIBUTE_REPARSE_POINT) ? get_reparse_tag(fcb->Vcb, r, inode, de->type, atts, ccb->lxss, Irp) : ealen; fifdi->FileId.QuadPart = r ? make_file_id(r, inode) : make_file_id(fcb->Vcb->dummy_fcb->subvol, fcb->Vcb->dummy_fcb->inode); RtlCopyMemory(fifdi->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileNamesInformation: { FILE_NAMES_INFORMATION* fni = buf; TRACE("FileNamesInformation\n"); needed = sizeof(FILE_NAMES_INFORMATION) - sizeof(WCHAR) + de->name.Length; if (needed > *len) { TRACE("buffer overflow - %u > %u\n", needed, *len); return STATUS_BUFFER_OVERFLOW; } fni->NextEntryOffset = 0; fni->FileIndex = 0; fni->FileNameLength = de->name.Length; RtlCopyMemory(fni->FileName, de->name.Buffer, de->name.Length); *len -= needed; return STATUS_SUCCESS; } case FileObjectIdInformation: FIXME("STUB: FileObjectIdInformation\n"); return STATUS_NOT_IMPLEMENTED; case FileQuotaInformation: FIXME("STUB: FileQuotaInformation\n"); return STATUS_NOT_IMPLEMENTED; case FileReparsePointInformation: FIXME("STUB: FileReparsePointInformation\n"); return STATUS_NOT_IMPLEMENTED; default: WARN("Unknown FileInformationClass %u\n", IrpSp->Parameters.QueryDirectory.FileInformationClass); return STATUS_NOT_IMPLEMENTED; } return STATUS_NO_MORE_FILES; }
static void STDCALL test_vol(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT mountmgr, PUNICODE_STRING pardir, PUNICODE_STRING us, BOOL part0, LIST_ENTRY* volumes) { KEVENT Event; PIRP Irp; IO_STATUS_BLOCK IoStatusBlock; NTSTATUS Status; PFILE_OBJECT FileObject; PDEVICE_OBJECT DeviceObject; LARGE_INTEGER Offset; ULONG toread; UINT8* data; UNICODE_STRING us2; BOOL added_entry = FALSE; UINT32 sector_size; TRACE("%.*S\n", us->Length / sizeof(WCHAR), us->Buffer); us2.Length = pardir->Length + sizeof(WCHAR) + us->Length; us2.MaximumLength = us2.Length; us2.Buffer = ExAllocatePoolWithTag(PagedPool, us2.Length, ALLOC_TAG); if (!us2.Buffer) { ERR("out of memory\n"); return; } RtlCopyMemory(us2.Buffer, pardir->Buffer, pardir->Length); us2.Buffer[pardir->Length / sizeof(WCHAR)] = '\\'; RtlCopyMemory(&us2.Buffer[(pardir->Length / sizeof(WCHAR))+1], us->Buffer, us->Length); TRACE("%.*S\n", us2.Length / sizeof(WCHAR), us2.Buffer); Status = IoGetDeviceObjectPointer(&us2, FILE_READ_ATTRIBUTES, &FileObject, &DeviceObject); if (!NT_SUCCESS(Status)) { ERR("IoGetDeviceObjectPointer returned %08x\n", Status); goto exit; } sector_size = DeviceObject->SectorSize; if (sector_size == 0) { DISK_GEOMETRY geometry; IO_STATUS_BLOCK iosb; Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb); if (!NT_SUCCESS(Status)) { ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08x\n", us2.Length / sizeof(WCHAR), us2.Buffer, Status); goto exit; } if (iosb.Information < sizeof(DISK_GEOMETRY)) { ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %u bytes, expected %u\n", us2.Length / sizeof(WCHAR), us2.Buffer, iosb.Information, sizeof(DISK_GEOMETRY)); } sector_size = geometry.BytesPerSector; if (sector_size == 0) { ERR("%.*S had a sector size of 0\n", us2.Length / sizeof(WCHAR), us2.Buffer); goto exit; } } KeInitializeEvent(&Event, NotificationEvent, FALSE); Offset.QuadPart = superblock_addrs[0]; toread = sector_align(sizeof(superblock), sector_size); data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); if (!data) { ERR("out of memory\n"); goto deref; } Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock); if (!Irp) { ERR("IoBuildSynchronousFsdRequest failed\n"); goto deref; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } if (NT_SUCCESS(Status) && IoStatusBlock.Information > 0 && ((superblock*)data)->magic == BTRFS_MAGIC) { int i; GET_LENGTH_INFORMATION gli; superblock* sb = (superblock*)data; volume* v = ExAllocatePoolWithTag(PagedPool, sizeof(volume), ALLOC_TAG); if (!v) { ERR("out of memory\n"); goto deref; } Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &gli, sizeof(gli), TRUE, NULL); if (!NT_SUCCESS(Status)) { ERR("error reading length information: %08x\n", Status); goto deref; } if (part0) { UNICODE_STRING us3; Status = create_part0(DriverObject, DeviceObject, pardir, &us3, &sb->dev_item.device_uuid); if (!NT_SUCCESS(Status)) { ERR("create_part0 returned %08x\n", Status); goto deref; } ExFreePool(us2.Buffer); us2 = us3; } RtlCopyMemory(&v->fsuuid, &sb->uuid, sizeof(BTRFS_UUID)); RtlCopyMemory(&v->devuuid, &sb->dev_item.device_uuid, sizeof(BTRFS_UUID)); v->devnum = sb->dev_item.dev_id; v->devpath = us2; v->processed = FALSE; v->length = gli.Length.QuadPart; v->gen1 = sb->generation; v->gen2 = 0; InsertTailList(volumes, &v->list_entry); i = 1; while (superblock_addrs[i] != 0 && superblock_addrs[i] + toread <= v->length) { KeInitializeEvent(&Event, NotificationEvent, FALSE); Offset.QuadPart = superblock_addrs[i]; Irp = IoBuildSynchronousFsdRequest(IRP_MJ_READ, DeviceObject, data, toread, &Offset, &Event, &IoStatusBlock); if (!Irp) { ERR("IoBuildSynchronousFsdRequest failed\n"); goto deref; } Status = IoCallDriver(DeviceObject, Irp); if (Status == STATUS_PENDING) { KeWaitForSingleObject(&Event, Executive, KernelMode, FALSE, NULL); Status = IoStatusBlock.Status; } if (NT_SUCCESS(Status)) { UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); if (crc32 != *((UINT32*)sb->checksum)) WARN("superblock %u CRC error\n", i); else if (sb->generation > v->gen1) { v->gen2 = v->gen1; v->gen1 = sb->generation; } } else { ERR("got error %08x while reading superblock %u\n", Status, i); } i++; } TRACE("volume found\n"); TRACE("gen1 = %llx, gen2 = %llx\n", v->gen1, v->gen2); TRACE("FS uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", v->fsuuid.uuid[0], v->fsuuid.uuid[1], v->fsuuid.uuid[2], v->fsuuid.uuid[3], v->fsuuid.uuid[4], v->fsuuid.uuid[5], v->fsuuid.uuid[6], v->fsuuid.uuid[7], v->fsuuid.uuid[8], v->fsuuid.uuid[9], v->fsuuid.uuid[10], v->fsuuid.uuid[11], v->fsuuid.uuid[12], v->fsuuid.uuid[13], v->fsuuid.uuid[14], v->fsuuid.uuid[15]); TRACE("device uuid %02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", v->devuuid.uuid[0], v->devuuid.uuid[1], v->devuuid.uuid[2], v->devuuid.uuid[3], v->devuuid.uuid[4], v->devuuid.uuid[5], v->devuuid.uuid[6], v->devuuid.uuid[7], v->devuuid.uuid[8], v->devuuid.uuid[9], v->devuuid.uuid[10], v->devuuid.uuid[11], v->devuuid.uuid[12], v->devuuid.uuid[13], v->devuuid.uuid[14], v->devuuid.uuid[15]); TRACE("device number %llx\n", v->devnum); added_entry = TRUE; } deref: ExFreePool(data); ObDereferenceObject(FileObject); exit: if (!added_entry) ExFreePool(us2.Buffer); }
static void test_vol(PDEVICE_OBJECT mountmgr, PDEVICE_OBJECT DeviceObject, PUNICODE_STRING devpath, DWORD disk_num, DWORD part_num, UINT64 length) { NTSTATUS Status; ULONG toread; UINT8* data = NULL; UINT32 sector_size; TRACE("%.*S\n", devpath->Length / sizeof(WCHAR), devpath->Buffer); sector_size = DeviceObject->SectorSize; if (sector_size == 0) { DISK_GEOMETRY geometry; IO_STATUS_BLOCK iosb; Status = dev_ioctl(DeviceObject, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &geometry, sizeof(DISK_GEOMETRY), TRUE, &iosb); if (!NT_SUCCESS(Status)) { ERR("%.*S had a sector size of 0, and IOCTL_DISK_GET_DRIVE_GEOMETRY returned %08x\n", devpath->Length / sizeof(WCHAR), devpath->Buffer, Status); goto deref; } if (iosb.Information < sizeof(DISK_GEOMETRY)) { ERR("%.*S: IOCTL_DISK_GET_DRIVE_GEOMETRY returned %u bytes, expected %u\n", devpath->Length / sizeof(WCHAR), devpath->Buffer, iosb.Information, sizeof(DISK_GEOMETRY)); } sector_size = geometry.BytesPerSector; if (sector_size == 0) { ERR("%.*S had a sector size of 0\n", devpath->Length / sizeof(WCHAR), devpath->Buffer); goto deref; } } toread = (ULONG)sector_align(sizeof(superblock), sector_size); data = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); if (!data) { ERR("out of memory\n"); goto deref; } Status = sync_read_phys(DeviceObject, superblock_addrs[0], toread, data, TRUE); if (NT_SUCCESS(Status) && ((superblock*)data)->magic == BTRFS_MAGIC) { superblock* sb = (superblock*)data; UINT32 crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb->uuid, (ULONG)sizeof(superblock) - sizeof(sb->checksum)); if (crc32 != *((UINT32*)sb->checksum)) ERR("checksum error on superblock\n"); else { TRACE("volume found\n"); if (length >= superblock_addrs[1] + toread) { ULONG i = 1; superblock* sb2 = ExAllocatePoolWithTag(NonPagedPool, toread, ALLOC_TAG); if (!sb2) { ERR("out of memory\n"); goto deref; } while (superblock_addrs[i] > 0 && length >= superblock_addrs[i] + toread) { Status = sync_read_phys(DeviceObject, superblock_addrs[i], toread, (PUCHAR)sb2, TRUE); if (NT_SUCCESS(Status) && sb2->magic == BTRFS_MAGIC) { crc32 = ~calc_crc32c(0xffffffff, (UINT8*)&sb2->uuid, (ULONG)sizeof(superblock) - sizeof(sb2->checksum)); if (crc32 == *((UINT32*)sb2->checksum) && sb2->generation > sb->generation) RtlCopyMemory(sb, sb2, toread); } i++; } ExFreePool(sb2); } if (!fs_ignored(&sb->uuid)) { DeviceObject->Flags &= ~DO_VERIFY_VOLUME; add_volume_device(sb, mountmgr, devpath, length, disk_num, part_num); } } } deref: if (data) ExFreePool(data); }
NTSTATUS STDCALL read_file(device_extension* Vcb, root* subvol, UINT64 inode, UINT8* data, UINT64 start, UINT64 length, ULONG* pbr) { KEY searchkey; NTSTATUS Status; traverse_ptr tp, next_tp; EXTENT_DATA* ed; UINT64 bytes_read = 0; TRACE("(%p, %llx, %llx, %p, %llx, %llx, %p)\n", Vcb, subvol->id, inode, data, start, length, pbr); if (pbr) *pbr = 0; searchkey.obj_id = inode; searchkey.obj_type = TYPE_EXTENT_DATA; searchkey.offset = start; Status = find_item(Vcb, subvol, &tp, &searchkey, FALSE); if (!NT_SUCCESS(Status)) { ERR("error - find_item returned %08x\n", Status); goto exit; } if (tp.item->key.obj_id < searchkey.obj_id || tp.item->key.obj_type < searchkey.obj_type) { if (find_next_item(Vcb, &tp, &next_tp, FALSE)) { tp = next_tp; TRACE("moving on to %llx,%x,%llx\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset); } } if (tp.item->key.obj_id != searchkey.obj_id || tp.item->key.obj_type != searchkey.obj_type) { ERR("couldn't find EXTENT_DATA for inode %llx in subvol %llx\n", searchkey.obj_id, subvol->id); Status = STATUS_INTERNAL_ERROR; goto exit; } if (tp.item->key.offset > start) { ERR("first EXTENT_DATA was after offset\n"); Status = STATUS_INTERNAL_ERROR; goto exit; } // while (TRUE) { // BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, NULL, FALSE); // // if (!foundnext || next_tp.item->key.obj_id != inode || // next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset > start) { // if (foundnext) // free_traverse_ptr(&next_tp); // // break; // } // // free_traverse_ptr(&tp); // tp = next_tp; // } do { UINT64 len; EXTENT_DATA2* ed2; ed = (EXTENT_DATA*)tp.item->data; if (tp.item->size < sizeof(EXTENT_DATA)) { ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA)); Status = STATUS_INTERNAL_ERROR; goto exit; } if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && tp.item->size < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) { ERR("(%llx,%x,%llx) was %u bytes, expected at least %u\n", tp.item->key.obj_id, tp.item->key.obj_type, tp.item->key.offset, tp.item->size, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)); Status = STATUS_INTERNAL_ERROR; goto exit; } ed2 = (EXTENT_DATA2*)ed->data; len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes; if (tp.item->key.offset + len < start) { ERR("Tried to read beyond end of file\n"); Status = STATUS_END_OF_FILE; goto exit; } if (ed->compression != BTRFS_COMPRESSION_NONE) { FIXME("FIXME - compression not yet supported\n"); Status = STATUS_NOT_IMPLEMENTED; goto exit; } if (ed->encryption != BTRFS_ENCRYPTION_NONE) { WARN("Encryption not supported\n"); Status = STATUS_NOT_IMPLEMENTED; goto exit; } if (ed->encoding != BTRFS_ENCODING_NONE) { WARN("Other encodings not supported\n"); Status = STATUS_NOT_IMPLEMENTED; goto exit; } switch (ed->type) { case EXTENT_TYPE_INLINE: { UINT64 off = start + bytes_read - tp.item->key.offset; UINT64 read = len - off; if (read > length) read = length; RtlCopyMemory(data + bytes_read, &ed->data[off], read); bytes_read += read; length -= read; break; } case EXTENT_TYPE_REGULAR: { UINT64 off = start + bytes_read - tp.item->key.offset; UINT32 to_read, read; UINT8* buf; read = len - off; if (read > length) read = length; if (ed2->address == 0) { RtlZeroMemory(data + bytes_read, read); } else { to_read = sector_align(read, Vcb->superblock.sector_size); buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG); if (!buf) { ERR("out of memory\n"); Status = STATUS_INSUFFICIENT_RESOURCES; goto exit; } // FIXME - load checksums Status = read_data(Vcb, ed2->address + ed2->offset + off, to_read, buf); if (!NT_SUCCESS(Status)) { ERR("read_data returned %08x\n", Status); ExFreePool(buf); goto exit; } RtlCopyMemory(data + bytes_read, buf, read); ExFreePool(buf); } bytes_read += read; length -= read; break; } case EXTENT_TYPE_PREALLOC: { UINT64 off = start + bytes_read - tp.item->key.offset; UINT32 read = len - off; if (read > length) read = length; RtlZeroMemory(data + bytes_read, read); bytes_read += read; length -= read; break; } default: WARN("Unsupported extent data type %u\n", ed->type); Status = STATUS_NOT_IMPLEMENTED; goto exit; } if (length > 0) { BOOL foundnext = find_next_item(Vcb, &tp, &next_tp, FALSE); if (!foundnext) break; else if (next_tp.item->key.obj_id != inode || next_tp.item->key.obj_type != TYPE_EXTENT_DATA || next_tp.item->key.offset != tp.item->key.offset + len ) { break; } else { TRACE("found next key (%llx,%x,%llx)\n", next_tp.item->key.obj_id, next_tp.item->key.obj_type, next_tp.item->key.offset); tp = next_tp; } } else break; } while (TRUE); Status = STATUS_SUCCESS; if (pbr) *pbr = bytes_read; exit: return Status; }