truncate.c 5.98 KB
Newer Older
Linus Torvalds's avatar
Linus Torvalds committed
1 2 3 4 5 6 7 8 9
/*
 * truncate.c
 *
 * PURPOSE
 *	Truncate handling routines for the OSTA-UDF(tm) filesystem.
 *
 * CONTACTS
 *	E-mail regarding any portion of the Linux UDF file system should be
 *	directed to the development team mailing list (run by majordomo):
Linus Torvalds's avatar
Linus Torvalds committed
10
 *		linux_udf@hpesjro.fc.hp.com
Linus Torvalds's avatar
Linus Torvalds committed
11 12 13 14 15 16 17
 *
 * COPYRIGHT
 *	This file is distributed under the terms of the GNU General Public
 *	License (GPL). Copies of the GPL can be obtained from:
 *		ftp://prep.ai.mit.edu/pub/gnu/GPL
 *	Each contributing author retains all rights to their own work.
 *
18
 *  (C) 1999-2001 Ben Fennema
Linus Torvalds's avatar
Linus Torvalds committed
19 20 21 22 23 24 25 26 27 28 29 30
 *  (C) 1999 Stelias Computing Inc
 *
 * HISTORY
 *
 *  02/24/99 blf  Created.
 *
 */

#include "udfdecl.h"
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/udf_fs.h>
31
#include <linux/buffer_head.h>
Linus Torvalds's avatar
Linus Torvalds committed
32 33 34 35 36

#include "udf_i.h"
#include "udf_sb.h"

static void extent_trunc(struct inode * inode, lb_addr bloc, int extoffset,
37
	lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen)
Linus Torvalds's avatar
Linus Torvalds committed
38 39 40 41 42 43 44
{
	lb_addr neloc = { 0, 0 };
	int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
	int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;

	if (nelen)
	{
45
		if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
46 47
		{
			udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
48
			etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
49 50 51
		}
		else
			neloc = eloc;
Linus Torvalds's avatar
Linus Torvalds committed
52 53 54 55 56 57 58 59
		nelen = (etype << 30) | nelen;
	}

	if (elen != nelen)
	{
		udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
		if (last_block - first_block > 0)
		{
60
			if (etype == (EXT_RECORDED_ALLOCATED >> 30))
Linus Torvalds's avatar
Linus Torvalds committed
61
				mark_inode_dirty(inode);
Linus Torvalds's avatar
Linus Torvalds committed
62

63
			if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
Linus Torvalds's avatar
Linus Torvalds committed
64
				udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
Linus Torvalds's avatar
Linus Torvalds committed
65 66 67 68
		}
	}
}

Linus Torvalds's avatar
Linus Torvalds committed
69
void udf_truncate_extents(struct inode * inode)
Linus Torvalds's avatar
Linus Torvalds committed
70 71
{
	lb_addr bloc, eloc, neloc = { 0, 0 };
72 73
	uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc;
	int8_t etype;
Linus Torvalds's avatar
Linus Torvalds committed
74 75 76 77
	int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits;
	struct buffer_head *bh = NULL;
	int adsize;

78
	if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_SHORT)
Linus Torvalds's avatar
Linus Torvalds committed
79
		adsize = sizeof(short_ad);
80
	else if (UDF_I_ALLOCTYPE(inode) == ICBTAG_FLAG_AD_LONG)
Linus Torvalds's avatar
Linus Torvalds committed
81 82 83 84 85
		adsize = sizeof(long_ad);
	else
		adsize = 0;

	etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
Linus Torvalds's avatar
Linus Torvalds committed
86
	offset += (inode->i_size & (inode->i_sb->s_blocksize - 1));
Linus Torvalds's avatar
Linus Torvalds committed
87 88 89
	if (etype != -1)
	{
		extoffset -= adsize;
Linus Torvalds's avatar
Linus Torvalds committed
90
		extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset);
Linus Torvalds's avatar
Linus Torvalds committed
91 92 93 94 95 96 97
		extoffset += adsize;

		if (offset)
			lenalloc = extoffset;
		else
			lenalloc = extoffset - adsize;

Ben Fennema's avatar
Ben Fennema committed
98
		if (!bh)
Linus Torvalds's avatar
Linus Torvalds committed
99 100
			lenalloc -= udf_file_entry_alloc_offset(inode);
		else
101
			lenalloc -= sizeof(struct allocExtDesc);
