xref: /openbmc/linux/fs/udf/truncate.c (revision e868d61272caa648214046a096e5a6bfc068dc8c)
1 /*
2  * truncate.c
3  *
4  * PURPOSE
5  *	Truncate handling routines for the OSTA-UDF(tm) filesystem.
6  *
7  * COPYRIGHT
8  *	This file is distributed under the terms of the GNU General Public
9  *	License (GPL). Copies of the GPL can be obtained from:
10  *		ftp://prep.ai.mit.edu/pub/gnu/GPL
11  *	Each contributing author retains all rights to their own work.
12  *
13  *  (C) 1999-2004 Ben Fennema
14  *  (C) 1999 Stelias Computing Inc
15  *
16  * HISTORY
17  *
18  *  02/24/99 blf  Created.
19  *
20  */
21 
22 #include "udfdecl.h"
23 #include <linux/fs.h>
24 #include <linux/mm.h>
25 #include <linux/udf_fs.h>
26 #include <linux/buffer_head.h>
27 
28 #include "udf_i.h"
29 #include "udf_sb.h"
30 
31 static void extent_trunc(struct inode * inode, struct extent_position *epos,
32 	kernel_lb_addr eloc, int8_t etype, uint32_t elen, uint32_t nelen)
33 {
34 	kernel_lb_addr neloc = { 0, 0 };
35 	int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
36 	int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
37 
38 	if (nelen)
39 	{
40 		if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
41 		{
42 			udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
43 			etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
44 		}
45 		else
46 			neloc = eloc;
47 		nelen = (etype << 30) | nelen;
48 	}
49 
50 	if (elen != nelen)
51 	{
52 		udf_write_aext(inode, epos, neloc, nelen, 0);
53 		if (last_block - first_block > 0)
54 		{
55 			if (etype == (EXT_RECORDED_ALLOCATED >> 30))
56 				mark_inode_dirty(inode);
57 
58 			if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
59 				udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
60 		}
61 	}
62 }
63 
64 void udf_discard_prealloc(struct inode * inode)
65 {
66 	struct extent_position epos = { NULL, 0, {0, 0}};
67 	kernel_lb_addr eloc;
68 	uint32_t elen, nelen;
69 	uint64_t lbcount = 0;
70 	int8_t etype = -1, netype;
71 	int adsize;
72 
73 	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_IN_ICB ||
74 		inode->i_size == UDF_I_LENEXTENTS(inode))
75 		return;
76 
77 	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
78 		adsize = sizeof(short_ad);
79 	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
80 		adsize = sizeof(long_ad);
81 	else
82 		adsize = 0;
83 
84 	epos.block = UDF_I_LOCATION(inode);
85 
86 	/* Find the last extent in the file */
87 	while ((netype = udf_next_aext(inode, &epos, &eloc, &elen, 1)) != -1)
88 	{
89 		etype = netype;
90 		lbcount += elen;
91 		if (lbcount > inode->i_size && lbcount - elen < inode->i_size)
92 		{
93 			WARN_ON(lbcount - inode->i_size >= inode->i_sb->s_blocksize);
94 			nelen = elen - (lbcount - inode->i_size);
95 			epos.offset -= adsize;
96 			extent_trunc(inode, &epos, eloc, etype, elen, nelen);
97 			epos.offset += adsize;
98 			lbcount = inode->i_size;
99 		}
100 	}
101 	if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30)) {
102 		epos.offset -= adsize;
103 		lbcount -= elen;
104 		extent_trunc(inode, &epos, eloc, etype, elen, 0);
105 		if (!epos.bh)
106 		{
107 			UDF_I_LENALLOC(inode) = epos.offset - udf_file_entry_alloc_offset(inode);
108 			mark_inode_dirty(inode);
109 		}
110 		else
111 		{
112 			struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
113 			aed->lengthAllocDescs = cpu_to_le32(epos.offset - sizeof(struct allocExtDesc));
114 			if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
115 				udf_update_tag(epos.bh->b_data, epos.offset);
116 			else
117 				udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc));
118 			mark_buffer_dirty_inode(epos.bh, inode);
119 		}
120 	}
121 	UDF_I_LENEXTENTS(inode) = lbcount;
122 
123 	WARN_ON(lbcount != inode->i_size);
124 	brelse(epos.bh);
125 }
126 
127 void udf_truncate_extents(struct inode * inode)
128 {
129 	struct extent_position epos;
130 	kernel_lb_addr eloc, neloc = { 0, 0 };
131 	uint32_t elen, nelen = 0, indirect_ext_len = 0, lenalloc;
132 	int8_t etype;
133 	struct super_block *sb = inode->i_sb;
134 	sector_t first_block = inode->i_size >> sb->s_blocksize_bits, offset;
135 	loff_t byte_offset;
136 	int adsize;
137 
138 	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
139 		adsize = sizeof(short_ad);
140 	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
141 		adsize = sizeof(long_ad);
142 	else
143 		BUG();
144 
145 	etype = inode_bmap(inode, first_block, &epos, &eloc, &elen, &offset);
146 	byte_offset = (offset << sb->s_blocksize_bits) + (inode->i_size & (sb->s_blocksize-1));
147 	if (etype != -1)
148 	{
149 		epos.offset -= adsize;
150 		extent_trunc(inode, &epos, eloc, etype, elen, byte_offset);
151 		epos.offset += adsize;
152 		if (byte_offset)
153 			lenalloc = epos.offset;
154 		else
155 			lenalloc = epos.offset - adsize;
156 
157 		if (!epos.bh)
158 			lenalloc -= udf_file_entry_alloc_offset(inode);
159 		else
160 			lenalloc -= sizeof(struct allocExtDesc);
161 
162 		while ((etype = udf_current_aext(inode, &epos, &eloc, &elen, 0)) != -1)
163 		{
164 			if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
165 			{
166 				udf_write_aext(inode, &epos, neloc, nelen, 0);
167 				if (indirect_ext_len)
168 				{
169 					/* We managed to free all extents in the
170 					 * indirect extent - free it too */
171 					if (!epos.bh)
172 						BUG();
173 					udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len);
174 				}
175 				else
176 				{
177 					if (!epos.bh)
178 					{
179 						UDF_I_LENALLOC(inode) = lenalloc;
180 						mark_inode_dirty(inode);
181 					}
182 					else
183 					{
184 						struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
185 						aed->lengthAllocDescs = cpu_to_le32(lenalloc);
186 						if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201)
187 							udf_update_tag(epos.bh->b_data, lenalloc +
188 								sizeof(struct allocExtDesc));
189 						else
190 							udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc));
191 						mark_buffer_dirty_inode(epos.bh, inode);
192 					}
193 				}
194 				brelse(epos.bh);
195 				epos.offset = sizeof(struct allocExtDesc);
196 				epos.block = eloc;
197 				epos.bh = udf_tread(sb, udf_get_lb_pblock(sb, eloc, 0));
198 				if (elen)
199 					indirect_ext_len = (elen +
200 						sb->s_blocksize - 1) >>
201 						sb->s_blocksize_bits;
202 				else
203 					indirect_ext_len = 1;
204 			}
205 			else
206 			{
207 				extent_trunc(inode, &epos, eloc, etype, elen, 0);
208 				epos.offset += adsize;
209 			}
210 		}
211 
212 		if (indirect_ext_len)
213 		{
214 			if (!epos.bh)
215 				BUG();
216 			udf_free_blocks(sb, inode, epos.block, 0, indirect_ext_len);
217 		}
218 		else
219 		{
220 			if (!epos.bh)
221 			{
222 				UDF_I_LENALLOC(inode) = lenalloc;
223 				mark_inode_dirty(inode);
224 			}
225 			else
226 			{
227 				struct allocExtDesc *aed = (struct allocExtDesc *)(epos.bh->b_data);
228 				aed->lengthAllocDescs = cpu_to_le32(lenalloc);
229 				if (!UDF_QUERY_FLAG(sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(sb) >= 0x0201)
230 					udf_update_tag(epos.bh->b_data, lenalloc +
231 						sizeof(struct allocExtDesc));
232 				else
233 					udf_update_tag(epos.bh->b_data, sizeof(struct allocExtDesc));
234 				mark_buffer_dirty_inode(epos.bh, inode);
235 			}
236 		}
237 	}
238 	else if (inode->i_size)
239 	{
240 		if (byte_offset)
241 		{
242 			kernel_long_ad extent;
243 
244 			/*
245 			 *  OK, there is not extent covering inode->i_size and
246 			 *  no extent above inode->i_size => truncate is
247 			 *  extending the file by 'offset' blocks.
248 			 */
249 			if ((!epos.bh && epos.offset == udf_file_entry_alloc_offset(inode)) ||
250 			    (epos.bh && epos.offset == sizeof(struct allocExtDesc))) {
251 				/* File has no extents at all or has empty last
252 				 * indirect extent! Create a fake extent... */
253 				extent.extLocation.logicalBlockNum = 0;
254 				extent.extLocation.partitionReferenceNum = 0;
255 				extent.extLength = EXT_NOT_RECORDED_NOT_ALLOCATED;
256 			}
257 			else {
258 				epos.offset -= adsize;
259 				etype = udf_next_aext(inode, &epos,
260 					&extent.extLocation, &extent.extLength, 0);
261 				extent.extLength |= etype << 30;
262 			}
263 			udf_extend_file(inode, &epos, &extent, offset+((inode->i_size & (sb->s_blocksize-1)) != 0));
264 		}
265 	}
266 	UDF_I_LENEXTENTS(inode) = inode->i_size;
267 
268 	brelse(epos.bh);
269 }
270