include upstream ip1000a driver version 2.09f
[linux-2.4.git] / fs / udf / truncate.c
1 /*
2  * truncate.c
3  *
4  * PURPOSE
5  *      Truncate handling routines for the OSTA-UDF(tm) filesystem.
6  *
7  * CONTACTS
8  *      E-mail regarding any portion of the Linux UDF file system should be
9  *      directed to the development team mailing list (run by majordomo):
10  *              linux_udf@hpesjro.fc.hp.com
11  *
12  * COPYRIGHT
13  *      This file is distributed under the terms of the GNU General Public
14  *      License (GPL). Copies of the GPL can be obtained from:
15  *              ftp://prep.ai.mit.edu/pub/gnu/GPL
16  *      Each contributing author retains all rights to their own work.
17  *
18  *  (C) 1999-2001 Ben Fennema
19  *  (C) 1999 Stelias Computing Inc
20  *
21  * HISTORY
22  *
23  *  02/24/99 blf  Created.
24  *
25  */
26
27 #include "udfdecl.h"
28 #include <linux/fs.h>
29 #include <linux/mm.h>
30 #include <linux/udf_fs.h>
31
32 #include "udf_i.h"
33 #include "udf_sb.h"
34
35 static void extent_trunc(struct inode * inode, lb_addr bloc, int extoffset,
36         lb_addr eloc, int8_t etype, uint32_t elen, struct buffer_head *bh, uint32_t nelen)
37 {
38         lb_addr neloc = { 0, 0 };
39         int last_block = (elen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
40         int first_block = (nelen + inode->i_sb->s_blocksize - 1) >> inode->i_sb->s_blocksize_bits;
41
42         if (nelen)
43         {
44                 if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
45                 {
46                         udf_free_blocks(inode->i_sb, inode, eloc, 0, last_block);
47                         etype = (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30);
48                 }
49                 else
50                         neloc = eloc;
51                 nelen = (etype << 30) | nelen;
52         }
53
54         if (elen != nelen)
55         {
56                 udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
57                 if (last_block - first_block > 0)
58                 {
59                         if (etype == (EXT_RECORDED_ALLOCATED >> 30))
60                                 mark_inode_dirty(inode);
61
62                         if (etype != (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
63                                 udf_free_blocks(inode->i_sb, inode, eloc, first_block, last_block - first_block);
64                 }
65         }
66 }
67
68 void udf_truncate_extents(struct inode * inode)
69 {
70         lb_addr bloc, eloc, neloc = { 0, 0 };
71         uint32_t extoffset, elen, offset, nelen = 0, lelen = 0, lenalloc;
72         int8_t etype;
73         int first_block = inode->i_size >> inode->i_sb->s_blocksize_bits;
74         struct buffer_head *bh = NULL;
75         int adsize;
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         etype = inode_bmap(inode, first_block, &bloc, &extoffset, &eloc, &elen, &offset, &bh);
85         offset += (inode->i_size & (inode->i_sb->s_blocksize - 1));
86         if (etype != -1)
87         {
88                 extoffset -= adsize;
89                 extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, offset);
90                 extoffset += adsize;
91
92                 if (offset)
93                         lenalloc = extoffset;
94                 else
95                         lenalloc = extoffset - adsize;
96
97                 if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
98                         lenalloc -= udf_file_entry_alloc_offset(inode);
99                 else
100                         lenalloc -= sizeof(struct allocExtDesc);
101
102                 while ((etype = udf_current_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 0)) != -1)
103                 {
104                         if (etype == (EXT_NEXT_EXTENT_ALLOCDECS >> 30))
105                         {
106                                 udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 0);
107                                 extoffset = 0;
108                                 if (lelen)
109                                 {
110                                         if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
111                                                 memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode));
112                                         else
113                                                 memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
114                                         udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
115                                 }
116                                 else
117                                 {
118                                         if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
119                                         {
120                                                 UDF_I_LENALLOC(inode) = lenalloc;
121                                                 mark_inode_dirty(inode);
122                                         }
123                                         else
124                                         {
125                                                 struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
126                                                 aed->lengthAllocDescs = cpu_to_le32(lenalloc);
127                                                 if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
128                                                         udf_update_tag(bh->b_data, lenalloc +
129                                                                 sizeof(struct allocExtDesc));
130                                                 else
131                                                         udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
132                                                 mark_buffer_dirty_inode(bh, inode);
133                                         }
134                                 }
135
136                                 udf_release_data(bh);
137                                 bh = NULL;
138
139                                 bloc = eloc;
140                                 if (elen)
141                                         lelen = (elen + inode->i_sb->s_blocksize - 1) >>
142                                                 inode->i_sb->s_blocksize_bits;
143                                 else
144                                         lelen = 1;
145                         }
146                         else
147                         {
148                                 extent_trunc(inode, bloc, extoffset, eloc, etype, elen, bh, 0);
149                                 extoffset += adsize;
150                         }
151                 }
152
153                 if (lelen)
154                 {
155                         if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
156                                 memset(bh->b_data, 0x00, udf_file_entry_alloc_offset(inode));
157                         else
158                                 memset(bh->b_data, 0x00, sizeof(struct allocExtDesc));
159                         udf_free_blocks(inode->i_sb, inode, bloc, 0, lelen);
160                 }
161                 else
162                 {
163                         if (!memcmp(&UDF_I_LOCATION(inode), &bloc, sizeof(lb_addr)))
164                         {
165                                 UDF_I_LENALLOC(inode) = lenalloc;
166                                 mark_inode_dirty(inode);
167                         }
168                         else
169                         {
170                                 struct allocExtDesc *aed = (struct allocExtDesc *)(bh->b_data);
171                                 aed->lengthAllocDescs = cpu_to_le32(lenalloc);
172                                 if (!UDF_QUERY_FLAG(inode->i_sb, UDF_FLAG_STRICT) || UDF_SB_UDFREV(inode->i_sb) >= 0x0201)
173                                         udf_update_tag(bh->b_data, lenalloc +
174                                                 sizeof(struct allocExtDesc));
175                                 else
176                                         udf_update_tag(bh->b_data, sizeof(struct allocExtDesc));
177                                 mark_buffer_dirty_inode(bh, inode);
178                         }
179                 }
180         }
181         else if (inode->i_size)
182         {
183                 if (offset)
184                 {
185                         /*
186                          *  OK, there is not extent covering inode->i_size and
187                          *  no extent above inode->i_size => truncate is
188                          *  extending the file by 'offset'.
189                          */
190                         if ((!bh && extoffset == udf_file_entry_alloc_offset(inode)) ||
191                             (bh && extoffset == sizeof(struct allocExtDesc))) {
192                                 /* File has no extents at all! */
193                                 memset(&eloc, 0x00, sizeof(lb_addr));
194                                 elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
195                                 udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
196                         }
197                         else {
198                                 extoffset -= adsize;
199                                 etype = udf_next_aext(inode, &bloc, &extoffset, &eloc, &elen, &bh, 1);
200                                 if (etype == (EXT_NOT_RECORDED_NOT_ALLOCATED >> 30))
201                                 {
202                                         extoffset -= adsize;
203                                         elen = EXT_NOT_RECORDED_NOT_ALLOCATED | (elen + offset);
204                                         udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 0);
205                                 }
206                                 else if (etype == (EXT_NOT_RECORDED_ALLOCATED >> 30))
207                                 {
208                                         lb_addr neloc = { 0, 0 };
209                                         extoffset -= adsize;
210                                         nelen = EXT_NOT_RECORDED_NOT_ALLOCATED |
211                                                 ((elen + offset + inode->i_sb->s_blocksize - 1) &
212                                                 ~(inode->i_sb->s_blocksize - 1));
213                                         udf_write_aext(inode, bloc, &extoffset, neloc, nelen, bh, 1);
214                                         udf_add_aext(inode, &bloc, &extoffset, eloc, (etype << 30) | elen, &bh, 1);
215                                 }
216                                 else
217                                 {
218                                         if (elen & (inode->i_sb->s_blocksize - 1))
219                                         {
220                                                 extoffset -= adsize;
221                                                 elen = EXT_RECORDED_ALLOCATED |
222                                                         ((elen + inode->i_sb->s_blocksize - 1) &
223                                                         ~(inode->i_sb->s_blocksize - 1));
224                                                 udf_write_aext(inode, bloc, &extoffset, eloc, elen, bh, 1);
225                                         }
226                                         memset(&eloc, 0x00, sizeof(lb_addr));
227                                         elen = EXT_NOT_RECORDED_NOT_ALLOCATED | offset;
228                                         udf_add_aext(inode, &bloc, &extoffset, eloc, elen, &bh, 1);
229                                 }
230                         }
231                 }
232         }
233         UDF_I_LENEXTENTS(inode) = inode->i_size;
234
235         udf_release_data(bh);
236 }