1b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
220fa1902SPeng Tao /*
320fa1902SPeng Tao * Copyright (c) 2015, Primary Data, Inc. All rights reserved.
420fa1902SPeng Tao *
520fa1902SPeng Tao * Tao Peng <bergwolf@primarydata.com>
620fa1902SPeng Tao */
720fa1902SPeng Tao #include <linux/dcache.h>
820fa1902SPeng Tao #include <linux/exportfs.h>
920fa1902SPeng Tao #include <linux/nfs.h>
1020fa1902SPeng Tao #include <linux/nfs_fs.h>
1120fa1902SPeng Tao
1220fa1902SPeng Tao #include "internal.h"
1320fa1902SPeng Tao #include "nfstrace.h"
1420fa1902SPeng Tao
1520fa1902SPeng Tao #define NFSDBG_FACILITY NFSDBG_VFS
1620fa1902SPeng Tao
1720fa1902SPeng Tao enum {
1820fa1902SPeng Tao FILEID_HIGH_OFF = 0, /* inode fileid high */
1920fa1902SPeng Tao FILEID_LOW_OFF, /* inode fileid low */
2020fa1902SPeng Tao FILE_I_TYPE_OFF, /* inode type */
2120fa1902SPeng Tao EMBED_FH_OFF /* embeded server fh */
2220fa1902SPeng Tao };
2320fa1902SPeng Tao
2420fa1902SPeng Tao
nfs_exp_embedfh(__u32 * p)2520fa1902SPeng Tao static struct nfs_fh *nfs_exp_embedfh(__u32 *p)
2620fa1902SPeng Tao {
2720fa1902SPeng Tao return (struct nfs_fh *)(p + EMBED_FH_OFF);
2820fa1902SPeng Tao }
2920fa1902SPeng Tao
3020fa1902SPeng Tao /*
3120fa1902SPeng Tao * Let's break subtree checking for now... otherwise we'll have to embed parent fh
3220fa1902SPeng Tao * but there might not be enough space.
3320fa1902SPeng Tao */
3420fa1902SPeng Tao static int
nfs_encode_fh(struct inode * inode,__u32 * p,int * max_len,struct inode * parent)3520fa1902SPeng Tao nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
3620fa1902SPeng Tao {
3720fa1902SPeng Tao struct nfs_fh *server_fh = NFS_FH(inode);
3820fa1902SPeng Tao struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
3920fa1902SPeng Tao size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
4020fa1902SPeng Tao int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
4120fa1902SPeng Tao
4220fa1902SPeng Tao dprintk("%s: max fh len %d inode %p parent %p",
4320fa1902SPeng Tao __func__, *max_len, inode, parent);
4420fa1902SPeng Tao
45f78e4454SRichard Weinberger if (*max_len < len) {
4620fa1902SPeng Tao dprintk("%s: fh len %d too small, required %d\n",
4720fa1902SPeng Tao __func__, *max_len, len);
4820fa1902SPeng Tao *max_len = len;
4920fa1902SPeng Tao return FILEID_INVALID;
5020fa1902SPeng Tao }
5120fa1902SPeng Tao
5220fa1902SPeng Tao p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
5320fa1902SPeng Tao p[FILEID_LOW_OFF] = NFS_FILEID(inode);
5420fa1902SPeng Tao p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
5520fa1902SPeng Tao p[len - 1] = 0; /* Padding */
5620fa1902SPeng Tao nfs_copy_fh(clnt_fh, server_fh);
5720fa1902SPeng Tao *max_len = len;
5820fa1902SPeng Tao dprintk("%s: result fh fileid %llu mode %u size %d\n",
5920fa1902SPeng Tao __func__, NFS_FILEID(inode), inode->i_mode, *max_len);
6020fa1902SPeng Tao return *max_len;
6120fa1902SPeng Tao }
6220fa1902SPeng Tao
6320fa1902SPeng Tao static struct dentry *
nfs_fh_to_dentry(struct super_block * sb,struct fid * fid,int fh_len,int fh_type)6420fa1902SPeng Tao nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
6520fa1902SPeng Tao int fh_len, int fh_type)
6620fa1902SPeng Tao {
6720fa1902SPeng Tao struct nfs_fattr *fattr = NULL;
6820fa1902SPeng Tao struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
6920fa1902SPeng Tao size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
7020fa1902SPeng Tao const struct nfs_rpc_ops *rpc_ops;
7120fa1902SPeng Tao struct dentry *dentry;
7220fa1902SPeng Tao struct inode *inode;
7320fa1902SPeng Tao int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
7420fa1902SPeng Tao u32 *p = fid->raw;
7520fa1902SPeng Tao int ret;
7620fa1902SPeng Tao
7720fa1902SPeng Tao /* NULL translates to ESTALE */
7820fa1902SPeng Tao if (fh_len < len || fh_type != len)
7920fa1902SPeng Tao return NULL;
8020fa1902SPeng Tao
812ef61e0eSAnna Schumaker fattr = nfs_alloc_fattr_with_label(NFS_SB(sb));
8220fa1902SPeng Tao if (fattr == NULL) {
8320fa1902SPeng Tao dentry = ERR_PTR(-ENOMEM);
8420fa1902SPeng Tao goto out;
8520fa1902SPeng Tao }
8620fa1902SPeng Tao
8720fa1902SPeng Tao fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
8820fa1902SPeng Tao fattr->mode = p[FILE_I_TYPE_OFF];
8920fa1902SPeng Tao fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
9020fa1902SPeng Tao
9120fa1902SPeng Tao dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
9220fa1902SPeng Tao
9320fa1902SPeng Tao inode = nfs_ilookup(sb, fattr, server_fh);
9420fa1902SPeng Tao if (inode)
9520fa1902SPeng Tao goto out_found;
9620fa1902SPeng Tao
9720fa1902SPeng Tao rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
982ef61e0eSAnna Schumaker ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, NULL);
9920fa1902SPeng Tao if (ret) {
10020fa1902SPeng Tao dprintk("%s: getattr failed %d\n", __func__, ret);
10143622eabSTrond Myklebust trace_nfs_fh_to_dentry(sb, server_fh, fattr->fileid, ret);
10220fa1902SPeng Tao dentry = ERR_PTR(ret);
1032ef61e0eSAnna Schumaker goto out_free_fattr;
10420fa1902SPeng Tao }
10520fa1902SPeng Tao
106cf7ab00aSAnna Schumaker inode = nfs_fhget(sb, server_fh, fattr);
10720fa1902SPeng Tao
10820fa1902SPeng Tao out_found:
10920fa1902SPeng Tao dentry = d_obtain_alias(inode);
11020fa1902SPeng Tao out_free_fattr:
11120fa1902SPeng Tao nfs_free_fattr(fattr);
11220fa1902SPeng Tao out:
11320fa1902SPeng Tao return dentry;
11420fa1902SPeng Tao }
11520fa1902SPeng Tao
11620fa1902SPeng Tao static struct dentry *
nfs_get_parent(struct dentry * dentry)11720fa1902SPeng Tao nfs_get_parent(struct dentry *dentry)
11820fa1902SPeng Tao {
11920fa1902SPeng Tao int ret;
12020fa1902SPeng Tao struct inode *inode = d_inode(dentry), *pinode;
12120fa1902SPeng Tao struct super_block *sb = inode->i_sb;
12220fa1902SPeng Tao struct nfs_server *server = NFS_SB(sb);
12320fa1902SPeng Tao struct nfs_fattr *fattr = NULL;
12420fa1902SPeng Tao struct dentry *parent;
12520fa1902SPeng Tao struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
12620fa1902SPeng Tao struct nfs_fh fh;
12720fa1902SPeng Tao
12820fa1902SPeng Tao if (!ops->lookupp)
12920fa1902SPeng Tao return ERR_PTR(-EACCES);
13020fa1902SPeng Tao
131ba4bc8dcSAnna Schumaker fattr = nfs_alloc_fattr_with_label(server);
132ba4bc8dcSAnna Schumaker if (fattr == NULL)
133ba4bc8dcSAnna Schumaker return ERR_PTR(-ENOMEM);
134ba4bc8dcSAnna Schumaker
135ba4bc8dcSAnna Schumaker ret = ops->lookupp(inode, &fh, fattr);
136ba4bc8dcSAnna Schumaker if (ret) {
137ba4bc8dcSAnna Schumaker parent = ERR_PTR(ret);
13820fa1902SPeng Tao goto out;
13920fa1902SPeng Tao }
14020fa1902SPeng Tao
141cf7ab00aSAnna Schumaker pinode = nfs_fhget(sb, &fh, fattr);
14220fa1902SPeng Tao parent = d_obtain_alias(pinode);
14320fa1902SPeng Tao out:
144ba4bc8dcSAnna Schumaker nfs_free_fattr(fattr);
14520fa1902SPeng Tao return parent;
14620fa1902SPeng Tao }
14720fa1902SPeng Tao
14820fa1902SPeng Tao const struct export_operations nfs_export_ops = {
14920fa1902SPeng Tao .encode_fh = nfs_encode_fh,
15020fa1902SPeng Tao .fh_to_dentry = nfs_fh_to_dentry,
15120fa1902SPeng Tao .get_parent = nfs_get_parent,
152*dcb779fcSJeff Layton .flags = EXPORT_OP_NOWCC |
153*dcb779fcSJeff Layton EXPORT_OP_NOSUBTREECHK |
154*dcb779fcSJeff Layton EXPORT_OP_CLOSE_BEFORE_UNLINK |
155*dcb779fcSJeff Layton EXPORT_OP_REMOTE_FS |
156*dcb779fcSJeff Layton EXPORT_OP_NOATOMIC_ATTR |
157*dcb779fcSJeff Layton EXPORT_OP_FLUSH_ON_CLOSE,
15820fa1902SPeng Tao };
159