diff options
Diffstat (limited to 'fs/nfs/nfs4proc.c')
-rw-r--r-- | fs/nfs/nfs4proc.c | 46 |
1 files changed, 46 insertions, 0 deletions
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 | } |