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