diff options
-rw-r--r-- | fs/nfs/inode.c | 5 | ||||
-rw-r--r-- | fs/nfs/namespace.c | 9 | ||||
-rw-r--r-- | fs/nfs/nfs4proc.c | 46 | ||||
-rw-r--r-- | include/linux/nfs_fs.h | 1 | ||||
-rw-r--r-- | include/linux/nfs_xdr.h | 1 |
5 files changed, 60 insertions, 2 deletions
diff --git a/fs/nfs/inode.c b/fs/nfs/inode.c index 0d8302e59d69..ee13cb01b56e 100644 --- a/fs/nfs/inode.c +++ b/fs/nfs/inode.c | |||
@@ -888,7 +888,10 @@ nfs_fhget(struct super_block *sb, struct nfs_fh *fh, struct nfs_fattr *fattr) | |||
888 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); | 888 | set_bit(NFS_INO_ADVISE_RDPLUS, &NFS_FLAGS(inode)); |
889 | /* Deal with crossing mountpoints */ | 889 | /* Deal with crossing mountpoints */ |
890 | if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { | 890 | if (!nfs_fsid_equal(&NFS_SB(sb)->fsid, &fattr->fsid)) { |
891 | inode->i_op = &nfs_mountpoint_inode_operations; | 891 | if (fattr->valid & NFS_ATTR_FATTR_V4_REFERRAL) |
892 | inode->i_op = &nfs_referral_inode_operations; | ||
893 | else | ||
894 | inode->i_op = &nfs_mountpoint_inode_operations; | ||
892 | inode->i_fop = NULL; | 895 | inode->i_fop = NULL; |
893 | } | 896 | } |
894 | } else if (S_ISLNK(inode->i_mode)) | 897 | } else if (S_ISLNK(inode->i_mode)) |
diff --git a/fs/nfs/namespace.c b/fs/nfs/namespace.c index e426516c1116..8ca44b7b25c3 100644 --- a/fs/nfs/namespace.c +++ b/fs/nfs/namespace.c | |||
@@ -58,7 +58,10 @@ static void * nfs_follow_mountpoint(struct dentry *dentry, struct nameidata *nd) | |||
58 | if (err != 0) | 58 | if (err != 0) |
59 | goto out_err; | 59 | goto out_err; |
60 | 60 | ||
61 | mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); | 61 | if (fattr.valid & NFS_ATTR_FATTR_V4_REFERRAL) |
62 | mnt = nfs_do_refmount(nd->mnt, nd->dentry); | ||
63 | else | ||
64 | mnt = nfs_do_submount(nd->mnt, nd->dentry, &fh, &fattr); | ||
62 | err = PTR_ERR(mnt); | 65 | err = PTR_ERR(mnt); |
63 | if (IS_ERR(mnt)) | 66 | if (IS_ERR(mnt)) |
64 | goto out_err; | 67 | goto out_err; |
@@ -94,6 +97,10 @@ struct inode_operations nfs_mountpoint_inode_operations = { | |||
94 | .getattr = nfs_getattr, | 97 | .getattr = nfs_getattr, |
95 | }; | 98 | }; |
96 | 99 | ||
100 | struct inode_operations nfs_referral_inode_operations = { | ||
101 | .follow_link = nfs_follow_mountpoint, | ||
102 | }; | ||
103 | |||
97 | static void nfs_expire_automounts(void *data) | 104 | static void nfs_expire_automounts(void *data) |
98 | { | 105 | { |
99 | struct list_head *list = (struct list_head *)data; | 106 | struct list_head *list = (struct list_head *)data; |
diff --git a/fs/nfs/nfs4proc.c b/fs/nfs/nfs4proc.c index 90ee21a07b3e..3300e35d74ad 100644 --- a/fs/nfs/nfs4proc.c +++ b/fs/nfs/nfs4proc.c | |||
@@ -1462,6 +1462,50 @@ out: | |||
1462 | return nfs4_map_errors(status); | 1462 | return nfs4_map_errors(status); |
1463 | } | 1463 | } |
1464 | 1464 | ||
1465 | /* | ||
1466 | * Get locations and (maybe) other attributes of a referral. | ||
1467 | * Note that we'll actually follow the referral later when | ||
1468 | * we detect fsid mismatch in inode revalidation | ||
1469 | */ | ||
1470 | static int nfs4_get_referral(struct inode *dir, struct qstr *name, struct nfs_fattr *fattr, struct nfs_fh *fhandle) | ||
1471 | { | ||
1472 | int status = -ENOMEM; | ||
1473 | struct page *page = NULL; | ||
1474 | struct nfs4_fs_locations *locations = NULL; | ||
1475 | struct dentry dentry = {}; | ||
1476 | |||
1477 | page = alloc_page(GFP_KERNEL); | ||
1478 | if (page == NULL) | ||
1479 | goto out; | ||
1480 | locations = kmalloc(sizeof(struct nfs4_fs_locations), GFP_KERNEL); | ||
1481 | if (locations == NULL) | ||
1482 | goto out; | ||
1483 | |||
1484 | dentry.d_name.name = name->name; | ||
1485 | dentry.d_name.len = name->len; | ||
1486 | status = nfs4_proc_fs_locations(dir, &dentry, locations, page); | ||
1487 | if (status != 0) | ||
1488 | goto out; | ||
1489 | /* Make sure server returned a different fsid for the referral */ | ||
1490 | if (nfs_fsid_equal(&NFS_SERVER(dir)->fsid, &locations->fattr.fsid)) { | ||
1491 | dprintk("%s: server did not return a different fsid for a referral at %s\n", __FUNCTION__, name->name); | ||
1492 | status = -EIO; | ||
1493 | goto out; | ||
1494 | } | ||
1495 | |||
1496 | memcpy(fattr, &locations->fattr, sizeof(struct nfs_fattr)); | ||
1497 | fattr->valid |= NFS_ATTR_FATTR_V4_REFERRAL; | ||
1498 | if (!fattr->mode) | ||
1499 | fattr->mode = S_IFDIR; | ||
1500 | memset(fhandle, 0, sizeof(struct nfs_fh)); | ||
1501 | out: | ||
1502 | if (page) | ||
1503 | __free_page(page); | ||
1504 | if (locations) | ||
1505 | kfree(locations); | ||
1506 | return status; | ||
1507 | } | ||
1508 | |||
1465 | static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) | 1509 | static int _nfs4_proc_getattr(struct nfs_server *server, struct nfs_fh *fhandle, struct nfs_fattr *fattr) |
1466 | { | 1510 | { |
1467 | struct nfs4_getattr_arg args = { | 1511 | struct nfs4_getattr_arg args = { |
@@ -1566,6 +1610,8 @@ static int _nfs4_proc_lookup(struct inode *dir, struct qstr *name, | |||
1566 | 1610 | ||
1567 | dprintk("NFS call lookup %s\n", name->name); | 1611 | dprintk("NFS call lookup %s\n", name->name); |
1568 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); | 1612 | status = rpc_call_sync(NFS_CLIENT(dir), &msg, 0); |
1613 | if (status == -NFS4ERR_MOVED) | ||
1614 | status = nfs4_get_referral(dir, name, fattr, fhandle); | ||
1569 | dprintk("NFS reply lookup: %d\n", status); | 1615 | dprintk("NFS reply lookup: %d\n", status); |
1570 | return status; | 1616 | return status; |
1571 | } | 1617 | } |
diff --git a/include/linux/nfs_fs.h b/include/linux/nfs_fs.h index 09271b10f9a8..152798949113 100644 --- a/include/linux/nfs_fs.h +++ b/include/linux/nfs_fs.h | |||
@@ -409,6 +409,7 @@ extern void nfs_unregister_sysctl(void); | |||
409 | */ | 409 | */ |
410 | extern struct list_head nfs_automount_list; | 410 | extern struct list_head nfs_automount_list; |
411 | extern struct inode_operations nfs_mountpoint_inode_operations; | 411 | extern struct inode_operations nfs_mountpoint_inode_operations; |
412 | extern struct inode_operations nfs_referral_inode_operations; | ||
412 | extern int nfs_mountpoint_expiry_timeout; | 413 | extern int nfs_mountpoint_expiry_timeout; |
413 | extern void nfs_release_automount_timer(void); | 414 | extern void nfs_release_automount_timer(void); |
414 | 415 | ||
diff --git a/include/linux/nfs_xdr.h b/include/linux/nfs_xdr.h index d6eea8348728..7c7320fa51aa 100644 --- a/include/linux/nfs_xdr.h +++ b/include/linux/nfs_xdr.h | |||
@@ -63,6 +63,7 @@ struct nfs_fattr { | |||
63 | #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ | 63 | #define NFS_ATTR_FATTR 0x0002 /* post-op attributes */ |
64 | #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ | 64 | #define NFS_ATTR_FATTR_V3 0x0004 /* NFSv3 attributes */ |
65 | #define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ | 65 | #define NFS_ATTR_FATTR_V4 0x0008 /* NFSv4 change attribute */ |
66 | #define NFS_ATTR_FATTR_V4_REFERRAL 0x0010 /* NFSv4 referral */ | ||
66 | 67 | ||
67 | /* | 68 | /* |
68 | * Info on the file system | 69 | * Info on the file system |