xref: /freebsd/sys/contrib/openzfs/module/os/linux/zfs/zpl_export.c (revision 61145dc2b94f12f6a47344fb9aac702321880e43)
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;
41 	int len_bytes, rc;
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 	spl_fstrans_unmark(cookie);
60 	len_bytes = offsetof(fid_t, fid_data) + fid->fid_len;
61 	*max_len = roundup(len_bytes, sizeof (__u32)) / sizeof (__u32);
62 
63 	return (rc == 0 ? FILEID_INO32_GEN : 255);
64 }
65 
66 static struct dentry *
zpl_fh_to_dentry(struct super_block * sb,struct fid * fh,int fh_len,int fh_type)67 zpl_fh_to_dentry(struct super_block *sb, struct fid *fh,
68     int fh_len, int fh_type)
69 {
70 	fid_t *fid = (fid_t *)fh;
71 	fstrans_cookie_t cookie;
72 	struct inode *ip;
73 	int len_bytes, rc;
74 
75 	len_bytes = fh_len * sizeof (__u32);
76 
77 	if (fh_type != FILEID_INO32_GEN ||
78 	    len_bytes < offsetof(fid_t, fid_data) ||
79 	    len_bytes < offsetof(fid_t, fid_data) + fid->fid_len)
80 		return (ERR_PTR(-EINVAL));
81 
82 	cookie = spl_fstrans_mark();
83 	rc = zfs_vget(sb, &ip, fid);
84 	spl_fstrans_unmark(cookie);
85 
86 	if (rc) {
87 		/*
88 		 * If we see ENOENT it might mean that an NFSv4 * client
89 		 * is using a cached inode value in a file handle and
90 		 * that the sought after file has had its inode changed
91 		 * by a third party.  So change the error to ESTALE
92 		 * which will trigger a full lookup by the client and
93 		 * will find the new filename/inode pair if it still
94 		 * exists.
95 		 */
96 		if (rc == ENOENT)
97 			rc = ESTALE;
98 
99 		return (ERR_PTR(-rc));
100 	}
101 
102 	ASSERT((ip != NULL) && !IS_ERR(ip));
103 
104 	return (d_obtain_alias(ip));
105 }
106 
107 /*
108  * In case the filesystem contains name longer than 255, we need to override
109  * the default get_name so we don't get buffer overflow. Unfortunately, since
110  * the buffer size is hardcoded in Linux, we will get ESTALE error in this
111  * case.
112  */
113 static int
zpl_get_name(struct dentry * parent,char * name,struct dentry * child)114 zpl_get_name(struct dentry *parent, char *name, struct dentry *child)
115 {
116 	cred_t *cr = CRED();
117 	fstrans_cookie_t cookie;
118 	struct inode *dir = parent->d_inode;
119 	struct inode *ip = child->d_inode;
120 	int error;
121 
122 	if (!dir || !S_ISDIR(dir->i_mode))
123 		return (-ENOTDIR);
124 
125 	crhold(cr);
126 	cookie = spl_fstrans_mark();
127 	spl_inode_lock_shared(dir);
128 	error = -zfs_get_name(ITOZ(dir), name, ITOZ(ip));
129 	spl_inode_unlock_shared(dir);
130 	spl_fstrans_unmark(cookie);
131 	crfree(cr);
132 
133 	return (error);
134 }
135 
136 static struct dentry *
zpl_get_parent(struct dentry * child)137 zpl_get_parent(struct dentry *child)
138 {
139 	cred_t *cr = CRED();
140 	fstrans_cookie_t cookie;
141 	znode_t *zp;
142 	int error;
143 
144 	crhold(cr);
145 	cookie = spl_fstrans_mark();
146 	error = -zfs_lookup(ITOZ(child->d_inode), "..", &zp, 0, cr, NULL, NULL);
147 	spl_fstrans_unmark(cookie);
148 	crfree(cr);
149 	ASSERT3S(error, <=, 0);
150 
151 	if (error)
152 		return (ERR_PTR(error));
153 
154 	return (d_obtain_alias(ZTOI(zp)));
155 }
156 
157 static int
zpl_commit_metadata(struct inode * inode)158 zpl_commit_metadata(struct inode *inode)
159 {
160 	cred_t *cr = CRED();
161 	fstrans_cookie_t cookie;
162 	int error;
163 
164 	if (zfsctl_is_node(inode))
165 		return (0);
166 
167 	crhold(cr);
168 	cookie = spl_fstrans_mark();
169 	error = -zfs_fsync(ITOZ(inode), 0, cr);
170 	spl_fstrans_unmark(cookie);
171 	crfree(cr);
172 	ASSERT3S(error, <=, 0);
173 
174 	return (error);
175 }
176 
177 const struct export_operations zpl_export_operations = {
178 	.encode_fh		= zpl_encode_fh,
179 	.fh_to_dentry		= zpl_fh_to_dentry,
180 	.get_name		= zpl_get_name,
181 	.get_parent		= zpl_get_parent,
182 	.commit_metadata	= zpl_commit_metadata,
183 };
184