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
49int
50zfs_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
108void
109zfs_file_close(zfs_file_t *fp)
110{
111	fdrop(fp, curthread);
112}
113
114static int
115zfs_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
151int
152zfs_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
164int
165zfs_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
171static int
172zfs_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
203int
204zfs_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
215int
216zfs_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
222int
223zfs_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
237int
238zfs_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
259static __inline int
260zfs_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);
275drop:
276	return (SET_ERROR(error));
277}
278
279int
280zfs_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
288zfs_file_t *
289zfs_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
299void
300zfs_file_put(zfs_file_t *fp)
301{
302	zfs_file_close(fp);
303}
304
305loff_t
306zfs_file_off(zfs_file_t *fp)
307{
308	return (fp->f_offset);
309}
310
311void *
312zfs_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
327int
328zfs_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