~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

Linux Cross Reference
Linux/fs/sysv/truncate.c

Version: ~ [ 2.4.0 ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  *  linux/fs/sysv/truncate.c
  3  *
  4  *  minix/truncate.c
  5  *  Copyright (C) 1991, 1992  Linus Torvalds
  6  *
  7  *  coh/truncate.c
  8  *  Copyright (C) 1993  Pascal Haible, Bruno Haible
  9  *
 10  *  sysv/truncate.c
 11  *  Copyright (C) 1993  Bruno Haible
 12  */
 13 
 14 #include <linux/sched.h>
 15 #include <linux/fs.h>
 16 #include <linux/sysv_fs.h>
 17 #include <linux/stat.h>
 18 
 19 
 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.
 23  */
 24 
 25 /*
 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.
 28  *
 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...
 33  *
 34  * The new code handles normal truncates (size = 0) as well as the more
 35  * general case (size = XXX). I hope.
 36  */
 37 
 38 #define DATA_BUFFER_USED(bh) \
 39         (atomic_read(&bh->b_count)>1 || buffer_locked(bh))
 40 
 41 /* We throw away any data beyond inode->i_size. */
 42 
 43 static int trunc_direct(struct inode * inode)
 44 {
 45         struct super_block * sb;
 46         unsigned int i;
 47         u32 * p;
 48         u32 block;
 49         struct buffer_head * bh;
 50         int retry = 0;
 51 
 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;
 63                 }
 64                 if ((bh && DATA_BUFFER_USED(bh)) || (block != *p)) {
 65                         retry = 1;
 66                         brelse(bh);
 67                         continue;
 68                 }
 69                 *p = 0;
 70                 mark_inode_dirty(inode);
 71                 brelse(bh);
 72                 sysv_free_block(sb,block);
 73         }
 74         return retry;
 75 }
 76 
 77 static int trunc_indirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
 78 {
 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;
 87 
 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;
 98         }
 99         if (!indbh) {
100                 *p = 0;
101                 *dirt = 1;
102                 return 0;
103         }
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;
120                 }
121                 if ((bh && DATA_BUFFER_USED(bh)) || (tmp != *ind)) {
122                         retry = 1;
123                         brelse(bh);
124                         continue;
125                 }
126                 *ind = 0;
127                 mark_buffer_dirty(indbh);
128                 brelse(bh);
129                 sysv_free_block(sb,block);
130         }
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;
137         }
138         *p = 0;
139         *dirt = 1;
140         sysv_free_block(sb,indblock);
141 done:
142         brelse(indbh);
143         return retry;
144 }
145 
146 static int trunc_dindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
147 {
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;
155 
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;
166         }
167         if (!indbh) {
168                 *p = 0;
169                 *dirt = 1;
170                 return 0;
171         }
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);
187         }
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;
194         }
195         *p = 0;
196         *dirt = 1;
197         sysv_free_block(sb,indblock);
198 done:
199         brelse(indbh);
200         return retry;
201 }
202 
203 static int trunc_tindirect(struct inode * inode, unsigned long offset, sysv_zone_t * p, int convert, unsigned char * dirt)
204 {
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;
212 
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;
223         }
224         if (!indbh) {
225                 *p = 0;
226                 *dirt = 1;
227                 return 0;
228         }
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);
244         }
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;
251         }
252         *p = 0;
253         *dirt = 1;
254         sysv_free_block(sb,indblock);
255 done:
256         brelse(indbh);
257         return retry;
258 }
259 
260 static int trunc_all(struct inode * inode)
261 {
262         struct super_block * sb;
263         char dirty;
264 
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);
270 }
271 
272 
273 void sysv_truncate(struct inode * inode)
274 {
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.
282          */
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();
290         }
291         inode->i_mtime = inode->i_ctime = CURRENT_TIME;
292         mark_inode_dirty(inode);
293 }
294 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~ [ freetext search ] ~ [ file search ] ~

This page was automatically generated by the LXR engine.
Visit the LXR main site for more information.