[BTRFS]
[reactos.git] / reactos / drivers / filesystems / btrfs / read.c
index 3d50d0a..869f97a 100644 (file)
@@ -1,3 +1,20 @@
+/* Copyright (c) Mark Harmstone 2016
+ * 
+ * This file is part of WinBtrfs.
+ * 
+ * WinBtrfs is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public Licence as published by
+ * the Free Software Foundation, either version 3 of the Licence, or
+ * (at your option) any later version.
+ * 
+ * WinBtrfs is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Lesser General Public Licence for more details.
+ * 
+ * You should have received a copy of the GNU Lesser General Public Licence
+ * along with WinBtrfs.  If not, see <http://www.gnu.org/licenses/>. */
+
 #include "btrfs_drv.h"
 
 enum read_data_status {
@@ -159,6 +176,12 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI
     UINT64 *stripestart = NULL, *stripeend = NULL;
     UINT16 startoffstripe;
     
+    Status = verify_vcb(Vcb, Irp);
+    if (!NT_SUCCESS(Status)) {
+        ERR("verify_vcb returned %08x\n", Status);
+        return Status;
+    }
+    
     if (Vcb->log_to_phys_loaded) {
         chunk* c = get_chunk_from_address(Vcb, addr);
         
@@ -433,7 +456,23 @@ NTSTATUS STDCALL read_data(device_extension* Vcb, UINT64 addr, UINT32 length, UI
     
     for (i = 0; i < ci->num_stripes; i++) {
         if (context->stripes[i].status == ReadDataStatus_Error && IoIsErrorUserInduced(context->stripes[i].iosb.Status)) {
-            IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
+            if (Irp && context->stripes[i].iosb.Status == STATUS_VERIFY_REQUIRED) {
+                PDEVICE_OBJECT dev;
+                
+                dev = IoGetDeviceToVerify(Irp->Tail.Overlay.Thread);
+                IoSetDeviceToVerify(Irp->Tail.Overlay.Thread, NULL);
+                
+                if (!dev) {
+                    dev = IoGetDeviceToVerify(PsGetCurrentThread());
+                    IoSetDeviceToVerify(PsGetCurrentThread(), NULL);
+                }
+                
+                dev = Vcb->Vpb ? Vcb->Vpb->RealDevice : NULL;
+                
+                if (dev)
+                    IoVerifyVolume(dev, FALSE);
+            }
+//             IoSetHardErrorOrVerifyDevice(context->stripes[i].Irp, devices[i]->devobj);
             
             Status = context->stripes[i].iosb.Status;
             goto exit;
@@ -702,7 +741,7 @@ static NTSTATUS STDCALL read_stream(fcb* fcb, UINT8* data, UINT64 start, ULONG l
     return Status;
 }
 
-static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length) {
+static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64 start, UINT64 length, PIRP Irp) {
     NTSTATUS Status;
     KEY searchkey;
     traverse_ptr tp, next_tp;
@@ -713,7 +752,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
     searchkey.obj_type = TYPE_EXTENT_CSUM;
     searchkey.offset = start;
     
-    Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE);
+    Status = find_item(Vcb, Vcb->checksum_root, &tp, &searchkey, FALSE, Irp);
     if (!NT_SUCCESS(Status)) {
         ERR("error - find_item returned %08x\n", Status);
         return Status;
@@ -742,7 +781,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
                 break;
         }
         
-        b = find_next_item(Vcb, &tp, &next_tp, FALSE);
+        b = find_next_item(Vcb, &tp, &next_tp, FALSE, Irp);
         
         if (b)
             tp = next_tp;
@@ -756,7 +795,7 @@ static NTSTATUS load_csum_from_disk(device_extension* Vcb, UINT32* csum, UINT64
     return STATUS_SUCCESS;
 }
 
-static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum) {
+static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UINT32** pcsum, PIRP Irp) {
     UINT32* csum = NULL;
     NTSTATUS Status;
     UINT64 end;
@@ -827,7 +866,7 @@ static NTSTATUS load_csum(device_extension* Vcb, UINT64 start, UINT64 length, UI
     runlength = RtlFindFirstRunClear(&bmp, &index);
             
     while (runlength != 0) {
-        Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength);
+        Status = load_csum_from_disk(Vcb, &csum[index], start + (index * Vcb->superblock.sector_size), runlength, Irp);
         if (!NT_SUCCESS(Status)) {
             ERR("load_csum_from_disk returned %08x\n", Status);
             goto end;
@@ -854,72 +893,23 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
     NTSTATUS Status;
     EXTENT_DATA* ed;
     UINT64 bytes_read = 0;
+    UINT64 last_end;
     LIST_ENTRY* le;
     
     TRACE("(%p, %p, %llx, %llx, %p)\n", fcb, data, start, length, pbr);
     
     if (pbr)
         *pbr = 0;
-
-    le = fcb->extents.Flink;
     
-    while (le != &fcb->extents) {
-        extent* ext = CONTAINING_RECORD(le, extent, list_entry);
-        
-        if (!ext->ignore) {
-            if (ext->offset == start)
-                break;
-            else if (ext->offset > start) {
-                LIST_ENTRY* le2 = le->Blink;
-                
-                ext = NULL;
-                
-                while (le2 != &fcb->extents) {
-                    extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-                    
-                    if (!ext2->ignore) {
-                        le = le2;
-                        ext = ext2;
-                        break;
-                    }
-                    
-                    le2 = le2->Blink;
-                }
-                
-                if (!ext) {
-                    ERR("first extent was after offset\n");
-                    Status = STATUS_INTERNAL_ERROR;
-                    goto exit;
-                } else
-                    break;
-            }
-        }
-        
-        le = le->Flink;
+    if (start >= fcb->inode_item.st_size) {
+        WARN("Tried to read beyond end of file\n");
+        Status = STATUS_END_OF_FILE;
+        goto exit;        
     }
-    
-    if (le == &fcb->extents) {
-        LIST_ENTRY* le2 = le->Blink;
-        extent* ext = NULL;
 
-        while (le2 != &fcb->extents) {
-            extent* ext2 = CONTAINING_RECORD(le2, extent, list_entry);
-            
-            if (!ext2->ignore) {
-                le = le2;
-                ext = ext2;
-                break;
-            }
-            
-            le2 = le2->Blink;
-        }
-        
-        if (!ext) {
-            ERR("could not find extent\n");
-            Status = STATUS_INTERNAL_ERROR;
-            goto exit;
-        }
-    }
+    le = fcb->extents.Flink;
+
+    last_end = start;
 
     while (le != &fcb->extents) {
         UINT64 len;
@@ -929,34 +919,26 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
         if (!ext->ignore) {
             ed = ext->data;
             
-            if (ext->datalen < sizeof(EXTENT_DATA)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA));
-                Status = STATUS_INTERNAL_ERROR;
-                goto exit;
-            }
-            
-            if ((ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) && ext->datalen < sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2)) {
-                ERR("extent %llx was %u bytes, expected at least %u\n", ext->offset, ext->datalen, sizeof(EXTENT_DATA) - 1 + sizeof(EXTENT_DATA2));
-                Status = STATUS_INTERNAL_ERROR;
-                goto exit;
-            }
-            
-            ed2 = (EXTENT_DATA2*)ed->data;
+            ed2 = (ed->type == EXTENT_TYPE_REGULAR || ed->type == EXTENT_TYPE_PREALLOC) ? (EXTENT_DATA2*)ed->data : NULL;
             
-            len = ed->type == EXTENT_TYPE_INLINE ? ed->decoded_size : ed2->num_bytes;
+            len = ed2 ? ed2->num_bytes : ed->decoded_size;
             
-            if (ext->offset + len < start) {
-                ERR("Tried to read beyond end of file\n");
-                Status = STATUS_END_OF_FILE;
-                goto exit;
+            if (ext->offset + len <= start) {
+                last_end = ext->offset + len;
+                goto nextitem;
             }
             
-            if (ed->compression != BTRFS_COMPRESSION_NONE) {
-                FIXME("FIXME - compression not yet supported\n");
-                Status = STATUS_NOT_IMPLEMENTED;
-                goto exit;
+            if (ext->offset > last_end && ext->offset > start + bytes_read) {
+                UINT32 read = min(length, ext->offset - max(start, last_end));
+                
+                RtlZeroMemory(data + bytes_read, read);
+                bytes_read += read;
+                length -= read;
             }
             
+            if (length == 0 || ext->offset > start + bytes_read + length)
+                break;
+            
             if (ed->encryption != BTRFS_ENCRYPTION_NONE) {
                 WARN("Encryption not supported\n");
                 Status = STATUS_NOT_IMPLEMENTED;
@@ -979,6 +961,8 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     
                     RtlCopyMemory(data + bytes_read, &ed->data[off], read);
                     
+                    // FIXME - can we have compressed inline extents?
+                    
                     bytes_read += read;
                     length -= read;
                     break;
@@ -989,16 +973,13 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     UINT64 off = start + bytes_read - ext->offset;
                     UINT32 to_read, read;
                     UINT8* buf;
+                    UINT32 *csum, bumpoff = 0;
+                    UINT64 addr;
                     
                     read = len - off;
                     if (read > length) read = length;
                     
-                    if (ed2->address == 0) {
-                        RtlZeroMemory(data + bytes_read, read);
-                    } else {
-                        UINT32 *csum, bumpoff = 0;
-                        UINT64 addr;
-                        
+                    if (ed->compression == BTRFS_COMPRESSION_NONE) {
                         addr = ed2->address + ed2->offset + off;
                         to_read = sector_align(read, fcb->Vcb->superblock.sector_size);
                         
@@ -1007,41 +988,71 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                             addr -= bumpoff;
                             to_read = sector_align(read + bumpoff, fcb->Vcb->superblock.sector_size);
                         }
+                    } else {
+                        addr = ed2->address;
+                        to_read = sector_align(ed2->size, fcb->Vcb->superblock.sector_size);
+                    }
+                    
+                    buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+                    
+                    if (!buf) {
+                        ERR("out of memory\n");
+                        Status = STATUS_INSUFFICIENT_RESOURCES;
+                        goto exit;
+                    }
+                    
+                    if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
+                        Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum, Irp);
                         
-                        buf = ExAllocatePoolWithTag(PagedPool, to_read, ALLOC_TAG);
+                        if (!NT_SUCCESS(Status)) {
+                            ERR("load_csum returned %08x\n", Status);
+                            ExFreePool(buf);
+                            goto exit;
+                        }
+                    } else
+                        csum = NULL;
+                    
+                    Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
+                    if (!NT_SUCCESS(Status)) {
+                        ERR("read_data returned %08x\n", Status);
+                        ExFreePool(buf);
+                        goto exit;
+                    }
+                    
+                    if (ed->compression == BTRFS_COMPRESSION_NONE) {
+                        RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+                    } else {
+                        UINT8* decomp = NULL;
+                        
+                        // FIXME - don't mess around with decomp if we're reading the whole extent
                         
-                        if (!buf) {
+                        decomp = ExAllocatePoolWithTag(PagedPool, ed->decoded_size, ALLOC_TAG);
+                        if (!decomp) {
                             ERR("out of memory\n");
+                            ExFreePool(buf);
                             Status = STATUS_INSUFFICIENT_RESOURCES;
                             goto exit;
                         }
                         
-                        if (!(fcb->inode_item.flags & BTRFS_INODE_NODATASUM)) {
-                            Status = load_csum(fcb->Vcb, addr, to_read / fcb->Vcb->superblock.sector_size, &csum);
-                            
-                            if (!NT_SUCCESS(Status)) {
-                                ERR("load_csum returned %08x\n", Status);
-                                ExFreePool(buf);
-                                goto exit;
-                            }
-                        } else
-                            csum = NULL;
+                        Status = decompress(ed->compression, buf, ed2->size, decomp, ed->decoded_size);
                         
-                        Status = read_data(fcb->Vcb, addr, to_read, csum, FALSE, buf, NULL, Irp);
                         if (!NT_SUCCESS(Status)) {
-                            ERR("read_data returned %08x\n", Status);
+                            ERR("decompress returned %08x\n", Status);
                             ExFreePool(buf);
+                            ExFreePool(decomp);
                             goto exit;
                         }
                         
-                        RtlCopyMemory(data + bytes_read, buf + bumpoff, read);
+                        RtlCopyMemory(data + bytes_read, decomp + ed2->offset + off, min(read, ed2->num_bytes - off));
                         
-                        ExFreePool(buf);
-                        
-                        if (csum)
-                            ExFreePool(csum);
+                        ExFreePool(decomp);
                     }
                     
+                    ExFreePool(buf);
+                    
+                    if (csum)
+                        ExFreePool(csum);
+                    
                     bytes_read += read;
                     length -= read;
                     
@@ -1068,14 +1079,26 @@ NTSTATUS STDCALL read_file(fcb* fcb, UINT8* data, UINT64 start, UINT64 length, U
                     Status = STATUS_NOT_IMPLEMENTED;
                     goto exit;
             }
+
+            last_end = ext->offset + len;
             
             if (length == 0)
                 break;
         }
 
+nextitem:
         le = le->Flink;
     }
     
+    if (length > 0 && start + bytes_read < fcb->inode_item.st_size) {
+        UINT32 read = min(fcb->inode_item.st_size - start - bytes_read, length);
+        
+        RtlZeroMemory(data + bytes_read, read);
+        
+        bytes_read += read;
+        length -= read;
+    }
+    
     Status = STATUS_SUCCESS;
     if (pbr)
         *pbr = bytes_read;
@@ -1131,7 +1154,7 @@ NTSTATUS do_read(PIRP Irp, BOOL wait, ULONG* bytes_read) {
         }
         
         if (start >= fcb->Header.ValidDataLength.QuadPart) {
-            length = min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart;
+            length = min(length, min(start + length, fcb->Header.FileSize.QuadPart) - fcb->Header.ValidDataLength.QuadPart);
             RtlZeroMemory(data, length);
             Irp->IoStatus.Information = *bytes_read = length;
             return STATUS_SUCCESS;
@@ -1233,7 +1256,7 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
     BOOL top_level;
     fcb* fcb;
     ccb* ccb;
-    BOOL tree_lock = FALSE, fcb_lock = FALSE;
+    BOOL tree_lock = FALSE, fcb_lock = FALSE, pagefile;
     
     FsRtlEnterFileSystem();
     
@@ -1266,6 +1289,12 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    if (fcb == Vcb->volume_fcb) {
+        TRACE("not allowing read of volume FCB\n");
+        Status = STATUS_INVALID_PARAMETER;
+        goto exit;
+    }
+    
     ccb = FileObject->FsContext2;
     
     if (!ccb) {
@@ -1280,14 +1309,18 @@ NTSTATUS STDCALL drv_read(PDEVICE_OBJECT DeviceObject, PIRP Irp) {
         goto exit;
     }
     
+    pagefile = fcb->Header.Flags2 & FSRTL_FLAG2_IS_PAGING_FILE && Irp->Flags & IRP_PAGING_IO;
+    
     if (Irp->Flags & IRP_NOCACHE) {
-        if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
-            Status = STATUS_PENDING;
-            IoMarkIrpPending(Irp);
-            goto exit;
+        if (!pagefile) {
+            if (!ExAcquireResourceSharedLite(&Vcb->tree_lock, IoIsOperationSynchronous(Irp))) {
+                Status = STATUS_PENDING;
+                IoMarkIrpPending(Irp);
+                goto exit;
+            }
+            
+            tree_lock = TRUE;
         }
-        
-        tree_lock = TRUE;
     
         if (!ExAcquireResourceSharedLite(fcb->Header.Resource, IoIsOperationSynchronous(Irp))) {
             Status = STATUS_PENDING;