xref: /src/sys/contrib/openzfs/module/os/linux/zfs/zpl_export.c (revision 8a62a2a5659d1839d8799b4274c04469d7f17c78)
1 // SPDX-License-Identifier: CDDL-1.0
2 /*
3  * CDDL HEADER START
4  *
5  * The contents of this file are subject to the terms of the
6  * Common Development and Distribution License (the "License").
7  * You may not use this file except in compliance with the License.
8  *
9  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10  * or https://opensource.org/licenses/CDDL-1.0.
11  * See the License for the specific language governing permissions
12  * and limitations under the License.
13  *
14  * When distributing Covered Code, include this CDDL HEADER in each
15  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16  * If applicable, add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your own identifying
18  * information: Portions Copyright [yyyy] [name of copyright owner]
19  *
20  * CDDL HEADER END
21  */
22 /*
23  * Copyright (c) 2011 Gunnar Beutner
24  * Copyright (c) 2012 Cyril Plisko. All rights reserved.
25  */
26 
27 
28 #include <sys/file.h>
29 #include <sys/zfs_znode.h>
30 #include <sys/zfs_vnops.h>
31 #include <sys/zfs_ctldir.h>
32 #include <sys/zpl.h>
33 
34 
35 static int
zpl_encode_fh(struct inode * ip,__u32 * fh,int * max_len,struct inode * parent)36 zpl_encode_fh(struct inode *ip, __u32 *fh, int *max_len, struct inode *parent)
37 {
38 	fstrans_cookie_t cookie;
39 	ushort_t empty_fid = 0;
40 	fid_t *fid, *pfid;
41 	int len_bytes, required_len, parent_len, rc, prc, fh_type;
42 
43 	len_bytes = *max_len * sizeof (__u32);
44 
45 	if (len_bytes < offsetof(fid_t, fid_data)) {
46 		fid = (fid_t *)&empty_fid;
47 	} else {
48 		fid = (fid_t *)fh;
49 		fid->fid_len = len_bytes - offsetof(fid_t, fid_data);
50 	}
51 
52 	cookie = spl_fstrans_mark();
53 
54 	if (zfsctl_is_node(ip))
55 		rc = zfsctl_fid(ip, fid);
56 	else
57 		rc = zfs_fid(ip, fid);
58 
59 	required_len = offsetof(fid_t, fid_data) + fid->fid_len;
60 
61 	/*
62 	 * Kernel has requested that the resulting file handle contain
63 	 * a reference to the provided parent. This typically would happen
64 	 * if the NFS export has subtree checking enabled.
65 	 */
66 	if (parent != NULL) {
67 		if ((rc == 0) && (len_bytes >
68 		    required_len + offsetof(fid_t, fid_data))) {
69 			parent_len = len_bytes - required_len;
70 			pfid = (fid_t *)((char *)fh + required_len);
71 			pfid->fid_len = parent_len - offsetof(fid_t, fid_data);
72 		} else {
73 			empty_fid = 0;
74 			pfid = (fid_t *)&empty_fid;
75 		}
76 
77 		if (zfsctl_is_node(parent))
78 			prc = zfsctl_fid(parent, pfid);
79 		else
80 			prc = zfs_fid(parent, pfid);
81 
82 		if (rc == 0 && prc != 0)
83 			rc = prc;
84 
85 		required_len += offsetof(fid_t, fid_data) +
86 		    pfid->fid_len;
87 		fh_type = FILEID_INO32_GEN_PARENT;
88 	} else {
89 		fh_type = FILEID_INO32_GEN;
90 	}
91 
92 	spl_fstrans_unmark(cookie);
93 
94 	*max_len = roundup(required_len, sizeof (__u32)) / sizeof (__u32);
95 
96 	return (rc == 0 ? fh_type : FILEID_INVALID);
97 }
98 
99 static struct dentry *
zpl_fh_to_dentry(struct super_block * sb,struct fid * fh,int fh_len,int fh_type)100 zpl_fh_to_dentry(struct super_block *sb, struct fid *fh,
101     int fh_len, int fh_type)
102 {
103 	fid_t *fid = (fid_t *)fh;
104 	fstrans_cookie_t cookie;
105 	struct inode *ip;
106 	int len_bytes, rc;
107 
108 	len_bytes = fh_len * sizeof (__u32);
109 
110 	if ((fh_type != FILEID_INO32_GEN &&
111 	    fh_type != FILEID_INO32_GEN_PARENT) ||
112 	    len_bytes < offsetof(fid_t, fid_data) ||
113 	    len_bytes < offsetof(fid_t, fid_data) + fid->fid_len)
114 		return (ERR_PTR(-EINVAL));
115 
116 	cookie = spl_fstrans_mark();
117 	rc = zfs_vget(sb, &ip, fid);
118 	spl_fstrans_unmark(cookie);
119 
120 	if (rc) {
121 		/*
122 		 * If we see ENOENT it might mean that an NFSv4 * client
123 		 * is using a cached inode value in a file handle and
124 		 * that the sought after file has had its inode changed
125 		 * by a third party.  So change the error to ESTALE
126 		 * which will trigger a full lookup by the client and
127 		 * will find the new filename/inode pair if it still
128 		 * exists.
129 		 */
130 		if (rc == ENOENT)
131 			rc = ESTALE;
132 
133 		return (ERR_PTR(-rc));
134 	}
135 
136 	ASSERT((ip != NULL) && !IS_ERR(ip));
137 
138 	return (d_obtain_alias(ip));
139 }
140 
141 static struct dentry *
zpl_fh_to_parent(struct super_block * sb,struct fid * fh,int fh_len,int fh_type)142 zpl_fh_to_parent(struct super_block *sb, struct fid *fh,
143     int fh_len, int fh_type)
144 {
145 	/*
146 	 * Convert the provided struct fid to a dentry for the parent
147 	 * This is possible only if it was created with the parent,
148 	 * e.g. type is FILEID_INO32_GEN_PARENT. When this type of
149 	 * filehandle is created we simply pack the parent fid_t
150 	 * after the entry's fid_t. So this function will adjust
151 	 * offset in the provided buffer to the begining of the
152 	 * parent fid_t and call zpl_fh_to_dentry() on it.
153 	 */
154 	fid_t *fid = (fid_t *)fh;
155 	fid_t *pfid;
156 	int len_bytes, parent_len_bytes, child_fid_bytes, parent_fh_len;
157 
158 	len_bytes = fh_len * sizeof (__u32);
159 
160 	if ((fh_type != FILEID_INO32_GEN_PARENT) ||
161 	    len_bytes < offsetof(fid_t, fid_data) ||
162 	    len_bytes < offsetof(fid_t, fid_data) + fid->fid_len)
163 		return (ERR_PTR(-EINVAL));
164 
165 	child_fid_bytes = offsetof(fid_t, fid_data) + fid->fid_len;
166 	parent_len_bytes = len_bytes - child_fid_bytes;
167 
168 	if (parent_len_bytes < offsetof(fid_t, fid_data))
169 		return (ERR_PTR(-EINVAL));
170 
171 	pfid = (fid_t *)((char *)fh + child_fid_bytes);
172 
173 	if (parent_len_bytes < offsetof(fid_t, fid_data) + pfid->fid_len)
174 		return (ERR_PTR(-EINVAL));
175 
176 	parent_fh_len = parent_len_bytes / sizeof (__u32);
177 	return (zpl_fh_to_dentry(sb, (struct fid *)pfid, parent_fh_len,
178 	    FILEID_INO32_GEN));
179 }
180 
181 /*
182  * In case the filesystem contains name longer than 255, we need to override
183  * the default get_name so we don't get buffer overflow. Unfortunately, since
184  * the buffer size is hardcoded in Linux, we will get ESTALE error in this
185  * case.
186  */
187 static int
zpl_get_name(struct dentry * parent,char * name,struct dentry * child)188 zpl_get_name(struct dentry *parent, char *name, struct dentry *child)
189 {
190 	cred_t *cr = CRED();
191 	fstrans_cookie_t cookie;
192 	struct inode *dir = parent->d_inode;
193 	struct inode *ip = child->d_inode;
194 	int error;
195 
196 	if (!dir || !S_ISDIR(dir->i_mode))
197 		return (-ENOTDIR);
198 
199 	crhold(cr);
200 	cookie = spl_fstrans_mark();
201 	spl_inode_lock_shared(dir);
202 	error = -zfs_get_name(ITOZ(dir), name, ITOZ(ip));
203 	spl_inode_unlock_shared(dir);
204 	spl_fstrans_unmark(cookie);
205 	crfree(cr);
206 
207 	return (error);
208 }
209 
210 static struct dentry *
zpl_get_parent(struct dentry * child)211 zpl_get_parent(struct dentry *child)
212 {
213 	cred_t *cr = CRED();
214 	fstrans_cookie_t cookie;
215 	znode_t *zp;
216 	int error;
217 
218 	crhold(cr);
219 	cookie = spl_fstrans_mark();
220 	error = -zfs_lookup(ITOZ(child->d_inode), "..", &zp, 0, cr, NULL, NULL);
221 	spl_fstrans_unmark(cookie);
222 	crfree(cr);
223 	ASSERT3S(error, <=, 0);
224 
225 	if (error)
226 		return (ERR_PTR(error));
227 
228 	return (d_obtain_alias(ZTOI(zp)));
229 }
230 
231 static int
zpl_commit_metadata(struct inode * inode)232 zpl_commit_metadata(struct inode *inode)
233 {
234 	cred_t *cr = CRED();
235 	fstrans_cookie_t cookie;
236 	int error;
237 
238 	if (zfsctl_is_node(inode))
239 		return (0);
240 
241 	crhold(cr);
242 	cookie = spl_fstrans_mark();
243 	error = -zfs_fsync(ITOZ(inode), 0, cr);
244 	spl_fstrans_unmark(cookie);
245 	crfree(cr);
246 	ASSERT3S(error, <=, 0);
247 
248 	return (error);
249 }
250 
251 const struct export_operations zpl_export_operations = {
252 	.encode_fh		= zpl_encode_fh,
253 	.fh_to_dentry		= zpl_fh_to_dentry,
254 	.fh_to_parent		= zpl_fh_to_parent,
255 	.get_name		= zpl_get_name,
256 	.get_parent		= zpl_get_parent,
257 	.commit_metadata	= zpl_commit_metadata,
258 };
259