diff options
Diffstat (limited to 'fs/nfs/namespace.c')
-rw-r--r-- | fs/nfs/namespace.c | 261 |
1 files changed, 183 insertions, 78 deletions
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index db6aa3673cf3..1f063bacd285 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
@@ -15,6 +15,7 @@ | |||
15 | #include <linux/string.h> | 15 | #include <linux/string.h> |
16 | #include <linux/sunrpc/clnt.h> | 16 | #include <linux/sunrpc/clnt.h> |
17 | #include <linux/vfs.h> | 17 | #include <linux/vfs.h> |
18 | #include <linux/sunrpc/gss_api.h> | ||
18 | #include "internal.h" | 19 | #include "internal.h" |
19 | 20 | ||
20 | #define NFSDBG_FACILITY NFSDBG_VFS | 21 | #define NFSDBG_FACILITY NFSDBG_VFS |
@@ -25,37 +26,43 @@ static LIST_HEAD(nfs_automount_list); | |||
25 | static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); | 26 | static DECLARE_DELAYED_WORK(nfs_automount_task, nfs_expire_automounts); |
26 | int nfs_mountpoint_expiry_timeout = 500 * HZ; | 27 | int nfs_mountpoint_expiry_timeout = 500 * HZ; |
27 | 28 | ||
28 | static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, | 29 | static struct vfsmount *nfs_do_submount(struct dentry *dentry, |
29 | const struct dentry *dentry, | ||
30 | struct nfs_fh *fh, | 30 | struct nfs_fh *fh, |
31 | struct nfs_fattr *fattr); | 31 | struct nfs_fattr *fattr, |
32 | rpc_authflavor_t authflavor); | ||
32 | 33 | ||
33 | /* | 34 | /* |
34 | * nfs_path - reconstruct the path given an arbitrary dentry | 35 | * nfs_path - reconstruct the path given an arbitrary dentry |
35 | * @base - arbitrary string to prepend to the path | 36 | * @base - used to return pointer to the end of devname part of path |
36 | * @droot - pointer to root dentry for mountpoint | ||
37 | * @dentry - pointer to dentry | 37 | * @dentry - pointer to dentry |
38 | * @buffer - result buffer | 38 | * @buffer - result buffer |
39 | * @buflen - length of buffer | 39 | * @buflen - length of buffer |
40 | * | 40 | * |
41 | * Helper function for constructing the path from the | 41 | * Helper function for constructing the server pathname |
42 | * root dentry to an arbitrary hashed dentry. | 42 | * by arbitrary hashed dentry. |
43 | * | 43 | * |
44 | * This is mainly for use in figuring out the path on the | 44 | * This is mainly for use in figuring out the path on the |
45 | * server side when automounting on top of an existing partition. | 45 | * server side when automounting on top of an existing partition |
46 | * and in generating /proc/mounts and friends. | ||
46 | */ | 47 | */ |
47 | char *nfs_path(const char *base, | 48 | char *nfs_path(char **p, struct dentry *dentry, char *buffer, ssize_t buflen) |
48 | const struct dentry *droot, | ||
49 | const struct dentry *dentry, | ||
50 | char *buffer, ssize_t buflen) | ||
51 | { | 49 | { |
52 | char *end = buffer+buflen; | 50 | char *end; |
53 | int namelen; | 51 | int namelen; |
52 | unsigned seq; | ||
53 | const char *base; | ||
54 | 54 | ||
55 | rename_retry: | ||
56 | end = buffer+buflen; | ||
55 | *--end = '\0'; | 57 | *--end = '\0'; |
56 | buflen--; | 58 | buflen--; |
57 | spin_lock(&dcache_lock); | 59 | |
58 | while (!IS_ROOT(dentry) && dentry != droot) { | 60 | seq = read_seqbegin(&rename_lock); |
61 | rcu_read_lock(); | ||
62 | while (1) { | ||
63 | spin_lock(&dentry->d_lock); | ||
64 | if (IS_ROOT(dentry)) | ||
65 | break; | ||
59 | namelen = dentry->d_name.len; | 66 | namelen = dentry->d_name.len; |
60 | buflen -= namelen + 1; | 67 | buflen -= namelen + 1; |
61 | if (buflen < 0) | 68 | if (buflen < 0) |
@@ -63,34 +70,150 @@ char *nfs_path(const char *base, | |||
63 | end -= namelen; | 70 | end -= namelen; |
64 | memcpy(end, dentry->d_name.name, namelen); | 71 | memcpy(end, dentry->d_name.name, namelen); |
65 | *--end = '/'; | 72 | *--end = '/'; |
73 | spin_unlock(&dentry->d_lock); | ||
66 | dentry = dentry->d_parent; | 74 | dentry = dentry->d_parent; |
67 | } | 75 | } |
68 | spin_unlock(&dcache_lock); | 76 | if (read_seqretry(&rename_lock, seq)) { |
77 | spin_unlock(&dentry->d_lock); | ||
78 | rcu_read_unlock(); | ||
79 | goto rename_retry; | ||
80 | } | ||
69 | if (*end != '/') { | 81 | if (*end != '/') { |
70 | if (--buflen < 0) | 82 | if (--buflen < 0) { |
83 | spin_unlock(&dentry->d_lock); | ||
84 | rcu_read_unlock(); | ||
71 | goto Elong; | 85 | goto Elong; |
86 | } | ||
72 | *--end = '/'; | 87 | *--end = '/'; |
73 | } | 88 | } |
89 | *p = end; | ||
90 | base = dentry->d_fsdata; | ||
91 | if (!base) { | ||
92 | spin_unlock(&dentry->d_lock); | ||
93 | rcu_read_unlock(); | ||
94 | WARN_ON(1); | ||
95 | return end; | ||
96 | } | ||
74 | namelen = strlen(base); | 97 | namelen = strlen(base); |
75 | /* Strip off excess slashes in base string */ | 98 | /* Strip off excess slashes in base string */ |
76 | while (namelen > 0 && base[namelen - 1] == '/') | 99 | while (namelen > 0 && base[namelen - 1] == '/') |
77 | namelen--; | 100 | namelen--; |
78 | buflen -= namelen; | 101 | buflen -= namelen; |
79 | if (buflen < 0) | 102 | if (buflen < 0) { |
103 | spin_unlock(&dentry->d_lock); | ||
104 | rcu_read_unlock(); | ||
80 | goto Elong; | 105 | goto Elong; |
106 | } | ||
81 | end -= namelen; | 107 | end -= namelen; |
82 | memcpy(end, base, namelen); | 108 | memcpy(end, base, namelen); |
109 | spin_unlock(&dentry->d_lock); | ||
110 | rcu_read_unlock(); | ||
83 | return end; | 111 | return end; |
84 | Elong_unlock: | 112 | Elong_unlock: |
85 | spin_unlock(&dcache_lock); | 113 | spin_unlock(&dentry->d_lock); |
114 | rcu_read_unlock(); | ||
115 | if (read_seqretry(&rename_lock, seq)) | ||
116 | goto rename_retry; | ||
86 | Elong: | 117 | Elong: |
87 | return ERR_PTR(-ENAMETOOLONG); | 118 | return ERR_PTR(-ENAMETOOLONG); |
88 | } | 119 | } |
89 | 120 | ||
121 | #ifdef CONFIG_NFS_V4 | ||
122 | static rpc_authflavor_t nfs_find_best_sec(struct nfs4_secinfo_flavors *flavors) | ||
123 | { | ||
124 | struct gss_api_mech *mech; | ||
125 | struct xdr_netobj oid; | ||
126 | int i; | ||
127 | rpc_authflavor_t pseudoflavor = RPC_AUTH_UNIX; | ||
128 | |||
129 | for (i = 0; i < flavors->num_flavors; i++) { | ||
130 | struct nfs4_secinfo_flavor *flavor; | ||
131 | flavor = &flavors->flavors[i]; | ||
132 | |||
133 | if (flavor->flavor == RPC_AUTH_NULL || flavor->flavor == RPC_AUTH_UNIX) { | ||
134 | pseudoflavor = flavor->flavor; | ||
135 | break; | ||
136 | } else if (flavor->flavor == RPC_AUTH_GSS) { | ||
137 | oid.len = flavor->gss.sec_oid4.len; | ||
138 | oid.data = flavor->gss.sec_oid4.data; | ||
139 | mech = gss_mech_get_by_OID(&oid); | ||
140 | if (!mech) | ||
141 | continue; | ||
142 | pseudoflavor = gss_svc_to_pseudoflavor(mech, flavor->gss.service); | ||
143 | gss_mech_put(mech); | ||
144 | break; | ||
145 | } | ||
146 | } | ||
147 | |||
148 | return pseudoflavor; | ||
149 | } | ||
150 | |||
151 | static int nfs_negotiate_security(const struct dentry *parent, | ||
152 | const struct dentry *dentry, | ||
153 | rpc_authflavor_t *flavor) | ||
154 | { | ||
155 | struct page *page; | ||
156 | struct nfs4_secinfo_flavors *flavors; | ||
157 | int (*secinfo)(struct inode *, const struct qstr *, struct nfs4_secinfo_flavors *); | ||
158 | int ret = -EPERM; | ||
159 | |||
160 | secinfo = NFS_PROTO(parent->d_inode)->secinfo; | ||
161 | if (secinfo != NULL) { | ||
162 | page = alloc_page(GFP_KERNEL); | ||
163 | if (!page) { | ||
164 | ret = -ENOMEM; | ||
165 | goto out; | ||
166 | } | ||
167 | flavors = page_address(page); | ||
168 | ret = secinfo(parent->d_inode, &dentry->d_name, flavors); | ||
169 | *flavor = nfs_find_best_sec(flavors); | ||
170 | put_page(page); | ||
171 | } | ||
172 | |||
173 | out: | ||
174 | return ret; | ||
175 | } | ||
176 | |||
177 | static int nfs_lookup_with_sec(struct nfs_server *server, struct dentry *parent, | ||
178 | struct dentry *dentry, struct path *path, | ||
179 | struct nfs_fh *fh, struct nfs_fattr *fattr, | ||
180 | rpc_authflavor_t *flavor) | ||
181 | { | ||
182 | struct rpc_clnt *clone; | ||
183 | struct rpc_auth *auth; | ||
184 | int err; | ||
185 | |||
186 | err = nfs_negotiate_security(parent, path->dentry, flavor); | ||
187 | if (err < 0) | ||
188 | goto out; | ||
189 | clone = rpc_clone_client(server->client); | ||
190 | auth = rpcauth_create(*flavor, clone); | ||
191 | if (!auth) { | ||
192 | err = -EIO; | ||
193 | goto out_shutdown; | ||
194 | } | ||
195 | err = server->nfs_client->rpc_ops->lookup(clone, parent->d_inode, | ||
196 | &path->dentry->d_name, | ||
197 | fh, fattr); | ||
198 | out_shutdown: | ||
199 | rpc_shutdown_client(clone); | ||
200 | out: | ||
201 | return err; | ||
202 | } | ||
203 | #else /* CONFIG_NFS_V4 */ | ||
204 | static inline int nfs_lookup_with_sec(struct nfs_server *server, | ||
205 | struct dentry *parent, struct dentry *dentry, | ||
206 | struct path *path, struct nfs_fh *fh, | ||
207 | struct nfs_fattr *fattr, | ||
208 | rpc_authflavor_t *flavor) | ||
209 | { | ||
210 | return -EPERM; | ||
211 | } | ||
212 | #endif /* CONFIG_NFS_V4 */ | ||
213 | |||
90 | /* | 214 | /* |
91 | * nfs_follow_mountpoint - handle crossing a mountpoint on the server | 215 | * nfs_d_automount - Handle crossing a mountpoint on the server |
92 | * @dentry - dentry of mountpoint | 216 | * @path - The mountpoint |
93 | * @nd - nameidata info | ||
94 | * | 217 | * |
95 | * When we encounter a mountpoint on the server, we want to set up | 218 | * When we encounter a mountpoint on the server, we want to set up |
96 | * a mountpoint on the client too, to prevent inode numbers from | 219 | * a mountpoint on the client too, to prevent inode numbers from |
@@ -100,87 +223,68 @@ Elong: | |||
100 | * situation, and that different filesystems may want to use | 223 | * situation, and that different filesystems may want to use |
101 | * different security flavours. | 224 | * different security flavours. |
102 | */ | 225 | */ |
103 | static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | 226 | struct vfsmount *nfs_d_automount(struct path *path) |
104 | { | 227 | { |
105 | struct vfsmount *mnt; | 228 | struct vfsmount *mnt; |
106 | struct nfs_server *server = NFS_SERVER(dentry->d_inode); | 229 | struct nfs_server *server = NFS_SERVER(path->dentry->d_inode); |
107 | struct dentry *parent; | 230 | struct dentry *parent; |
108 | struct nfs_fh *fh = NULL; | 231 | struct nfs_fh *fh = NULL; |
109 | struct nfs_fattr *fattr = NULL; | 232 | struct nfs_fattr *fattr = NULL; |
110 | int err; | 233 | int err; |
234 | rpc_authflavor_t flavor = RPC_AUTH_UNIX; | ||
111 | 235 | ||
112 | dprintk("--> nfs_follow_mountpoint()\n"); | 236 | dprintk("--> nfs_d_automount()\n"); |
113 | 237 | ||
114 | err = -ESTALE; | 238 | mnt = ERR_PTR(-ESTALE); |
115 | if (IS_ROOT(dentry)) | 239 | if (IS_ROOT(path->dentry)) |
116 | goto out_err; | 240 | goto out_nofree; |
117 | 241 | ||
118 | err = -ENOMEM; | 242 | mnt = ERR_PTR(-ENOMEM); |
119 | fh = nfs_alloc_fhandle(); | 243 | fh = nfs_alloc_fhandle(); |
120 | fattr = nfs_alloc_fattr(); | 244 | fattr = nfs_alloc_fattr(); |
121 | if (fh == NULL || fattr == NULL) | 245 | if (fh == NULL || fattr == NULL) |
122 | goto out_err; | 246 | goto out; |
123 | 247 | ||
124 | dprintk("%s: enter\n", __func__); | 248 | dprintk("%s: enter\n", __func__); |
125 | dput(nd->path.dentry); | ||
126 | nd->path.dentry = dget(dentry); | ||
127 | 249 | ||
128 | /* Look it up again */ | 250 | /* Look it up again to get its attributes */ |
129 | parent = dget_parent(nd->path.dentry); | 251 | parent = dget_parent(path->dentry); |
130 | err = server->nfs_client->rpc_ops->lookup(parent->d_inode, | 252 | err = server->nfs_client->rpc_ops->lookup(server->client, parent->d_inode, |
131 | &nd->path.dentry->d_name, | 253 | &path->dentry->d_name, |
132 | fh, fattr); | 254 | fh, fattr); |
255 | if (err == -EPERM && NFS_PROTO(parent->d_inode)->secinfo != NULL) | ||
256 | err = nfs_lookup_with_sec(server, parent, path->dentry, path, fh, fattr, &flavor); | ||
133 | dput(parent); | 257 | dput(parent); |
134 | if (err != 0) | 258 | if (err != 0) { |
135 | goto out_err; | 259 | mnt = ERR_PTR(err); |
260 | goto out; | ||
261 | } | ||
136 | 262 | ||
137 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) | 263 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) |
138 | mnt = nfs_do_refmount(nd->path.mnt, nd->path.dentry); | 264 | mnt = nfs_do_refmount(path->dentry); |
139 | else | 265 | else |
140 | mnt = nfs_do_submount(nd->path.mnt, nd->path.dentry, fh, | 266 | mnt = nfs_do_submount(path->dentry, fh, fattr, flavor); |
141 | fattr); | ||
142 | err = PTR_ERR(mnt); | ||
143 | if (IS_ERR(mnt)) | 267 | if (IS_ERR(mnt)) |
144 | goto out_err; | 268 | goto out; |
145 | 269 | ||
146 | mntget(mnt); | 270 | dprintk("%s: done, success\n", __func__); |
147 | err = do_add_mount(mnt, &nd->path, nd->path.mnt->mnt_flags|MNT_SHRINKABLE, | 271 | mntget(mnt); /* prevent immediate expiration */ |
148 | &nfs_automount_list); | 272 | mnt_set_expiry(mnt, &nfs_automount_list); |
149 | if (err < 0) { | ||
150 | mntput(mnt); | ||
151 | if (err == -EBUSY) | ||
152 | goto out_follow; | ||
153 | goto out_err; | ||
154 | } | ||
155 | path_put(&nd->path); | ||
156 | nd->path.mnt = mnt; | ||
157 | nd->path.dentry = dget(mnt->mnt_root); | ||
158 | schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); | 273 | schedule_delayed_work(&nfs_automount_task, nfs_mountpoint_expiry_timeout); |
274 | |||
159 | out: | 275 | out: |
160 | nfs_free_fattr(fattr); | 276 | nfs_free_fattr(fattr); |
161 | nfs_free_fhandle(fh); | 277 | nfs_free_fhandle(fh); |
162 | dprintk("%s: done, returned %d\n", __func__, err); | 278 | out_nofree: |
163 | 279 | dprintk("<-- nfs_follow_mountpoint() = %p\n", mnt); | |
164 | dprintk("<-- nfs_follow_mountpoint() = %d\n", err); | 280 | return mnt; |
165 | return ERR_PTR(err); | ||
166 | out_err: | ||
167 | path_put(&nd->path); | ||
168 | goto out; | ||
169 | out_follow: | ||
170 | while (d_mountpoint(nd->path.dentry) && | ||
171 | follow_down(&nd->path)) | ||
172 | ; | ||
173 | err = 0; | ||
174 | goto out; | ||
175 | } | 281 | } |
176 | 282 | ||
177 | const struct inode_operations nfs_mountpoint_inode_operations = { | 283 | const struct inode_operations nfs_mountpoint_inode_operations = { |
178 | .follow_link = nfs_follow_mountpoint, | ||
179 | .getattr = nfs_getattr, | 284 | .getattr = nfs_getattr, |
180 | }; | 285 | }; |
181 | 286 | ||
182 | const struct inode_operations nfs_referral_inode_operations = { | 287 | const struct inode_operations nfs_referral_inode_operations = { |
183 | .follow_link = nfs_follow_mountpoint, | ||
184 | }; | 288 | }; |
185 | 289 | ||
186 | static void nfs_expire_automounts(struct work_struct *work) | 290 | static void nfs_expire_automounts(struct work_struct *work) |
@@ -223,22 +327,23 @@ static struct vfsmount *nfs_do_clone_mount(struct nfs_server *server, | |||
223 | 327 | ||
224 | /** | 328 | /** |
225 | * nfs_do_submount - set up mountpoint when crossing a filesystem boundary | 329 | * nfs_do_submount - set up mountpoint when crossing a filesystem boundary |
226 | * @mnt_parent - mountpoint of parent directory | ||
227 | * @dentry - parent directory | 330 | * @dentry - parent directory |
228 | * @fh - filehandle for new root dentry | 331 | * @fh - filehandle for new root dentry |
229 | * @fattr - attributes for new root inode | 332 | * @fattr - attributes for new root inode |
333 | * @authflavor - security flavor to use when performing the mount | ||
230 | * | 334 | * |
231 | */ | 335 | */ |
232 | static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, | 336 | static struct vfsmount *nfs_do_submount(struct dentry *dentry, |
233 | const struct dentry *dentry, | ||
234 | struct nfs_fh *fh, | 337 | struct nfs_fh *fh, |
235 | struct nfs_fattr *fattr) | 338 | struct nfs_fattr *fattr, |
339 | rpc_authflavor_t authflavor) | ||
236 | { | 340 | { |
237 | struct nfs_clone_mount mountdata = { | 341 | struct nfs_clone_mount mountdata = { |
238 | .sb = mnt_parent->mnt_sb, | 342 | .sb = dentry->d_sb, |
239 | .dentry = dentry, | 343 | .dentry = dentry, |
240 | .fh = fh, | 344 | .fh = fh, |
241 | .fattr = fattr, | 345 | .fattr = fattr, |
346 | .authflavor = authflavor, | ||
242 | }; | 347 | }; |
243 | struct vfsmount *mnt = ERR_PTR(-ENOMEM); | 348 | struct vfsmount *mnt = ERR_PTR(-ENOMEM); |
244 | char *page = (char *) __get_free_page(GFP_USER); | 349 | char *page = (char *) __get_free_page(GFP_USER); |
@@ -251,11 +356,11 @@ static struct vfsmount *nfs_do_submount(const struct vfsmount *mnt_parent, | |||
251 | dentry->d_name.name); | 356 | dentry->d_name.name); |
252 | if (page == NULL) | 357 | if (page == NULL) |
253 | goto out; | 358 | goto out; |
254 | devname = nfs_devname(mnt_parent, dentry, page, PAGE_SIZE); | 359 | devname = nfs_devname(dentry, page, PAGE_SIZE); |
255 | mnt = (struct vfsmount *)devname; | 360 | mnt = (struct vfsmount *)devname; |
256 | if (IS_ERR(devname)) | 361 | if (IS_ERR(devname)) |
257 | goto free_page; | 362 | goto free_page; |
258 | mnt = nfs_do_clone_mount(NFS_SB(mnt_parent->mnt_sb), devname, &mountdata); | 363 | mnt = nfs_do_clone_mount(NFS_SB(dentry->d_sb), devname, &mountdata); |
259 | free_page: | 364 | free_page: |
260 | free_page((unsigned long)page); | 365 | free_page((unsigned long)page); |
261 | out: | 366 | out: |