nfs4namespace.c (597473720f4dc69749542bfcfed4a927a43d935e) | nfs4namespace.c (f2aedb713c284429987dc66c7aaf38decfc8da2a) |
---|---|
1// SPDX-License-Identifier: GPL-2.0 2/* 3 * linux/fs/nfs/nfs4namespace.c 4 * 5 * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> 6 * - Modified by David Howells <dhowells@redhat.com> 7 * 8 * NFSv4 namespace 9 */ 10 | 1// SPDX-License-Identifier: GPL-2.0 2/* 3 * linux/fs/nfs/nfs4namespace.c 4 * 5 * Copyright (C) 2005 Trond Myklebust <Trond.Myklebust@netapp.com> 6 * - Modified by David Howells <dhowells@redhat.com> 7 * 8 * NFSv4 namespace 9 */ 10 |
11#include <linux/module.h> |
|
11#include <linux/dcache.h> 12#include <linux/mount.h> 13#include <linux/namei.h> 14#include <linux/nfs_fs.h> 15#include <linux/nfs_mount.h> 16#include <linux/slab.h> 17#include <linux/string.h> 18#include <linux/sunrpc/clnt.h> 19#include <linux/sunrpc/addr.h> 20#include <linux/vfs.h> 21#include <linux/inet.h> 22#include "internal.h" 23#include "nfs4_fs.h" | 12#include <linux/dcache.h> 13#include <linux/mount.h> 14#include <linux/namei.h> 15#include <linux/nfs_fs.h> 16#include <linux/nfs_mount.h> 17#include <linux/slab.h> 18#include <linux/string.h> 19#include <linux/sunrpc/clnt.h> 20#include <linux/sunrpc/addr.h> 21#include <linux/vfs.h> 22#include <linux/inet.h> 23#include "internal.h" 24#include "nfs4_fs.h" |
25#include "nfs.h" |
|
24#include "dns_resolve.h" 25 26#define NFSDBG_FACILITY NFSDBG_VFS 27 28/* | 26#include "dns_resolve.h" 27 28#define NFSDBG_FACILITY NFSDBG_VFS 29 30/* |
31 * Work out the length that an NFSv4 path would render to as a standard posix 32 * path, with a leading slash but no terminating slash. 33 */ 34static ssize_t nfs4_pathname_len(const struct nfs4_pathname *pathname) 35{ 36 ssize_t len = 0; 37 int i; 38 39 for (i = 0; i < pathname->ncomponents; i++) { 40 const struct nfs4_string *component = &pathname->components[i]; 41 42 if (component->len > NAME_MAX) 43 goto too_long; 44 len += 1 + component->len; /* Adding "/foo" */ 45 if (len > PATH_MAX) 46 goto too_long; 47 } 48 return len; 49 50too_long: 51 return -ENAMETOOLONG; 52} 53 54/* |
|
29 * Convert the NFSv4 pathname components into a standard posix path. | 55 * Convert the NFSv4 pathname components into a standard posix path. |
30 * 31 * Note that the resulting string will be placed at the end of the buffer | |
32 */ | 56 */ |
33static inline char *nfs4_pathname_string(const struct nfs4_pathname *pathname, 34 char *buffer, ssize_t buflen) | 57static char *nfs4_pathname_string(const struct nfs4_pathname *pathname, 58 unsigned short *_len) |
35{ | 59{ |
36 char *end = buffer + buflen; 37 int n; | 60 ssize_t len; 61 char *buf, *p; 62 int i; |
38 | 63 |
39 *--end = '\0'; 40 buflen--; | 64 len = nfs4_pathname_len(pathname); 65 if (len < 0) 66 return ERR_PTR(len); 67 *_len = len; |
41 | 68 |
42 n = pathname->ncomponents; 43 while (--n >= 0) { 44 const struct nfs4_string *component = &pathname->components[n]; 45 buflen -= component->len + 1; 46 if (buflen < 0) 47 goto Elong; 48 end -= component->len; 49 memcpy(end, component->data, component->len); 50 *--end = '/'; | 69 p = buf = kmalloc(len + 1, GFP_KERNEL); 70 if (!buf) 71 return ERR_PTR(-ENOMEM); 72 73 for (i = 0; i < pathname->ncomponents; i++) { 74 const struct nfs4_string *component = &pathname->components[i]; 75 76 *p++ = '/'; 77 memcpy(p, component->data, component->len); 78 p += component->len; |
51 } | 79 } |
52 return end; 53Elong: 54 return ERR_PTR(-ENAMETOOLONG); | 80 81 *p = 0; 82 return buf; |
55} 56 57/* 58 * return the path component of "<server>:<path>" 59 * nfspath - the "<server>:<path>" string 60 * end - one past the last char that could contain "<server>:" 61 * returns NULL on failure 62 */ --- 32 unchanged lines hidden (view full) --- 95} 96 97/* 98 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we 99 * believe to be the server path to this dentry 100 */ 101static int nfs4_validate_fspath(struct dentry *dentry, 102 const struct nfs4_fs_locations *locations, | 83} 84 85/* 86 * return the path component of "<server>:<path>" 87 * nfspath - the "<server>:<path>" string 88 * end - one past the last char that could contain "<server>:" 89 * returns NULL on failure 90 */ --- 32 unchanged lines hidden (view full) --- 123} 124 125/* 126 * Check that fs_locations::fs_root [RFC3530 6.3] is a prefix for what we 127 * believe to be the server path to this dentry 128 */ 129static int nfs4_validate_fspath(struct dentry *dentry, 130 const struct nfs4_fs_locations *locations, |
103 char *page, char *page2) | 131 struct nfs_fs_context *ctx) |
104{ 105 const char *path, *fs_path; | 132{ 133 const char *path, *fs_path; |
134 char *buf; 135 unsigned short len; 136 int n; |
|
106 | 137 |
107 path = nfs4_path(dentry, page, PAGE_SIZE); 108 if (IS_ERR(path)) | 138 buf = kmalloc(4096, GFP_KERNEL); 139 path = nfs4_path(dentry, buf, 4096); 140 if (IS_ERR(path)) { 141 kfree(buf); |
109 return PTR_ERR(path); | 142 return PTR_ERR(path); |
143 } |
|
110 | 144 |
111 fs_path = nfs4_pathname_string(&locations->fs_path, page2, PAGE_SIZE); 112 if (IS_ERR(fs_path)) | 145 fs_path = nfs4_pathname_string(&locations->fs_path, &len); 146 if (IS_ERR(fs_path)) { 147 kfree(buf); |
113 return PTR_ERR(fs_path); | 148 return PTR_ERR(fs_path); |
149 } |
|
114 | 150 |
115 if (strncmp(path, fs_path, strlen(fs_path)) != 0) { | 151 n = strncmp(path, fs_path, len); 152 kfree(buf); 153 kfree(fs_path); 154 if (n != 0) { |
116 dprintk("%s: path %s does not begin with fsroot %s\n", | 155 dprintk("%s: path %s does not begin with fsroot %s\n", |
117 __func__, path, fs_path); | 156 __func__, path, ctx->nfs_server.export_path); |
118 return -ENOENT; 119 } 120 121 return 0; 122} 123 124static size_t nfs_parse_server_name(char *string, size_t len, 125 struct sockaddr *sa, size_t salen, struct net *net) --- 105 unchanged lines hidden (view full) --- 231 232 new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors); 233 234out: 235 put_page(page); 236 return new; 237} 238 | 157 return -ENOENT; 158 } 159 160 return 0; 161} 162 163static size_t nfs_parse_server_name(char *string, size_t len, 164 struct sockaddr *sa, size_t salen, struct net *net) --- 105 unchanged lines hidden (view full) --- 270 271 new = nfs_find_best_sec(clnt, NFS_SERVER(inode), flavors); 272 273out: 274 put_page(page); 275 return new; 276} 277 |
239static struct vfsmount *try_location(struct nfs_clone_mount *mountdata, 240 char *page, char *page2, 241 const struct nfs4_fs_location *location) | 278static int try_location(struct fs_context *fc, 279 const struct nfs4_fs_location *location) |
242{ 243 const size_t addr_bufsize = sizeof(struct sockaddr_storage); | 280{ 281 const size_t addr_bufsize = sizeof(struct sockaddr_storage); |
244 struct net *net = rpc_net_ns(NFS_SB(mountdata->sb)->client); 245 struct vfsmount *mnt = ERR_PTR(-ENOENT); 246 char *mnt_path; 247 unsigned int maxbuflen; 248 unsigned int s; | 282 struct nfs_fs_context *ctx = nfs_fc2context(fc); 283 unsigned int len, s; 284 char *export_path, *source, *p; 285 int ret = -ENOENT; |
249 | 286 |
250 mnt_path = nfs4_pathname_string(&location->rootpath, page2, PAGE_SIZE); 251 if (IS_ERR(mnt_path)) 252 return ERR_CAST(mnt_path); 253 mountdata->mnt_path = mnt_path; 254 maxbuflen = mnt_path - 1 - page2; | 287 /* Allocate a buffer big enough to hold any of the hostnames plus a 288 * terminating char and also a buffer big enough to hold the hostname 289 * plus a colon plus the path. 290 */ 291 len = 0; 292 for (s = 0; s < location->nservers; s++) { 293 const struct nfs4_string *buf = &location->servers[s]; 294 if (buf->len > len) 295 len = buf->len; 296 } |
255 | 297 |
256 mountdata->addr = kmalloc(addr_bufsize, GFP_KERNEL); 257 if (mountdata->addr == NULL) 258 return ERR_PTR(-ENOMEM); | 298 kfree(ctx->nfs_server.hostname); 299 ctx->nfs_server.hostname = kmalloc(len + 1, GFP_KERNEL); 300 if (!ctx->nfs_server.hostname) 301 return -ENOMEM; |
259 | 302 |
303 export_path = nfs4_pathname_string(&location->rootpath, 304 &ctx->nfs_server.export_path_len); 305 if (IS_ERR(export_path)) 306 return PTR_ERR(export_path); 307 308 ctx->nfs_server.export_path = export_path; 309 310 source = kmalloc(len + 1 + ctx->nfs_server.export_path_len + 1, 311 GFP_KERNEL); 312 if (!source) 313 return -ENOMEM; 314 315 kfree(fc->source); 316 fc->source = source; 317 318 ctx->clone_data.addr = kmalloc(addr_bufsize, GFP_KERNEL); 319 if (ctx->clone_data.addr == NULL) 320 return -ENOMEM; |
|
260 for (s = 0; s < location->nservers; s++) { 261 const struct nfs4_string *buf = &location->servers[s]; 262 | 321 for (s = 0; s < location->nservers; s++) { 322 const struct nfs4_string *buf = &location->servers[s]; 323 |
263 if (buf->len <= 0 || buf->len >= maxbuflen) 264 continue; 265 | |
266 if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) 267 continue; 268 | 324 if (memchr(buf->data, IPV6_SCOPE_DELIMITER, buf->len)) 325 continue; 326 |
269 mountdata->addrlen = nfs_parse_server_name(buf->data, buf->len, 270 mountdata->addr, addr_bufsize, net); 271 if (mountdata->addrlen == 0) | 327 ctx->clone_data.addrlen = 328 nfs_parse_server_name(buf->data, buf->len, 329 ctx->clone_data.addr, 330 addr_bufsize, 331 fc->net_ns); 332 if (ctx->clone_data.addrlen == 0) |
272 continue; 273 | 333 continue; 334 |
274 memcpy(page2, buf->data, buf->len); 275 page2[buf->len] = '\0'; 276 mountdata->hostname = page2; | 335 rpc_set_port(ctx->clone_data.addr, NFS_PORT); |
277 | 336 |
278 snprintf(page, PAGE_SIZE, "%s:%s", 279 mountdata->hostname, 280 mountdata->mnt_path); | 337 memcpy(ctx->nfs_server.hostname, buf->data, buf->len); 338 ctx->nfs_server.hostname[buf->len] = '\0'; 339 ctx->clone_data.hostname = ctx->nfs_server.hostname; |
281 | 340 |
282 mnt = vfs_submount(mountdata->dentry, &nfs4_referral_fs_type, page, mountdata); 283 if (!IS_ERR(mnt)) 284 break; | 341 p = source; 342 memcpy(p, buf->data, buf->len); 343 p += buf->len; 344 *p++ = ':'; 345 memcpy(p, ctx->nfs_server.export_path, ctx->nfs_server.export_path_len); 346 p += ctx->nfs_server.export_path_len; 347 *p = 0; 348 349 ret = nfs4_get_referral_tree(fc); 350 if (ret == 0) 351 return 0; |
285 } | 352 } |
286 kfree(mountdata->addr); 287 return mnt; | 353 354 return ret; |
288} 289 290/** 291 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error 292 * @dentry: parent directory 293 * @locations: array of NFSv4 server location information 294 * 295 */ | 355} 356 357/** 358 * nfs_follow_referral - set up mountpoint when hitting a referral on moved error 359 * @dentry: parent directory 360 * @locations: array of NFSv4 server location information 361 * 362 */ |
296static struct vfsmount *nfs_follow_referral(struct dentry *dentry, 297 const struct nfs4_fs_locations *locations) | 363static int nfs_follow_referral(struct fs_context *fc, 364 const struct nfs4_fs_locations *locations) |
298{ | 365{ |
299 struct vfsmount *mnt = ERR_PTR(-ENOENT); 300 struct nfs_clone_mount mountdata = { 301 .sb = dentry->d_sb, 302 .dentry = dentry, 303 .authflavor = NFS_SB(dentry->d_sb)->client->cl_auth->au_flavor, 304 }; 305 char *page = NULL, *page2 = NULL; | 366 struct nfs_fs_context *ctx = nfs_fc2context(fc); |
306 int loc, error; 307 308 if (locations == NULL || locations->nlocations <= 0) | 367 int loc, error; 368 369 if (locations == NULL || locations->nlocations <= 0) |
309 goto out; | 370 return -ENOENT; |
310 | 371 |
311 dprintk("%s: referral at %pd2\n", __func__, dentry); | 372 dprintk("%s: referral at %pd2\n", __func__, ctx->clone_data.dentry); |
312 | 373 |
313 page = (char *) __get_free_page(GFP_USER); 314 if (!page) 315 goto out; 316 317 page2 = (char *) __get_free_page(GFP_USER); 318 if (!page2) 319 goto out; 320 | |
321 /* Ensure fs path is a prefix of current dentry path */ | 374 /* Ensure fs path is a prefix of current dentry path */ |
322 error = nfs4_validate_fspath(dentry, locations, page, page2); 323 if (error < 0) { 324 mnt = ERR_PTR(error); 325 goto out; 326 } | 375 error = nfs4_validate_fspath(ctx->clone_data.dentry, locations, ctx); 376 if (error < 0) 377 return error; |
327 | 378 |
379 error = -ENOENT; |
|
328 for (loc = 0; loc < locations->nlocations; loc++) { 329 const struct nfs4_fs_location *location = &locations->locations[loc]; 330 331 if (location == NULL || location->nservers <= 0 || 332 location->rootpath.ncomponents == 0) 333 continue; 334 | 380 for (loc = 0; loc < locations->nlocations; loc++) { 381 const struct nfs4_fs_location *location = &locations->locations[loc]; 382 383 if (location == NULL || location->nservers <= 0 || 384 location->rootpath.ncomponents == 0) 385 continue; 386 |
335 mnt = try_location(&mountdata, page, page2, location); 336 if (!IS_ERR(mnt)) 337 break; | 387 error = try_location(fc, location); 388 if (error == 0) 389 return 0; |
338 } 339 | 390 } 391 |
340out: 341 free_page((unsigned long) page); 342 free_page((unsigned long) page2); 343 return mnt; | 392 return error; |
344} 345 346/* 347 * nfs_do_refmount - handle crossing a referral on server 348 * @dentry - dentry of referral 349 * 350 */ | 393} 394 395/* 396 * nfs_do_refmount - handle crossing a referral on server 397 * @dentry - dentry of referral 398 * 399 */ |
351static struct vfsmount *nfs_do_refmount(struct rpc_clnt *client, struct dentry *dentry) | 400static int nfs_do_refmount(struct fs_context *fc, struct rpc_clnt *client) |
352{ | 401{ |
353 struct vfsmount *mnt = ERR_PTR(-ENOMEM); 354 struct dentry *parent; | 402 struct nfs_fs_context *ctx = nfs_fc2context(fc); 403 struct dentry *dentry, *parent; |
355 struct nfs4_fs_locations *fs_locations = NULL; 356 struct page *page; | 404 struct nfs4_fs_locations *fs_locations = NULL; 405 struct page *page; |
357 int err; | 406 int err = -ENOMEM; |
358 359 /* BUG_ON(IS_ROOT(dentry)); */ 360 page = alloc_page(GFP_KERNEL); | 407 408 /* BUG_ON(IS_ROOT(dentry)); */ 409 page = alloc_page(GFP_KERNEL); |
361 if (page == NULL) 362 return mnt; | 410 if (!page) 411 return -ENOMEM; |
363 364 fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); | 412 413 fs_locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); |
365 if (fs_locations == NULL) | 414 if (!fs_locations) |
366 goto out_free; 367 368 /* Get locations */ | 415 goto out_free; 416 417 /* Get locations */ |
369 mnt = ERR_PTR(-ENOENT); 370 | 418 dentry = ctx->clone_data.dentry; |
371 parent = dget_parent(dentry); 372 dprintk("%s: getting locations for %pd2\n", 373 __func__, dentry); 374 375 err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page); 376 dput(parent); | 419 parent = dget_parent(dentry); 420 dprintk("%s: getting locations for %pd2\n", 421 __func__, dentry); 422 423 err = nfs4_proc_fs_locations(client, d_inode(parent), &dentry->d_name, fs_locations, page); 424 dput(parent); |
377 if (err != 0 || 378 fs_locations->nlocations <= 0 || | 425 if (err != 0) 426 goto out_free_2; 427 428 err = -ENOENT; 429 if (fs_locations->nlocations <= 0 || |
379 fs_locations->fs_path.ncomponents <= 0) | 430 fs_locations->fs_path.ncomponents <= 0) |
380 goto out_free; | 431 goto out_free_2; |
381 | 432 |
382 mnt = nfs_follow_referral(dentry, fs_locations); | 433 err = nfs_follow_referral(fc, fs_locations); 434out_free_2: 435 kfree(fs_locations); |
383out_free: 384 __free_page(page); | 436out_free: 437 __free_page(page); |
385 kfree(fs_locations); 386 return mnt; | 438 return err; |
387} 388 | 439} 440 |
389struct vfsmount *nfs4_submount(struct nfs_server *server, struct dentry *dentry, 390 struct nfs_fh *fh, struct nfs_fattr *fattr) | 441int nfs4_submount(struct fs_context *fc, struct nfs_server *server) |
391{ | 442{ |
392 rpc_authflavor_t flavor = server->client->cl_auth->au_flavor; | 443 struct nfs_fs_context *ctx = nfs_fc2context(fc); 444 struct dentry *dentry = ctx->clone_data.dentry; |
393 struct dentry *parent = dget_parent(dentry); 394 struct inode *dir = d_inode(parent); 395 const struct qstr *name = &dentry->d_name; 396 struct rpc_clnt *client; | 445 struct dentry *parent = dget_parent(dentry); 446 struct inode *dir = d_inode(parent); 447 const struct qstr *name = &dentry->d_name; 448 struct rpc_clnt *client; |
397 struct vfsmount *mnt; | 449 int ret; |
398 399 /* Look it up again to get its attributes and sec flavor */ | 450 451 /* Look it up again to get its attributes and sec flavor */ |
400 client = nfs4_proc_lookup_mountpoint(dir, name, fh, fattr); | 452 client = nfs4_proc_lookup_mountpoint(dir, name, ctx->mount_info.mntfh, 453 ctx->clone_data.fattr); |
401 dput(parent); 402 if (IS_ERR(client)) | 454 dput(parent); 455 if (IS_ERR(client)) |
403 return ERR_CAST(client); | 456 return PTR_ERR(client); |
404 | 457 |
405 if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 406 mnt = nfs_do_refmount(client, dentry); 407 goto out; | 458 ctx->selected_flavor = client->cl_auth->au_flavor; 459 if (ctx->clone_data.fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) { 460 ret = nfs_do_refmount(fc, client); 461 } else { 462 ret = nfs_do_submount(fc); |
408 } 409 | 463 } 464 |
410 if (client->cl_auth->au_flavor != flavor) 411 flavor = client->cl_auth->au_flavor; 412 mnt = nfs_do_submount(dentry, fh, fattr, flavor); 413out: | |
414 rpc_shutdown_client(client); | 465 rpc_shutdown_client(client); |
415 return mnt; | 466 return ret; |
416} 417 418/* 419 * Try one location from the fs_locations array. 420 * 421 * Returns zero on success, or a negative errno value. 422 */ 423static int nfs4_try_replacing_one_location(struct nfs_server *server, --- 95 unchanged lines hidden --- | 467} 468 469/* 470 * Try one location from the fs_locations array. 471 * 472 * Returns zero on success, or a negative errno value. 473 */ 474static int nfs4_try_replacing_one_location(struct nfs_server *server, --- 95 unchanged lines hidden --- |