1 /*
2  * Copyright (c) 2020 iXsystems, Inc.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  */
27 
28 #include <sys/dmu.h>
29 #include <sys/dmu_impl.h>
30 #include <sys/dmu_recv.h>
31 #include <sys/dmu_tx.h>
32 #include <sys/dbuf.h>
33 #include <sys/dnode.h>
34 #include <sys/zfs_context.h>
35 #include <sys/dmu_objset.h>
36 #include <sys/dmu_traverse.h>
37 #include <sys/dsl_dataset.h>
38 #include <sys/dsl_dir.h>
39 #include <sys/dsl_pool.h>
40 #include <sys/dsl_synctask.h>
41 #include <sys/zfs_ioctl.h>
42 #include <sys/zap.h>
43 #include <sys/zio_checksum.h>
44 #include <sys/zfs_znode.h>
45 #include <sys/zfs_file.h>
46 #include <sys/buf.h>
47 #include <sys/stat.h>
48 
49 int
50 zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp)
51 {
52 	struct thread *td;
53 	struct vnode *vp;
54 	struct file *fp;
55 	struct nameidata nd;
56 	int error;
57 
58 	td = curthread;
59 	pwd_ensure_dirs();
60 
61 	KASSERT((flags & (O_EXEC | O_PATH)) == 0,
62 	    ("invalid flags: 0x%x", flags));
63 	KASSERT((flags & O_ACCMODE) != O_ACCMODE,
64 	    ("invalid flags: 0x%x", flags));
65 	flags = FFLAGS(flags);
66 
67 	error = falloc_noinstall(td, &fp);
68 	if (error != 0) {
69 		return (error);
70 	}
71 	fp->f_flag = flags & FMASK;
72 
73 #if __FreeBSD_version >= 1400043
74 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path);
75 #else
76 	NDINIT(&nd, LOOKUP, FOLLOW, UIO_SYSSPACE, path, td);
77 #endif
78 	error = vn_open(&nd, &flags, mode, fp);
79 	if (error != 0) {
80 		falloc_abort(td, fp);
81 		return (SET_ERROR(error));
82 	}
83 	NDFREE_PNBUF(&nd);
84 	vp = nd.ni_vp;
85 	fp->f_vnode = vp;
86 	if (fp->f_ops == &badfileops) {
87 		finit_vnode(fp, flags, NULL, &vnops);
88 	}
89 	VOP_UNLOCK(vp);
90 	if (vp->v_type != VREG) {
91 		zfs_file_close(fp);
92 		return (SET_ERROR(EACCES));
93 	}
94 
95 	if (flags & O_TRUNC) {
96 		error = fo_truncate(fp, 0, td->td_ucred, td);
97 		if (error != 0) {
98 			zfs_file_close(fp);
99 			return (SET_ERROR(error));
100 		}
101 	}
102 
103 	*fpp = fp;
104 
105 	return (0);
106 }
107 
108 void
109 zfs_file_close(zfs_file_t *fp)
110 {
111 	fdrop(fp, curthread);
112 }
113 
114 static int
115 zfs_file_write_impl(zfs_file_t *fp, const void *buf, size_t count, loff_t *offp,
116     ssize_t *resid)
117 {
118 	ssize_t rc;
119 	struct uio auio;
120 	struct thread *td;
121 	struct iovec aiov;
122 
123 	td = curthread;
124 	aiov.iov_base = (void *)(uintptr_t)buf;
125 	aiov.iov_len = count;
126 	auio.uio_iov = &aiov;
127 	auio.uio_iovcnt = 1;
128 	auio.uio_segflg = UIO_SYSSPACE;
129 	auio.uio_resid = count;
130 	auio.uio_rw = UIO_WRITE;
131 	auio.uio_td = td;
132 	auio.uio_offset = *offp;
133 
134 	if ((fp->f_flag & FWRITE) == 0)
135 		return (SET_ERROR(EBADF));
136 
137 	if (fp->f_type == DTYPE_VNODE)
138 		bwillwrite();
139 
140 	rc = fo_write(fp, &auio, td->td_ucred, FOF_OFFSET, td);
141 	if (rc)
142 		return (SET_ERROR(rc));
143 	if (resid)
144 		*resid = auio.uio_resid;
145 	else if (auio.uio_resid)
146 		return (SET_ERROR(EIO));
147 	*offp += count - auio.uio_resid;
148 	return (rc);
149 }
150 
151 int
152 zfs_file_write(zfs_file_t *fp, const void *buf, size_t count, ssize_t *resid)
153 {
154 	loff_t off = fp->f_offset;
155 	ssize_t rc;
156 
157 	rc = zfs_file_write_impl(fp, buf, count, &off, resid);
158 	if (rc == 0)
159 		fp->f_offset = off;
160 
161 	return (SET_ERROR(rc));
162 }
163 
164 int
165 zfs_file_pwrite(zfs_file_t *fp, const void *buf, size_t count, loff_t off,
166     ssize_t *resid)
167 {
168 	return (zfs_file_write_impl(fp, buf, count, &off, resid));
169 }
170 
171 static int
172 zfs_file_read_impl(zfs_file_t *fp, void *buf, size_t count, loff_t *offp,
173     ssize_t *resid)
174 {
175 	ssize_t rc;
176 	struct uio auio;
177 	struct thread *td;
178 	struct iovec aiov;
179 
180 	td = curthread;
181 	aiov.iov_base = (void *)(uintptr_t)buf;
182 	aiov.iov_len = count;
183 	auio.uio_iov = &aiov;
184 	auio.uio_iovcnt = 1;
185 	auio.uio_segflg = UIO_SYSSPACE;
186 	auio.uio_resid = count;
187 	auio.uio_rw = UIO_READ;
188 	auio.uio_td = td;
189 	auio.uio_offset = *offp;
190 
191 	if ((fp->f_flag & FREAD) == 0)
192 		return (SET_ERROR(EBADF));
193 
194 	rc = fo_read(fp, &auio, td->td_ucred, FOF_OFFSET, td);
195 	if (rc)
196 		return (SET_ERROR(rc));
197 	if (resid)
198 		*resid = auio.uio_resid;
199 	*offp += count - auio.uio_resid;
200 	return (SET_ERROR(0));
201 }
202 
203 int
204 zfs_file_read(zfs_file_t *fp, void *buf, size_t count, ssize_t *resid)
205 {
206 	loff_t off = fp->f_offset;
207 	ssize_t rc;
208 
209 	rc = zfs_file_read_impl(fp, buf, count, &off, resid);
210 	if (rc == 0)
211 		fp->f_offset = off;
212 	return (rc);
213 }
214 
215 int
216 zfs_file_pread(zfs_file_t *fp, void *buf, size_t count, loff_t off,
217     ssize_t *resid)
218 {
219 	return (zfs_file_read_impl(fp, buf, count, &off, resid));
220 }
221 
222 int
223 zfs_file_seek(zfs_file_t *fp, loff_t *offp, int whence)
224 {
225 	int rc;
226 	struct thread *td;
227 
228 	td = curthread;
229 	if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0)
230 		return (SET_ERROR(ESPIPE));
231 	rc = fo_seek(fp, *offp, whence, td);
232 	if (rc == 0)
233 		*offp = td->td_uretoff.tdu_off;
234 	return (SET_ERROR(rc));
235 }
236 
237 int
238 zfs_file_getattr(zfs_file_t *fp, zfs_file_attr_t *zfattr)
239 {
240 	struct thread *td;
241 	struct stat sb;
242 	int rc;
243 
244 	td = curthread;
245 
246 #if __FreeBSD_version < 1400037
247 	rc = fo_stat(fp, &sb, td->td_ucred, td);
248 #else
249 	rc = fo_stat(fp, &sb, td->td_ucred);
250 #endif
251 	if (rc)
252 		return (SET_ERROR(rc));
253 	zfattr->zfa_size = sb.st_size;
254 	zfattr->zfa_mode = sb.st_mode;
255 
256 	return (0);
257 }
258 
259 static __inline int
260 zfs_vop_fsync(vnode_t *vp)
261 {
262 	struct mount *mp;
263 	int error;
264 
265 #if __FreeBSD_version < 1400068
266 	if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
267 #else
268 	if ((error = vn_start_write(vp, &mp, V_WAIT | V_PCATCH)) != 0)
269 #endif
270 		goto drop;
271 	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
272 	error = VOP_FSYNC(vp, MNT_WAIT, curthread);
273 	VOP_UNLOCK1(vp);
274 	vn_finished_write(mp);
275 drop:
276 	return (SET_ERROR(error));
277 }
278 
279 int
280 zfs_file_fsync(zfs_file_t *fp, int flags)
281 {
282 	if (fp->f_type != DTYPE_VNODE)
283 		return (EINVAL);
284 
285 	return (zfs_vop_fsync(fp->f_vnode));
286 }
287 
288 zfs_file_t *
289 zfs_file_get(int fd)
290 {
291 	struct file *fp;
292 
293 	if (fget(curthread, fd, &cap_no_rights, &fp))
294 		return (NULL);
295 
296 	return (fp);
297 }
298 
299 void
300 zfs_file_put(zfs_file_t *fp)
301 {
302 	zfs_file_close(fp);
303 }
304 
305 loff_t
306 zfs_file_off(zfs_file_t *fp)
307 {
308 	return (fp->f_offset);
309 }
310 
311 void *
312 zfs_file_private(zfs_file_t *fp)
313 {
314 	file_t *tmpfp;
315 	void *data;
316 	int error;
317 
318 	tmpfp = curthread->td_fpop;
319 	curthread->td_fpop = fp;
320 	error = devfs_get_cdevpriv(&data);
321 	curthread->td_fpop = tmpfp;
322 	if (error != 0)
323 		return (NULL);
324 	return (data);
325 }
326 
327 int
328 zfs_file_unlink(const char *fnamep)
329 {
330 	zfs_uio_seg_t seg = UIO_SYSSPACE;
331 	int rc;
332 
333 #if __FreeBSD_version >= 1300018
334 	rc = kern_funlinkat(curthread, AT_FDCWD, fnamep, FD_NONE, seg, 0, 0);
335 #elif __FreeBSD_version >= 1202504 || defined(AT_BENEATH)
336 	rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep),
337 	    seg, 0, 0);
338 #else
339 	rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep),
340 	    seg, 0);
341 #endif
342 	return (SET_ERROR(rc));
343 }
344