Import 2.1.55pre1
[davej-history.git] / fs / sysv / truncate.c
blob9130672d0496a17b9d80ec9df73d02cddc379b1b
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 /* We throw away any data beyond inode->i_size. */
40 static int trunc_direct(struct inode * inode)
42 struct super_block * sb;
43 unsigned int i;
44 unsigned long * p;
45 unsigned long block;
46 struct buffer_head * bh;
47 int retry = 0;
49 sb = inode->i_sb;
50 repeat:
51 for (i = ((unsigned long) inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits; i < 10; i++) {
52 p = inode->u.sysv_i.i_data + i;
53 block = *p;
54 if (!block)
55 continue;
56 bh = sv_get_hash_table(sb, inode->i_dev, block);
57 if ((i << sb->sv_block_size_bits) < inode->i_size) {
58 brelse(bh);
59 goto repeat;
61 if ((bh && bh->b_count != 1) || (block != *p)) {
62 retry = 1;
63 brelse(bh);
64 continue;
66 *p = 0;
67 mark_inode_dirty(inode);
68 brelse(bh);
69 sysv_free_block(sb,block);
71 return retry;
74 static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
76 unsigned long indtmp, indblock;
77 struct super_block * sb;
78 struct buffer_head * indbh;
79 unsigned int i;
80 sysv_zone_t * ind;
81 unsigned long tmp, block;
82 struct buffer_head * bh;
83 int retry = 0;
85 indblock = indtmp = *p;
86 if (convert)
87 indblock = from_coh_ulong(indblock);
88 if (!indblock)
89 return 0;
90 sb = inode->i_sb;
91 indbh = sv_bread(sb, inode->i_dev, indblock);
92 if (indtmp != *p) {
93 brelse(indbh);
94 return 1;
96 if (!indbh) {
97 *p = 0;
98 *dirt = 1;
99 return 0;
101 repeat:
102 if (inode->i_size < offset)
103 i = 0;
104 else
105 i = (inode->i_size - offset + sb->sv_block_size_1) >> sb->sv_block_size_bits;
106 for (; i < sb->sv_ind_per_block; i++) {
107 ind = ((sysv_zone_t *) indbh->b_data) + i;
108 block = tmp = *ind;
109 if (sb->sv_convert)
110 block = from_coh_ulong(block);
111 if (!block)
112 continue;
113 bh = sv_get_hash_table(sb, inode->i_dev, block);
114 if ((i << sb->sv_block_size_bits) + offset < inode->i_size) {
115 brelse(bh);
116 goto repeat;
118 if ((bh && bh->b_count != 1) || (tmp != *ind)) {
119 retry = 1;
120 brelse(bh);
121 continue;
123 *ind = 0;
124 mark_buffer_dirty(indbh, 1);
125 brelse(bh);
126 sysv_free_block(sb,block);
128 for (i = 0; i < sb->sv_ind_per_block; i++)
129 if (((sysv_zone_t *) indbh->b_data)[i])
130 goto done;
131 if ((indbh->b_count != 1) || (indtmp != *p)) {
132 brelse(indbh);
133 return 1;
135 *p = 0;
136 *dirt = 1;
137 sysv_free_block(sb,indblock);
138 done:
139 brelse(indbh);
140 return retry;
143 static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
145 unsigned long indtmp, indblock;
146 struct super_block * sb;
147 struct buffer_head * indbh;
148 unsigned int i;
149 sysv_zone_t * ind;
150 unsigned long tmp, block;
151 int retry = 0;
153 indblock = indtmp = *p;
154 if (convert)
155 indblock = from_coh_ulong(indblock);
156 if (!indblock)
157 return 0;
158 sb = inode->i_sb;
159 indbh = sv_bread(sb, inode->i_dev, indblock);
160 if (indtmp != *p) {
161 brelse(indbh);
162 return 1;
164 if (!indbh) {
165 *p = 0;
166 *dirt = 1;
167 return 0;
169 if (inode->i_size < offset)
170 i = 0;
171 else
172 i = (inode->i_size - offset + sb->sv_ind_per_block_block_size_1) >> sb->sv_ind_per_block_block_size_bits;
173 for (; i < sb->sv_ind_per_block; i++) {
174 unsigned char dirty = 0;
175 ind = ((sysv_zone_t *) indbh->b_data) + i;
176 block = tmp = *ind;
177 if (sb->sv_convert)
178 block = from_coh_ulong(block);
179 if (!block)
180 continue;
181 retry |= trunc_indirect(inode,offset+(i<<sb->sv_ind_per_block_bits),ind,sb->sv_convert,&dirty);
182 if (dirty)
183 mark_buffer_dirty(indbh, 1);
185 for (i = 0; i < sb->sv_ind_per_block; i++)
186 if (((sysv_zone_t *) indbh->b_data)[i])
187 goto done;
188 if ((indbh->b_count != 1) || (indtmp != *p)) {
189 brelse(indbh);
190 return 1;
192 *p = 0;
193 *dirt = 1;
194 sysv_free_block(sb,indblock);
195 done:
196 brelse(indbh);
197 return retry;
200 static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt)
202 unsigned long indtmp, indblock;
203 struct super_block * sb;
204 struct buffer_head * indbh;
205 unsigned int i;
206 sysv_zone_t * ind;
207 unsigned long tmp, block;
208 int retry = 0;
210 indblock = indtmp = *p;
211 if (convert)
212 indblock = from_coh_ulong(indblock);
213 if (!indblock)
214 return 0;
215 sb = inode->i_sb;
216 indbh = sv_bread(sb, inode->i_dev, indblock);
217 if (indtmp != *p) {
218 brelse(indbh);
219 return 1;
221 if (!indbh) {
222 *p = 0;
223 *dirt = 1;
224 return 0;
226 if (inode->i_size < offset)
227 i = 0;
228 else
229 i = (inode->i_size - offset + sb->sv_ind_per_block_2_block_size_1) >> sb->sv_ind_per_block_2_block_size_bits;
230 for (; i < sb->sv_ind_per_block; i++) {
231 unsigned char dirty = 0;
232 ind = ((sysv_zone_t *) indbh->b_data) + i;
233 block = tmp = *ind;
234 if (sb->sv_convert)
235 block = from_coh_ulong(block);
236 if (!block)
237 continue;
238 retry |= trunc_dindirect(inode,offset+(i<<sb->sv_ind_per_block_2_bits),ind,sb->sv_convert,&dirty);
239 if (dirty)
240 mark_buffer_dirty(indbh, 1);
242 for (i = 0; i < sb->sv_ind_per_block; i++)
243 if (((sysv_zone_t *) indbh->b_data)[i])
244 goto done;
245 if ((indbh->b_count != 1) || (indtmp != *p)) {
246 brelse(indbh);
247 return 1;
249 *p = 0;
250 *dirt = 1;
251 sysv_free_block(sb,indblock);
252 done:
253 brelse(indbh);
254 return retry;
257 static int trunc_all(struct inode * inode)
259 struct super_block * sb;
260 char dirty;
262 sb = inode->i_sb;
263 return trunc_direct(inode)
264 | trunc_indirect(inode,sb->sv_ind0_size,&inode->u.sysv_i.i_data[10],0,&dirty)
265 | trunc_dindirect(inode,sb->sv_ind1_size,&inode->u.sysv_i.i_data[11],0,&dirty)
266 | trunc_tindirect(inode,sb->sv_ind2_size,&inode->u.sysv_i.i_data[12],0,&dirty);
270 void sysv_truncate(struct inode * inode)
272 /* If this is called from sysv_put_inode, we needn't worry about
273 * races as we are just losing the last reference to the inode.
274 * If this is called from another place, let's hope it's a regular
275 * file.
276 * Truncating symbolic links is strange. We assume we don't truncate
277 * a directory we are just modifying. We ensure we don't truncate
278 * a regular file we are just writing to, by use of a lock.
280 if (S_ISLNK(inode->i_mode))
281 printk("sysv_truncate: truncating symbolic link\n");
282 else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))
283 return;
284 while (trunc_all(inode)) {
285 current->counter = 0;
286 schedule();
288 inode->i_mtime = inode->i_ctime = CURRENT_TIME;
289 mark_inode_dirty(inode);