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 inttrunc_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 inttrunc_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 inttrunc_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 inttrunc_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 inttrunc_all(struct inode
* inode
) 262 struct super_block
* sb
; 266 returntrunc_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 voidsysv_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
)) { 291 inode
->i_mtime
= inode
->i_ctime
= CURRENT_TIME
; 292 mark_inode_dirty(inode
);