diff options
Diffstat (limited to 'fs/ext3/namei.c')
| -rw-r--r-- | fs/ext3/namei.c | 400 | 
1 files changed, 253 insertions, 147 deletions
diff --git a/fs/ext3/namei.c b/fs/ext3/namei.c index bce9dce639b..f197736dccf 100644 --- a/fs/ext3/namei.c +++ b/fs/ext3/namei.c @@ -24,19 +24,8 @@   *	Theodore Ts'o, 2002   */ -#include <linux/fs.h> -#include <linux/pagemap.h> -#include <linux/jbd.h> -#include <linux/time.h> -#include <linux/ext3_fs.h> -#include <linux/ext3_jbd.h> -#include <linux/fcntl.h> -#include <linux/stat.h> -#include <linux/string.h>  #include <linux/quotaops.h> -#include <linux/buffer_head.h> -#include <linux/bio.h> - +#include "ext3.h"  #include "namei.h"  #include "xattr.h"  #include "acl.h" @@ -47,7 +36,6 @@  #define NAMEI_RA_CHUNKS  2  #define NAMEI_RA_BLOCKS  4  #define NAMEI_RA_SIZE        (NAMEI_RA_CHUNKS * NAMEI_RA_BLOCKS) -#define NAMEI_RA_INDEX(c,b)  (((c) * NAMEI_RA_BLOCKS) + (b))  static struct buffer_head *ext3_append(handle_t *handle,  					struct inode *inode, @@ -57,8 +45,7 @@ static struct buffer_head *ext3_append(handle_t *handle,  	*block = inode->i_size >> inode->i_sb->s_blocksize_bits; -	bh = ext3_bread(handle, inode, *block, 1, err); -	if (bh) { +	if ((bh = ext3_dir_bread(handle, inode, *block, 1, err))) {  		inode->i_size += inode->i_sb->s_blocksize;  		EXT3_I(inode)->i_disksize = inode->i_size;  		*err = ext3_journal_get_write_access(handle, bh); @@ -287,7 +274,7 @@ static struct stats dx_show_leaf(struct dx_hash_info *hinfo, struct ext3_dir_ent  				while (len--) printk("%c", *name++);  				ext3fs_dirhash(de->name, de->name_len, &h);  				printk(":%x.%u ", h.hash, -				       ((char *) de - base)); +				       (unsigned) ((char *) de - base));  			}  			space += EXT3_DIR_REC_LEN(de->name_len);  			names++; @@ -350,8 +337,10 @@ dx_probe(struct qstr *entry, struct inode *dir,  	u32 hash;  	frame->bh = NULL; -	if (!(bh = ext3_bread (NULL,dir, 0, 0, err))) +	if (!(bh = ext3_dir_bread(NULL, dir, 0, 0, err))) { +		*err = ERR_BAD_DX_DIR;  		goto fail; +	}  	root = (struct dx_root *) bh->b_data;  	if (root->info.hash_version != DX_HASH_TEA &&  	    root->info.hash_version != DX_HASH_HALF_MD4 && @@ -447,8 +436,10 @@ dx_probe(struct qstr *entry, struct inode *dir,  		frame->entries = entries;  		frame->at = at;  		if (!indirect--) return frame; -		if (!(bh = ext3_bread (NULL,dir, dx_get_block(at), 0, err))) +		if (!(bh = ext3_dir_bread(NULL, dir, dx_get_block(at), 0, err))) { +			*err = ERR_BAD_DX_DIR;  			goto fail2; +		}  		at = entries = ((struct dx_node *) bh->b_data)->entries;  		if (dx_get_limit(entries) != dx_node_limit (dir)) {  			ext3_warning(dir->i_sb, __func__, @@ -546,8 +537,8 @@ static int ext3_htree_next_block(struct inode *dir, __u32 hash,  	 * block so no check is necessary  	 */  	while (num_frames--) { -		if (!(bh = ext3_bread(NULL, dir, dx_get_block(p->at), -				      0, &err))) +		if (!(bh = ext3_dir_bread(NULL, dir, dx_get_block(p->at), +					  0, &err)))  			return err; /* Failure */  		p++;  		brelse (p->bh); @@ -570,10 +561,11 @@ static int htree_dirblock_to_tree(struct file *dir_file,  {  	struct buffer_head *bh;  	struct ext3_dir_entry_2 *de, *top; -	int err, count = 0; +	int err = 0, count = 0;  	dxtrace(printk("In htree dirblock_to_tree: block %d\n", block)); -	if (!(bh = ext3_bread (NULL, dir, block, 0, &err))) + +	if (!(bh = ext3_dir_bread(NULL, dir, block, 0, &err)))  		return err;  	de = (struct ext3_dir_entry_2 *) bh->b_data; @@ -584,11 +576,8 @@ static int htree_dirblock_to_tree(struct file *dir_file,  		if (!ext3_check_dir_entry("htree_dirblock_to_tree", dir, de, bh,  					(block<<EXT3_BLOCK_SIZE_BITS(dir->i_sb))  						+((char *)de - bh->b_data))) { -			/* On error, skip the f_pos to the next block. */ -			dir_file->f_pos = (dir_file->f_pos | -					(dir->i_sb->s_blocksize - 1)) + 1; -			brelse (bh); -			return count; +			/* silently ignore the rest of the block */ +			break;  		}  		ext3fs_dirhash(de->name, de->name_len, hinfo);  		if ((hinfo->hash < start_hash) || @@ -631,7 +620,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,  	dxtrace(printk("In htree_fill_tree, start hash: %x:%x\n", start_hash,  		       start_minor_hash)); -	dir = dir_file->f_path.dentry->d_inode; +	dir = file_inode(dir_file);  	if (!(EXT3_I(dir)->i_flags & EXT3_INDEX_FL)) {  		hinfo.hash_version = EXT3_SB(dir->i_sb)->s_def_hash_version;  		if (hinfo.hash_version <= DX_HASH_TEA) @@ -645,7 +634,7 @@ int ext3_htree_fill_tree(struct file *dir_file, __u32 start_hash,  	}  	hinfo.hash = start_hash;  	hinfo.minor_hash = 0; -	frame = dx_probe(NULL, dir_file->f_path.dentry->d_inode, &hinfo, frames, &err); +	frame = dx_probe(NULL, file_inode(dir_file), &hinfo, frames, &err);  	if (!frame)  		return err; @@ -858,6 +847,7 @@ static struct buffer_head *ext3_find_entry(struct inode *dir,  	struct buffer_head * bh_use[NAMEI_RA_SIZE];  	struct buffer_head * bh, *ret = NULL;  	unsigned long start, block, b; +	const u8 *name = entry->name;  	int ra_max = 0;		/* Number of bh's in the readahead  				   buffer, bh_use[] */  	int ra_ptr = 0;		/* Current index into readahead @@ -871,6 +861,16 @@ static struct buffer_head *ext3_find_entry(struct inode *dir,  	namelen = entry->len;  	if (namelen > EXT3_NAME_LEN)  		return NULL; +	if ((namelen <= 2) && (name[0] == '.') && +	    (name[1] == '.' || name[1] == 0)) { +		/* +		 * "." or ".." will only be in the first block +		 * NFS may look up ".."; "." should be handled by the VFS +		 */ +		block = start = 0; +		nblocks = 1; +		goto restart; +	}  	if (is_dx(dir)) {  		bh = ext3_dx_find_entry(dir, entry, res_dir, &err);  		/* @@ -909,8 +909,12 @@ restart:  				num++;  				bh = ext3_getblk(NULL, dir, b++, 0, &err);  				bh_use[ra_max] = bh; -				if (bh) -					ll_rw_block(READ_META, 1, &bh); +				if (bh && !bh_uptodate_or_lock(bh)) { +					get_bh(bh); +					bh->b_end_io = end_buffer_read_sync; +					submit_bh(READ | REQ_META | REQ_PRIO, +						  bh); +				}  			}  		}  		if ((bh = bh_use[ra_ptr++]) == NULL) @@ -961,55 +965,35 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir,  			struct qstr *entry, struct ext3_dir_entry_2 **res_dir,  			int *err)  { -	struct super_block * sb; +	struct super_block *sb = dir->i_sb;  	struct dx_hash_info	hinfo; -	u32 hash;  	struct dx_frame frames[2], *frame; -	struct ext3_dir_entry_2 *de, *top;  	struct buffer_head *bh;  	unsigned long block;  	int retval; -	int namelen = entry->len; -	const u8 *name = entry->name; -	sb = dir->i_sb; -	/* NFS may look up ".." - look at dx_root directory block */ -	if (namelen > 2 || name[0] != '.'|| (namelen == 2 && name[1] != '.')) { -		if (!(frame = dx_probe(entry, dir, &hinfo, frames, err))) -			return NULL; -	} else { -		frame = frames; -		frame->bh = NULL;			/* for dx_release() */ -		frame->at = (struct dx_entry *)frames;	/* hack for zero entry*/ -		dx_set_block(frame->at, 0);		/* dx_root block is 0 */ -	} -	hash = hinfo.hash; +	if (!(frame = dx_probe(entry, dir, &hinfo, frames, err))) +		return NULL;  	do {  		block = dx_get_block(frame->at); -		if (!(bh = ext3_bread (NULL,dir, block, 0, err))) +		if (!(bh = ext3_dir_bread (NULL, dir, block, 0, err)))  			goto errout; -		de = (struct ext3_dir_entry_2 *) bh->b_data; -		top = (struct ext3_dir_entry_2 *) ((char *) de + sb->s_blocksize - -				       EXT3_DIR_REC_LEN(0)); -		for (; de < top; de = ext3_next_entry(de)) { -			int off = (block << EXT3_BLOCK_SIZE_BITS(sb)) -				  + ((char *) de - bh->b_data); - -			if (!ext3_check_dir_entry(__func__, dir, de, bh, off)) { -				brelse(bh); -				*err = ERR_BAD_DX_DIR; -				goto errout; -			} -			if (ext3_match(namelen, name, de)) { -				*res_dir = de; -				dx_release(frames); -				return bh; -			} +		retval = search_dirblock(bh, dir, entry, +					 block << EXT3_BLOCK_SIZE_BITS(sb), +					 res_dir); +		if (retval == 1) { +			dx_release(frames); +			return bh;  		} -		brelse (bh); +		brelse(bh); +		if (retval == -1) { +			*err = ERR_BAD_DX_DIR; +			goto errout; +		} +  		/* Check to see if we should continue to search */ -		retval = ext3_htree_next_block(dir, hash, frame, +		retval = ext3_htree_next_block(dir, hinfo.hash, frame,  					       frames, NULL);  		if (retval < 0) {  			ext3_warning(sb, __func__, @@ -1022,12 +1006,12 @@ static struct buffer_head * ext3_dx_find_entry(struct inode *dir,  	*err = -ENOENT;  errout: -	dxtrace(printk("%s not found\n", name)); +	dxtrace(printk("%s not found\n", entry->name));  	dx_release (frames);  	return NULL;  } -static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, struct nameidata *nd) +static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, unsigned int flags)  {  	struct inode * inode;  	struct ext3_dir_entry_2 * de; @@ -1047,15 +1031,11 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str  			return ERR_PTR(-EIO);  		}  		inode = ext3_iget(dir->i_sb, ino); -		if (unlikely(IS_ERR(inode))) { -			if (PTR_ERR(inode) == -ESTALE) { -				ext3_error(dir->i_sb, __func__, -						"deleted inode referenced: %lu", -						ino); -				return ERR_PTR(-EIO); -			} else { -				return ERR_CAST(inode); -			} +		if (inode == ERR_PTR(-ESTALE)) { +			ext3_error(dir->i_sb, __func__, +					"deleted inode referenced: %lu", +					ino); +			return ERR_PTR(-EIO);  		}  	}  	return d_splice_alias(inode, dentry); @@ -1065,7 +1045,7 @@ static struct dentry *ext3_lookup(struct inode * dir, struct dentry *dentry, str  struct dentry *ext3_get_parent(struct dentry *child)  {  	unsigned long ino; -	struct qstr dotdot = {.name = "..", .len = 2}; +	struct qstr dotdot = QSTR_INIT("..", 2);  	struct ext3_dir_entry_2 * de;  	struct buffer_head *bh; @@ -1425,10 +1405,19 @@ static int make_indexed_dir(handle_t *handle, struct dentry *dentry,  	frame->at = entries;  	frame->bh = bh;  	bh = bh2; +	/* +	 * Mark buffers dirty here so that if do_split() fails we write a +	 * consistent set of buffers to disk. +	 */ +	ext3_journal_dirty_metadata(handle, frame->bh); +	ext3_journal_dirty_metadata(handle, bh);  	de = do_split(handle,dir, &bh, frame, &hinfo, &retval); -	dx_release (frames); -	if (!(de)) +	if (!de) { +		ext3_mark_inode_dirty(handle, dir); +		dx_release(frames);  		return retval; +	} +	dx_release(frames);  	return add_dirent_to_buf(handle, dentry, inode, de, bh);  } @@ -1469,9 +1458,9 @@ static int ext3_add_entry (handle_t *handle, struct dentry *dentry,  	}  	blocks = dir->i_size >> sb->s_blocksize_bits;  	for (block = 0; block < blocks; block++) { -		bh = ext3_bread(handle, dir, block, 0, &retval); -		if(!bh) +		if (!(bh = ext3_dir_bread(handle, dir, block, 0, &retval)))  			return retval; +  		retval = add_dirent_to_buf(handle, dentry, inode, NULL, bh);  		if (retval != -ENOSPC)  			return retval; @@ -1511,7 +1500,7 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,  	entries = frame->entries;  	at = frame->at; -	if (!(bh = ext3_bread(handle,dir, dx_get_block(frame->at), 0, &err))) +	if (!(bh = ext3_dir_bread(handle, dir, dx_get_block(frame->at), 0, &err)))  		goto cleanup;  	BUFFER_TRACE(bh, "get_write_access"); @@ -1549,8 +1538,8 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,  			goto cleanup;  		node2 = (struct dx_node *)(bh2->b_data);  		entries2 = node2->entries; +		memset(&node2->fake, 0, sizeof(struct fake_dirent));  		node2->fake.rec_len = ext3_rec_len_to_disk(sb->s_blocksize); -		node2->fake.inode = 0;  		BUFFER_TRACE(frame->bh, "get_write_access");  		err = ext3_journal_get_write_access(handle, frame->bh);  		if (err) @@ -1607,7 +1596,9 @@ static int ext3_dx_add_entry(handle_t *handle, struct dentry *dentry,  			if (err)  				goto journal_error;  		} -		ext3_journal_dirty_metadata(handle, frames[0].bh); +		err = ext3_journal_dirty_metadata(handle, frames[0].bh); +		if (err) +			goto journal_error;  	}  	de = do_split(handle, dir, &bh, frame, &hinfo, &err);  	if (!de) @@ -1644,8 +1635,13 @@ static int ext3_delete_entry (handle_t *handle,  		if (!ext3_check_dir_entry("ext3_delete_entry", dir, de, bh, i))  			return -EIO;  		if (de == de_del)  { +			int err; +  			BUFFER_TRACE(bh, "get_write_access"); -			ext3_journal_get_write_access(handle, bh); +			err = ext3_journal_get_write_access(handle, bh); +			if (err) +				goto journal_error; +  			if (pde)  				pde->rec_len = ext3_rec_len_to_disk(  					ext3_rec_len_from_disk(pde->rec_len) + @@ -1654,7 +1650,12 @@ static int ext3_delete_entry (handle_t *handle,  				de->inode = 0;  			dir->i_version++;  			BUFFER_TRACE(bh, "call ext3_journal_dirty_metadata"); -			ext3_journal_dirty_metadata(handle, bh); +			err = ext3_journal_dirty_metadata(handle, bh); +			if (err) { +journal_error: +				ext3_std_error(dir->i_sb, err); +				return err; +			}  			return 0;  		}  		i += ext3_rec_len_from_disk(de->rec_len); @@ -1670,8 +1671,8 @@ static int ext3_add_nondir(handle_t *handle,  	int err = ext3_add_entry(handle, dentry, inode);  	if (!err) {  		ext3_mark_inode_dirty(handle, inode); -		d_instantiate(dentry, inode);  		unlock_new_inode(inode); +		d_instantiate(dentry, inode);  		return 0;  	}  	drop_nlink(inode); @@ -1688,8 +1689,8 @@ static int ext3_add_nondir(handle_t *handle,   * If the create succeeds, we fill in the inode information   * with d_instantiate().   */ -static int ext3_create (struct inode * dir, struct dentry * dentry, int mode, -		struct nameidata *nd) +static int ext3_create (struct inode * dir, struct dentry * dentry, umode_t mode, +		bool excl)  {  	handle_t *handle;  	struct inode * inode; @@ -1707,7 +1708,7 @@ retry:  	if (IS_DIRSYNC(dir))  		handle->h_sync = 1; -	inode = ext3_new_inode (handle, dir, mode); +	inode = ext3_new_inode (handle, dir, &dentry->d_name, mode);  	err = PTR_ERR(inode);  	if (!IS_ERR(inode)) {  		inode->i_op = &ext3_file_inode_operations; @@ -1722,7 +1723,7 @@ retry:  }  static int ext3_mknod (struct inode * dir, struct dentry *dentry, -			int mode, dev_t rdev) +			umode_t mode, dev_t rdev)  {  	handle_t *handle;  	struct inode *inode; @@ -1743,7 +1744,7 @@ retry:  	if (IS_DIRSYNC(dir))  		handle->h_sync = 1; -	inode = ext3_new_inode (handle, dir, mode); +	inode = ext3_new_inode (handle, dir, &dentry->d_name, mode);  	err = PTR_ERR(inode);  	if (!IS_ERR(inode)) {  		init_special_inode(inode, inode->i_mode, rdev); @@ -1758,11 +1759,49 @@ retry:  	return err;  } -static int ext3_mkdir(struct inode * dir, struct dentry * dentry, int mode) +static int ext3_tmpfile(struct inode *dir, struct dentry *dentry, umode_t mode) +{ +	handle_t *handle; +	struct inode *inode; +	int err, retries = 0; + +	dquot_initialize(dir); + +retry: +	handle = ext3_journal_start(dir, EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + +			  4 + EXT3_XATTR_TRANS_BLOCKS); + +	if (IS_ERR(handle)) +		return PTR_ERR(handle); + +	inode = ext3_new_inode (handle, dir, NULL, mode); +	err = PTR_ERR(inode); +	if (!IS_ERR(inode)) { +		inode->i_op = &ext3_file_inode_operations; +		inode->i_fop = &ext3_file_operations; +		ext3_set_aops(inode); +		d_tmpfile(dentry, inode); +		err = ext3_orphan_add(handle, inode); +		if (err) +			goto err_unlock_inode; +		mark_inode_dirty(inode); +		unlock_new_inode(inode); +	} +	ext3_journal_stop(handle); +	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries)) +		goto retry; +	return err; +err_unlock_inode: +	ext3_journal_stop(handle); +	unlock_new_inode(inode); +	return err; +} + +static int ext3_mkdir(struct inode * dir, struct dentry * dentry, umode_t mode)  {  	handle_t *handle;  	struct inode * inode; -	struct buffer_head * dir_block; +	struct buffer_head * dir_block = NULL;  	struct ext3_dir_entry_2 * de;  	int err, retries = 0; @@ -1781,7 +1820,7 @@ retry:  	if (IS_DIRSYNC(dir))  		handle->h_sync = 1; -	inode = ext3_new_inode (handle, dir, S_IFDIR | mode); +	inode = ext3_new_inode (handle, dir, &dentry->d_name, S_IFDIR | mode);  	err = PTR_ERR(inode);  	if (IS_ERR(inode))  		goto out_stop; @@ -1789,16 +1828,14 @@ retry:  	inode->i_op = &ext3_dir_inode_operations;  	inode->i_fop = &ext3_dir_operations;  	inode->i_size = EXT3_I(inode)->i_disksize = inode->i_sb->s_blocksize; -	dir_block = ext3_bread (handle, inode, 0, 1, &err); -	if (!dir_block) { -		drop_nlink(inode); /* is this nlink == 0? */ -		unlock_new_inode(inode); -		ext3_mark_inode_dirty(handle, inode); -		iput (inode); -		goto out_stop; -	} +	if (!(dir_block = ext3_dir_bread(handle, inode, 0, 1, &err))) +		goto out_clear_inode; +  	BUFFER_TRACE(dir_block, "get_write_access"); -	ext3_journal_get_write_access(handle, dir_block); +	err = ext3_journal_get_write_access(handle, dir_block); +	if (err) +		goto out_clear_inode; +  	de = (struct ext3_dir_entry_2 *) dir_block->b_data;  	de->inode = cpu_to_le32(inode->i_ino);  	de->name_len = 1; @@ -1812,14 +1849,19 @@ retry:  	de->name_len = 2;  	strcpy (de->name, "..");  	ext3_set_de_type(dir->i_sb, de, S_IFDIR); -	inode->i_nlink = 2; +	set_nlink(inode, 2);  	BUFFER_TRACE(dir_block, "call ext3_journal_dirty_metadata"); -	ext3_journal_dirty_metadata(handle, dir_block); -	brelse (dir_block); -	ext3_mark_inode_dirty(handle, inode); -	err = ext3_add_entry (handle, dentry, inode); +	err = ext3_journal_dirty_metadata(handle, dir_block); +	if (err) +		goto out_clear_inode; + +	err = ext3_mark_inode_dirty(handle, inode); +	if (!err) +		err = ext3_add_entry (handle, dentry, inode); +  	if (err) { -		inode->i_nlink = 0; +out_clear_inode: +		clear_nlink(inode);  		unlock_new_inode(inode);  		ext3_mark_inode_dirty(handle, inode);  		iput (inode); @@ -1827,10 +1869,14 @@ retry:  	}  	inc_nlink(dir);  	ext3_update_dx_flag(dir); -	ext3_mark_inode_dirty(handle, dir); -	d_instantiate(dentry, inode); +	err = ext3_mark_inode_dirty(handle, dir); +	if (err) +		goto out_clear_inode; +  	unlock_new_inode(inode); +	d_instantiate(dentry, inode);  out_stop: +	brelse(dir_block);  	ext3_journal_stop(handle);  	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))  		goto retry; @@ -1850,7 +1896,7 @@ static int empty_dir (struct inode * inode)  	sb = inode->i_sb;  	if (inode->i_size < EXT3_DIR_REC_LEN(1) + EXT3_DIR_REC_LEN(2) || -	    !(bh = ext3_bread (NULL, inode, 0, 0, &err))) { +	    !(bh = ext3_dir_bread(NULL, inode, 0, 0, &err))) {  		if (err)  			ext3_error(inode->i_sb, __func__,  				   "error %d reading directory #%lu offset 0", @@ -1881,9 +1927,8 @@ static int empty_dir (struct inode * inode)  			(void *) de >= (void *) (bh->b_data+sb->s_blocksize)) {  			err = 0;  			brelse (bh); -			bh = ext3_bread (NULL, inode, -				offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err); -			if (!bh) { +			if (!(bh = ext3_dir_bread (NULL, inode, +				offset >> EXT3_BLOCK_SIZE_BITS(sb), 0, &err))) {  				if (err)  					ext3_error(sb, __func__,  						   "error %d reading directory" @@ -2124,6 +2169,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)  	struct ext3_dir_entry_2 * de;  	handle_t *handle; +	trace_ext3_unlink_enter(dir, dentry);  	/* Initialize quotas before so that eventual writes go  	 * in separate transaction */  	dquot_initialize(dir); @@ -2151,7 +2197,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)  		ext3_warning (inode->i_sb, "ext3_unlink",  			      "Deleting nonexistent file (%lu), %d",  			      inode->i_ino, inode->i_nlink); -		inode->i_nlink = 1; +		set_nlink(inode, 1);  	}  	retval = ext3_delete_entry(handle, dir, de, bh);  	if (retval) @@ -2169,6 +2215,7 @@ static int ext3_unlink(struct inode * dir, struct dentry *dentry)  end_unlink:  	ext3_journal_stop(handle);  	brelse (bh); +	trace_ext3_unlink_exit(dentry, retval);  	return retval;  } @@ -2178,6 +2225,7 @@ static int ext3_symlink (struct inode * dir,  	handle_t *handle;  	struct inode * inode;  	int l, err, retries = 0; +	int credits;  	l = strlen(symname)+1;  	if (l > dir->i_sb->s_blocksize) @@ -2185,36 +2233,78 @@ static int ext3_symlink (struct inode * dir,  	dquot_initialize(dir); +	if (l > EXT3_N_BLOCKS * 4) { +		/* +		 * For non-fast symlinks, we just allocate inode and put it on +		 * orphan list in the first transaction => we need bitmap, +		 * group descriptor, sb, inode block, quota blocks, and +		 * possibly selinux xattr blocks. +		 */ +		credits = 4 + EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb) + +			  EXT3_XATTR_TRANS_BLOCKS; +	} else { +		/* +		 * Fast symlink. We have to add entry to directory +		 * (EXT3_DATA_TRANS_BLOCKS + EXT3_INDEX_EXTRA_TRANS_BLOCKS), +		 * allocate new inode (bitmap, group descriptor, inode block, +		 * quota blocks, sb is already counted in previous macros). +		 */ +		credits = EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + +			  EXT3_INDEX_EXTRA_TRANS_BLOCKS + 3 + +			  EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb); +	}  retry: -	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + -					EXT3_INDEX_EXTRA_TRANS_BLOCKS + 5 + -					EXT3_MAXQUOTAS_INIT_BLOCKS(dir->i_sb)); +	handle = ext3_journal_start(dir, credits);  	if (IS_ERR(handle))  		return PTR_ERR(handle);  	if (IS_DIRSYNC(dir))  		handle->h_sync = 1; -	inode = ext3_new_inode (handle, dir, S_IFLNK|S_IRWXUGO); +	inode = ext3_new_inode (handle, dir, &dentry->d_name, S_IFLNK|S_IRWXUGO);  	err = PTR_ERR(inode);  	if (IS_ERR(inode))  		goto out_stop; -	if (l > sizeof (EXT3_I(inode)->i_data)) { +	if (l > EXT3_N_BLOCKS * 4) {  		inode->i_op = &ext3_symlink_inode_operations;  		ext3_set_aops(inode);  		/* -		 * page_symlink() calls into ext3_prepare/commit_write. -		 * We have a transaction open.  All is sweetness.  It also sets -		 * i_size in generic_commit_write(). +		 * We cannot call page_symlink() with transaction started +		 * because it calls into ext3_write_begin() which acquires page +		 * lock which ranks below transaction start (and it can also +		 * wait for journal commit if we are running out of space). So +		 * we have to stop transaction now and restart it when symlink +		 * contents is written.  +		 * +		 * To keep fs consistent in case of crash, we have to put inode +		 * to orphan list in the mean time.  		 */ +		drop_nlink(inode); +		err = ext3_orphan_add(handle, inode); +		ext3_journal_stop(handle); +		if (err) +			goto err_drop_inode;  		err = __page_symlink(inode, symname, l, 1); +		if (err) +			goto err_drop_inode; +		/* +		 * Now inode is being linked into dir (EXT3_DATA_TRANS_BLOCKS +		 * + EXT3_INDEX_EXTRA_TRANS_BLOCKS), inode is also modified +		 */ +		handle = ext3_journal_start(dir, +				EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + +				EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1); +		if (IS_ERR(handle)) { +			err = PTR_ERR(handle); +			goto err_drop_inode; +		} +		set_nlink(inode, 1); +		err = ext3_orphan_del(handle, inode);  		if (err) { +			ext3_journal_stop(handle);  			drop_nlink(inode); -			unlock_new_inode(inode); -			ext3_mark_inode_dirty(handle, inode); -			iput (inode); -			goto out_stop; +			goto err_drop_inode;  		}  	} else {  		inode->i_op = &ext3_fast_symlink_inode_operations; @@ -2228,6 +2318,10 @@ out_stop:  	if (err == -ENOSPC && ext3_should_retry_alloc(dir->i_sb, &retries))  		goto retry;  	return err; +err_drop_inode: +	unlock_new_inode(inode); +	iput(inode); +	return err;  }  static int ext3_link (struct dentry * old_dentry, @@ -2242,16 +2336,9 @@ static int ext3_link (struct dentry * old_dentry,  	dquot_initialize(dir); -	/* -	 * Return -ENOENT if we've raced with unlink and i_nlink is 0.  Doing -	 * otherwise has the potential to corrupt the orphan inode list. -	 */ -	if (inode->i_nlink == 0) -		return -ENOENT; -  retry:  	handle = ext3_journal_start(dir, EXT3_DATA_TRANS_BLOCKS(dir->i_sb) + -					EXT3_INDEX_EXTRA_TRANS_BLOCKS); +					EXT3_INDEX_EXTRA_TRANS_BLOCKS + 1);  	if (IS_ERR(handle))  		return PTR_ERR(handle); @@ -2265,6 +2352,11 @@ retry:  	err = ext3_add_entry(handle, dentry, inode);  	if (!err) {  		ext3_mark_inode_dirty(handle, inode); +		/* this can happen only for tmpfile being +		 * linked the first time +		 */ +		if (inode->i_nlink == 1) +			ext3_orphan_del(handle, inode);  		d_instantiate(dentry, inode);  	} else {  		drop_nlink(inode); @@ -2337,7 +2429,7 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,  				goto end_rename;  		}  		retval = -EIO; -		dir_bh = ext3_bread (handle, old_inode, 0, 0, &retval); +		dir_bh = ext3_dir_bread(handle, old_inode, 0, 0, &retval);  		if (!dir_bh)  			goto end_rename;  		if (le32_to_cpu(PARENT_INO(dir_bh->b_data)) != old_dir->i_ino) @@ -2353,7 +2445,9 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,  			goto end_rename;  	} else {  		BUFFER_TRACE(new_bh, "get write access"); -		ext3_journal_get_write_access(handle, new_bh); +		retval = ext3_journal_get_write_access(handle, new_bh); +		if (retval) +			goto journal_error;  		new_de->inode = cpu_to_le32(old_inode->i_ino);  		if (EXT3_HAS_INCOMPAT_FEATURE(new_dir->i_sb,  					      EXT3_FEATURE_INCOMPAT_FILETYPE)) @@ -2362,7 +2456,9 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,  		new_dir->i_ctime = new_dir->i_mtime = CURRENT_TIME_SEC;  		ext3_mark_inode_dirty(handle, new_dir);  		BUFFER_TRACE(new_bh, "call ext3_journal_dirty_metadata"); -		ext3_journal_dirty_metadata(handle, new_bh); +		retval = ext3_journal_dirty_metadata(handle, new_bh); +		if (retval) +			goto journal_error;  		brelse(new_bh);  		new_bh = NULL;  	} @@ -2411,10 +2507,17 @@ static int ext3_rename (struct inode * old_dir, struct dentry *old_dentry,  	ext3_update_dx_flag(old_dir);  	if (dir_bh) {  		BUFFER_TRACE(dir_bh, "get_write_access"); -		ext3_journal_get_write_access(handle, dir_bh); +		retval = ext3_journal_get_write_access(handle, dir_bh); +		if (retval) +			goto journal_error;  		PARENT_INO(dir_bh->b_data) = cpu_to_le32(new_dir->i_ino);  		BUFFER_TRACE(dir_bh, "call ext3_journal_dirty_metadata"); -		ext3_journal_dirty_metadata(handle, dir_bh); +		retval = ext3_journal_dirty_metadata(handle, dir_bh); +		if (retval) { +journal_error: +			ext3_std_error(new_dir->i_sb, retval); +			goto end_rename; +		}  		drop_nlink(old_dir);  		if (new_inode) {  			drop_nlink(new_inode); @@ -2456,6 +2559,7 @@ const struct inode_operations ext3_dir_inode_operations = {  	.mkdir		= ext3_mkdir,  	.rmdir		= ext3_rmdir,  	.mknod		= ext3_mknod, +	.tmpfile	= ext3_tmpfile,  	.rename		= ext3_rename,  	.setattr	= ext3_setattr,  #ifdef CONFIG_EXT3_FS_XATTR @@ -2464,7 +2568,8 @@ const struct inode_operations ext3_dir_inode_operations = {  	.listxattr	= ext3_listxattr,  	.removexattr	= generic_removexattr,  #endif -	.check_acl	= ext3_check_acl, +	.get_acl	= ext3_get_acl, +	.set_acl	= ext3_set_acl,  };  const struct inode_operations ext3_special_inode_operations = { @@ -2475,5 +2580,6 @@ const struct inode_operations ext3_special_inode_operations = {  	.listxattr	= ext3_listxattr,  	.removexattr	= generic_removexattr,  #endif -	.check_acl	= ext3_check_acl, +	.get_acl	= ext3_get_acl, +	.set_acl	= ext3_set_acl,  };  | 
