Import 2.3.9pre7
[davej-history.git] / fs / sysv / truncate.c
bloba8c0e074561c60ff8f37688e3bfc9e45869c47e7
1 /*
2 * linux/fs/sysv/truncate.c
4 * minix/truncate.c
5 * Copyright (C) 1991, 1992 Linus Torvalds
7 * coh/truncate.c
8 * Copyright (C) 1993 Pascal Haible, Bruno Haible
10 * sysv/truncate.c
11 * Copyright (C) 1993 Bruno Haible
14 #include <linux/sched.h>
15 #include <linux/fs.h>
16 #include <linux/sysv_fs.h>
17 #include <linux/stat.h>
20 /* Linus' implementation of truncate.
21 * It doesn't need locking because it can tell from looking at bh->b_count
22 * whether a given block is in use elsewhere.
26 * Truncate has the most races in the whole filesystem: coding it is
27 * a pain in the a**, especially as I don't do any locking.
29 * The code may look a bit weird, but that's just because I've tried to
30 * handle things like file-size changes in a somewhat graceful manner.
31 * Anyway, truncating a file at the same time somebody else writes to it
32 * is likely to result in pretty weird behaviour...
34 * The new code handles normal truncates (size = 0) as well as the more
35 * general case (size = XXX). I hope.
38 #define DATA_BUFFER_USED(bh) \
39 ((bh->b_count > 1) || buffer_locked(bh))
41 /* We throw away any data beyond inode->i_size. */
43 static int trunc_direct(struct inode * inode)
45 struct super_block * sb;
46 unsigned int i;
47 u32 * p;
48 u32 block;
49 struct buffer_head * bh;
50 int retry = 0;
52 sb = inode->i_sb;
53 repeat:
54 for (i = ((unsigned long) inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits; i < 10; i++) {
55 p = inode->u.sysv_i.i_data + i;
56 block = *p;
57 if (!block)
58 continue;
59 bh = sv_get_hash_table(sb, inode->i_dev, block);
60 if ((i << sb->sv_block_size_bits) < inode->i_size) {
61 brelse(bh);
62 goto repeat;
64 if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) {
65 retry = 1;
66 brelse(bh);
67 continue;
69 *p = 0;
70 mark_inode_dirty(inode);
71 brelse(bh);
72 sysv_free_block(sb,block);
74 return retry;
77 static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
79 unsigned long indtmp, indblock;
80 struct super_block * sb;
81 struct buffer_head * indbh;
82 unsigned int i;
83 sysv_zone_t * ind;
84 unsigned long tmp, block;
85 struct buffer_head * bh;
86 int retry = 0;
88 indblock = indtmp = *p;
89 if (convert)
90 indblock = from_coh_ulong(indblock);
91 if (!indblock)
92 return 0;
93 sb = inode->i_sb;
94 indbh = sv_bread(sb, inode->i_dev, indblock);
95 if (indtmp != *p) {
96 brelse(indbh);
97 return 1;
99 if (!indbh) {
100 *p = 0;
101 *dirt = 1;
102 return 0;
104 repeat:
105 if (inode->i_size < offset)
106 i = 0;
107 else
108 i = (inode->i_size - offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
109 for (; i < sb->sv_ind_per_block; i++) {
110 ind = ((sysv_zone_t *) indbh->b_data) + i;
111 block = tmp = *ind;
112 if (sb->sv_convert)
113 block = from_coh_ulong(block);
114 if (!block)
115 continue;
116 bh = sv_get_hash_table(sb, inode->i_dev, block);
117 if ((i << sb->sv_block_size_bits) + offset < inode->i_size) {
118 brelse(bh);
119 goto repeat;
121 if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) {
122 retry = 1;
123 brelse(bh);
124 continue;
126 *ind = 0;
127 mark_buffer_dirty(indbh, 1);
128 brelse(bh);
129 sysv_free_block(sb,block);
131 for (i = 0; i < sb->sv_ind_per_block; i++)
132 if (((sysv_zone_t *) indbh->b_data)[i])
133 goto done;
134 if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
135 brelse(indbh);
136 return 1;
138 *p = 0;
139 *dirt = 1;
140 sysv_free_block(sb,indblock);
141 done:
142 brelse(indbh);
143 return retry;
146 static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
148 u32 indtmp, indblock;
149 struct super_block * sb;
150 struct buffer_head * indbh;
151 unsigned int i;
152 sysv_zone_t * ind;
153 u32 tmp, block;
154 int retry = 0;
156 indblock = indtmp = *p;
157 if (convert)
158 indblock = from_coh_ulong(indblock);
159 if (!indblock)
160 return 0;
161 sb = inode->i_sb;
162 indbh = sv_bread(sb, inode->i_dev, indblock);
163 if (indtmp != *p) {
164 brelse(indbh);
165 return 1;
167 if (!indbh) {
168 *p = 0;
169 *dirt = 1;
170 return 0;
172 if (inode->i_size < offset)
173 i = 0;
174 else
175 i = (inode->i_size - offset + sb->sv_ind_per_block_block_size_1) >> sb->sv_ind_per_block_block_size_bits;
176 for (; i < sb->sv_ind_per_block; i++) {
177 unsigned char dirty = 0;
178 ind = ((sysv_zone_t *) indbh->b_data) + i;
179 block = tmp = *ind;
180 if (sb->sv_convert)
181 block = from_coh_ulong(block);
182 if (!block)
183 continue;
184 retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&dirty);
185 if (dirty)
186 mark_buffer_dirty(indbh, 1);
188 for (i = 0; i < sb->sv_ind_per_block; i++)
189 if (((sysv_zone_t *) indbh->b_data)[i])
190 goto done;
191 if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
192 brelse(indbh);
193 return 1;
195 *p = 0;
196 *dirt = 1;
197 sysv_free_block(sb,indblock);
198 done:
199 brelse(indbh);
200 return retry;
203 static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
205 u32 indtmp, indblock;
206 struct super_block * sb;
207 struct buffer_head * indbh;
208 unsigned int i;
209 sysv_zone_t * ind;
210 u32 tmp, block;
211 int retry = 0;
213 indblock = indtmp = *p;
214 if (convert)
215 indblock = from_coh_ulong(indblock);
216 if (!indblock)
217 return 0;
218 sb = inode->i_sb;
219 indbh = sv_bread(sb, inode->i_dev, indblock);
220 if (indtmp != *p) {
221 brelse(indbh);
222 return 1;
224 if (!indbh) {
225 *p = 0;
226 *dirt = 1;
227 return 0;
229 if (inode->i_size < offset)
230 i = 0;
231 else
232 i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
233 for (; i < sb->sv_ind_per_block; i++) {
234 unsigned char dirty = 0;
235 ind = ((sysv_zone_t *) indbh->b_data) + i;
236 block = tmp = *ind;
237 if (sb->sv_convert)
238 block = from_coh_ulong(block);
239 if (!block)
240 continue;
241 retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&dirty);
242 if (dirty)
243 mark_buffer_dirty(indbh, 1);
245 for (i = 0; i < sb->sv_ind_per_block; i++)
246 if (((sysv_zone_t *) indbh->b_data)[i])
247 goto done;
248 if (DATA_BUFFER_USED(indbh) || (indtmp != *p)) {
249 brelse(indbh);
250 return 1;
252 *p = 0;
253 *dirt = 1;
254 sysv_free_block(sb,indblock);
255 done:
256 brelse(indbh);
257 return retry;
260 static int trunc_all(struct inode * inode)
262 struct super_block * sb;
263 char dirty;
265 sb = inode->i_sb;
266 return trunc_direct(inode)
267 | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&dirty)
268 | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&dirty)
269 | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&dirty);
273 void sysv_truncate(struct inode * inode)
275 /* If this is called from sysv_put_inode, we needn't worry about
276 * races as we are just losing the last reference to the inode.
277 * If this is called from another place, let's hope it's a regular
278 * file.
279 * Truncating symbolic links is strange. We assume we don't truncate
280 * a directory we are just modifying. We ensure we don't truncate
281 * a regular file we are just writing to, by use of a lock.
283 if (S_ISLNK(inode->i_mode))
284 printk("sysv_truncate: truncating symbolic link\n");
285 else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
286 return;
287 while (trunc_all(inode)) {
288 current->counter = 0;
289 schedule();
291 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
292 mark_inode_dirty(inode);