diff options
| -rw-r--r-- | fs/9p/fid.c | 114 | ||||
| -rw-r--r-- | fs/9p/v9fs.c | 1 | ||||
| -rw-r--r-- | fs/9p/v9fs.h | 1 | ||||
| -rw-r--r-- | fs/9p/vfs_inode.c | 13 | ||||
| -rw-r--r-- | fs/9p/vfs_super.c | 1 | 
5 files changed, 91 insertions, 39 deletions
| diff --git a/fs/9p/fid.c b/fs/9p/fid.c index 5d6cfcbf73e7..358563689064 100644 --- a/fs/9p/fid.c +++ b/fs/9p/fid.c | |||
| @@ -97,6 +97,34 @@ static struct p9_fid *v9fs_fid_find(struct dentry *dentry, u32 uid, int any) | |||
| 97 | return ret; | 97 | return ret; | 
| 98 | } | 98 | } | 
| 99 | 99 | ||
| 100 | /* | ||
| 101 | * We need to hold v9ses->rename_sem as long as we hold references | ||
| 102 | * to returned path array. Array element contain pointers to | ||
| 103 | * dentry names. | ||
| 104 | */ | ||
| 105 | static int build_path_from_dentry(struct v9fs_session_info *v9ses, | ||
| 106 | struct dentry *dentry, char ***names) | ||
| 107 | { | ||
| 108 | int n = 0, i; | ||
| 109 | char **wnames; | ||
| 110 | struct dentry *ds; | ||
| 111 | |||
| 112 | for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) | ||
| 113 | n++; | ||
| 114 | |||
| 115 | wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); | ||
| 116 | if (!wnames) | ||
| 117 | goto err_out; | ||
| 118 | |||
| 119 | for (ds = dentry, i = (n-1); i >= 0; i--, ds = ds->d_parent) | ||
| 120 | wnames[i] = (char *)ds->d_name.name; | ||
| 121 | |||
| 122 | *names = wnames; | ||
| 123 | return n; | ||
| 124 | err_out: | ||
| 125 | return -ENOMEM; | ||
| 126 | } | ||
| 127 | |||
| 100 | /** | 128 | /** | 
| 101 | * v9fs_fid_lookup - lookup for a fid, try to walk if not found | 129 | * v9fs_fid_lookup - lookup for a fid, try to walk if not found | 
| 102 | * @dentry: dentry to look for fid in | 130 | * @dentry: dentry to look for fid in | 
| @@ -112,7 +140,7 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
| 112 | int i, n, l, clone, any, access; | 140 | int i, n, l, clone, any, access; | 
| 113 | u32 uid; | 141 | u32 uid; | 
| 114 | struct p9_fid *fid, *old_fid = NULL; | 142 | struct p9_fid *fid, *old_fid = NULL; | 
| 115 | struct dentry *d, *ds; | 143 | struct dentry *ds; | 
| 116 | struct v9fs_session_info *v9ses; | 144 | struct v9fs_session_info *v9ses; | 
| 117 | char **wnames, *uname; | 145 | char **wnames, *uname; | 
| 118 | 146 | ||
| @@ -139,50 +167,62 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
| 139 | fid = v9fs_fid_find(dentry, uid, any); | 167 | fid = v9fs_fid_find(dentry, uid, any); | 
| 140 | if (fid) | 168 | if (fid) | 
| 141 | return fid; | 169 | return fid; | 
| 142 | 170 | /* | |
| 171 | * we don't have a matching fid. To do a TWALK we need | ||
| 172 | * parent fid. We need to prevent rename when we want to | ||
| 173 | * look at the parent. | ||
| 174 | */ | ||
| 175 | down_read(&v9ses->rename_sem); | ||
| 143 | ds = dentry->d_parent; | 176 | ds = dentry->d_parent; | 
| 144 | fid = v9fs_fid_find(ds, uid, any); | 177 | fid = v9fs_fid_find(ds, uid, any); | 
| 145 | if (!fid) { /* walk from the root */ | 178 | if (fid) { | 
| 146 | n = 0; | 179 | /* Found the parent fid do a lookup with that */ | 
| 147 | for (ds = dentry; !IS_ROOT(ds); ds = ds->d_parent) | 180 | fid = p9_client_walk(fid, 1, (char **)&dentry->d_name.name, 1); | 
| 148 | n++; | 181 | goto fid_out; | 
| 149 | 182 | } | |
| 150 | fid = v9fs_fid_find(ds, uid, any); | 183 | up_read(&v9ses->rename_sem); | 
| 151 | if (!fid) { /* the user is not attached to the fs yet */ | ||
| 152 | if (access == V9FS_ACCESS_SINGLE) | ||
| 153 | return ERR_PTR(-EPERM); | ||
| 154 | |||
| 155 | if (v9fs_proto_dotu(v9ses) || | ||
| 156 | v9fs_proto_dotl(v9ses)) | ||
| 157 | uname = NULL; | ||
| 158 | else | ||
| 159 | uname = v9ses->uname; | ||
| 160 | 184 | ||
| 161 | fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, | 185 | /* start from the root and try to do a lookup */ | 
| 162 | v9ses->aname); | 186 | fid = v9fs_fid_find(dentry->d_sb->s_root, uid, any); | 
| 187 | if (!fid) { | ||
| 188 | /* the user is not attached to the fs yet */ | ||
| 189 | if (access == V9FS_ACCESS_SINGLE) | ||
| 190 | return ERR_PTR(-EPERM); | ||
| 163 | 191 | ||
| 164 | if (IS_ERR(fid)) | 192 | if (v9fs_proto_dotu(v9ses) || v9fs_proto_dotl(v9ses)) | 
| 165 | return fid; | 193 | uname = NULL; | 
| 194 | else | ||
| 195 | uname = v9ses->uname; | ||
| 166 | 196 | ||
| 167 | v9fs_fid_add(ds, fid); | 197 | fid = p9_client_attach(v9ses->clnt, NULL, uname, uid, | 
| 168 | } | 198 | v9ses->aname); | 
| 169 | } else /* walk from the parent */ | 199 | if (IS_ERR(fid)) | 
| 170 | n = 1; | 200 | return fid; | 
| 171 | 201 | ||
| 172 | if (ds == dentry) | 202 | v9fs_fid_add(dentry->d_sb->s_root, fid); | 
| 203 | } | ||
| 204 | /* If we are root ourself just return that */ | ||
| 205 | if (dentry->d_sb->s_root == dentry) | ||
| 173 | return fid; | 206 | return fid; | 
| 174 | 207 | /* | |
| 175 | wnames = kmalloc(sizeof(char *) * n, GFP_KERNEL); | 208 | * Do a multipath walk with attached root. | 
| 176 | if (!wnames) | 209 | * When walking parent we need to make sure we | 
| 177 | return ERR_PTR(-ENOMEM); | 210 | * don't have a parallel rename happening | 
| 178 | 211 | */ | |
| 179 | for (d = dentry, i = (n-1); i >= 0; i--, d = d->d_parent) | 212 | down_read(&v9ses->rename_sem); | 
| 180 | wnames[i] = (char *) d->d_name.name; | 213 | n = build_path_from_dentry(v9ses, dentry, &wnames); | 
| 181 | 214 | if (n < 0) { | |
| 215 | fid = ERR_PTR(n); | ||
| 216 | goto err_out; | ||
| 217 | } | ||
| 182 | clone = 1; | 218 | clone = 1; | 
| 183 | i = 0; | 219 | i = 0; | 
| 184 | while (i < n) { | 220 | while (i < n) { | 
| 185 | l = min(n - i, P9_MAXWELEM); | 221 | l = min(n - i, P9_MAXWELEM); | 
| 222 | /* | ||
| 223 | * We need to hold rename lock when doing a multipath | ||
| 224 | * walk to ensure none of the patch component change | ||
| 225 | */ | ||
| 186 | fid = p9_client_walk(fid, l, &wnames[i], clone); | 226 | fid = p9_client_walk(fid, l, &wnames[i], clone); | 
| 187 | if (IS_ERR(fid)) { | 227 | if (IS_ERR(fid)) { | 
| 188 | if (old_fid) { | 228 | if (old_fid) { | 
| @@ -194,15 +234,17 @@ struct p9_fid *v9fs_fid_lookup(struct dentry *dentry) | |||
| 194 | p9_client_clunk(old_fid); | 234 | p9_client_clunk(old_fid); | 
| 195 | } | 235 | } | 
| 196 | kfree(wnames); | 236 | kfree(wnames); | 
| 197 | return fid; | 237 | goto err_out; | 
| 198 | } | 238 | } | 
| 199 | old_fid = fid; | 239 | old_fid = fid; | 
| 200 | i += l; | 240 | i += l; | 
| 201 | clone = 0; | 241 | clone = 0; | 
| 202 | } | 242 | } | 
| 203 | |||
| 204 | kfree(wnames); | 243 | kfree(wnames); | 
| 244 | fid_out: | ||
| 205 | v9fs_fid_add(dentry, fid); | 245 | v9fs_fid_add(dentry, fid); | 
| 246 | err_out: | ||
| 247 | up_read(&v9ses->rename_sem); | ||
| 206 | return fid; | 248 | return fid; | 
| 207 | } | 249 | } | 
| 208 | 250 | ||
| diff --git a/fs/9p/v9fs.c b/fs/9p/v9fs.c index 3c492011221c..38dc0e067599 100644 --- a/fs/9p/v9fs.c +++ b/fs/9p/v9fs.c | |||
| @@ -237,6 +237,7 @@ struct p9_fid *v9fs_session_init(struct v9fs_session_info *v9ses, | |||
| 237 | __putname(v9ses->uname); | 237 | __putname(v9ses->uname); | 
| 238 | return ERR_PTR(-ENOMEM); | 238 | return ERR_PTR(-ENOMEM); | 
| 239 | } | 239 | } | 
| 240 | init_rwsem(&v9ses->rename_sem); | ||
| 240 | 241 | ||
| 241 | rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY); | 242 | rc = bdi_setup_and_register(&v9ses->bdi, "9p", BDI_CAP_MAP_COPY); | 
| 242 | if (rc) { | 243 | if (rc) { | 
| diff --git a/fs/9p/v9fs.h b/fs/9p/v9fs.h index bec4d0bcb458..4c963c9fc41f 100644 --- a/fs/9p/v9fs.h +++ b/fs/9p/v9fs.h | |||
| @@ -104,6 +104,7 @@ struct v9fs_session_info { | |||
| 104 | struct p9_client *clnt; /* 9p client */ | 104 | struct p9_client *clnt; /* 9p client */ | 
| 105 | struct list_head slist; /* list of sessions registered with v9fs */ | 105 | struct list_head slist; /* list of sessions registered with v9fs */ | 
| 106 | struct backing_dev_info bdi; | 106 | struct backing_dev_info bdi; | 
| 107 | struct rw_semaphore rename_sem; | ||
| 107 | }; | 108 | }; | 
| 108 | 109 | ||
| 109 | struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, | 110 | struct p9_fid *v9fs_session_init(struct v9fs_session_info *, const char *, | 
| diff --git a/fs/9p/vfs_inode.c b/fs/9p/vfs_inode.c index 39352ef954dc..75c261fdc7b4 100644 --- a/fs/9p/vfs_inode.c +++ b/fs/9p/vfs_inode.c | |||
| @@ -948,6 +948,7 @@ static struct dentry *v9fs_vfs_lookup(struct inode *dir, struct dentry *dentry, | |||
| 948 | 948 | ||
| 949 | sb = dir->i_sb; | 949 | sb = dir->i_sb; | 
| 950 | v9ses = v9fs_inode2v9ses(dir); | 950 | v9ses = v9fs_inode2v9ses(dir); | 
| 951 | /* We can walk d_parent because we hold the dir->i_mutex */ | ||
| 951 | dfid = v9fs_fid_lookup(dentry->d_parent); | 952 | dfid = v9fs_fid_lookup(dentry->d_parent); | 
| 952 | if (IS_ERR(dfid)) | 953 | if (IS_ERR(dfid)) | 
| 953 | return ERR_CAST(dfid); | 954 | return ERR_CAST(dfid); | 
| @@ -1055,27 +1056,33 @@ v9fs_vfs_rename(struct inode *old_dir, struct dentry *old_dentry, | |||
| 1055 | goto clunk_olddir; | 1056 | goto clunk_olddir; | 
| 1056 | } | 1057 | } | 
| 1057 | 1058 | ||
| 1059 | down_write(&v9ses->rename_sem); | ||
| 1058 | if (v9fs_proto_dotl(v9ses)) { | 1060 | if (v9fs_proto_dotl(v9ses)) { | 
| 1059 | retval = p9_client_rename(oldfid, newdirfid, | 1061 | retval = p9_client_rename(oldfid, newdirfid, | 
| 1060 | (char *) new_dentry->d_name.name); | 1062 | (char *) new_dentry->d_name.name); | 
| 1061 | if (retval != -ENOSYS) | 1063 | if (retval != -ENOSYS) | 
| 1062 | goto clunk_newdir; | 1064 | goto clunk_newdir; | 
| 1063 | } | 1065 | } | 
| 1066 | if (old_dentry->d_parent != new_dentry->d_parent) { | ||
| 1067 | /* | ||
| 1068 | * 9P .u can only handle file rename in the same directory | ||
| 1069 | */ | ||
| 1064 | 1070 | ||
| 1065 | /* 9P can only handle file rename in the same directory */ | ||
| 1066 | if (memcmp(&olddirfid->qid, &newdirfid->qid, sizeof(newdirfid->qid))) { | ||
| 1067 | P9_DPRINTK(P9_DEBUG_ERROR, | 1071 | P9_DPRINTK(P9_DEBUG_ERROR, | 
| 1068 | "old dir and new dir are different\n"); | 1072 | "old dir and new dir are different\n"); | 
| 1069 | retval = -EXDEV; | 1073 | retval = -EXDEV; | 
| 1070 | goto clunk_newdir; | 1074 | goto clunk_newdir; | 
| 1071 | } | 1075 | } | 
| 1072 | |||
| 1073 | v9fs_blank_wstat(&wstat); | 1076 | v9fs_blank_wstat(&wstat); | 
| 1074 | wstat.muid = v9ses->uname; | 1077 | wstat.muid = v9ses->uname; | 
| 1075 | wstat.name = (char *) new_dentry->d_name.name; | 1078 | wstat.name = (char *) new_dentry->d_name.name; | 
| 1076 | retval = p9_client_wstat(oldfid, &wstat); | 1079 | retval = p9_client_wstat(oldfid, &wstat); | 
| 1077 | 1080 | ||
| 1078 | clunk_newdir: | 1081 | clunk_newdir: | 
| 1082 | if (!retval) | ||
| 1083 | /* successful rename */ | ||
| 1084 | d_move(old_dentry, new_dentry); | ||
| 1085 | up_write(&v9ses->rename_sem); | ||
| 1079 | p9_client_clunk(newdirfid); | 1086 | p9_client_clunk(newdirfid); | 
| 1080 | 1087 | ||
| 1081 | clunk_olddir: | 1088 | clunk_olddir: | 
| diff --git a/fs/9p/vfs_super.c b/fs/9p/vfs_super.c index 0a2e2030c844..4b9ede0b41b7 100644 --- a/fs/9p/vfs_super.c +++ b/fs/9p/vfs_super.c | |||
| @@ -287,4 +287,5 @@ struct file_system_type v9fs_fs_type = { | |||
| 287 | .get_sb = v9fs_get_sb, | 287 | .get_sb = v9fs_get_sb, | 
| 288 | .kill_sb = v9fs_kill_super, | 288 | .kill_sb = v9fs_kill_super, | 
| 289 | .owner = THIS_MODULE, | 289 | .owner = THIS_MODULE, | 
| 290 | .fs_flags = FS_RENAME_DOES_D_MOVE, | ||
| 290 | }; | 291 | }; | 
