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