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