diff options
Diffstat (limited to 'fs/9p/vfs_inode.c')
| -rw-r--r-- | fs/9p/vfs_inode.c | 1684 | 
1 files changed, 543 insertions, 1141 deletions
diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 34bf71b5654..7fa4f7a7653 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c @@ -23,6 +23,8 @@   *   */ +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt +  #include <linux/module.h>  #include <linux/errno.h>  #include <linux/fs.h> @@ -49,15 +51,8 @@  static const struct inode_operations v9fs_dir_inode_operations;  static const struct inode_operations v9fs_dir_inode_operations_dotu; -static const struct inode_operations v9fs_dir_inode_operations_dotl;  static const struct inode_operations v9fs_file_inode_operations; -static const struct inode_operations v9fs_file_inode_operations_dotl;  static const struct inode_operations v9fs_symlink_inode_operations; -static const struct inode_operations v9fs_symlink_inode_operations_dotl; - -static int -v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode, -		    dev_t rdev);  /**   * unixmode2p9mode - convert unix mode bits to plan 9 @@ -66,15 +61,13 @@ v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode,   *   */ -static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode) +static u32 unixmode2p9mode(struct v9fs_session_info *v9ses, umode_t mode)  {  	int res;  	res = mode & 0777;  	if (S_ISDIR(mode))  		res |= P9_DMDIR;  	if (v9fs_proto_dotu(v9ses)) { -		if (S_ISLNK(mode)) -			res |= P9_DMSYMLINK;  		if (v9ses->nodev == 0) {  			if (S_ISSOCK(mode))  				res |= P9_DMSOCKET; @@ -92,25 +85,51 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)  			res |= P9_DMSETGID;  		if ((mode & S_ISVTX) == S_ISVTX)  			res |= P9_DMSETVTX; -		if ((mode & P9_DMLINK)) -			res |= P9_DMLINK;  	} +	return res; +} + +/** + * p9mode2perm- convert plan9 mode bits to unix permission bits + * @v9ses: v9fs session information + * @stat: p9_wstat from which mode need to be derived + * + */ +static int p9mode2perm(struct v9fs_session_info *v9ses, +		       struct p9_wstat *stat) +{ +	int res; +	int mode = stat->mode; +	res = mode & S_IALLUGO; +	if (v9fs_proto_dotu(v9ses)) { +		if ((mode & P9_DMSETUID) == P9_DMSETUID) +			res |= S_ISUID; + +		if ((mode & P9_DMSETGID) == P9_DMSETGID) +			res |= S_ISGID; + +		if ((mode & P9_DMSETVTX) == P9_DMSETVTX) +			res |= S_ISVTX; +	}  	return res;  }  /**   * p9mode2unixmode- convert plan9 mode bits to unix mode bits   * @v9ses: v9fs session information - * @mode: mode to convert + * @stat: p9_wstat from which mode need to be derived + * @rdev: major number, minor number in case of device files.   *   */ - -static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode) +static umode_t p9mode2unixmode(struct v9fs_session_info *v9ses, +			       struct p9_wstat *stat, dev_t *rdev)  {  	int res; +	u32 mode = stat->mode; -	res = mode & 0777; +	*rdev = 0; +	res = p9mode2perm(v9ses, stat);  	if ((mode & P9_DMDIR) == P9_DMDIR)  		res |= S_IFDIR; @@ -123,21 +142,26 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)  		 && (v9ses->nodev == 0))  		res |= S_IFIFO;  	else if ((mode & P9_DMDEVICE) && (v9fs_proto_dotu(v9ses)) -		 && (v9ses->nodev == 0)) -		res |= S_IFBLK; -	else -		res |= S_IFREG; - -	if (v9fs_proto_dotu(v9ses)) { -		if ((mode & P9_DMSETUID) == P9_DMSETUID) -			res |= S_ISUID; - -		if ((mode & P9_DMSETGID) == P9_DMSETGID) -			res |= S_ISGID; +		 && (v9ses->nodev == 0)) { +		char type = 0, ext[32]; +		int major = -1, minor = -1; -		if ((mode & P9_DMSETVTX) == P9_DMSETVTX) -			res |= S_ISVTX; -	} +		strlcpy(ext, stat->extension, sizeof(ext)); +		sscanf(ext, "%c %i %i", &type, &major, &minor); +		switch (type) { +		case 'c': +			res |= S_IFCHR; +			break; +		case 'b': +			res |= S_IFBLK; +			break; +		default: +			p9_debug(P9_DEBUG_ERROR, "Unknown special type %c %s\n", +				 type, stat->extension); +		}; +		*rdev = MKDEV(major, minor); +	} else +		res |= S_IFREG;  	return res;  } @@ -168,9 +192,6 @@ int v9fs_uflags2omode(int uflags, int extended)  		break;  	} -	if (uflags & O_TRUNC) -		ret |= P9_OTRUNC; -  	if (extended) {  		if (uflags & O_EXCL)  			ret |= P9_OEXCL; @@ -204,32 +225,31 @@ v9fs_blank_wstat(struct p9_wstat *wstat)  	wstat->uid = NULL;  	wstat->gid = NULL;  	wstat->muid = NULL; -	wstat->n_uid = ~0; -	wstat->n_gid = ~0; -	wstat->n_muid = ~0; +	wstat->n_uid = INVALID_UID; +	wstat->n_gid = INVALID_GID; +	wstat->n_muid = INVALID_UID;  	wstat->extension = NULL;  } -#ifdef CONFIG_9P_FSCACHE  /**   * v9fs_alloc_inode - helper function to allocate an inode - * This callback is executed before setting up the inode so that we - * can associate a vcookie with each inode.   *   */ -  struct inode *v9fs_alloc_inode(struct super_block *sb)  { -	struct v9fs_cookie *vcookie; -	vcookie = (struct v9fs_cookie *)kmem_cache_alloc(vcookie_cache, -							 GFP_KERNEL); -	if (!vcookie) +	struct v9fs_inode *v9inode; +	v9inode = (struct v9fs_inode *)kmem_cache_alloc(v9fs_inode_cache, +							GFP_KERNEL); +	if (!v9inode)  		return NULL; - -	vcookie->fscache = NULL; -	vcookie->qid = NULL; -	spin_lock_init(&vcookie->lock); -	return &vcookie->inode; +#ifdef CONFIG_9P_FSCACHE +	v9inode->fscache = NULL; +	spin_lock_init(&v9inode->fscache_lock); +#endif +	v9inode->writeback_fid = NULL; +	v9inode->cache_validity = 0; +	mutex_init(&v9inode->v_mutex); +	return &v9inode->vfs_inode;  }  /** @@ -237,71 +257,25 @@ struct inode *v9fs_alloc_inode(struct super_block *sb)   *   */ -void v9fs_destroy_inode(struct inode *inode) +static void v9fs_i_callback(struct rcu_head *head)  { -	kmem_cache_free(vcookie_cache, v9fs_inode2cookie(inode)); +	struct inode *inode = container_of(head, struct inode, i_rcu); +	kmem_cache_free(v9fs_inode_cache, V9FS_I(inode));  } -#endif - -/** - * v9fs_get_fsgid_for_create - Helper function to get the gid for creating a - * new file system object. This checks the S_ISGID to determine the owning - * group of the new file system object. - */ -static gid_t v9fs_get_fsgid_for_create(struct inode *dir_inode) +void v9fs_destroy_inode(struct inode *inode)  { -	BUG_ON(dir_inode == NULL); - -	if (dir_inode->i_mode & S_ISGID) { -		/* set_gid bit is set.*/ -		return dir_inode->i_gid; -	} -	return current_fsgid(); +	call_rcu(&inode->i_rcu, v9fs_i_callback);  } -/** - * v9fs_dentry_from_dir_inode - helper function to get the dentry from - * dir inode. - * - */ - -static struct dentry *v9fs_dentry_from_dir_inode(struct inode *inode) +int v9fs_init_inode(struct v9fs_session_info *v9ses, +		    struct inode *inode, umode_t mode, dev_t rdev)  { -	struct dentry *dentry; - -	spin_lock(&dcache_lock); -	/* Directory should have only one entry. */ -	BUG_ON(S_ISDIR(inode->i_mode) && !list_is_singular(&inode->i_dentry)); -	dentry = list_entry(inode->i_dentry.next, struct dentry, d_alias); -	spin_unlock(&dcache_lock); -	return dentry; -} - -/** - * v9fs_get_inode - helper function to setup an inode - * @sb: superblock - * @mode: mode to setup inode with - * - */ - -struct inode *v9fs_get_inode(struct super_block *sb, int mode) -{ -	int err; -	struct inode *inode; -	struct v9fs_session_info *v9ses = sb->s_fs_info; - -	P9_DPRINTK(P9_DEBUG_VFS, "super block: %p mode: %o\n", sb, mode); - -	inode = new_inode(sb); -	if (!inode) { -		P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n"); -		return ERR_PTR(-ENOMEM); -	} +	int err = 0;  	inode_init_owner(inode, NULL, mode);  	inode->i_blocks = 0; -	inode->i_rdev = 0; +	inode->i_rdev = rdev;  	inode->i_atime = inode->i_mtime = inode->i_ctime = CURRENT_TIME;  	inode->i_mapping->a_ops = &v9fs_addr_operations; @@ -312,13 +286,11 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)  	case S_IFSOCK:  		if (v9fs_proto_dotl(v9ses)) {  			inode->i_op = &v9fs_file_inode_operations_dotl; -			inode->i_fop = &v9fs_file_operations_dotl;  		} else if (v9fs_proto_dotu(v9ses)) {  			inode->i_op = &v9fs_file_inode_operations; -			inode->i_fop = &v9fs_file_operations;  		} else { -			P9_DPRINTK(P9_DEBUG_ERROR, -				   "special files without extended mode\n"); +			p9_debug(P9_DEBUG_ERROR, +				 "special files without extended mode\n");  			err = -EINVAL;  			goto error;  		} @@ -327,18 +299,31 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)  	case S_IFREG:  		if (v9fs_proto_dotl(v9ses)) {  			inode->i_op = &v9fs_file_inode_operations_dotl; -			inode->i_fop = &v9fs_file_operations_dotl; +			if (v9ses->cache == CACHE_LOOSE || +			    v9ses->cache == CACHE_FSCACHE) +				inode->i_fop = +					&v9fs_cached_file_operations_dotl; +			else if (v9ses->cache == CACHE_MMAP) +				inode->i_fop = &v9fs_mmap_file_operations_dotl; +			else +				inode->i_fop = &v9fs_file_operations_dotl;  		} else {  			inode->i_op = &v9fs_file_inode_operations; -			inode->i_fop = &v9fs_file_operations; +			if (v9ses->cache == CACHE_LOOSE || +			    v9ses->cache == CACHE_FSCACHE) +				inode->i_fop = +					&v9fs_cached_file_operations; +			else if (v9ses->cache == CACHE_MMAP) +				inode->i_fop = &v9fs_mmap_file_operations; +			else +				inode->i_fop = &v9fs_file_operations;  		}  		break; -  	case S_IFLNK:  		if (!v9fs_proto_dotu(v9ses) && !v9fs_proto_dotl(v9ses)) { -			P9_DPRINTK(P9_DEBUG_ERROR, "extended modes used with " -						"legacy protocol.\n"); +			p9_debug(P9_DEBUG_ERROR, +				 "extended modes used with legacy protocol\n");  			err = -EINVAL;  			goto error;  		} @@ -365,17 +350,43 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)  		break;  	default: -		P9_DPRINTK(P9_DEBUG_ERROR, "BAD mode 0x%x S_IFMT 0x%x\n", -			   mode, mode & S_IFMT); +		p9_debug(P9_DEBUG_ERROR, "BAD mode 0x%hx S_IFMT 0x%x\n", +			 mode, mode & S_IFMT);  		err = -EINVAL;  		goto error;  	} +error: +	return err; -	return inode; +} -error: -	iput(inode); -	return ERR_PTR(err); +/** + * v9fs_get_inode - helper function to setup an inode + * @sb: superblock + * @mode: mode to setup inode with + * + */ + +struct inode *v9fs_get_inode(struct super_block *sb, umode_t mode, dev_t rdev) +{ +	int err; +	struct inode *inode; +	struct v9fs_session_info *v9ses = sb->s_fs_info; + +	p9_debug(P9_DEBUG_VFS, "super block: %p mode: %ho\n", sb, mode); + +	inode = new_inode(sb); +	if (!inode) { +		pr_warn("%s (%d): Problem allocating inode\n", +			__func__, task_pid_nr(current)); +		return ERR_PTR(-ENOMEM); +	} +	err = v9fs_init_inode(v9ses, inode, mode, rdev); +	if (err) { +		iput(inode); +		return ERR_PTR(err); +	} +	return inode;  }  /* @@ -438,129 +449,183 @@ error:   */  void v9fs_evict_inode(struct inode *inode)  { -	truncate_inode_pages(inode->i_mapping, 0); -	end_writeback(inode); +	struct v9fs_inode *v9inode = V9FS_I(inode); + +	truncate_inode_pages_final(inode->i_mapping); +	clear_inode(inode);  	filemap_fdatawrite(inode->i_mapping); -#ifdef CONFIG_9P_FSCACHE  	v9fs_cache_inode_put_cookie(inode); -#endif +	/* clunk the fid stashed in writeback_fid */ +	if (v9inode->writeback_fid) { +		p9_client_clunk(v9inode->writeback_fid); +		v9inode->writeback_fid = NULL; +	}  } -static struct inode * -v9fs_inode(struct v9fs_session_info *v9ses, struct p9_fid *fid, -	struct super_block *sb) +static int v9fs_test_inode(struct inode *inode, void *data)  { -	int err, umode; -	struct inode *ret = NULL; -	struct p9_wstat *st; +	int umode; +	dev_t rdev; +	struct v9fs_inode *v9inode = V9FS_I(inode); +	struct p9_wstat *st = (struct p9_wstat *)data; +	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(inode); + +	umode = p9mode2unixmode(v9ses, st, &rdev); +	/* don't match inode of different type */ +	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT)) +		return 0; + +	/* compare qid details */ +	if (memcmp(&v9inode->qid.version, +		   &st->qid.version, sizeof(v9inode->qid.version))) +		return 0; + +	if (v9inode->qid.type != st->qid.type) +		return 0; +	return 1; +} -	st = p9_client_stat(fid); -	if (IS_ERR(st)) -		return ERR_CAST(st); +static int v9fs_test_new_inode(struct inode *inode, void *data) +{ +	return 0; +} -	umode = p9mode2unixmode(v9ses, st->mode); -	ret = v9fs_get_inode(sb, umode); -	if (IS_ERR(ret)) { -		err = PTR_ERR(ret); -		goto error; -	} +static int v9fs_set_inode(struct inode *inode,  void *data) +{ +	struct v9fs_inode *v9inode = V9FS_I(inode); +	struct p9_wstat *st = (struct p9_wstat *)data; -	v9fs_stat2inode(st, ret, sb); -	ret->i_ino = v9fs_qid2ino(&st->qid); +	memcpy(&v9inode->qid, &st->qid, sizeof(st->qid)); +	return 0; +} -#ifdef CONFIG_9P_FSCACHE -	v9fs_vcookie_set_qid(ret, &st->qid); -	v9fs_cache_inode_get_cookie(ret); -#endif -	p9stat_free(st); -	kfree(st); -	return ret; +static struct inode *v9fs_qid_iget(struct super_block *sb, +				   struct p9_qid *qid, +				   struct p9_wstat *st, +				   int new) +{ +	dev_t rdev; +	int retval; +	umode_t umode; +	unsigned long i_ino; +	struct inode *inode; +	struct v9fs_session_info *v9ses = sb->s_fs_info; +	int (*test)(struct inode *, void *); + +	if (new) +		test = v9fs_test_new_inode; +	else +		test = v9fs_test_inode; + +	i_ino = v9fs_qid2ino(qid); +	inode = iget5_locked(sb, i_ino, test, v9fs_set_inode, st); +	if (!inode) +		return ERR_PTR(-ENOMEM); +	if (!(inode->i_state & I_NEW)) +		return inode; +	/* +	 * initialize the inode with the stat info +	 * FIXME!! we may need support for stale inodes +	 * later. +	 */ +	inode->i_ino = i_ino; +	umode = p9mode2unixmode(v9ses, st, &rdev); +	retval = v9fs_init_inode(v9ses, inode, umode, rdev); +	if (retval) +		goto error; + +	v9fs_stat2inode(st, inode, sb); +	v9fs_cache_inode_get_cookie(inode); +	unlock_new_inode(inode); +	return inode;  error: -	p9stat_free(st); -	kfree(st); -	return ERR_PTR(err); +	unlock_new_inode(inode); +	iput(inode); +	return ERR_PTR(retval); +  } -static struct inode * -v9fs_inode_dotl(struct v9fs_session_info *v9ses, struct p9_fid *fid, -	struct super_block *sb) +struct inode * +v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, +		    struct super_block *sb, int new)  { -	struct inode *ret = NULL; -	int err; -	struct p9_stat_dotl *st; +	struct p9_wstat *st; +	struct inode *inode = NULL; -	st = p9_client_getattr_dotl(fid, P9_STATS_BASIC); +	st = p9_client_stat(fid);  	if (IS_ERR(st))  		return ERR_CAST(st); -	ret = v9fs_get_inode(sb, st->st_mode); -	if (IS_ERR(ret)) { -		err = PTR_ERR(ret); -		goto error; -	} - -	v9fs_stat2inode_dotl(st, ret); -	ret->i_ino = v9fs_qid2ino(&st->qid); -#ifdef CONFIG_9P_FSCACHE -	v9fs_vcookie_set_qid(ret, &st->qid); -	v9fs_cache_inode_get_cookie(ret); -#endif -	err = v9fs_get_acl(ret, fid); -	if (err) { -		iput(ret); -		goto error; -	} -	kfree(st); -	return ret; -error: +	inode = v9fs_qid_iget(sb, &st->qid, st, new); +	p9stat_free(st);  	kfree(st); -	return ERR_PTR(err); +	return inode;  }  /** - * v9fs_inode_from_fid - Helper routine to populate an inode by - * issuing a attribute request - * @v9ses: session information - * @fid: fid to issue attribute request for - * @sb: superblock on which to create inode - * + * v9fs_at_to_dotl_flags- convert Linux specific AT flags to + * plan 9 AT flag. + * @flags: flags to convert   */ -static inline struct inode * -v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid, -			struct super_block *sb) +static int v9fs_at_to_dotl_flags(int flags)  { -	if (v9fs_proto_dotl(v9ses)) -		return v9fs_inode_dotl(v9ses, fid, sb); -	else -		return v9fs_inode(v9ses, fid, sb); +	int rflags = 0; +	if (flags & AT_REMOVEDIR) +		rflags |= P9_DOTL_AT_REMOVEDIR; +	return rflags;  }  /**   * v9fs_remove - helper function to remove files and directories   * @dir: directory inode that is being deleted - * @file:  dentry that is being deleted - * @rmdir: removing a directory + * @dentry:  dentry that is being deleted + * @flags: removing a directory   *   */ -static int v9fs_remove(struct inode *dir, struct dentry *file, int rmdir) +static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)  { -	int retval; -	struct inode *file_inode; -	struct p9_fid *v9fid; +	struct inode *inode; +	int retval = -EOPNOTSUPP; +	struct p9_fid *v9fid, *dfid; +	struct v9fs_session_info *v9ses; -	P9_DPRINTK(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %d\n", dir, file, -		rmdir); +	p9_debug(P9_DEBUG_VFS, "inode: %p dentry: %p rmdir: %x\n", +		 dir, dentry, flags); -	file_inode = file->d_inode; -	v9fid = v9fs_fid_clone(file); -	if (IS_ERR(v9fid)) -		return PTR_ERR(v9fid); +	v9ses = v9fs_inode2v9ses(dir); +	inode = dentry->d_inode; +	dfid = v9fs_fid_lookup(dentry->d_parent); +	if (IS_ERR(dfid)) { +		retval = PTR_ERR(dfid); +		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", retval); +		return retval; +	} +	if (v9fs_proto_dotl(v9ses)) +		retval = p9_client_unlinkat(dfid, dentry->d_name.name, +					    v9fs_at_to_dotl_flags(flags)); +	if (retval == -EOPNOTSUPP) { +		/* Try the one based on path */ +		v9fid = v9fs_fid_clone(dentry); +		if (IS_ERR(v9fid)) +			return PTR_ERR(v9fid); +		retval = p9_client_remove(v9fid); +	} +	if (!retval) { +		/* +		 * directories on unlink should have zero +		 * link count +		 */ +		if (flags & AT_REMOVEDIR) { +			clear_nlink(inode); +			drop_nlink(dir); +		} else +			drop_nlink(inode); -	retval = p9_client_remove(v9fid); -	if (!retval) -		drop_nlink(file_inode); +		v9fs_invalidate_inode_attr(inode); +		v9fs_invalidate_inode_attr(dir); +	}  	return retval;  } @@ -583,7 +648,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,  	struct p9_fid *dfid, *ofid, *fid;  	struct inode *inode; -	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); +	p9_debug(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);  	err = 0;  	ofid = NULL; @@ -592,7 +657,7 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,  	dfid = v9fs_fid_lookup(dentry->d_parent);  	if (IS_ERR(dfid)) {  		err = PTR_ERR(dfid); -		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); +		p9_debug(P9_DEBUG_VFS, "fid lookup failed %d\n", err);  		return ERR_PTR(err);  	} @@ -600,250 +665,79 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,  	ofid = p9_client_walk(dfid, 0, NULL, 1);  	if (IS_ERR(ofid)) {  		err = PTR_ERR(ofid); -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); +		p9_debug(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err);  		return ERR_PTR(err);  	}  	err = p9_client_fcreate(ofid, name, perm, mode, extension);  	if (err < 0) { -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err); -		goto error; -	} - -	/* now walk from the parent so we can get unopened fid */ -	fid = p9_client_walk(dfid, 1, &name, 1); -	if (IS_ERR(fid)) { -		err = PTR_ERR(fid); -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); -		fid = NULL; +		p9_debug(P9_DEBUG_VFS, "p9_client_fcreate failed %d\n", err);  		goto error;  	} -	/* instantiate inode and assign the unopened fid to the dentry */ -	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -	if (IS_ERR(inode)) { -		err = PTR_ERR(inode); -		P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err); -		goto error; -	} - -	if (v9ses->cache) -		dentry->d_op = &v9fs_cached_dentry_operations; -	else -		dentry->d_op = &v9fs_dentry_operations; - -	d_instantiate(dentry, inode); -	err = v9fs_fid_add(dentry, fid); -	if (err < 0) -		goto error; - -	return ofid; - -error: -	if (ofid) -		p9_client_clunk(ofid); - -	if (fid) -		p9_client_clunk(fid); - -	return ERR_PTR(err); -} - -/** - * v9fs_vfs_create_dotl - VFS hook to create files for 9P2000.L protocol. - * @dir: directory inode that is being created - * @dentry:  dentry that is being deleted - * @mode: create permissions - * @nd: path information - * - */ - -static int -v9fs_vfs_create_dotl(struct inode *dir, struct dentry *dentry, int omode, -		struct nameidata *nd) -{ -	int err = 0; -	char *name = NULL; -	gid_t gid; -	int flags; -	mode_t mode; -	struct v9fs_session_info *v9ses; -	struct p9_fid *fid = NULL; -	struct p9_fid *dfid, *ofid; -	struct file *filp; -	struct p9_qid qid; -	struct inode *inode; -	struct posix_acl *pacl = NULL, *dacl = NULL; - -	v9ses = v9fs_inode2v9ses(dir); -	if (nd && nd->flags & LOOKUP_OPEN) -		flags = nd->intent.open.flags - 1; -	else { -		/* -		 * create call without LOOKUP_OPEN is due -		 * to mknod of regular files. So use mknod -		 * operation. -		 */ -		return v9fs_vfs_mknod_dotl(dir, dentry, omode, 0); -	} - -	name = (char *) dentry->d_name.name; -	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_create_dotl: name:%s flags:0x%x " -			"mode:0x%x\n", name, flags, omode); - -	dfid = v9fs_fid_lookup(dentry->d_parent); -	if (IS_ERR(dfid)) { -		err = PTR_ERR(dfid); -		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); -		return err; -	} - -	/* clone a fid to use for creation */ -	ofid = p9_client_walk(dfid, 0, NULL, 1); -	if (IS_ERR(ofid)) { -		err = PTR_ERR(ofid); -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", err); -		return err; -	} - -	gid = v9fs_get_fsgid_for_create(dir); - -	mode = omode; -	/* Update mode based on ACL value */ -	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl); -	if (err) { -		P9_DPRINTK(P9_DEBUG_VFS, -			   "Failed to get acl values in creat %d\n", err); -		goto error; -	} -	err = p9_client_create_dotl(ofid, name, flags, mode, gid, &qid); -	if (err < 0) { -		P9_DPRINTK(P9_DEBUG_VFS, -				"p9_client_open_dotl failed in creat %d\n", -				err); -		goto error; -	} -	/* instantiate inode and assign the unopened fid to the dentry */ -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE || -	    (nd && nd->flags & LOOKUP_OPEN)) { +	if (!(perm & P9_DMLINK)) { +		/* now walk from the parent so we can get unopened fid */  		fid = p9_client_walk(dfid, 1, &name, 1);  		if (IS_ERR(fid)) {  			err = PTR_ERR(fid); -			P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", -				err); +			p9_debug(P9_DEBUG_VFS, +				   "p9_client_walk failed %d\n", err);  			fid = NULL;  			goto error;  		} - -		inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", -				err); -			goto error; -		} -		dentry->d_op = &v9fs_cached_dentry_operations; -		d_instantiate(dentry, inode); -		err = v9fs_fid_add(dentry, fid); -		if (err < 0) -			goto error; -		/* The fid would get clunked via a dput */ -		fid = NULL; -	} else {  		/* -		 * Not in cached mode. No need to populate -		 * inode with stat. We need to get an inode -		 * so that we can set the acl with dentry +		 * instantiate inode and assign the unopened fid to the dentry  		 */ -		inode = v9fs_get_inode(dir->i_sb, mode); +		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb);  		if (IS_ERR(inode)) {  			err = PTR_ERR(inode); +			p9_debug(P9_DEBUG_VFS, +				   "inode creation failed %d\n", err);  			goto error;  		} -		dentry->d_op = &v9fs_dentry_operations; +		v9fs_fid_add(dentry, fid);  		d_instantiate(dentry, inode);  	} -	/* Now set the ACL based on the default value */ -	v9fs_set_create_acl(dentry, dacl, pacl); - -	/* if we are opening a file, assign the open fid to the file */ -	if (nd && nd->flags & LOOKUP_OPEN) { -		filp = lookup_instantiate_filp(nd, dentry, generic_file_open); -		if (IS_ERR(filp)) { -			p9_client_clunk(ofid); -			return PTR_ERR(filp); -		} -		filp->private_data = ofid; -	} else -		p9_client_clunk(ofid); - -	return 0; - +	return ofid;  error:  	if (ofid)  		p9_client_clunk(ofid); +  	if (fid)  		p9_client_clunk(fid); -	return err; + +	return ERR_PTR(err);  }  /** - * v9fs_vfs_create - VFS hook to create files + * v9fs_vfs_create - VFS hook to create a regular file + * + * open(.., O_CREAT) is handled in v9fs_vfs_atomic_open().  This is only called + * for mknod(2). + *   * @dir: directory inode that is being created   * @dentry:  dentry that is being deleted   * @mode: create permissions - * @nd: path information   *   */  static int -v9fs_vfs_create(struct inode *dir, struct dentry *dentry, int mode, -		struct nameidata *nd) +v9fs_vfs_create(struct inode *dir, struct dentry *dentry, umode_t mode, +		bool excl)  { -	int err; -	u32 perm; -	int flags; -	struct v9fs_session_info *v9ses; +	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir); +	u32 perm = unixmode2p9mode(v9ses, mode);  	struct p9_fid *fid; -	struct file *filp; - -	err = 0; -	fid = NULL; -	v9ses = v9fs_inode2v9ses(dir); -	perm = unixmode2p9mode(v9ses, mode); -	if (nd && nd->flags & LOOKUP_OPEN) -		flags = nd->intent.open.flags - 1; -	else -		flags = O_RDWR; - -	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, -				v9fs_uflags2omode(flags, -						v9fs_proto_dotu(v9ses))); -	if (IS_ERR(fid)) { -		err = PTR_ERR(fid); -		fid = NULL; -		goto error; -	} -	/* if we are opening a file, assign the open fid to the file */ -	if (nd && nd->flags & LOOKUP_OPEN) { -		filp = lookup_instantiate_filp(nd, dentry, generic_file_open); -		if (IS_ERR(filp)) { -			err = PTR_ERR(filp); -			goto error; -		} +	/* P9_OEXCL? */ +	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, P9_ORDWR); +	if (IS_ERR(fid)) +		return PTR_ERR(fid); -		filp->private_data = fid; -	} else -		p9_client_clunk(fid); +	v9fs_invalidate_inode_attr(dir); +	p9_client_clunk(fid);  	return 0; - -error: -	if (fid) -		p9_client_clunk(fid); - -	return err;  }  /** @@ -854,14 +748,14 @@ error:   *   */ -static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode) +static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode)  {  	int err;  	u32 perm; -	struct v9fs_session_info *v9ses;  	struct p9_fid *fid; +	struct v9fs_session_info *v9ses; -	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); +	p9_debug(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name);  	err = 0;  	v9ses = v9fs_inode2v9ses(dir);  	perm = unixmode2p9mode(v9ses, mode | S_IFDIR); @@ -869,112 +763,14 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)  	if (IS_ERR(fid)) {  		err = PTR_ERR(fid);  		fid = NULL; -	} - -	if (fid) -		p9_client_clunk(fid); - -	return err; -} - - -/** - * v9fs_vfs_mkdir_dotl - VFS mkdir hook to create a directory - * @dir:  inode that is being unlinked - * @dentry: dentry that is being unlinked - * @mode: mode for new directory - * - */ - -static int v9fs_vfs_mkdir_dotl(struct inode *dir, -			       struct dentry *dentry, int omode) -{ -	int err; -	struct v9fs_session_info *v9ses; -	struct p9_fid *fid = NULL, *dfid = NULL; -	gid_t gid; -	char *name; -	mode_t mode; -	struct inode *inode; -	struct p9_qid qid; -	struct dentry *dir_dentry; -	struct posix_acl *dacl = NULL, *pacl = NULL; - -	P9_DPRINTK(P9_DEBUG_VFS, "name %s\n", dentry->d_name.name); -	err = 0; -	v9ses = v9fs_inode2v9ses(dir); - -	omode |= S_IFDIR; -	if (dir->i_mode & S_ISGID) -		omode |= S_ISGID; - -	dir_dentry = v9fs_dentry_from_dir_inode(dir); -	dfid = v9fs_fid_lookup(dir_dentry); -	if (IS_ERR(dfid)) { -		err = PTR_ERR(dfid); -		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); -		dfid = NULL; -		goto error; -	} - -	gid = v9fs_get_fsgid_for_create(dir); -	mode = omode; -	/* Update mode based on ACL value */ -	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl); -	if (err) { -		P9_DPRINTK(P9_DEBUG_VFS, -			   "Failed to get acl values in mkdir %d\n", err); -		goto error; -	} -	name = (char *) dentry->d_name.name; -	err = p9_client_mkdir_dotl(dfid, name, mode, gid, &qid); -	if (err < 0) -		goto error; - -	/* instantiate inode and assign the unopened fid to the dentry */ -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { -		fid = p9_client_walk(dfid, 1, &name, 1); -		if (IS_ERR(fid)) { -			err = PTR_ERR(fid); -			P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", -				err); -			fid = NULL; -			goto error; -		} - -		inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", -				err); -			goto error; -		} -		dentry->d_op = &v9fs_cached_dentry_operations; -		d_instantiate(dentry, inode); -		err = v9fs_fid_add(dentry, fid); -		if (err < 0) -			goto error; -		fid = NULL;  	} else { -		/* -		 * Not in cached mode. No need to populate -		 * inode with stat. We need to get an inode -		 * so that we can set the acl with dentry -		 */ -		inode = v9fs_get_inode(dir->i_sb, mode); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			goto error; -		} -		dentry->d_op = &v9fs_dentry_operations; -		d_instantiate(dentry, inode); +		inc_nlink(dir); +		v9fs_invalidate_inode_attr(dir);  	} -	/* Now set the ACL based on the default value */ -	v9fs_set_create_acl(dentry, dacl, pacl); -error:  	if (fid)  		p9_client_clunk(fid); +  	return err;  } @@ -982,27 +778,25 @@ error:   * v9fs_vfs_lookup - VFS lookup hook to "walk" to a new inode   * @dir:  inode that is being walked from   * @dentry: dentry that is being walked to? - * @nameidata: path data + * @flags: lookup flags (unused)   *   */ -static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, -				      struct nameidata *nameidata) +struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, +				      unsigned int flags)  { -	struct super_block *sb; +	struct dentry *res;  	struct v9fs_session_info *v9ses;  	struct p9_fid *dfid, *fid;  	struct inode *inode;  	char *name; -	int result = 0; -	P9_DPRINTK(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p nameidata: %p\n", -		dir, dentry->d_name.name, dentry, nameidata); +	p9_debug(P9_DEBUG_VFS, "dir: %p dentry: (%s) %p flags: %x\n", +		 dir, dentry->d_name.name, dentry, flags);  	if (dentry->d_name.len > NAME_MAX)  		return ERR_PTR(-ENAMETOOLONG); -	sb = dir->i_sb;  	v9ses = v9fs_inode2v9ses(dir);  	/* We can walk d_parent because we hold the dir->i_mutex */  	dfid = v9fs_fid_lookup(dentry->d_parent); @@ -1012,41 +806,119 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,  	name = (char *) dentry->d_name.name;  	fid = p9_client_walk(dfid, 1, &name, 1);  	if (IS_ERR(fid)) { -		result = PTR_ERR(fid); -		if (result == -ENOENT) { -			inode = NULL; -			goto inst_out; +		if (fid == ERR_PTR(-ENOENT)) { +			d_add(dentry, NULL); +			return NULL;  		} +		return ERR_CAST(fid); +	} +	/* +	 * Make sure we don't use a wrong inode due to parallel +	 * unlink. For cached mode create calls request for new +	 * inode. But with cache disabled, lookup should do this. +	 */ +	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) +		inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb); +	else +		inode = v9fs_get_new_inode_from_fid(v9ses, fid, dir->i_sb); +	if (IS_ERR(inode)) { +		p9_client_clunk(fid); +		return ERR_CAST(inode); +	} +	/* +	 * If we had a rename on the server and a parallel lookup +	 * for the new name, then make sure we instantiate with +	 * the new name. ie look up for a/b, while on server somebody +	 * moved b under k and client parallely did a lookup for +	 * k/b. +	 */ +	res = d_materialise_unique(dentry, inode); +	if (!res) +		v9fs_fid_add(dentry, fid); +	else if (!IS_ERR(res)) +		v9fs_fid_add(res, fid); +	else +		p9_client_clunk(fid); +	return res; +} + +static int +v9fs_vfs_atomic_open(struct inode *dir, struct dentry *dentry, +		     struct file *file, unsigned flags, umode_t mode, +		     int *opened) +{ +	int err; +	u32 perm; +	struct v9fs_inode *v9inode; +	struct v9fs_session_info *v9ses; +	struct p9_fid *fid, *inode_fid; +	struct dentry *res = NULL; + +	if (d_unhashed(dentry)) { +		res = v9fs_vfs_lookup(dir, dentry, 0); +		if (IS_ERR(res)) +			return PTR_ERR(res); -		return ERR_PTR(result); +		if (res) +			dentry = res;  	} -	inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -	if (IS_ERR(inode)) { -		result = PTR_ERR(inode); -		inode = NULL; +	/* Only creates */ +	if (!(flags & O_CREAT) || dentry->d_inode) +		return finish_no_open(file, res); + +	err = 0; + +	v9ses = v9fs_inode2v9ses(dir); +	perm = unixmode2p9mode(v9ses, mode); +	fid = v9fs_create(v9ses, dir, dentry, NULL, perm, +				v9fs_uflags2omode(flags, +						v9fs_proto_dotu(v9ses))); +	if (IS_ERR(fid)) { +		err = PTR_ERR(fid); +		fid = NULL;  		goto error;  	} -	result = v9fs_fid_add(dentry, fid); -	if (result < 0) -		goto error_iput; +	v9fs_invalidate_inode_attr(dir); +	v9inode = V9FS_I(dentry->d_inode); +	mutex_lock(&v9inode->v_mutex); +	if ((v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) && +	    !v9inode->writeback_fid && +	    ((flags & O_ACCMODE) != O_RDONLY)) { +		/* +		 * clone a fid and add it to writeback_fid +		 * we do it during open time instead of +		 * page dirty time via write_begin/page_mkwrite +		 * because we want write after unlink usecase +		 * to work. +		 */ +		inode_fid = v9fs_writeback_fid(dentry); +		if (IS_ERR(inode_fid)) { +			err = PTR_ERR(inode_fid); +			mutex_unlock(&v9inode->v_mutex); +			goto error; +		} +		v9inode->writeback_fid = (void *) inode_fid; +	} +	mutex_unlock(&v9inode->v_mutex); +	err = finish_open(file, dentry, generic_file_open, opened); +	if (err) +		goto error; -inst_out: -	if (v9ses->cache) -		dentry->d_op = &v9fs_cached_dentry_operations; -	else -		dentry->d_op = &v9fs_dentry_operations; +	file->private_data = fid; +	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) +		v9fs_cache_inode_set_cookie(dentry->d_inode, file); -	d_add(dentry, inode); -	return NULL; +	*opened |= FILE_CREATED; +out: +	dput(res); +	return err; -error_iput: -	iput(inode);  error: -	p9_client_clunk(fid); - -	return ERR_PTR(result); +	if (fid) +		p9_client_clunk(fid); +	goto out;  }  /** @@ -1056,7 +928,7 @@ error:   *   */ -static int v9fs_vfs_unlink(struct inode *i, struct dentry *d) +int v9fs_vfs_unlink(struct inode *i, struct dentry *d)  {  	return v9fs_remove(i, d, 0);  } @@ -1068,9 +940,9 @@ static int v9fs_vfs_unlink(struct inode *i, struct dentry *d)   *   */ -static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d) +int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)  { -	return v9fs_remove(i, d, 1); +	return v9fs_remove(i, d, AT_REMOVEDIR);  }  /** @@ -1082,21 +954,23 @@ static int v9fs_vfs_rmdir(struct inode *i, struct dentry *d)   *   */ -static int +int  v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,  		struct inode *new_dir, struct dentry *new_dentry)  { +	int retval;  	struct inode *old_inode; +	struct inode *new_inode;  	struct v9fs_session_info *v9ses;  	struct p9_fid *oldfid;  	struct p9_fid *olddirfid;  	struct p9_fid *newdirfid;  	struct p9_wstat wstat; -	int retval; -	P9_DPRINTK(P9_DEBUG_VFS, "\n"); +	p9_debug(P9_DEBUG_VFS, "\n");  	retval = 0;  	old_inode = old_dentry->d_inode; +	new_inode = new_dentry->d_inode;  	v9ses = v9fs_inode2v9ses(old_inode);  	oldfid = v9fs_fid_lookup(old_dentry);  	if (IS_ERR(oldfid)) @@ -1116,9 +990,12 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,  	down_write(&v9ses->rename_sem);  	if (v9fs_proto_dotl(v9ses)) { -		retval = p9_client_rename(oldfid, newdirfid, -					(char *) new_dentry->d_name.name); -		if (retval != -ENOSYS) +		retval = p9_client_renameat(olddirfid, old_dentry->d_name.name, +					    newdirfid, new_dentry->d_name.name); +		if (retval == -EOPNOTSUPP) +			retval = p9_client_rename(oldfid, newdirfid, +						  new_dentry->d_name.name); +		if (retval != -EOPNOTSUPP)  			goto clunk_newdir;  	}  	if (old_dentry->d_parent != new_dentry->d_parent) { @@ -1126,8 +1003,7 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,  		 * 9P .u can only handle file rename in the same directory  		 */ -		P9_DPRINTK(P9_DEBUG_ERROR, -				"old dir and new dir are different\n"); +		p9_debug(P9_DEBUG_ERROR, "old dir and new dir are different\n");  		retval = -EXDEV;  		goto clunk_newdir;  	} @@ -1137,9 +1013,25 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry,  	retval = p9_client_wstat(oldfid, &wstat);  clunk_newdir: -	if (!retval) +	if (!retval) { +		if (new_inode) { +			if (S_ISDIR(new_inode->i_mode)) +				clear_nlink(new_inode); +			else +				drop_nlink(new_inode); +		} +		if (S_ISDIR(old_inode->i_mode)) { +			if (!new_inode) +				inc_nlink(new_dir); +			drop_nlink(old_dir); +		} +		v9fs_invalidate_inode_attr(old_inode); +		v9fs_invalidate_inode_attr(old_dir); +		v9fs_invalidate_inode_attr(new_dir); +  		/* successful rename */  		d_move(old_dentry, new_dentry); +	}  	up_write(&v9ses->rename_sem);  	p9_client_clunk(newdirfid); @@ -1162,17 +1054,16 @@ static int  v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,  		 struct kstat *stat)  { -	int err;  	struct v9fs_session_info *v9ses;  	struct p9_fid *fid;  	struct p9_wstat *st; -	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); -	err = -EPERM; -	v9ses = v9fs_inode2v9ses(dentry->d_inode); -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) -		return simple_getattr(mnt, dentry, stat); - +	p9_debug(P9_DEBUG_VFS, "dentry: %p\n", dentry); +	v9ses = v9fs_dentry2v9ses(dentry); +	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { +		generic_fillattr(dentry->d_inode, stat); +		return 0; +	}  	fid = v9fs_fid_lookup(dentry);  	if (IS_ERR(fid))  		return PTR_ERR(fid); @@ -1182,45 +1073,9 @@ v9fs_vfs_getattr(struct vfsmount *mnt, struct dentry *dentry,  		return PTR_ERR(st);  	v9fs_stat2inode(st, dentry->d_inode, dentry->d_inode->i_sb); -		generic_fillattr(dentry->d_inode, stat); - -	p9stat_free(st); -	kfree(st); -	return 0; -} - -static int -v9fs_vfs_getattr_dotl(struct vfsmount *mnt, struct dentry *dentry, -		 struct kstat *stat) -{ -	int err; -	struct v9fs_session_info *v9ses; -	struct p9_fid *fid; -	struct p9_stat_dotl *st; - -	P9_DPRINTK(P9_DEBUG_VFS, "dentry: %p\n", dentry); -	err = -EPERM; -	v9ses = v9fs_inode2v9ses(dentry->d_inode); -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) -		return simple_getattr(mnt, dentry, stat); - -	fid = v9fs_fid_lookup(dentry); -	if (IS_ERR(fid)) -		return PTR_ERR(fid); - -	/* Ask for all the fields in stat structure. Server will return -	 * whatever it supports -	 */ - -	st = p9_client_getattr_dotl(fid, P9_STATS_ALL); -	if (IS_ERR(st)) -		return PTR_ERR(st); - -	v9fs_stat2inode_dotl(st, dentry->d_inode);  	generic_fillattr(dentry->d_inode, stat); -	/* Change block size to what the server returned */ -	stat->blksize = st->st_blksize; +	p9stat_free(st);  	kfree(st);  	return 0;  } @@ -1239,9 +1094,13 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)  	struct p9_fid *fid;  	struct p9_wstat wstat; -	P9_DPRINTK(P9_DEBUG_VFS, "\n"); +	p9_debug(P9_DEBUG_VFS, "\n"); +	retval = inode_change_ok(dentry->d_inode, iattr); +	if (retval) +		return retval; +  	retval = -EPERM; -	v9ses = v9fs_inode2v9ses(dentry->d_inode); +	v9ses = v9fs_dentry2v9ses(dentry);  	fid = v9fs_fid_lookup(dentry);  	if(IS_ERR(fid))  		return PTR_ERR(fid); @@ -1267,77 +1126,22 @@ static int v9fs_vfs_setattr(struct dentry *dentry, struct iattr *iattr)  			wstat.n_gid = iattr->ia_gid;  	} +	/* Write all dirty data */ +	if (S_ISREG(dentry->d_inode->i_mode)) +		filemap_write_and_wait(dentry->d_inode->i_mapping); +  	retval = p9_client_wstat(fid, &wstat);  	if (retval < 0)  		return retval;  	if ((iattr->ia_valid & ATTR_SIZE) && -	    iattr->ia_size != i_size_read(dentry->d_inode)) { -		retval = vmtruncate(dentry->d_inode, iattr->ia_size); -		if (retval) -			return retval; -	} +	    iattr->ia_size != i_size_read(dentry->d_inode)) +		truncate_setsize(dentry->d_inode, iattr->ia_size); -	setattr_copy(dentry->d_inode, iattr); -	mark_inode_dirty(dentry->d_inode); -	return 0; -} - -/** - * v9fs_vfs_setattr_dotl - set file metadata - * @dentry: file whose metadata to set - * @iattr: metadata assignment structure - * - */ - -int v9fs_vfs_setattr_dotl(struct dentry *dentry, struct iattr *iattr) -{ -	int retval; -	struct v9fs_session_info *v9ses; -	struct p9_fid *fid; -	struct p9_iattr_dotl p9attr; - -	P9_DPRINTK(P9_DEBUG_VFS, "\n"); - -	retval = inode_change_ok(dentry->d_inode, iattr); -	if (retval) -		return retval; - -	p9attr.valid = iattr->ia_valid; -	p9attr.mode = iattr->ia_mode; -	p9attr.uid = iattr->ia_uid; -	p9attr.gid = iattr->ia_gid; -	p9attr.size = iattr->ia_size; -	p9attr.atime_sec = iattr->ia_atime.tv_sec; -	p9attr.atime_nsec = iattr->ia_atime.tv_nsec; -	p9attr.mtime_sec = iattr->ia_mtime.tv_sec; -	p9attr.mtime_nsec = iattr->ia_mtime.tv_nsec; - -	retval = -EPERM; -	v9ses = v9fs_inode2v9ses(dentry->d_inode); -	fid = v9fs_fid_lookup(dentry); -	if (IS_ERR(fid)) -		return PTR_ERR(fid); - -	retval = p9_client_setattr(fid, &p9attr); -	if (retval < 0) -		return retval; - -	if ((iattr->ia_valid & ATTR_SIZE) && -	    iattr->ia_size != i_size_read(dentry->d_inode)) { -		retval = vmtruncate(dentry->d_inode, iattr->ia_size); -		if (retval) -			return retval; -	} +	v9fs_invalidate_inode_attr(dentry->d_inode);  	setattr_copy(dentry->d_inode, iattr);  	mark_inode_dirty(dentry->d_inode); -	if (iattr->ia_valid & ATTR_MODE) { -		/* We also want to update ACL when we update mode bits */ -		retval = v9fs_acl_chmod(dentry); -		if (retval < 0) -			return retval; -	}  	return 0;  } @@ -1353,12 +1157,14 @@ void  v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,  	struct super_block *sb)  { +	umode_t mode;  	char ext[32];  	char tag_name[14];  	unsigned int i_nlink;  	struct v9fs_session_info *v9ses = sb->s_fs_info; +	struct v9fs_inode *v9inode = V9FS_I(inode); -	inode->i_nlink = 1; +	set_nlink(inode, 1);  	inode->i_atime.tv_sec = stat->atime;  	inode->i_mtime.tv_sec = stat->mtime; @@ -1380,113 +1186,21 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,  			 * this even with .u extension. So check  			 * for non NULL stat->extension  			 */ -			strncpy(ext, stat->extension, sizeof(ext)); +			strlcpy(ext, stat->extension, sizeof(ext));  			/* HARDLINKCOUNT %u */  			sscanf(ext, "%13s %u", tag_name, &i_nlink);  			if (!strncmp(tag_name, "HARDLINKCOUNT", 13)) -				inode->i_nlink = i_nlink; +				set_nlink(inode, i_nlink);  		}  	} -	inode->i_mode = p9mode2unixmode(v9ses, stat->mode); -	if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) { -		char type = 0; -		int major = -1; -		int minor = -1; - -		strncpy(ext, stat->extension, sizeof(ext)); -		sscanf(ext, "%c %u %u", &type, &major, &minor); -		switch (type) { -		case 'c': -			inode->i_mode &= ~S_IFBLK; -			inode->i_mode |= S_IFCHR; -			break; -		case 'b': -			break; -		default: -			P9_DPRINTK(P9_DEBUG_ERROR, -				"Unknown special type %c %s\n", type, -				stat->extension); -		}; -		inode->i_rdev = MKDEV(major, minor); -		init_special_inode(inode, inode->i_mode, inode->i_rdev); -	} else -		inode->i_rdev = 0; - +	mode = p9mode2perm(v9ses, stat); +	mode |= inode->i_mode & ~S_IALLUGO; +	inode->i_mode = mode;  	i_size_write(inode, stat->length);  	/* not real number of blocks, but 512 byte ones ... */  	inode->i_blocks = (i_size_read(inode) + 512 - 1) >> 9; -} - -/** - * v9fs_stat2inode_dotl - populate an inode structure with stat info - * @stat: stat structure - * @inode: inode to populate - * @sb: superblock of filesystem - * - */ - -void -v9fs_stat2inode_dotl(struct p9_stat_dotl *stat, struct inode *inode) -{ - -	if ((stat->st_result_mask & P9_STATS_BASIC) == P9_STATS_BASIC) { -		inode->i_atime.tv_sec = stat->st_atime_sec; -		inode->i_atime.tv_nsec = stat->st_atime_nsec; -		inode->i_mtime.tv_sec = stat->st_mtime_sec; -		inode->i_mtime.tv_nsec = stat->st_mtime_nsec; -		inode->i_ctime.tv_sec = stat->st_ctime_sec; -		inode->i_ctime.tv_nsec = stat->st_ctime_nsec; -		inode->i_uid = stat->st_uid; -		inode->i_gid = stat->st_gid; -		inode->i_nlink = stat->st_nlink; -		inode->i_mode = stat->st_mode; -		inode->i_rdev = new_decode_dev(stat->st_rdev); - -		if ((S_ISBLK(inode->i_mode)) || (S_ISCHR(inode->i_mode))) -			init_special_inode(inode, inode->i_mode, inode->i_rdev); - -		i_size_write(inode, stat->st_size); -		inode->i_blocks = stat->st_blocks; -	} else { -		if (stat->st_result_mask & P9_STATS_ATIME) { -			inode->i_atime.tv_sec = stat->st_atime_sec; -			inode->i_atime.tv_nsec = stat->st_atime_nsec; -		} -		if (stat->st_result_mask & P9_STATS_MTIME) { -			inode->i_mtime.tv_sec = stat->st_mtime_sec; -			inode->i_mtime.tv_nsec = stat->st_mtime_nsec; -		} -		if (stat->st_result_mask & P9_STATS_CTIME) { -			inode->i_ctime.tv_sec = stat->st_ctime_sec; -			inode->i_ctime.tv_nsec = stat->st_ctime_nsec; -		} -		if (stat->st_result_mask & P9_STATS_UID) -			inode->i_uid = stat->st_uid; -		if (stat->st_result_mask & P9_STATS_GID) -			inode->i_gid = stat->st_gid; -		if (stat->st_result_mask & P9_STATS_NLINK) -			inode->i_nlink = stat->st_nlink; -		if (stat->st_result_mask & P9_STATS_MODE) { -			inode->i_mode = stat->st_mode; -			if ((S_ISBLK(inode->i_mode)) || -						(S_ISCHR(inode->i_mode))) -				init_special_inode(inode, inode->i_mode, -								inode->i_rdev); -		} -		if (stat->st_result_mask & P9_STATS_RDEV) -			inode->i_rdev = new_decode_dev(stat->st_rdev); -		if (stat->st_result_mask & P9_STATS_SIZE) -			i_size_write(inode, stat->st_size); -		if (stat->st_result_mask & P9_STATS_BLOCKS) -			inode->i_blocks = stat->st_blocks; -	} -	if (stat->st_result_mask & P9_STATS_GEN) -			inode->i_generation = stat->st_gen; - -	/* Currently we don't support P9_STATS_BTIME and P9_STATS_DATA_VERSION -	 * because the inode structure does not have fields for them. -	 */ +	v9inode->cache_validity &= ~V9FS_INO_INVALID_ATTR;  }  /** @@ -1525,9 +1239,9 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)  	struct p9_fid *fid;  	struct p9_wstat *st; -	P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name); +	p9_debug(P9_DEBUG_VFS, " %s\n", dentry->d_name.name);  	retval = -EPERM; -	v9ses = v9fs_inode2v9ses(dentry->d_inode); +	v9ses = v9fs_dentry2v9ses(dentry);  	fid = v9fs_fid_lookup(dentry);  	if (IS_ERR(fid))  		return PTR_ERR(fid); @@ -1545,12 +1259,12 @@ static int v9fs_readlink(struct dentry *dentry, char *buffer, int buflen)  	}  	/* copy extension buffer into buffer */ -	strncpy(buffer, st->extension, buflen); +	retval = min(strlen(st->extension)+1, (size_t)buflen); +	memcpy(buffer, st->extension, retval); -	P9_DPRINTK(P9_DEBUG_VFS, -		"%s -> %s (%s)\n", dentry->d_name.name, st->extension, buffer); +	p9_debug(P9_DEBUG_VFS, "%s -> %s (%.*s)\n", +		 dentry->d_name.name, st->extension, buflen, buffer); -	retval = strnlen(buffer, buflen);  done:  	p9stat_free(st);  	kfree(st); @@ -1569,7 +1283,7 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)  	int len = 0;  	char *link = __getname(); -	P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name); +	p9_debug(P9_DEBUG_VFS, "%s\n", dentry->d_name.name);  	if (!link)  		link = ERR_PTR(-ENOMEM); @@ -1595,13 +1309,13 @@ static void *v9fs_vfs_follow_link(struct dentry *dentry, struct nameidata *nd)   *   */ -static void +void  v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)  {  	char *s = nd_get_link(nd); -	P9_DPRINTK(P9_DEBUG_VFS, " %s %s\n", dentry->d_name.name, -		IS_ERR(s) ? "<error>" : s); +	p9_debug(P9_DEBUG_VFS, " %s %s\n", +		 dentry->d_name.name, IS_ERR(s) ? "<error>" : s);  	if (!IS_ERR(s))  		__putname(s);  } @@ -1610,123 +1324,34 @@ v9fs_vfs_put_link(struct dentry *dentry, struct nameidata *nd, void *p)   * v9fs_vfs_mkspecial - create a special file   * @dir: inode to create special file in   * @dentry: dentry to create - * @mode: mode to create special file + * @perm: mode to create special file   * @extension: 9p2000.u format extension string representing special file   *   */  static int v9fs_vfs_mkspecial(struct inode *dir, struct dentry *dentry, -	int mode, const char *extension) +	u32 perm, const char *extension)  { -	u32 perm; -	struct v9fs_session_info *v9ses;  	struct p9_fid *fid; +	struct v9fs_session_info *v9ses;  	v9ses = v9fs_inode2v9ses(dir);  	if (!v9fs_proto_dotu(v9ses)) { -		P9_DPRINTK(P9_DEBUG_ERROR, "not extended\n"); +		p9_debug(P9_DEBUG_ERROR, "not extended\n");  		return -EPERM;  	} -	perm = unixmode2p9mode(v9ses, mode);  	fid = v9fs_create(v9ses, dir, dentry, (char *) extension, perm,  								P9_OREAD);  	if (IS_ERR(fid))  		return PTR_ERR(fid); +	v9fs_invalidate_inode_attr(dir);  	p9_client_clunk(fid);  	return 0;  }  /** - * v9fs_vfs_symlink_dotl - helper function to create symlinks - * @dir: directory inode containing symlink - * @dentry: dentry for symlink - * @symname: symlink data - * - * See Also: 9P2000.L RFC for more information - * - */ - -static int -v9fs_vfs_symlink_dotl(struct inode *dir, struct dentry *dentry, -		const char *symname) -{ -	struct v9fs_session_info *v9ses; -	struct p9_fid *dfid; -	struct p9_fid *fid = NULL; -	struct inode *inode; -	struct p9_qid qid; -	char *name; -	int err; -	gid_t gid; - -	name = (char *) dentry->d_name.name; -	P9_DPRINTK(P9_DEBUG_VFS, "v9fs_vfs_symlink_dotl : %lu,%s,%s\n", -			dir->i_ino, name, symname); -	v9ses = v9fs_inode2v9ses(dir); - -	dfid = v9fs_fid_lookup(dentry->d_parent); -	if (IS_ERR(dfid)) { -		err = PTR_ERR(dfid); -		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); -		return err; -	} - -	gid = v9fs_get_fsgid_for_create(dir); - -	/* Server doesn't alter fid on TSYMLINK. Hence no need to clone it. */ -	err = p9_client_symlink(dfid, name, (char *)symname, gid, &qid); - -	if (err < 0) { -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_symlink failed %d\n", err); -		goto error; -	} - -	if (v9ses->cache) { -		/* Now walk from the parent so we can get an unopened fid. */ -		fid = p9_client_walk(dfid, 1, &name, 1); -		if (IS_ERR(fid)) { -			err = PTR_ERR(fid); -			P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", -					err); -			fid = NULL; -			goto error; -		} - -		/* instantiate inode and assign the unopened fid to dentry */ -		inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", -					err); -			goto error; -		} -		dentry->d_op = &v9fs_cached_dentry_operations; -		d_instantiate(dentry, inode); -		err = v9fs_fid_add(dentry, fid); -		if (err < 0) -			goto error; -		fid = NULL; -	} else { -		/* Not in cached mode. No need to populate inode with stat */ -		inode = v9fs_get_inode(dir->i_sb, S_IFLNK); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			goto error; -		} -		dentry->d_op = &v9fs_dentry_operations; -		d_instantiate(dentry, inode); -	} - -error: -	if (fid) -		p9_client_clunk(fid); - -	return err; -} - -/**   * v9fs_vfs_symlink - helper function to create symlinks   * @dir: directory inode containing symlink   * @dentry: dentry for symlink @@ -1739,10 +1364,10 @@ error:  static int  v9fs_vfs_symlink(struct inode *dir, struct dentry *dentry, const char *symname)  { -	P9_DPRINTK(P9_DEBUG_VFS, " %lu,%s,%s\n", dir->i_ino, -					dentry->d_name.name, symname); +	p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n", +		 dir->i_ino, dentry->d_name.name, symname); -	return v9fs_vfs_mkspecial(dir, dentry, S_IFLNK, symname); +	return v9fs_vfs_mkspecial(dir, dentry, P9_DMSYMLINK, symname);  }  /** @@ -1758,12 +1383,11 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,  	      struct dentry *dentry)  {  	int retval; -	struct p9_fid *oldfid;  	char *name; +	struct p9_fid *oldfid; -	P9_DPRINTK(P9_DEBUG_VFS, -		" %lu,%s,%s\n", dir->i_ino, dentry->d_name.name, -		old_dentry->d_name.name); +	p9_debug(P9_DEBUG_VFS, " %lu,%s,%s\n", +		 dir->i_ino, dentry->d_name.name, old_dentry->d_name.name);  	oldfid = v9fs_fid_clone(old_dentry);  	if (IS_ERR(oldfid)) @@ -1778,84 +1402,16 @@ v9fs_vfs_link(struct dentry *old_dentry, struct inode *dir,  	sprintf(name, "%d\n", oldfid->fid);  	retval = v9fs_vfs_mkspecial(dir, dentry, P9_DMLINK, name);  	__putname(name); - +	if (!retval) { +		v9fs_refresh_inode(oldfid, old_dentry->d_inode); +		v9fs_invalidate_inode_attr(dir); +	}  clunk_fid:  	p9_client_clunk(oldfid);  	return retval;  }  /** - * v9fs_vfs_link_dotl - create a hardlink for dotl - * @old_dentry: dentry for file to link to - * @dir: inode destination for new link - * @dentry: dentry for link - * - */ - -static int -v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir, -		struct dentry *dentry) -{ -	int err; -	struct p9_fid *dfid, *oldfid; -	char *name; -	struct v9fs_session_info *v9ses; -	struct dentry *dir_dentry; - -	P9_DPRINTK(P9_DEBUG_VFS, "dir ino: %lu, old_name: %s, new_name: %s\n", -			dir->i_ino, old_dentry->d_name.name, -			dentry->d_name.name); - -	v9ses = v9fs_inode2v9ses(dir); -	dir_dentry = v9fs_dentry_from_dir_inode(dir); -	dfid = v9fs_fid_lookup(dir_dentry); -	if (IS_ERR(dfid)) -		return PTR_ERR(dfid); - -	oldfid = v9fs_fid_lookup(old_dentry); -	if (IS_ERR(oldfid)) -		return PTR_ERR(oldfid); - -	name = (char *) dentry->d_name.name; - -	err = p9_client_link(dfid, oldfid, (char *)dentry->d_name.name); - -	if (err < 0) { -		P9_DPRINTK(P9_DEBUG_VFS, "p9_client_link failed %d\n", err); -		return err; -	} - -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { -		/* Get the latest stat info from server. */ -		struct p9_fid *fid; -		struct p9_stat_dotl *st; - -		fid = v9fs_fid_lookup(old_dentry); -		if (IS_ERR(fid)) -			return PTR_ERR(fid); - -		st = p9_client_getattr_dotl(fid, P9_STATS_BASIC); -		if (IS_ERR(st)) -			return PTR_ERR(st); - -		v9fs_stat2inode_dotl(st, old_dentry->d_inode); - -		kfree(st); -	} else { -		/* Caching disabled. No need to get upto date stat info. -		 * This dentry will be released immediately. So, just hold the -		 * inode -		 */ -		ihold(old_dentry->d_inode); -	} - -	dentry->d_op = old_dentry->d_op; -	d_instantiate(dentry, old_dentry->d_inode); - -	return err; -} - -/**   * v9fs_vfs_mknod - create a special file   * @dir: inode destination for new link   * @dentry: dentry for file @@ -1865,14 +1421,16 @@ v9fs_vfs_link_dotl(struct dentry *old_dentry, struct inode *dir,   */  static int -v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev) +v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, umode_t mode, dev_t rdev)  { +	struct v9fs_session_info *v9ses = v9fs_inode2v9ses(dir);  	int retval;  	char *name; +	u32 perm; -	P9_DPRINTK(P9_DEBUG_VFS, -		" %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, -		dentry->d_name.name, mode, MAJOR(rdev), MINOR(rdev)); +	p9_debug(P9_DEBUG_VFS, " %lu,%s mode: %hx MAJOR: %u MINOR: %u\n", +		 dir->i_ino, dentry->d_name.name, mode, +		 MAJOR(rdev), MINOR(rdev));  	if (!new_valid_dev(rdev))  		return -EINVAL; @@ -1894,169 +1452,52 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)  		return -EINVAL;  	} -	retval = v9fs_vfs_mkspecial(dir, dentry, mode, name); +	perm = unixmode2p9mode(v9ses, mode); +	retval = v9fs_vfs_mkspecial(dir, dentry, perm, name);  	__putname(name);  	return retval;  } -/** - * v9fs_vfs_mknod_dotl - create a special file - * @dir: inode destination for new link - * @dentry: dentry for file - * @mode: mode for creation - * @rdev: device associated with special file - * - */ -static int -v9fs_vfs_mknod_dotl(struct inode *dir, struct dentry *dentry, int omode, -		dev_t rdev) +int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)  { -	int err; -	char *name; -	mode_t mode; +	int umode; +	dev_t rdev; +	loff_t i_size; +	struct p9_wstat *st;  	struct v9fs_session_info *v9ses; -	struct p9_fid *fid = NULL, *dfid = NULL; -	struct inode *inode; -	gid_t gid; -	struct p9_qid qid; -	struct dentry *dir_dentry; -	struct posix_acl *dacl = NULL, *pacl = NULL; - -	P9_DPRINTK(P9_DEBUG_VFS, -		" %lu,%s mode: %x MAJOR: %u MINOR: %u\n", dir->i_ino, -		dentry->d_name.name, omode, MAJOR(rdev), MINOR(rdev)); -	if (!new_valid_dev(rdev)) -		return -EINVAL; - -	v9ses = v9fs_inode2v9ses(dir); -	dir_dentry = v9fs_dentry_from_dir_inode(dir); -	dfid = v9fs_fid_lookup(dir_dentry); -	if (IS_ERR(dfid)) { -		err = PTR_ERR(dfid); -		P9_DPRINTK(P9_DEBUG_VFS, "fid lookup failed %d\n", err); -		dfid = NULL; -		goto error; -	} - -	gid = v9fs_get_fsgid_for_create(dir); -	mode = omode; -	/* Update mode based on ACL value */ -	err = v9fs_acl_mode(dir, &mode, &dacl, &pacl); -	if (err) { -		P9_DPRINTK(P9_DEBUG_VFS, -			   "Failed to get acl values in mknod %d\n", err); -		goto error; -	} -	name = (char *) dentry->d_name.name; - -	err = p9_client_mknod_dotl(dfid, name, mode, rdev, gid, &qid); -	if (err < 0) -		goto error; - -	/* instantiate inode and assign the unopened fid to the dentry */ -	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) { -		fid = p9_client_walk(dfid, 1, &name, 1); -		if (IS_ERR(fid)) { -			err = PTR_ERR(fid); -			P9_DPRINTK(P9_DEBUG_VFS, "p9_client_walk failed %d\n", -				err); -			fid = NULL; -			goto error; -		} - -		inode = v9fs_inode_from_fid(v9ses, fid, dir->i_sb); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", -				err); -			goto error; -		} -		dentry->d_op = &v9fs_cached_dentry_operations; -		d_instantiate(dentry, inode); -		err = v9fs_fid_add(dentry, fid); -		if (err < 0) -			goto error; -		fid = NULL; -	} else { -		/* -		 * Not in cached mode. No need to populate inode with stat. -		 * socket syscall returns a fd, so we need instantiate -		 */ -		inode = v9fs_get_inode(dir->i_sb, mode); -		if (IS_ERR(inode)) { -			err = PTR_ERR(inode); -			goto error; -		} -		dentry->d_op = &v9fs_dentry_operations; -		d_instantiate(dentry, inode); -	} -	/* Now set the ACL based on the default value */ -	v9fs_set_create_acl(dentry, dacl, pacl); -error: -	if (fid) -		p9_client_clunk(fid); -	return err; -} - -static int -v9fs_vfs_readlink_dotl(struct dentry *dentry, char *buffer, int buflen) -{ -	int retval; -	struct p9_fid *fid; -	char *target = NULL; - -	P9_DPRINTK(P9_DEBUG_VFS, " %s\n", dentry->d_name.name); -	retval = -EPERM; -	fid = v9fs_fid_lookup(dentry); -	if (IS_ERR(fid)) -		return PTR_ERR(fid); - -	retval = p9_client_readlink(fid, &target); -	if (retval < 0) -		return retval; - -	strncpy(buffer, target, buflen); -	P9_DPRINTK(P9_DEBUG_VFS, "%s -> %s\n", dentry->d_name.name, buffer); - -	retval = strnlen(buffer, buflen); -	return retval; -} - -/** - * v9fs_vfs_follow_link_dotl - follow a symlink path - * @dentry: dentry for symlink - * @nd: nameidata - * - */ - -static void * -v9fs_vfs_follow_link_dotl(struct dentry *dentry, struct nameidata *nd) -{ -	int len = 0; -	char *link = __getname(); - -	P9_DPRINTK(P9_DEBUG_VFS, "%s n", dentry->d_name.name); - -	if (!link) -		link = ERR_PTR(-ENOMEM); -	else { -		len = v9fs_vfs_readlink_dotl(dentry, link, PATH_MAX); -		if (len < 0) { -			__putname(link); -			link = ERR_PTR(len); -		} else -			link[min(len, PATH_MAX-1)] = 0; -	} -	nd_set_link(nd, link); - -	return NULL; +	v9ses = v9fs_inode2v9ses(inode); +	st = p9_client_stat(fid); +	if (IS_ERR(st)) +		return PTR_ERR(st); +	/* +	 * Don't update inode if the file type is different +	 */ +	umode = p9mode2unixmode(v9ses, st, &rdev); +	if ((inode->i_mode & S_IFMT) != (umode & S_IFMT)) +		goto out; + +	spin_lock(&inode->i_lock); +	/* +	 * We don't want to refresh inode->i_size, +	 * because we may have cached data +	 */ +	i_size = inode->i_size; +	v9fs_stat2inode(st, inode, inode->i_sb); +	if (v9ses->cache == CACHE_LOOSE || v9ses->cache == CACHE_FSCACHE) +		inode->i_size = i_size; +	spin_unlock(&inode->i_lock); +out: +	p9stat_free(st); +	kfree(st); +	return 0;  }  static const struct inode_operations v9fs_dir_inode_operations_dotu = {  	.create = v9fs_vfs_create,  	.lookup = v9fs_vfs_lookup, +	.atomic_open = v9fs_vfs_atomic_open,  	.symlink = v9fs_vfs_symlink,  	.link = v9fs_vfs_link,  	.unlink = v9fs_vfs_unlink, @@ -2068,28 +1509,10 @@ static const struct inode_operations v9fs_dir_inode_operations_dotu = {  	.setattr = v9fs_vfs_setattr,  }; -static const struct inode_operations v9fs_dir_inode_operations_dotl = { -	.create = v9fs_vfs_create_dotl, -	.lookup = v9fs_vfs_lookup, -	.link = v9fs_vfs_link_dotl, -	.symlink = v9fs_vfs_symlink_dotl, -	.unlink = v9fs_vfs_unlink, -	.mkdir = v9fs_vfs_mkdir_dotl, -	.rmdir = v9fs_vfs_rmdir, -	.mknod = v9fs_vfs_mknod_dotl, -	.rename = v9fs_vfs_rename, -	.getattr = v9fs_vfs_getattr_dotl, -	.setattr = v9fs_vfs_setattr_dotl, -	.setxattr = generic_setxattr, -	.getxattr = generic_getxattr, -	.removexattr = generic_removexattr, -	.listxattr = v9fs_listxattr, -	.check_acl = v9fs_check_acl, -}; -  static const struct inode_operations v9fs_dir_inode_operations = {  	.create = v9fs_vfs_create,  	.lookup = v9fs_vfs_lookup, +	.atomic_open = v9fs_vfs_atomic_open,  	.unlink = v9fs_vfs_unlink,  	.mkdir = v9fs_vfs_mkdir,  	.rmdir = v9fs_vfs_rmdir, @@ -2104,16 +1527,6 @@ static const struct inode_operations v9fs_file_inode_operations = {  	.setattr = v9fs_vfs_setattr,  }; -static const struct inode_operations v9fs_file_inode_operations_dotl = { -	.getattr = v9fs_vfs_getattr_dotl, -	.setattr = v9fs_vfs_setattr_dotl, -	.setxattr = generic_setxattr, -	.getxattr = generic_getxattr, -	.removexattr = generic_removexattr, -	.listxattr = v9fs_listxattr, -	.check_acl = v9fs_check_acl, -}; -  static const struct inode_operations v9fs_symlink_inode_operations = {  	.readlink = generic_readlink,  	.follow_link = v9fs_vfs_follow_link, @@ -2122,14 +1535,3 @@ static const struct inode_operations v9fs_symlink_inode_operations = {  	.setattr = v9fs_vfs_setattr,  }; -static const struct inode_operations v9fs_symlink_inode_operations_dotl = { -	.readlink = v9fs_vfs_readlink_dotl, -	.follow_link = v9fs_vfs_follow_link_dotl, -	.put_link = v9fs_vfs_put_link, -	.getattr = v9fs_vfs_getattr_dotl, -	.setattr = v9fs_vfs_setattr_dotl, -	.setxattr = generic_setxattr, -	.getxattr = generic_getxattr, -	.removexattr = generic_removexattr, -	.listxattr = v9fs_listxattr, -};  | 
