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 /* We throw away any data beyond inode->i_size. */
40 static int trunc_direct(struct inode
* inode
)
42 struct super_block
* sb
;
46 struct buffer_head
* bh
;
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
;
56 bh
= sv_get_hash_table(sb
, inode
->i_dev
, block
);
57 if ((i
<< sb
->sv_block_size_bits
) < inode
->i_size
) {
61 if ((bh
&& bh
->b_count
!= 1) || (block
!= *p
)) {
67 mark_inode_dirty(inode
);
69 sysv_free_block(sb
,block
);
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
;
81 unsigned long tmp
, block
;
82 struct buffer_head
* bh
;
85 indblock
= indtmp
= *p
;
87 indblock
= from_coh_ulong(indblock
);
91 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
102 if (inode
->i_size
< offset
)
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
;
110 block
= from_coh_ulong(block
);
113 bh
= sv_get_hash_table(sb
, inode
->i_dev
, block
);
114 if ((i
<< sb
->sv_block_size_bits
) + offset
< inode
->i_size
) {
118 if ((bh
&& bh
->b_count
!= 1) || (tmp
!= *ind
)) {
124 mark_buffer_dirty(indbh
, 1);
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
])
131 if ((indbh
->b_count
!= 1) || (indtmp
!= *p
)) {
137 sysv_free_block(sb
,indblock
);
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
;
150 unsigned long tmp
, block
;
153 indblock
= indtmp
= *p
;
155 indblock
= from_coh_ulong(indblock
);
159 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
169 if (inode
->i_size
< offset
)
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
;
178 block
= from_coh_ulong(block
);
181 retry
|= trunc_indirect(inode
,offset
+(i
<<sb
->sv_ind_per_block_bits
),ind
,sb
->sv_convert
,&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
])
188 if ((indbh
->b_count
!= 1) || (indtmp
!= *p
)) {
194 sysv_free_block(sb
,indblock
);
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
;
207 unsigned long tmp
, block
;
210 indblock
= indtmp
= *p
;
212 indblock
= from_coh_ulong(indblock
);
216 indbh
= sv_bread(sb
, inode
->i_dev
, indblock
);
226 if (inode
->i_size
< offset
)
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
;
235 block
= from_coh_ulong(block
);
238 retry
|= trunc_dindirect(inode
,offset
+(i
<<sb
->sv_ind_per_block_2_bits
),ind
,sb
->sv_convert
,&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
])
245 if ((indbh
->b_count
!= 1) || (indtmp
!= *p
)) {
251 sysv_free_block(sb
,indblock
);
257 static int trunc_all(struct inode
* inode
)
259 struct super_block
* 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
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
)))
284 while (trunc_all(inode
)) {
285 current
->counter
= 0;
288 inode
->i_mtime
= inode
->i_ctime
= CURRENT_TIME
;
289 mark_inode_dirty(inode
);