⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 truncate.c

📁 linux1.1源代码
💻 C
字号:
/* *  linux/fs/sysv/truncate.c * *  minix/truncate.c *  Copyright (C) 1991, 1992  Linus Torvalds * *  coh/truncate.c *  Copyright (C) 1993  Pascal Haible, Bruno Haible * *  sysv/truncate.c *  Copyright (C) 1993  Bruno Haible */#include <linux/sched.h>#include <linux/fs.h>#include <linux/sysv_fs.h>#include <linux/stat.h>/* There are two different implementations of truncate() here. * One (by Bruno) needs to do locking to ensure that noone is writing * to a block being truncated away and incorporated into the free list. * The better one (by Linus) doesn't need locking because it can tell from * looking at bh->b_count whether a given block is in use elsewhere. * Alas, this doesn't work if block_size < BLOCK_SIZE. *//* Bruno's implementation of truncate. *//* Leave at most `blocks' direct blocks. */static int coh_trunc_direct (struct inode * inode, unsigned long blocks){	unsigned int i;	unsigned long * p;	unsigned long block;	for (i = blocks; i < 10 ; i++) {		p = &inode->u.sysv_i.i_data[i];		block = *p;		if (!block)			continue;		*p = 0;		inode->i_dirt = 1;		sysv_free_block(inode->i_sb,block);	}	return 0;}/* Leave at most `blocks' blocks out of an indirect block whose number is * from_coh_ulong(*p) if convert=1, *p if convert=0. */static int coh_trunc_indirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt){	struct super_block * sb = inode->i_sb;	unsigned long tmp, block, indblock;	struct buffer_head * bh;	char * bh_data;	unsigned long i;	sysv_zone_t * ind;	if (blocks >= sb->sv_ind_per_block)		return 0;	block = tmp = *p;	if (convert)		block = from_coh_ulong(block);	if (!block)		return 0;	bh = sysv_bread(sb,inode->i_dev,block,&bh_data);	if (tmp != *p) {		brelse(bh);		return 1;	}	if (!bh) {		*p = 0;		*dirt = 1;		return 0;	}	for (i = blocks; i < sb->sv_ind_per_block; i++) {		ind = &((sysv_zone_t *) bh_data)[i];		indblock = *ind;		if (sb->sv_convert)			indblock = from_coh_ulong(indblock);		if (!indblock)			continue;		*ind = 0;		bh->b_dirt = 1;		sysv_free_block(sb,indblock);	}	for (i = 0; i < sb->sv_ind_per_block; i++)		if (((sysv_zone_t *) bh_data)[i])			goto done;	if (tmp != *p) {		brelse(bh);		return 1;	}	*p = 0;	*dirt = 1;	sysv_free_block(sb,block);done:	brelse(bh);	return 0;}/* Leave at most `blocks' blocks out of an double indirect block whose number is * from_coh_ulong(*p) if convert=1, *p if convert=0. */static int coh_trunc_dindirect (struct inode * inode, unsigned long blocks, unsigned long * p, int convert, unsigned char * dirt){	struct super_block * sb = inode->i_sb;	unsigned long tmp, block, dindblock;	struct buffer_head * bh;	char * bh_data;	unsigned long i, j;	sysv_zone_t * dind;	int retry = 0;	if (blocks >= sb->sv_ind_per_block_2)		return 0;	block = tmp = *p;	if (convert)		block = from_coh_ulong(block);	if (!block)		return 0;	bh = sysv_bread(sb,inode->i_dev,block,&bh_data);	if (tmp != *p) {		brelse(bh);		return 1;	}	if (!bh) {		*p = 0;		*dirt = 1;		return 0;	}	for (i = blocks >> sb->sv_ind_per_block_bits, j = blocks & sb->sv_ind_per_block_1;	     i < sb->sv_ind_per_block;	     i++, j = 0) {		/* j = max(blocks-i*ind_per_block,0) */		dind = &((sysv_zone_t *) bh_data)[i];		dindblock = *dind;		if (sb->sv_convert)			dindblock = from_coh_ulong(dindblock);		if (!dindblock)			continue;		retry |= coh_trunc_indirect(inode,j,dind,sb->sv_convert,&bh->b_dirt);	}	for (i = 0; i < sb->sv_ind_per_block; i++)		if (((sysv_zone_t *) bh_data)[i])			goto done;	if (tmp != *p) {		brelse(bh);		return 1;	}	*p = 0;	*dirt = 1;	sysv_free_block(sb,block);done:	brelse(bh);	return retry;}/* Leave at most `blocks' blocks out of an triple indirect block whose number is * from_coh_ulong(*p) if convert=1, *p if convert=0. */static int coh_trunc_tindirect (struct inode * inode, unsigned long blocks, unsigned long * p){	struct super_block * sb = inode->i_sb;	unsigned long block, tindblock;	struct buffer_head * bh;	char * bh_data;	unsigned long i, j;	sysv_zone_t * tind;	int retry = 0;	if (blocks >= sb->sv_ind_per_block_3)		return 0;	block = *p;	if (!block)		return 0;	bh = sysv_bread(sb,inode->i_dev,block,&bh_data);	if (block != *p) {		brelse(bh);		return 1;	}	if (!bh) {		*p = 0;		inode->i_dirt = 1;		return 0;	}	for (i = blocks >> sb->sv_ind_per_block_2_bits, j = blocks & sb->sv_ind_per_block_2_1;	     i < sb->sv_ind_per_block;	     i++, j = 0) {		/* j = max(blocks-i*ind_per_block^2,0) */		tind = &((sysv_zone_t *) bh_data)[i];		tindblock = *tind;		if (sb->sv_convert)			tindblock = from_coh_ulong(tindblock);		if (!tindblock)			continue;		retry |= coh_trunc_dindirect(inode,j,tind,sb->sv_convert,&bh->b_dirt);	}	for (i = 0; i < sb->sv_ind_per_block; i++)		if (((sysv_zone_t *) bh_data)[i])			goto done;	if (block != *p) {		brelse(bh);		return 1;	}	*p = 0;	inode->i_dirt = 1;	sysv_free_block(sb,block);done:	brelse(bh);	return retry;}static int coh_trunc_all(struct inode * inode){	struct super_block * sb = inode->i_sb;	long blocks;	int retry;	blocks = (inode->i_size + sb->sv_block_size_1) >> sb->sv_block_size_bits;	retry = coh_trunc_direct(inode,blocks);	blocks -= 10;	if (blocks < 0) blocks = 0;	retry |= coh_trunc_indirect(inode,blocks,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt);	blocks -= sb->sv_ind_per_block;	if (blocks < 0) blocks = 0;	retry |= coh_trunc_dindirect(inode,blocks,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt);	blocks -= sb->sv_ind_per_block_2;	if (blocks < 0) blocks = 0;	retry |= coh_trunc_tindirect(inode,blocks,&inode->u.sysv_i.i_data[12]);	return retry;}/* Linus' implementation of truncate. Used only if block_size = BLOCK_SIZE. *//* * 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. *//* We throw away any data beyond inode->i_size. */static int trunc_direct(struct inode * inode){	struct super_block * sb;	unsigned int i;	unsigned long * p;	unsigned long block;	struct buffer_head * bh;	int retry = 0;	sb = inode->i_sb;repeat:	for (i = ((unsigned long) inode->i_size + BLOCK_SIZE-1) / BLOCK_SIZE; i < 10; i++) {		p = inode->u.sysv_i.i_data + i;		block = *p;		if (!block)			continue;		bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);		if (i*BLOCK_SIZE < inode->i_size) {			brelse(bh);			goto repeat;		}		if ((bh && bh->b_count != 1) || (block != *p)) {			retry = 1;			brelse(bh);			continue;		}		*p = 0;		inode->i_dirt = 1;		brelse(bh);		sysv_free_block(sb,block);	}	return retry;}#define IND_PER_BLOCK   (BLOCK_SIZE / sizeof(sysv_zone_t))static int trunc_indirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt){	unsigned long indtmp, indblock;	struct super_block * sb;	struct buffer_head * indbh;	unsigned int i;	sysv_zone_t * ind;	unsigned long tmp, block;	struct buffer_head * bh;	int retry = 0;	indblock = indtmp = *p;	if (convert)		indblock = from_coh_ulong(indblock);	if (!indblock)		return 0;	sb = inode->i_sb;	indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);	if (indtmp != *p) {		brelse(indbh);		return 1;	}	if (!indbh) {		*p = 0;		*dirt = 1;		return 0;	}repeat:	if (inode->i_size < offset)		i = 0;	else		i = (inode->i_size - offset + BLOCK_SIZE-1) / BLOCK_SIZE;	for (; i < IND_PER_BLOCK; i++) {		ind = ((sysv_zone_t *) indbh->b_data) + i;		block = tmp = *ind;		if (sb->sv_convert)			block = from_coh_ulong(block);		if (!block)			continue;		bh = get_hash_table(inode->i_dev,block+sb->sv_block_base,BLOCK_SIZE);		if (i*BLOCK_SIZE + offset < inode->i_size) {			brelse(bh);			goto repeat;		}		if ((bh && bh->b_count != 1) || (tmp != *ind)) {			retry = 1;			brelse(bh);			continue;		}		*ind = 0;		indbh->b_dirt = 1;		brelse(bh);		sysv_free_block(sb,block);	}	for (i = 0; i < IND_PER_BLOCK; i++)		if (((sysv_zone_t *) indbh->b_data)[i])			goto done;	if ((indbh->b_count != 1) || (indtmp != *p)) {		brelse(indbh);		return 1;	}	*p = 0;	*dirt = 1;	sysv_free_block(sb,indblock);done:	brelse(indbh);	return retry;}static int trunc_dindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt){	unsigned long indtmp, indblock;	struct super_block * sb;	struct buffer_head * indbh;	unsigned int i;	sysv_zone_t * ind;	unsigned long tmp, block;	int retry = 0;	indblock = indtmp = *p;	if (convert)		indblock = from_coh_ulong(indblock);	if (!indblock)		return 0;	sb = inode->i_sb;	indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);	if (indtmp != *p) {		brelse(indbh);		return 1;	}	if (!indbh) {		*p = 0;		*dirt = 1;		return 0;	}	if (inode->i_size < offset)		i = 0;	else		i = (inode->i_size - offset + IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*BLOCK_SIZE);	for (; i < IND_PER_BLOCK; i++) {		ind = ((sysv_zone_t *) indbh->b_data) + i;		block = tmp = *ind;		if (sb->sv_convert)			block = from_coh_ulong(block);		if (!block)			continue;		retry |= trunc_indirect(inode,offset+i*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);	}	for (i = 0; i < IND_PER_BLOCK; i++)		if (((sysv_zone_t *) indbh->b_data)[i])			goto done;	if ((indbh->b_count != 1) || (indtmp != *p)) {		brelse(indbh);		return 1;	}	*p = 0;	*dirt = 1;	sysv_free_block(sb,indblock);done:	brelse(indbh);	return retry;}static int trunc_tindirect(struct inode * inode, unsigned long offset, unsigned long * p, int convert, unsigned char * dirt){	unsigned long indtmp, indblock;	struct super_block * sb;	struct buffer_head * indbh;	unsigned int i;	sysv_zone_t * ind;	unsigned long tmp, block;	int retry = 0;	indblock = indtmp = *p;	if (convert)		indblock = from_coh_ulong(indblock);	if (!indblock)		return 0;	sb = inode->i_sb;	indbh = bread(inode->i_dev,indblock+sb->sv_block_base,BLOCK_SIZE);	if (indtmp != *p) {		brelse(indbh);		return 1;	}	if (!indbh) {		*p = 0;		*dirt = 1;		return 0;	}	if (inode->i_size < offset)		i = 0;	else		i = (inode->i_size - offset + IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE-1) / (IND_PER_BLOCK*IND_PER_BLOCK*BLOCK_SIZE);	for (; i < IND_PER_BLOCK; i++) {		ind = ((sysv_zone_t *) indbh->b_data) + i;		block = tmp = *ind;		if (sb->sv_convert)			block = from_coh_ulong(block);		if (!block)			continue;		retry |= trunc_dindirect(inode,offset+i*IND_PER_BLOCK*IND_PER_BLOCK,ind,sb->sv_convert,&indbh->b_dirt);	}	for (i = 0; i < IND_PER_BLOCK; i++)		if (((sysv_zone_t *) indbh->b_data)[i])			goto done;	if ((indbh->b_count != 1) || (indtmp != *p)) {		brelse(indbh);		return 1;	}	*p = 0;	*dirt = 1;	sysv_free_block(sb,indblock);done:	brelse(indbh);	return retry;}static int trunc_all(struct inode * inode){	return trunc_direct(inode)	     | trunc_indirect(inode,10*BLOCK_SIZE,&inode->u.sysv_i.i_data[10],0,&inode->i_dirt)	     | trunc_dindirect(inode,(10+IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[11],0,&inode->i_dirt)	     | trunc_tindirect(inode,(10+IND_PER_BLOCK+IND_PER_BLOCK*IND_PER_BLOCK)*BLOCK_SIZE,&inode->u.sysv_i.i_data[12],0,&inode->i_dirt);}void sysv_truncate(struct inode * inode){	/* If this is called from sysv_put_inode, we needn't worry about	 * races as we are just losing the last reference to the inode.	 * If this is called from another place, let's hope it's a regular	 * file.	 * Truncating symbolic links is strange. We assume we don't truncate	 * a directory we are just modifying. We ensure we don't truncate	 * a regular file we are just writing to, by use of a lock.	 */	if (S_ISLNK(inode->i_mode))		printk("sysv_truncate: truncating symbolic link\n");	else if (!(S_ISREG(inode->i_mode) || S_ISDIR(inode->i_mode)))		return;	if (inode->i_sb->sv_block_size_ratio_bits > 0) { /* block_size < BLOCK_SIZE ? */		coh_lock_inode(inode); /* do not write to the inode while we truncate */		while (coh_trunc_all(inode)) {			current->counter = 0;			schedule();		}		inode->i_mtime = inode->i_ctime = CURRENT_TIME;		inode->i_dirt = 1;		coh_unlock_inode(inode);	} else {		while (trunc_all(inode)) {			current->counter = 0;			schedule();		}		inode->i_mtime = inode->i_ctime = CURRENT_TIME;		inode->i_dirt = 1;	}}

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -