Skip to content

Commit 20fa190

Browse files
Peng Taoamschuma-ntap
authored andcommitted
nfs: add export operations
This support for opening files on NFS by file handle, both through the open_by_handle syscall, and for re-exporting NFS (for example using a different version). The support is very basic for now, as each open by handle will have to do an NFSv4 open operation on the wire. In the future this will hopefully be mitigated by an open file cache, as well as various optimizations in NFS for this specific case. Signed-off-by: Peng Tao <[email protected]> [hch: incorporated various changes, resplit the patches, new changelog] Signed-off-by: Christoph Hellwig <[email protected]> Signed-off-by: Anna Schumaker <[email protected]>
1 parent 5b5faaf commit 20fa190

File tree

4 files changed

+182
-1
lines changed

4 files changed

+182
-1
lines changed

fs/nfs/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ obj-$(CONFIG_NFS_FS) += nfs.o
77
CFLAGS_nfstrace.o += -I$(src)
88
nfs-y := client.o dir.o file.o getroot.o inode.o super.o \
99
io.o direct.o pagelist.o read.o symlink.o unlink.o \
10-
write.o namespace.o mount_clnt.o nfstrace.o
10+
write.o namespace.o mount_clnt.o nfstrace.o export.o
1111
nfs-$(CONFIG_ROOT_NFS) += nfsroot.o
1212
nfs-$(CONFIG_SYSCTL) += sysctl.o
1313
nfs-$(CONFIG_NFS_FSCACHE) += fscache.o fscache-index.o

fs/nfs/export.c

Lines changed: 177 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,177 @@
1+
/*
2+
* Copyright (c) 2015, Primary Data, Inc. All rights reserved.
3+
*
4+
* Tao Peng <[email protected]>
5+
*/
6+
#include <linux/dcache.h>
7+
#include <linux/exportfs.h>
8+
#include <linux/nfs.h>
9+
#include <linux/nfs_fs.h>
10+
11+
#include "internal.h"
12+
#include "nfstrace.h"
13+
14+
#define NFSDBG_FACILITY NFSDBG_VFS
15+
16+
enum {
17+
FILEID_HIGH_OFF = 0, /* inode fileid high */
18+
FILEID_LOW_OFF, /* inode fileid low */
19+
FILE_I_TYPE_OFF, /* inode type */
20+
EMBED_FH_OFF /* embeded server fh */
21+
};
22+
23+
24+
static struct nfs_fh *nfs_exp_embedfh(__u32 *p)
25+
{
26+
return (struct nfs_fh *)(p + EMBED_FH_OFF);
27+
}
28+
29+
/*
30+
* Let's break subtree checking for now... otherwise we'll have to embed parent fh
31+
* but there might not be enough space.
32+
*/
33+
static int
34+
nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
35+
{
36+
struct nfs_fh *server_fh = NFS_FH(inode);
37+
struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
38+
size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
39+
int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
40+
41+
dprintk("%s: max fh len %d inode %p parent %p",
42+
__func__, *max_len, inode, parent);
43+
44+
if (*max_len < len || IS_AUTOMOUNT(inode)) {
45+
dprintk("%s: fh len %d too small, required %d\n",
46+
__func__, *max_len, len);
47+
*max_len = len;
48+
return FILEID_INVALID;
49+
}
50+
if (IS_AUTOMOUNT(inode)) {
51+
*max_len = FILEID_INVALID;
52+
goto out;
53+
}
54+
55+
p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
56+
p[FILEID_LOW_OFF] = NFS_FILEID(inode);
57+
p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
58+
p[len - 1] = 0; /* Padding */
59+
nfs_copy_fh(clnt_fh, server_fh);
60+
*max_len = len;
61+
out:
62+
dprintk("%s: result fh fileid %llu mode %u size %d\n",
63+
__func__, NFS_FILEID(inode), inode->i_mode, *max_len);
64+
return *max_len;
65+
}
66+
67+
static struct dentry *
68+
nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
69+
int fh_len, int fh_type)
70+
{
71+
struct nfs4_label *label = NULL;
72+
struct nfs_fattr *fattr = NULL;
73+
struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
74+
size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
75+
const struct nfs_rpc_ops *rpc_ops;
76+
struct dentry *dentry;
77+
struct inode *inode;
78+
int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
79+
u32 *p = fid->raw;
80+
int ret;
81+
82+
/* NULL translates to ESTALE */
83+
if (fh_len < len || fh_type != len)
84+
return NULL;
85+
86+
fattr = nfs_alloc_fattr();
87+
if (fattr == NULL) {
88+
dentry = ERR_PTR(-ENOMEM);
89+
goto out;
90+
}
91+
92+
fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
93+
fattr->mode = p[FILE_I_TYPE_OFF];
94+
fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
95+
96+
dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
97+
98+
inode = nfs_ilookup(sb, fattr, server_fh);
99+
if (inode)
100+
goto out_found;
101+
102+
label = nfs4_label_alloc(NFS_SB(sb), GFP_KERNEL);
103+
if (IS_ERR(label)) {
104+
dentry = ERR_CAST(label);
105+
goto out_free_fattr;
106+
}
107+
108+
rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
109+
ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, label);
110+
if (ret) {
111+
dprintk("%s: getattr failed %d\n", __func__, ret);
112+
dentry = ERR_PTR(ret);
113+
goto out_free_label;
114+
}
115+
116+
inode = nfs_fhget(sb, server_fh, fattr, label);
117+
118+
out_found:
119+
dentry = d_obtain_alias(inode);
120+
121+
out_free_label:
122+
nfs4_label_free(label);
123+
out_free_fattr:
124+
nfs_free_fattr(fattr);
125+
out:
126+
return dentry;
127+
}
128+
129+
static struct dentry *
130+
nfs_get_parent(struct dentry *dentry)
131+
{
132+
int ret;
133+
struct inode *inode = d_inode(dentry), *pinode;
134+
struct super_block *sb = inode->i_sb;
135+
struct nfs_server *server = NFS_SB(sb);
136+
struct nfs_fattr *fattr = NULL;
137+
struct nfs4_label *label = NULL;
138+
struct dentry *parent;
139+
struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
140+
struct nfs_fh fh;
141+
142+
if (!ops->lookupp)
143+
return ERR_PTR(-EACCES);
144+
145+
fattr = nfs_alloc_fattr();
146+
if (fattr == NULL) {
147+
parent = ERR_PTR(-ENOMEM);
148+
goto out;
149+
}
150+
151+
label = nfs4_label_alloc(server, GFP_KERNEL);
152+
if (IS_ERR(label)) {
153+
parent = ERR_CAST(label);
154+
goto out_free_fattr;
155+
}
156+
157+
ret = ops->lookupp(inode, &fh, fattr, label);
158+
if (ret) {
159+
parent = ERR_PTR(ret);
160+
goto out_free_label;
161+
}
162+
163+
pinode = nfs_fhget(sb, &fh, fattr, label);
164+
parent = d_obtain_alias(pinode);
165+
out_free_label:
166+
nfs4_label_free(label);
167+
out_free_fattr:
168+
nfs_free_fattr(fattr);
169+
out:
170+
return parent;
171+
}
172+
173+
const struct export_operations nfs_export_ops = {
174+
.encode_fh = nfs_encode_fh,
175+
.fh_to_dentry = nfs_fh_to_dentry,
176+
.get_parent = nfs_get_parent,
177+
};

fs/nfs/internal.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@
1010

1111
#define NFS_MS_MASK (MS_RDONLY|MS_NOSUID|MS_NODEV|MS_NOEXEC|MS_SYNCHRONOUS)
1212

13+
extern const struct export_operations nfs_export_ops;
14+
1315
struct nfs_string;
1416

1517
/* Maximum number of readahead requests

fs/nfs/super.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2339,6 +2339,7 @@ void nfs_fill_super(struct super_block *sb, struct nfs_mount_info *mount_info)
23392339
*/
23402340
sb->s_flags |= MS_POSIXACL;
23412341
sb->s_time_gran = 1;
2342+
sb->s_export_op = &nfs_export_ops;
23422343
}
23432344

23442345
nfs_initialise_sb(sb);
@@ -2360,6 +2361,7 @@ static void nfs_clone_super(struct super_block *sb,
23602361
sb->s_xattr = old_sb->s_xattr;
23612362
sb->s_op = old_sb->s_op;
23622363
sb->s_time_gran = 1;
2364+
sb->s_export_op = old_sb->s_export_op;
23632365

23642366
if (server->nfs_client->rpc_ops->version != 2) {
23652367
/* The VFS shouldn't apply the umask to mode bits. We will do

0 commit comments

Comments
 (0)