|
@@ -95,15 +95,18 @@ static int unixmode2p9mode(struct v9fs_session_info *v9ses, int mode)
|
|
|
/**
|
|
|
* 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 int p9mode2unixmode(struct v9fs_session_info *v9ses,
|
|
|
+ struct p9_wstat *stat, dev_t *rdev)
|
|
|
{
|
|
|
int res;
|
|
|
+ int mode = stat->mode;
|
|
|
|
|
|
- res = mode & 0777;
|
|
|
+ res = mode & S_IALLUGO;
|
|
|
+ *rdev = 0;
|
|
|
|
|
|
if ((mode & P9_DMDIR) == P9_DMDIR)
|
|
|
res |= S_IFDIR;
|
|
@@ -116,9 +119,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
|
|
|
+ && (v9ses->nodev == 0)) {
|
|
|
+ char type = 0, ext[32];
|
|
|
+ int major = -1, minor = -1;
|
|
|
+
|
|
|
+ strncpy(ext, stat->extension, sizeof(ext));
|
|
|
+ sscanf(ext, "%c %u %u", &type, &major, &minor);
|
|
|
+ switch (type) {
|
|
|
+ case 'c':
|
|
|
+ res |= S_IFCHR;
|
|
|
+ break;
|
|
|
+ case 'b':
|
|
|
+ res |= S_IFBLK;
|
|
|
+ break;
|
|
|
+ default:
|
|
|
+ P9_DPRINTK(P9_DEBUG_ERROR,
|
|
|
+ "Unknown special type %c %s\n", type,
|
|
|
+ stat->extension);
|
|
|
+ };
|
|
|
+ *rdev = MKDEV(major, minor);
|
|
|
+ } else
|
|
|
res |= S_IFREG;
|
|
|
|
|
|
if (v9fs_proto_dotu(v9ses)) {
|
|
@@ -131,7 +151,6 @@ static int p9mode2unixmode(struct v9fs_session_info *v9ses, int mode)
|
|
|
if ((mode & P9_DMSETVTX) == P9_DMSETVTX)
|
|
|
res |= S_ISVTX;
|
|
|
}
|
|
|
-
|
|
|
return res;
|
|
|
}
|
|
|
|
|
@@ -242,13 +261,13 @@ void v9fs_destroy_inode(struct inode *inode)
|
|
|
}
|
|
|
|
|
|
int v9fs_init_inode(struct v9fs_session_info *v9ses,
|
|
|
- struct inode *inode, int mode)
|
|
|
+ struct inode *inode, int mode, dev_t rdev)
|
|
|
{
|
|
|
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;
|
|
|
|
|
@@ -335,7 +354,7 @@ error:
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
-struct inode *v9fs_get_inode(struct super_block *sb, int mode)
|
|
|
+struct inode *v9fs_get_inode(struct super_block *sb, int mode, dev_t rdev)
|
|
|
{
|
|
|
int err;
|
|
|
struct inode *inode;
|
|
@@ -348,7 +367,7 @@ struct inode *v9fs_get_inode(struct super_block *sb, int mode)
|
|
|
P9_EPRINTK(KERN_WARNING, "Problem allocating inode\n");
|
|
|
return ERR_PTR(-ENOMEM);
|
|
|
}
|
|
|
- err = v9fs_init_inode(v9ses, inode, mode);
|
|
|
+ err = v9fs_init_inode(v9ses, inode, mode, rdev);
|
|
|
if (err) {
|
|
|
iput(inode);
|
|
|
return ERR_PTR(err);
|
|
@@ -435,11 +454,12 @@ void v9fs_evict_inode(struct inode *inode)
|
|
|
static int v9fs_test_inode(struct inode *inode, void *data)
|
|
|
{
|
|
|
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->mode);
|
|
|
+ umode = p9mode2unixmode(v9ses, st, &rdev);
|
|
|
/* don't match inode of different type */
|
|
|
if ((inode->i_mode & S_IFMT) != (umode & S_IFMT))
|
|
|
return 0;
|
|
@@ -473,6 +493,7 @@ static struct inode *v9fs_qid_iget(struct super_block *sb,
|
|
|
struct p9_wstat *st,
|
|
|
int new)
|
|
|
{
|
|
|
+ dev_t rdev;
|
|
|
int retval, umode;
|
|
|
unsigned long i_ino;
|
|
|
struct inode *inode;
|
|
@@ -496,8 +517,8 @@ static struct inode *v9fs_qid_iget(struct super_block *sb,
|
|
|
* later.
|
|
|
*/
|
|
|
inode->i_ino = i_ino;
|
|
|
- umode = p9mode2unixmode(v9ses, st->mode);
|
|
|
- retval = v9fs_init_inode(v9ses, inode, umode);
|
|
|
+ umode = p9mode2unixmode(v9ses, st, &rdev);
|
|
|
+ retval = v9fs_init_inode(v9ses, inode, umode, rdev);
|
|
|
if (retval)
|
|
|
goto error;
|
|
|
|
|
@@ -531,6 +552,19 @@ v9fs_inode_from_fid(struct v9fs_session_info *v9ses, struct p9_fid *fid,
|
|
|
return inode;
|
|
|
}
|
|
|
|
|
|
+/**
|
|
|
+ * v9fs_at_to_dotl_flags- convert Linux specific AT flags to
|
|
|
+ * plan 9 AT flag.
|
|
|
+ * @flags: flags to convert
|
|
|
+ */
|
|
|
+static int v9fs_at_to_dotl_flags(int flags)
|
|
|
+{
|
|
|
+ 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
|
|
@@ -558,7 +592,8 @@ static int v9fs_remove(struct inode *dir, struct dentry *dentry, int flags)
|
|
|
return retval;
|
|
|
}
|
|
|
if (v9fs_proto_dotl(v9ses))
|
|
|
- retval = p9_client_unlinkat(dfid, dentry->d_name.name, flags);
|
|
|
+ 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);
|
|
@@ -645,13 +680,11 @@ v9fs_create(struct v9fs_session_info *v9ses, struct inode *dir,
|
|
|
P9_DPRINTK(P9_DEBUG_VFS, "inode creation failed %d\n", err);
|
|
|
goto error;
|
|
|
}
|
|
|
- d_instantiate(dentry, inode);
|
|
|
err = v9fs_fid_add(dentry, fid);
|
|
|
if (err < 0)
|
|
|
goto error;
|
|
|
-
|
|
|
+ d_instantiate(dentry, inode);
|
|
|
return ofid;
|
|
|
-
|
|
|
error:
|
|
|
if (ofid)
|
|
|
p9_client_clunk(ofid);
|
|
@@ -792,6 +825,7 @@ static int v9fs_vfs_mkdir(struct inode *dir, struct dentry *dentry, int mode)
|
|
|
struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
|
|
struct nameidata *nameidata)
|
|
|
{
|
|
|
+ struct dentry *res;
|
|
|
struct super_block *sb;
|
|
|
struct v9fs_session_info *v9ses;
|
|
|
struct p9_fid *dfid, *fid;
|
|
@@ -823,22 +857,35 @@ struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry,
|
|
|
|
|
|
return ERR_PTR(result);
|
|
|
}
|
|
|
-
|
|
|
- inode = v9fs_get_inode_from_fid(v9ses, fid, dir->i_sb);
|
|
|
+ /*
|
|
|
+ * 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)
|
|
|
+ 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)) {
|
|
|
result = PTR_ERR(inode);
|
|
|
inode = NULL;
|
|
|
goto error;
|
|
|
}
|
|
|
-
|
|
|
result = v9fs_fid_add(dentry, fid);
|
|
|
if (result < 0)
|
|
|
goto error_iput;
|
|
|
-
|
|
|
inst_out:
|
|
|
- d_add(dentry, inode);
|
|
|
- return NULL;
|
|
|
-
|
|
|
+ /*
|
|
|
+ * 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 (!IS_ERR(res))
|
|
|
+ return res;
|
|
|
+ result = PTR_ERR(res);
|
|
|
error_iput:
|
|
|
iput(inode);
|
|
|
error:
|
|
@@ -1002,7 +1049,7 @@ 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);
|
|
|
+ generic_fillattr(dentry->d_inode, stat);
|
|
|
|
|
|
p9stat_free(st);
|
|
|
kfree(st);
|
|
@@ -1086,6 +1133,7 @@ void
|
|
|
v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
|
|
struct super_block *sb)
|
|
|
{
|
|
|
+ mode_t mode;
|
|
|
char ext[32];
|
|
|
char tag_name[14];
|
|
|
unsigned int i_nlink;
|
|
@@ -1121,31 +1169,9 @@ v9fs_stat2inode(struct p9_wstat *stat, struct inode *inode,
|
|
|
inode->i_nlink = 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 = stat->mode & S_IALLUGO;
|
|
|
+ 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 ... */
|
|
@@ -1411,6 +1437,8 @@ v9fs_vfs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
|
|
|
|
|
|
int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
|
|
|
{
|
|
|
+ int umode;
|
|
|
+ dev_t rdev;
|
|
|
loff_t i_size;
|
|
|
struct p9_wstat *st;
|
|
|
struct v9fs_session_info *v9ses;
|
|
@@ -1419,6 +1447,12 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *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);
|
|
|
/*
|
|
@@ -1430,6 +1464,7 @@ int v9fs_refresh_inode(struct p9_fid *fid, struct inode *inode)
|
|
|
if (v9ses->cache)
|
|
|
inode->i_size = i_size;
|
|
|
spin_unlock(&inode->i_lock);
|
|
|
+out:
|
|
|
p9stat_free(st);
|
|
|
kfree(st);
|
|
|
return 0;
|