--- linux/fs/minix/truncate.c 2018/04/24 18:03:52 1.1 +++ linux/fs/minix/truncate.c 2018/04/24 18:14:29 1.1.1.7 @@ -1,104 +1,187 @@ /* * linux/fs/truncate.c * - * (C) 1991 Linus Torvalds + * Copyright (C) 1991, 1992 Linus Torvalds */ +#include #include #include #include +#include +#include -#include -#include -#include +/* + * Truncate has the most races in the whole filesystem: coding it is + * a pain in the a**. Especially as I don't do any locking... + * + * The code may look a bit weird, but that's just because I've tried to + * handle things like file-size changes in a somewhat graceful manner. + * Anyway, truncating a file at the same time somebody else writes to it + * is likely to result in pretty weird behaviour... + * + * The new code handles normal truncates (size = 0) as well as the more + * general case (size = XXX). I hope. + */ -static int minix_free_ind(int dev,int block) +static int trunc_direct(struct inode * inode) { - struct buffer_head * bh; unsigned short * p; - int i; - int block_busy; + struct buffer_head * bh; + int i, tmp; + int retry = 0; +#define DIRECT_BLOCK ((inode->i_size + 1023) >> 10) - if (!block) - return 1; - block_busy = 0; - if (bh=bread(dev,block)) { - p = (unsigned short *) bh->b_data; - for (i=0;i<512;i++,p++) - if (*p) - if (minix_free_block(dev,*p)) { - *p = 0; - bh->b_dirt = 1; - } else - block_busy = 1; +repeat: + for (i = DIRECT_BLOCK ; i < 7 ; i++) { + p = i + inode->u.minix_i.i_data; + if (!(tmp = *p)) + continue; + bh = getblk(inode->i_dev,tmp,BLOCK_SIZE); + if (i < DIRECT_BLOCK) { + brelse(bh); + goto repeat; + } + if ((bh && bh->b_count != 1) || tmp != *p) { + retry = 1; + brelse(bh); + continue; + } + *p = 0; + inode->i_dirt = 1; brelse(bh); + minix_free_block(inode->i_dev,tmp); } - if (block_busy) - return 0; - else - return minix_free_block(dev,block); + return retry; } -static int minix_free_dind(int dev,int block) +static int trunc_indirect(struct inode * inode, int offset, unsigned short * p) { struct buffer_head * bh; - unsigned short * p; - int i; - int block_busy; + int i, tmp; + struct buffer_head * ind_bh; + unsigned short * ind; + int retry = 0; +#define INDIRECT_BLOCK (DIRECT_BLOCK-offset) - if (!block) + tmp = *p; + if (!tmp) + return 0; + ind_bh = bread(inode->i_dev, tmp, BLOCK_SIZE); + if (tmp != *p) { + brelse(ind_bh); return 1; - block_busy = 0; - if (bh=bread(dev,block)) { - p = (unsigned short *) bh->b_data; - for (i=0;i<512;i++,p++) - if (*p) - if (minix_free_ind(dev,*p)) { - *p = 0; - bh->b_dirt = 1; - } else - block_busy = 1; - brelse(bh); } - if (block_busy) + if (!ind_bh) { + *p = 0; return 0; - else - return minix_free_block(dev,block); + } +repeat: + for (i = INDIRECT_BLOCK ; i < 512 ; i++) { + if (i < 0) + i = 0; + if (i < INDIRECT_BLOCK) + goto repeat; + ind = i+(unsigned short *) 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; + } + *ind = 0; + ind_bh->b_dirt = 1; + brelse(bh); + minix_free_block(inode->i_dev,tmp); + } + ind = (unsigned short *) ind_bh->b_data; + for (i = 0; i < 512; i++) + if (*(ind++)) + break; + if (i >= 512) + if (ind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + minix_free_block(inode->i_dev,tmp); + } + brelse(ind_bh); + return retry; } + +static int trunc_dindirect(struct inode * inode) +{ + int i, tmp; + struct buffer_head * dind_bh; + unsigned short * dind, * p; + int retry = 0; +#define DINDIRECT_BLOCK ((DIRECT_BLOCK-(512+7))>>9) + p = 8 + inode->u.minix_i.i_data; + if (!(tmp = *p)) + 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 < 512 ; i ++) { + if (i < 0) + i = 0; + if (i < DINDIRECT_BLOCK) + goto repeat; + dind = i+(unsigned short *) dind_bh->b_data; + retry |= trunc_indirect(inode,7+512+(i<<9),dind); + dind_bh->b_dirt = 1; + } + dind = (unsigned short *) dind_bh->b_data; + for (i = 0; i < 512; i++) + if (*(dind++)) + break; + if (i >= 512) + if (dind_bh->b_count != 1) + retry = 1; + else { + tmp = *p; + *p = 0; + inode->i_dirt = 1; + minix_free_block(inode->i_dev,tmp); + } + brelse(dind_bh); + return retry; +} + void minix_truncate(struct inode * inode) { - int i; - int block_busy; + int retry; if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode) || S_ISLNK(inode->i_mode))) return; -repeat: - block_busy = 0; - for (i=0;i<7;i++) - if (inode->i_data[i]) { - if (minix_free_block(inode->i_dev,inode->i_data[i])) - inode->i_data[i]=0; - else - block_busy = 1; - } - if (minix_free_ind(inode->i_dev,inode->i_data[7])) - inode->i_data[7] = 0; - else - block_busy = 1; - if (minix_free_dind(inode->i_dev,inode->i_data[8])) - inode->i_data[8] = 0; - else - block_busy = 1; - inode->i_dirt = 1; - if (block_busy) { + while (1) { + retry = trunc_direct(inode); + retry |= trunc_indirect(inode,7,inode->u.minix_i.i_data+7); + retry |= trunc_dindirect(inode); + if (!retry) + break; current->counter = 0; schedule(); - goto repeat; } - inode->i_size = 0; inode->i_mtime = inode->i_ctime = CURRENT_TIME; + inode->i_dirt = 1; } /* @@ -110,45 +193,3 @@ void minix_release(struct inode * inode, { printk("minix_release not implemented\n"); } - -static int check_char_dev(struct inode * inode, struct file * filp) -{ - struct tty_struct *tty; - int min, dev; - - dev = inode->i_rdev; - if (MAJOR(dev) == 4 || MAJOR(dev) == 5) { - if (MAJOR(dev) == 5) - min = current->tty; - else - min = MINOR(dev); - if (min < 0) - return -1; - if ((IS_A_PTY_MASTER(min)) && (inode->i_count>1)) - return -1; - tty = TTY_TABLE(min); - if (!(filp->f_flags & O_NOCTTY) && - current->leader && - current->tty<0 && - tty->session==0) { - current->tty = min; - tty->session= current->session; - tty->pgrp = current->pgrp; - } - } - return 0; -} - -/* - * Called every time a minix-file is opened - */ -int minix_open(struct inode * inode, struct file * filp) -{ - if (S_ISCHR(inode->i_mode)) { - if (check_char_dev(inode,filp)) - return -EAGAIN; - } else if (S_ISBLK(inode->i_mode)) - check_disk_change(inode->i_rdev); - filp->f_op = &minix_file_operations; - return 0; -}