18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * Copyright (c) 2015, Primary Data, Inc. All rights reserved.
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Tao Peng <bergwolf@primarydata.com>
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci#include <linux/dcache.h>
88c2ecf20Sopenharmony_ci#include <linux/exportfs.h>
98c2ecf20Sopenharmony_ci#include <linux/nfs.h>
108c2ecf20Sopenharmony_ci#include <linux/nfs_fs.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include "internal.h"
138c2ecf20Sopenharmony_ci#include "nfstrace.h"
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ci#define NFSDBG_FACILITY		NFSDBG_VFS
168c2ecf20Sopenharmony_ci
178c2ecf20Sopenharmony_cienum {
188c2ecf20Sopenharmony_ci	FILEID_HIGH_OFF = 0,	/* inode fileid high */
198c2ecf20Sopenharmony_ci	FILEID_LOW_OFF,		/* inode fileid low */
208c2ecf20Sopenharmony_ci	FILE_I_TYPE_OFF,	/* inode type */
218c2ecf20Sopenharmony_ci	EMBED_FH_OFF		/* embeded server fh */
228c2ecf20Sopenharmony_ci};
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_cistatic struct nfs_fh *nfs_exp_embedfh(__u32 *p)
268c2ecf20Sopenharmony_ci{
278c2ecf20Sopenharmony_ci	return (struct nfs_fh *)(p + EMBED_FH_OFF);
288c2ecf20Sopenharmony_ci}
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_ci/*
318c2ecf20Sopenharmony_ci * Let's break subtree checking for now... otherwise we'll have to embed parent fh
328c2ecf20Sopenharmony_ci * but there might not be enough space.
338c2ecf20Sopenharmony_ci */
348c2ecf20Sopenharmony_cistatic int
358c2ecf20Sopenharmony_cinfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
368c2ecf20Sopenharmony_ci{
378c2ecf20Sopenharmony_ci	struct nfs_fh *server_fh = NFS_FH(inode);
388c2ecf20Sopenharmony_ci	struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
398c2ecf20Sopenharmony_ci	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
408c2ecf20Sopenharmony_ci	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
418c2ecf20Sopenharmony_ci
428c2ecf20Sopenharmony_ci	dprintk("%s: max fh len %d inode %p parent %p",
438c2ecf20Sopenharmony_ci		__func__, *max_len, inode, parent);
448c2ecf20Sopenharmony_ci
458c2ecf20Sopenharmony_ci	if (*max_len < len || IS_AUTOMOUNT(inode)) {
468c2ecf20Sopenharmony_ci		dprintk("%s: fh len %d too small, required %d\n",
478c2ecf20Sopenharmony_ci			__func__, *max_len, len);
488c2ecf20Sopenharmony_ci		*max_len = len;
498c2ecf20Sopenharmony_ci		return FILEID_INVALID;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
538c2ecf20Sopenharmony_ci	p[FILEID_LOW_OFF] = NFS_FILEID(inode);
548c2ecf20Sopenharmony_ci	p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
558c2ecf20Sopenharmony_ci	p[len - 1] = 0; /* Padding */
568c2ecf20Sopenharmony_ci	nfs_copy_fh(clnt_fh, server_fh);
578c2ecf20Sopenharmony_ci	*max_len = len;
588c2ecf20Sopenharmony_ci	dprintk("%s: result fh fileid %llu mode %u size %d\n",
598c2ecf20Sopenharmony_ci		__func__, NFS_FILEID(inode), inode->i_mode, *max_len);
608c2ecf20Sopenharmony_ci	return *max_len;
618c2ecf20Sopenharmony_ci}
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cistatic struct dentry *
648c2ecf20Sopenharmony_cinfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
658c2ecf20Sopenharmony_ci		 int fh_len, int fh_type)
668c2ecf20Sopenharmony_ci{
678c2ecf20Sopenharmony_ci	struct nfs4_label *label = NULL;
688c2ecf20Sopenharmony_ci	struct nfs_fattr *fattr = NULL;
698c2ecf20Sopenharmony_ci	struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
708c2ecf20Sopenharmony_ci	size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
718c2ecf20Sopenharmony_ci	const struct nfs_rpc_ops *rpc_ops;
728c2ecf20Sopenharmony_ci	struct dentry *dentry;
738c2ecf20Sopenharmony_ci	struct inode *inode;
748c2ecf20Sopenharmony_ci	int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
758c2ecf20Sopenharmony_ci	u32 *p = fid->raw;
768c2ecf20Sopenharmony_ci	int ret;
778c2ecf20Sopenharmony_ci
788c2ecf20Sopenharmony_ci	/* NULL translates to ESTALE */
798c2ecf20Sopenharmony_ci	if (fh_len < len || fh_type != len)
808c2ecf20Sopenharmony_ci		return NULL;
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	fattr = nfs_alloc_fattr();
838c2ecf20Sopenharmony_ci	if (fattr == NULL) {
848c2ecf20Sopenharmony_ci		dentry = ERR_PTR(-ENOMEM);
858c2ecf20Sopenharmony_ci		goto out;
868c2ecf20Sopenharmony_ci	}
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
898c2ecf20Sopenharmony_ci	fattr->mode = p[FILE_I_TYPE_OFF];
908c2ecf20Sopenharmony_ci	fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
918c2ecf20Sopenharmony_ci
928c2ecf20Sopenharmony_ci	dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci	inode = nfs_ilookup(sb, fattr, server_fh);
958c2ecf20Sopenharmony_ci	if (inode)
968c2ecf20Sopenharmony_ci		goto out_found;
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	label = nfs4_label_alloc(NFS_SB(sb), GFP_KERNEL);
998c2ecf20Sopenharmony_ci	if (IS_ERR(label)) {
1008c2ecf20Sopenharmony_ci		dentry = ERR_CAST(label);
1018c2ecf20Sopenharmony_ci		goto out_free_fattr;
1028c2ecf20Sopenharmony_ci	}
1038c2ecf20Sopenharmony_ci
1048c2ecf20Sopenharmony_ci	rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
1058c2ecf20Sopenharmony_ci	ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, label, NULL);
1068c2ecf20Sopenharmony_ci	if (ret) {
1078c2ecf20Sopenharmony_ci		dprintk("%s: getattr failed %d\n", __func__, ret);
1088c2ecf20Sopenharmony_ci		trace_nfs_fh_to_dentry(sb, server_fh, fattr->fileid, ret);
1098c2ecf20Sopenharmony_ci		dentry = ERR_PTR(ret);
1108c2ecf20Sopenharmony_ci		goto out_free_label;
1118c2ecf20Sopenharmony_ci	}
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	inode = nfs_fhget(sb, server_fh, fattr, label);
1148c2ecf20Sopenharmony_ci
1158c2ecf20Sopenharmony_ciout_found:
1168c2ecf20Sopenharmony_ci	dentry = d_obtain_alias(inode);
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ciout_free_label:
1198c2ecf20Sopenharmony_ci	nfs4_label_free(label);
1208c2ecf20Sopenharmony_ciout_free_fattr:
1218c2ecf20Sopenharmony_ci	nfs_free_fattr(fattr);
1228c2ecf20Sopenharmony_ciout:
1238c2ecf20Sopenharmony_ci	return dentry;
1248c2ecf20Sopenharmony_ci}
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_cistatic struct dentry *
1278c2ecf20Sopenharmony_cinfs_get_parent(struct dentry *dentry)
1288c2ecf20Sopenharmony_ci{
1298c2ecf20Sopenharmony_ci	int ret;
1308c2ecf20Sopenharmony_ci	struct inode *inode = d_inode(dentry), *pinode;
1318c2ecf20Sopenharmony_ci	struct super_block *sb = inode->i_sb;
1328c2ecf20Sopenharmony_ci	struct nfs_server *server = NFS_SB(sb);
1338c2ecf20Sopenharmony_ci	struct nfs_fattr *fattr = NULL;
1348c2ecf20Sopenharmony_ci	struct nfs4_label *label = NULL;
1358c2ecf20Sopenharmony_ci	struct dentry *parent;
1368c2ecf20Sopenharmony_ci	struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
1378c2ecf20Sopenharmony_ci	struct nfs_fh fh;
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ci	if (!ops->lookupp)
1408c2ecf20Sopenharmony_ci		return ERR_PTR(-EACCES);
1418c2ecf20Sopenharmony_ci
1428c2ecf20Sopenharmony_ci	fattr = nfs_alloc_fattr();
1438c2ecf20Sopenharmony_ci	if (fattr == NULL) {
1448c2ecf20Sopenharmony_ci		parent = ERR_PTR(-ENOMEM);
1458c2ecf20Sopenharmony_ci		goto out;
1468c2ecf20Sopenharmony_ci	}
1478c2ecf20Sopenharmony_ci
1488c2ecf20Sopenharmony_ci	label = nfs4_label_alloc(server, GFP_KERNEL);
1498c2ecf20Sopenharmony_ci	if (IS_ERR(label)) {
1508c2ecf20Sopenharmony_ci		parent = ERR_CAST(label);
1518c2ecf20Sopenharmony_ci		goto out_free_fattr;
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	ret = ops->lookupp(inode, &fh, fattr, label);
1558c2ecf20Sopenharmony_ci	if (ret) {
1568c2ecf20Sopenharmony_ci		parent = ERR_PTR(ret);
1578c2ecf20Sopenharmony_ci		goto out_free_label;
1588c2ecf20Sopenharmony_ci	}
1598c2ecf20Sopenharmony_ci
1608c2ecf20Sopenharmony_ci	pinode = nfs_fhget(sb, &fh, fattr, label);
1618c2ecf20Sopenharmony_ci	parent = d_obtain_alias(pinode);
1628c2ecf20Sopenharmony_ciout_free_label:
1638c2ecf20Sopenharmony_ci	nfs4_label_free(label);
1648c2ecf20Sopenharmony_ciout_free_fattr:
1658c2ecf20Sopenharmony_ci	nfs_free_fattr(fattr);
1668c2ecf20Sopenharmony_ciout:
1678c2ecf20Sopenharmony_ci	return parent;
1688c2ecf20Sopenharmony_ci}
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ciconst struct export_operations nfs_export_ops = {
1718c2ecf20Sopenharmony_ci	.encode_fh = nfs_encode_fh,
1728c2ecf20Sopenharmony_ci	.fh_to_dentry = nfs_fh_to_dentry,
1738c2ecf20Sopenharmony_ci	.get_parent = nfs_get_parent,
1748c2ecf20Sopenharmony_ci};
175