Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0
0002 /*
0003  * Copyright (c) 2015, Primary Data, Inc. All rights reserved.
0004  *
0005  * Tao Peng <bergwolf@primarydata.com>
0006  */
0007 #include <linux/dcache.h>
0008 #include <linux/exportfs.h>
0009 #include <linux/nfs.h>
0010 #include <linux/nfs_fs.h>
0011 
0012 #include "internal.h"
0013 #include "nfstrace.h"
0014 
0015 #define NFSDBG_FACILITY     NFSDBG_VFS
0016 
0017 enum {
0018     FILEID_HIGH_OFF = 0,    /* inode fileid high */
0019     FILEID_LOW_OFF,     /* inode fileid low */
0020     FILE_I_TYPE_OFF,    /* inode type */
0021     EMBED_FH_OFF        /* embeded server fh */
0022 };
0023 
0024 
0025 static struct nfs_fh *nfs_exp_embedfh(__u32 *p)
0026 {
0027     return (struct nfs_fh *)(p + EMBED_FH_OFF);
0028 }
0029 
0030 /*
0031  * Let's break subtree checking for now... otherwise we'll have to embed parent fh
0032  * but there might not be enough space.
0033  */
0034 static int
0035 nfs_encode_fh(struct inode *inode, __u32 *p, int *max_len, struct inode *parent)
0036 {
0037     struct nfs_fh *server_fh = NFS_FH(inode);
0038     struct nfs_fh *clnt_fh = nfs_exp_embedfh(p);
0039     size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
0040     int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
0041 
0042     dprintk("%s: max fh len %d inode %p parent %p",
0043         __func__, *max_len, inode, parent);
0044 
0045     if (*max_len < len || IS_AUTOMOUNT(inode)) {
0046         dprintk("%s: fh len %d too small, required %d\n",
0047             __func__, *max_len, len);
0048         *max_len = len;
0049         return FILEID_INVALID;
0050     }
0051 
0052     p[FILEID_HIGH_OFF] = NFS_FILEID(inode) >> 32;
0053     p[FILEID_LOW_OFF] = NFS_FILEID(inode);
0054     p[FILE_I_TYPE_OFF] = inode->i_mode & S_IFMT;
0055     p[len - 1] = 0; /* Padding */
0056     nfs_copy_fh(clnt_fh, server_fh);
0057     *max_len = len;
0058     dprintk("%s: result fh fileid %llu mode %u size %d\n",
0059         __func__, NFS_FILEID(inode), inode->i_mode, *max_len);
0060     return *max_len;
0061 }
0062 
0063 static struct dentry *
0064 nfs_fh_to_dentry(struct super_block *sb, struct fid *fid,
0065          int fh_len, int fh_type)
0066 {
0067     struct nfs_fattr *fattr = NULL;
0068     struct nfs_fh *server_fh = nfs_exp_embedfh(fid->raw);
0069     size_t fh_size = offsetof(struct nfs_fh, data) + server_fh->size;
0070     const struct nfs_rpc_ops *rpc_ops;
0071     struct dentry *dentry;
0072     struct inode *inode;
0073     int len = EMBED_FH_OFF + XDR_QUADLEN(fh_size);
0074     u32 *p = fid->raw;
0075     int ret;
0076 
0077     /* NULL translates to ESTALE */
0078     if (fh_len < len || fh_type != len)
0079         return NULL;
0080 
0081     fattr = nfs_alloc_fattr_with_label(NFS_SB(sb));
0082     if (fattr == NULL) {
0083         dentry = ERR_PTR(-ENOMEM);
0084         goto out;
0085     }
0086 
0087     fattr->fileid = ((u64)p[FILEID_HIGH_OFF] << 32) + p[FILEID_LOW_OFF];
0088     fattr->mode = p[FILE_I_TYPE_OFF];
0089     fattr->valid |= NFS_ATTR_FATTR_FILEID | NFS_ATTR_FATTR_TYPE;
0090 
0091     dprintk("%s: fileid %llu mode %d\n", __func__, fattr->fileid, fattr->mode);
0092 
0093     inode = nfs_ilookup(sb, fattr, server_fh);
0094     if (inode)
0095         goto out_found;
0096 
0097     rpc_ops = NFS_SB(sb)->nfs_client->rpc_ops;
0098     ret = rpc_ops->getattr(NFS_SB(sb), server_fh, fattr, NULL);
0099     if (ret) {
0100         dprintk("%s: getattr failed %d\n", __func__, ret);
0101         trace_nfs_fh_to_dentry(sb, server_fh, fattr->fileid, ret);
0102         dentry = ERR_PTR(ret);
0103         goto out_free_fattr;
0104     }
0105 
0106     inode = nfs_fhget(sb, server_fh, fattr);
0107 
0108 out_found:
0109     dentry = d_obtain_alias(inode);
0110 out_free_fattr:
0111     nfs_free_fattr(fattr);
0112 out:
0113     return dentry;
0114 }
0115 
0116 static struct dentry *
0117 nfs_get_parent(struct dentry *dentry)
0118 {
0119     int ret;
0120     struct inode *inode = d_inode(dentry), *pinode;
0121     struct super_block *sb = inode->i_sb;
0122     struct nfs_server *server = NFS_SB(sb);
0123     struct nfs_fattr *fattr = NULL;
0124     struct dentry *parent;
0125     struct nfs_rpc_ops const *ops = server->nfs_client->rpc_ops;
0126     struct nfs_fh fh;
0127 
0128     if (!ops->lookupp)
0129         return ERR_PTR(-EACCES);
0130 
0131     fattr = nfs_alloc_fattr_with_label(server);
0132     if (fattr == NULL)
0133         return ERR_PTR(-ENOMEM);
0134 
0135     ret = ops->lookupp(inode, &fh, fattr);
0136     if (ret) {
0137         parent = ERR_PTR(ret);
0138         goto out;
0139     }
0140 
0141     pinode = nfs_fhget(sb, &fh, fattr);
0142     parent = d_obtain_alias(pinode);
0143 out:
0144     nfs_free_fattr(fattr);
0145     return parent;
0146 }
0147 
0148 static u64 nfs_fetch_iversion(struct inode *inode)
0149 {
0150     nfs_revalidate_inode(inode, NFS_INO_INVALID_CHANGE);
0151     return inode_peek_iversion_raw(inode);
0152 }
0153 
0154 const struct export_operations nfs_export_ops = {
0155     .encode_fh = nfs_encode_fh,
0156     .fh_to_dentry = nfs_fh_to_dentry,
0157     .get_parent = nfs_get_parent,
0158     .fetch_iversion = nfs_fetch_iversion,
0159     .flags = EXPORT_OP_NOWCC|EXPORT_OP_NOSUBTREECHK|
0160         EXPORT_OP_CLOSE_BEFORE_UNLINK|EXPORT_OP_REMOTE_FS|
0161         EXPORT_OP_NOATOMIC_ATTR,
0162 };