1 /* 2 * Copyright (c) 2015, Primary Data, Inc. All rights reserved. 3 * 4 * Tao Peng <bergwolf@primarydata.com> 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 }; 178