--- linux/fs/ext/truncate.c 2018/04/24 18:10:42 1.1 +++ linux/fs/ext/truncate.c 2018/04/24 18:14:23 1.1.1.3 @@ -1,13 +1,13 @@ /* * linux/fs/ext/truncate.c * - * (C) 1992 Remy Card (card@masi.ibp.fr) + * Copyright (C) 1992 Remy Card (card@masi.ibp.fr) * * from * * linux/fs/minix/truncate.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ #include @@ -15,8 +15,7 @@ #include #include #include - -#include +#include /* * Truncate has the most races in the whole filesystem: coding it is @@ -33,111 +32,208 @@ static int trunc_direct(struct inode * inode) { - int i; - int result = 0; + int i, tmp; + unsigned long * p; + struct buffer_head * bh; + int retry = 0; #define DIRECT_BLOCK ((inode->i_size + 1023) >> 10) repeat: for (i = DIRECT_BLOCK ; i < 9 ; i++) { - if (i < DIRECT_BLOCK) + p = inode->u.ext_i.i_data+i; + if (!(tmp = *p)) + continue; + bh = getblk(inode->i_dev,tmp,BLOCK_SIZE); + if (i < DIRECT_BLOCK) { + brelse(bh); goto repeat; - if (!inode->i_data[i]) + } + if ((bh && bh->b_count != 1) || tmp != *p) { + retry = 1; + brelse(bh); continue; - result = 1; - if (ext_free_block(inode->i_dev,inode->i_data[i])) - inode->i_data[i] = 0; + } + *p = 0; + inode->i_dirt = 1; + brelse(bh); + ext_free_block(inode->i_dev,tmp); } - return result; + return retry; } static int trunc_indirect(struct inode * inode, int offset, unsigned long * p) { - int i; - struct buffer_head * bh = NULL; + int i, tmp; + struct buffer_head * bh; + struct buffer_head * ind_bh; unsigned long * ind; - int result = 0; + int retry = 0; #define INDIRECT_BLOCK (DIRECT_BLOCK-offset) - if (*p) - bh = bread(inode->i_dev,*p); - if (!bh) + tmp = *p; + if (!tmp) + return 0; + ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp != *p) { + brelse(ind_bh); + return 1; + } + if (!ind_bh) { + *p = 0; return 0; + } repeat: for (i = INDIRECT_BLOCK ; i < 256 ; i++) { if (i < 0) i = 0; if (i < INDIRECT_BLOCK) goto repeat; - ind = i+(unsigned long *) bh->b_data; - if (!*ind) + ind = i+(unsigned long *) ind_bh->b_data; + tmp = *ind; + if (!tmp) + continue; + bh = getblk(inode->i_dev,tmp,BLOCK_SIZE); + if (i < INDIRECT_BLOCK) { + brelse(bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *ind) { + retry = 1; + brelse(bh); continue; - result = 1; - if (ext_free_block(inode->i_dev,*ind)) - *ind = 0; + } + *ind = 0; + ind_bh->b_dirt = 1; + brelse(bh); + ext_free_block(inode->i_dev,tmp); } - ind = (unsigned long *) bh->b_data; + ind = (unsigned long *) ind_bh->b_data; for (i = 0; i < 256; i++) if (*(ind++)) break; - brelse(bh); - if (i >= 256) { - result = 1; - if (ext_free_block(inode->i_dev,*p)) + if (i >= 256) + if (ind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; *p = 0; - } - return result; + inode->i_dirt = 1; + ext_free_block(inode->i_dev,tmp); + } + brelse(ind_bh); + return retry; } - -static int trunc_dindirect(struct inode * inode) + +static int trunc_dindirect(struct inode * inode, int offset, unsigned long * p) { - int i; - struct buffer_head * bh = NULL; + int i,tmp; + struct buffer_head * dind_bh; unsigned long * dind; - int result = 0; -#define DINDIRECT_BLOCK ((DIRECT_BLOCK-(256+9))>>8) + int retry = 0; +#define DINDIRECT_BLOCK ((DIRECT_BLOCK-offset)>>8) - if (inode->i_data[10]) - bh = bread(inode->i_dev,inode->i_data[10]); - if (!bh) + tmp = *p; + if (!tmp) + return 0; + dind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp != *p) { + brelse(dind_bh); + return 1; + } + if (!dind_bh) { + *p = 0; return 0; + } repeat: for (i = DINDIRECT_BLOCK ; i < 256 ; i ++) { if (i < 0) i = 0; if (i < DINDIRECT_BLOCK) goto repeat; - dind = i+(unsigned long *) bh->b_data; - if (!*dind) + dind = i+(unsigned long *) dind_bh->b_data; + tmp = *dind; + if (!tmp) continue; - result |= trunc_indirect(inode,9+256+(i<<8),dind); + retry |= trunc_indirect(inode,offset+(i<<8),dind); + dind_bh->b_dirt = 1; } - dind = (unsigned long *) bh->b_data; + dind = (unsigned long *) dind_bh->b_data; for (i = 0; i < 256; i++) if (*(dind++)) break; - brelse(bh); - if (i >= 256) { - result = 1; - if (ext_free_block(inode->i_dev,inode->i_data[10])) - inode->i_data[10] = 0; + if (i >= 256) + if (dind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_dirt = 1; + ext_free_block(inode->i_dev,tmp); + } + brelse(dind_bh); + return retry; +} + +static int trunc_tindirect(struct inode * inode) +{ + int i,tmp; + struct buffer_head * tind_bh; + unsigned long * tind, * p; + int retry = 0; +#define TINDIRECT_BLOCK ((DIRECT_BLOCK-(256*256+256+9))>>16) + + p = inode->u.ext_i.i_data+11; + if (!(tmp = *p)) + return 0; + tind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp != *p) { + brelse(tind_bh); + return 1; + } + if (!tind_bh) { + *p = 0; + return 0; + } +repeat: + for (i = TINDIRECT_BLOCK ; i < 256 ; i ++) { + if (i < 0) + i = 0; + if (i < TINDIRECT_BLOCK) + goto repeat; + tind = i+(unsigned long *) tind_bh->b_data; + retry |= trunc_dindirect(inode,9+256+256*256+(i<<16),tind); + tind_bh->b_dirt = 1; } - return result; + tind = (unsigned long *) tind_bh->b_data; + for (i = 0; i < 256; i++) + if (*(tind++)) + break; + if (i >= 256) + if (tind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_dirt = 1; + ext_free_block(inode->i_dev,tmp); + } + brelse(tind_bh); + return retry; } - + void ext_truncate(struct inode * inode) { - int flag; + int retry; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; -/* if (inode->i_data[7] & 0xffff0000) - printk("BAD! ext inode has 16 high bits set\n"); */ while (1) { - flag = trunc_direct(inode); - flag |= trunc_indirect(inode,9,(unsigned long *)&inode->i_data[9]); - flag |= trunc_dindirect(inode); - if (!flag) + retry = trunc_direct(inode); + retry |= trunc_indirect(inode,9,inode->u.ext_i.i_data+9); + retry |= trunc_dindirect(inode,9+256,inode->u.ext_i.i_data+10); + retry |= trunc_tindirect(inode); + if (!retry) break; current->counter = 0; schedule();