1 /* 2 * COPYRIGHT: See COPYRIGHT.TXT 3 * PROJECT: Ext2 File System Driver for WinNT/2K/XP 4 * FILE: extents.c 5 * PROGRAMMER: Matt Wu <mattwu@163.com> 6 * HOMEPAGE: http://www.ext2fsd.com 7 * UPDATE HISTORY: 8 */ 9 10 /* INCLUDES *****************************************************************/ 11 12 #include "ext2fs.h" 13 14 /* GLOBALS *****************************************************************/ 15 16 extern PEXT2_GLOBAL Ext2Global; 17 18 /* DEFINITIONS *************************************************************/ 19 20 #ifdef ALLOC_PRAGMA 21 #endif 22 23 24 NTSTATUS 25 Ext2MapExtent( 26 IN PEXT2_IRP_CONTEXT IrpContext, 27 IN PEXT2_VCB Vcb, 28 IN PEXT2_MCB Mcb, 29 IN ULONG Index, 30 IN BOOLEAN Alloc, 31 OUT PULONG Block, 32 OUT PULONG Number 33 ) 34 { 35 EXT4_EXTENT_HEADER *eh; 36 struct buffer_head bh_got = {0}; 37 int flags, rc; 38 ULONG max_blocks = 0; 39 40 memset(&bh_got, 0, sizeof(struct buffer_head)); 41 eh = get_ext4_header(&Mcb->Inode); 42 43 if (eh->eh_magic != EXT4_EXT_MAGIC) { 44 if (Alloc) { 45 /* now initialize inode extent root node */ 46 ext4_ext_tree_init(IrpContext, NULL, &Mcb->Inode); 47 } else { 48 /* return empty-mapping when inode extent isn't initialized */ 49 if (Block) 50 *Block = 0; 51 if (Number) { 52 LONGLONG _len = _len = Mcb->Inode.i_size; 53 if (Mcb->Fcb) 54 _len = Mcb->Fcb->Header.AllocationSize.QuadPart; 55 *Number = (ULONG)((_len + BLOCK_SIZE - 1) >> BLOCK_BITS); 56 } 57 return STATUS_SUCCESS; 58 } 59 } 60 61 /* IrpContext is NULL when called during journal initialization */ 62 if (IsMcbDirectory(Mcb) || IrpContext == NULL || 63 IrpContext->MajorFunction == IRP_MJ_WRITE || !Alloc){ 64 flags = EXT4_GET_BLOCKS_IO_CONVERT_EXT; 65 max_blocks = EXT_INIT_MAX_LEN; 66 } else { 67 flags = EXT4_GET_BLOCKS_IO_CREATE_EXT; 68 max_blocks = EXT_UNWRITTEN_MAX_LEN; 69 } 70 71 if (Alloc) { 72 if (Number && !*Number) { 73 if (max_blocks > *Number) { 74 max_blocks = *Number; 75 } 76 } else { 77 max_blocks = 1; 78 } 79 } 80 81 if ((rc = ext4_ext_get_blocks( 82 IrpContext, 83 NULL, 84 &Mcb->Inode, 85 Index, 86 max_blocks, 87 &bh_got, 88 Alloc, 89 flags)) < 0) { 90 DEBUG(DL_ERR, ("Block insufficient resources, err: %d\n", rc)); 91 return Ext2WinntError(rc); 92 } 93 if (Alloc) 94 Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); 95 if (Number) 96 *Number = rc ? rc : 1; 97 if (Block) 98 *Block = (ULONG)bh_got.b_blocknr; 99 100 return STATUS_SUCCESS; 101 } 102 103 104 NTSTATUS 105 Ext2DoExtentExpand( 106 IN PEXT2_IRP_CONTEXT IrpContext, 107 IN PEXT2_VCB Vcb, 108 IN PEXT2_MCB Mcb, 109 IN ULONG Index, 110 IN OUT PULONG Block, 111 IN OUT PULONG Number 112 ) 113 { 114 EXT4_EXTENT_HEADER *eh; 115 struct buffer_head bh_got; 116 int rc, flags; 117 118 if (IsMcbDirectory(Mcb) || IrpContext->MajorFunction == IRP_MJ_WRITE) { 119 flags = EXT4_GET_BLOCKS_IO_CONVERT_EXT; 120 } else { 121 flags = EXT4_GET_BLOCKS_IO_CREATE_EXT; 122 } 123 124 memset(&bh_got, 0, sizeof(struct buffer_head)); 125 eh = get_ext4_header(&Mcb->Inode); 126 127 if (eh->eh_magic != EXT4_EXT_MAGIC) { 128 ext4_ext_tree_init(IrpContext, NULL, &Mcb->Inode); 129 } 130 131 if ((rc = ext4_ext_get_blocks( IrpContext, NULL, &Mcb->Inode, Index, 132 *Number, &bh_got, 1, flags)) < 0) { 133 DEBUG(DL_ERR, ("Expand Block insufficient resources, Number: %u," 134 " err: %d\n", *Number, rc)); 135 DbgBreak(); 136 return Ext2WinntError(rc); 137 } 138 139 if (Number) 140 *Number = rc ? rc : 1; 141 if (Block) 142 *Block = (ULONG)bh_got.b_blocknr; 143 144 Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); 145 146 return STATUS_SUCCESS; 147 } 148 149 150 NTSTATUS 151 Ext2ExpandExtent( 152 PEXT2_IRP_CONTEXT IrpContext, 153 PEXT2_VCB Vcb, 154 PEXT2_MCB Mcb, 155 ULONG Start, 156 ULONG End, 157 PLARGE_INTEGER Size 158 ) 159 { 160 ULONG Count = 0, Number = 0, Block = 0; 161 NTSTATUS Status = STATUS_SUCCESS; 162 163 if (End <= Start) 164 return Status; 165 166 while (End > Start + Count) { 167 168 Number = End - Start - Count; 169 Status = Ext2DoExtentExpand(IrpContext, Vcb, Mcb, Start + Count, 170 &Block, &Number); 171 if (!NT_SUCCESS(Status)) { 172 Status = STATUS_INSUFFICIENT_RESOURCES; 173 break; 174 } 175 if (Number == 0) { 176 Status = STATUS_INSUFFICIENT_RESOURCES; 177 break; 178 } 179 180 if (Block && IsZoneInited(Mcb)) { 181 if (!Ext2AddBlockExtent(Vcb, Mcb, Start + Count, Block, Number)) { 182 DbgBreak(); 183 ClearFlag(Mcb->Flags, MCB_ZONE_INITED); 184 Ext2ClearAllExtents(&Mcb->Extents); 185 } 186 } 187 Count += Number; 188 } 189 190 Size->QuadPart = ((LONGLONG)(Start + Count)) << BLOCK_BITS; 191 192 /* save inode whatever it succeeds to expand or not */ 193 Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); 194 195 return Status; 196 } 197 198 199 NTSTATUS 200 Ext2TruncateExtent( 201 PEXT2_IRP_CONTEXT IrpContext, 202 PEXT2_VCB Vcb, 203 PEXT2_MCB Mcb, 204 PLARGE_INTEGER Size 205 ) 206 { 207 NTSTATUS Status = STATUS_SUCCESS; 208 209 ULONG Extra = 0; 210 ULONG Wanted = 0; 211 ULONG End; 212 ULONG Removed; 213 int err; 214 215 /* translate file size to block */ 216 End = Vcb->max_data_blocks; 217 Wanted = (ULONG)((Size->QuadPart + BLOCK_SIZE - 1) >> BLOCK_BITS); 218 219 /* calculate blocks to be freed */ 220 Extra = End - Wanted; 221 222 err = ext4_ext_truncate(IrpContext, &Mcb->Inode, Wanted); 223 if (err == 0) { 224 if (!Ext2RemoveBlockExtent(Vcb, Mcb, Wanted, Extra)) { 225 ClearFlag(Mcb->Flags, MCB_ZONE_INITED); 226 Ext2ClearAllExtents(&Mcb->Extents); 227 } 228 Extra = 0; 229 } else { 230 Status = STATUS_INSUFFICIENT_RESOURCES; 231 } 232 233 if (!NT_SUCCESS(Status)) { 234 Size->QuadPart += ((ULONGLONG)Extra << BLOCK_BITS); 235 } 236 237 if (Mcb->Inode.i_size > (loff_t)(Size->QuadPart)) 238 Mcb->Inode.i_size = (loff_t)(Size->QuadPart); 239 240 /* Save modifications on i_blocks field and i_size field of the inode. */ 241 Ext2SaveInode(IrpContext, Vcb, &Mcb->Inode); 242 243 return Status; 244 } 245