Linus Torvalds's avatar
Linus Torvalds committed
102 103 104

		while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1)
		{
105
			if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
Linus Torvalds's avatar
Linus Torvalds committed
106
			{
Linus Torvalds's avatar
Linus Torvalds committed
107
				udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
Linus Torvalds's avatar
Linus Torvalds committed
108 109 110
				extoffset = 0;
				if (lelen)
				{
Ben Fennema's avatar
Ben Fennema committed
111 112
					if (!bh)
						BUG();
Linus Torvalds's avatar
Linus Torvalds committed
113
					else
114
						memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
115
					udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
Linus Torvalds's avatar
Linus Torvalds committed
116 117 118
				}
				else
				{
Ben Fennema's avatar
Ben Fennema committed
119
					if (!bh)
Linus Torvalds's avatar
Linus Torvalds committed
120 121 122 123 124 125
					{
						UDF_I_LENALLOC(inode) = lenalloc;
						mark_inode_dirty(inode);
					}
					else
					{
126
						struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
Linus Torvalds's avatar
Linus Torvalds committed
127
						aed->lengthAllocDescs = cpu_to_le32(lenalloc);
Linus Torvalds's avatar
Linus Torvalds committed
128 129
						if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
							udf_update_tag(bh->b_data, lenalloc +
130
								sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
131
						else
132
							udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
133
						mark_buffer_dirty_inode(bh, inode);
Linus Torvalds's avatar
Linus Torvalds committed
134 135 136 137
					}
				}

				udf_release_data(bh);
Ben Fennema's avatar
Ben Fennema committed
138
				extoffset = sizeof(struct allocExtDesc);
Linus Torvalds's avatar
Linus Torvalds committed
139
				bloc = eloc;
Ben Fennema's avatar
Ben Fennema committed
140
				bh = udf_tread(inode->i_sb, udf_get_lb_pblock(inode->i_sb, bloc, 0));
Linus Torvalds's avatar
Linus Torvalds committed
141 142 143 144 145 146 147 148
				if (elen)
					lelen = (elen + inode->i_sb->s_blocksize - 1) >>
						inode->i_sb->s_blocksize_bits;
				else
					lelen = 1;
			}
			else
			{
Linus Torvalds's avatar
Linus Torvalds committed
149
				extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
Linus Torvalds's avatar
Linus Torvalds committed
150 151 152 153 154 155
				extoffset += adsize;
			}
		}

		if (lelen)
		{
Ben Fennema's avatar
Ben Fennema committed
156 157
			if (!bh)
				BUG();
Linus Torvalds's avatar
Linus Torvalds committed
158
			else
159
				memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
160
			udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
Linus Torvalds's avatar
Linus Torvalds committed
161 162 163
		}
		else
		{
Ben Fennema's avatar
Ben Fennema committed
164
			if (!bh)
Linus Torvalds's avatar
Linus Torvalds committed
165 166 167 168 169 170
			{
				UDF_I_LENALLOC(inode) = lenalloc;
				mark_inode_dirty(inode);
			}
			else
			{
171
				struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
Linus Torvalds's avatar
Linus Torvalds committed
172
				aed->lengthAllocDescs = cpu_to_le32(lenalloc);
Linus Torvalds's avatar
Linus Torvalds committed
173 174
				if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
					udf_update_tag(bh->b_data, lenalloc +
175
						sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
176
				else
177
					udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
Linus Torvalds's avatar
Linus Torvalds committed
178
				mark_buffer_dirty_inode(bh, inode);
Linus Torvalds's avatar
Linus Torvalds committed
179 180 181 182 183 184 185 186 187
			}
		}
	}
	else if (inode->i_size)
	{
		if (offset)
		{
			extoffset -= adsize;
			etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
188
			if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
Linus Torvalds's avatar
Linus Torvalds committed
189 190
			{
				extoffset -= adsize;
191
				elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
Linus Torvalds's avatar
Linus Torvalds committed
192
				udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
Linus Torvalds's avatar
Linus Torvalds committed
193
			}
194
			else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
Linus Torvalds's avatar
Linus Torvalds committed
195 196 197
			{
				lb_addr neloc = { 0, 0 };
				extoffset -= adsize;
198
				nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
Linus Torvalds's avatar
Linus Torvalds committed
199 200
					((elen + offset + inode->i_sb->s_blocksize - 1) &
					~(inode->i_sb->s_blocksize - 1));
Linus Torvalds's avatar
Linus Torvalds committed
201
				udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
202 203 204 205 206 207 208
				udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
			}
			else
			{
				if (elen & (inode->i_sb->s_blocksize - 1))
				{
					extoffset -= adsize;
209
					elen = EXT_RECORDED_ALLOCATED |
Linus Torvalds's avatar
Linus Torvalds committed
210 211
						((elen + inode->i_sb->s_blocksize - 1) &
						~(inode->i_sb->s_blocksize - 1));
Linus Torvalds's avatar
Linus Torvalds committed
212
					udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
Linus Torvalds's avatar
Linus Torvalds committed
213 214
				}
				memset(&eloc, 0x00, sizeof(lb_addr));
215
				elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
Linus Torvalds's avatar
Linus Torvalds committed
216 217 218 219
				udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
			}
		}
	}
Linus Torvalds's avatar
Linus Torvalds committed
220
	UDF_I_LENEXTENTS(inode) = inode->i_size;
Linus Torvalds's avatar
Linus Torvalds committed
221 222 223

	udf_release_data(bh);
}