2 * linux/fs/sysv/truncate.c
5 * Copyright (C) 1991, 1992 Linus Torvalds
8 * Copyright (C) 1993 Pascal Haible, Bruno Haible
11 * Copyright (C) 1993 Bruno Haible
14 #include <linux/sched.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
;
49 struct buffer_head
* bh
;
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
;
59 bh
= sv_get_hash_table(sb
, inode
->i_dev
, block
);
60 if ((i
<< sb
->sv_block_size_bits
) < inode
->i_size
) {
64 if ((bh
&& DATA_BUFFER_USED(bh
)) || (block
!= *p
)) {
70 mark_inode_dirty(inode
);
72 sysv_free_block(sb
,block
);
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
;
84 unsigned long tmp
, block
;
85 struct buffer_head
* bh
;
88 indblock
= indtmp
= *p
;
90 indblock
= from_coh_ulong(indblock
);
94 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
105 if (inode
->i_size
< offset
)
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
;
113 block
= from_coh_ulong(block
);
116 bh
= sv_get_hash_table(sb
, inode
->i_dev
, block
);
117 if ((i
<< sb
->sv_block_size_bits
) + offset
< inode
->i_size
) {
121 if ((bh
&& DATA_BUFFER_USED(bh
)) || (tmp
!= *ind
)) {
127 mark_buffer_dirty(indbh
, 1);
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
])
134 if (DATA_BUFFER_USED(indbh
) || (indtmp
!= *p
)) {
140 sysv_free_block(sb
,indblock
);
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
;
156 indblock
= indtmp
= *p
;
158 indblock
= from_coh_ulong(indblock
);
162 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
172 if (inode
->i_size
< offset
)
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
;
181 block
= from_coh_ulong(block
);
184 retry
|= trunc_indirect(inode
,offset
+(i
<<sb
->sv_ind_per_block_bits
),ind
,sb
->sv_convert
,&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
])
191 if (DATA_BUFFER_USED(indbh
) || (indtmp
!= *p
)) {
197 sysv_free_block(sb
,indblock
);
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
;
213 indblock
= indtmp
= *p
;
215 indblock
= from_coh_ulong(indblock
);
219 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
229 if (inode
->i_size
< offset
)
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
;
238 block
= from_coh_ulong(block
);
241 retry
|= trunc_dindirect(inode
,offset
+(i
<<sb
->sv_ind_per_block_2_bits
),ind
,sb
->sv_convert
,&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
])
248 if (DATA_BUFFER_USED(indbh
) || (indtmp
!= *p
)) {
254 sysv_free_block(sb
,indblock
);
260 static int trunc_all(struct inode
* inode
)
262 struct super_block
* 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
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
)))
287 while (trunc_all(inode
)) {
288 current
->counter
= 0;
291 inode
->i_mtime
= inode
->i_ctime
= CURRENT_TIME
;
292 mark_inode_dirty(inode
);