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/cdefs.h>
29__FBSDID("$FreeBSD$");
30
31#include <sys/dmu.h>
32#include <sys/dmu_impl.h>
33#include <sys/dmu_recv.h>
34#include <sys/dmu_tx.h>
35#include <sys/dbuf.h>
36#include <sys/dnode.h>
37#include <sys/zfs_context.h>
38#include <sys/dmu_objset.h>
39#include <sys/dmu_traverse.h>
40#include <sys/dsl_dataset.h>
41#include <sys/dsl_dir.h>
42#include <sys/dsl_pool.h>
43#include <sys/dsl_synctask.h>
44#include <sys/zfs_ioctl.h>
45#include <sys/zap.h>
46#include <sys/zio_checksum.h>
47#include <sys/zfs_znode.h>
48#include <sys/zfs_file.h>
49#include <sys/buf.h>
50#include <sys/stat.h>
51
52int
53zfs_file_open(const char *path, int flags, int mode, zfs_file_t **fpp)
54{
55	struct thread *td;
56	int rc, fd;
57
58	td = curthread;
59	pwd_ensure_dirs();
60	/* 12.x doesn't take a const char * */
61	rc = kern_openat(td, AT_FDCWD, __DECONST(char *, path),
62	    UIO_SYSSPACE, flags, mode);
63	if (rc)
64		return (SET_ERROR(rc));
65	fd = td->td_retval[0];
66	td->td_retval[0] = 0;
67	if (fget(curthread, fd, &cap_no_rights, fpp))
68		kern_close(td, fd);
69	return (0);
70}
71
72void
73zfs_file_close(zfs_file_t *fp)
74{
75	fo_close(fp, curthread);
76}
77
78static int
79zfs_file_write_impl(zfs_file_t *fp, const void *buf, size_t count, loff_t *offp,
80    ssize_t *resid)
81{
82	ssize_t rc;
83	struct uio auio;
84	struct thread *td;
85	struct iovec aiov;
86
87	td = curthread;
88	aiov.iov_base = (void *)(uintptr_t)buf;
89	aiov.iov_len = count;
90	auio.uio_iov = &aiov;
91	auio.uio_iovcnt = 1;
92	auio.uio_segflg = UIO_SYSSPACE;
93	auio.uio_resid = count;
94	auio.uio_rw = UIO_WRITE;
95	auio.uio_td = td;
96	auio.uio_offset = *offp;
97
98	if ((fp->f_flag & FWRITE) == 0)
99		return (SET_ERROR(EBADF));
100
101	if (fp->f_type == DTYPE_VNODE)
102		bwillwrite();
103
104	rc = fo_write(fp, &auio, td->td_ucred, FOF_OFFSET, td);
105	if (rc)
106		return (SET_ERROR(rc));
107	if (resid)
108		*resid = auio.uio_resid;
109	else if (auio.uio_resid)
110		return (SET_ERROR(EIO));
111	*offp += count - auio.uio_resid;
112	return (rc);
113}
114
115int
116zfs_file_write(zfs_file_t *fp, const void *buf, size_t count, ssize_t *resid)
117{
118	loff_t off = fp->f_offset;
119	ssize_t rc;
120
121	rc = zfs_file_write_impl(fp, buf, count, &off, resid);
122	if (rc == 0)
123		fp->f_offset = off;
124
125	return (SET_ERROR(rc));
126}
127
128int
129zfs_file_pwrite(zfs_file_t *fp, const void *buf, size_t count, loff_t off,
130    ssize_t *resid)
131{
132	return (zfs_file_write_impl(fp, buf, count, &off, resid));
133}
134
135static int
136zfs_file_read_impl(zfs_file_t *fp, void *buf, size_t count, loff_t *offp,
137    ssize_t *resid)
138{
139	ssize_t rc;
140	struct uio auio;
141	struct thread *td;
142	struct iovec aiov;
143
144	td = curthread;
145	aiov.iov_base = (void *)(uintptr_t)buf;
146	aiov.iov_len = count;
147	auio.uio_iov = &aiov;
148	auio.uio_iovcnt = 1;
149	auio.uio_segflg = UIO_SYSSPACE;
150	auio.uio_resid = count;
151	auio.uio_rw = UIO_READ;
152	auio.uio_td = td;
153	auio.uio_offset = *offp;
154
155	if ((fp->f_flag & FREAD) == 0)
156		return (SET_ERROR(EBADF));
157
158	rc = fo_read(fp, &auio, td->td_ucred, FOF_OFFSET, td);
159	if (rc)
160		return (SET_ERROR(rc));
161	if (resid)
162		*resid = auio.uio_resid;
163	*offp += count - auio.uio_resid;
164	return (SET_ERROR(0));
165}
166
167int
168zfs_file_read(zfs_file_t *fp, void *buf, size_t count, ssize_t *resid)
169{
170	loff_t off = fp->f_offset;
171	ssize_t rc;
172
173	rc = zfs_file_read_impl(fp, buf, count, &off, resid);
174	if (rc == 0)
175		fp->f_offset = off;
176	return (rc);
177}
178
179int
180zfs_file_pread(zfs_file_t *fp, void *buf, size_t count, loff_t off,
181    ssize_t *resid)
182{
183	return (zfs_file_read_impl(fp, buf, count, &off, resid));
184}
185
186int
187zfs_file_seek(zfs_file_t *fp, loff_t *offp, int whence)
188{
189	int rc;
190	struct thread *td;
191
192	td = curthread;
193	if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0)
194		return (SET_ERROR(ESPIPE));
195	rc = fo_seek(fp, *offp, whence, td);
196	if (rc == 0)
197		*offp = td->td_uretoff.tdu_off;
198	return (SET_ERROR(rc));
199}
200
201int
202zfs_file_getattr(zfs_file_t *fp, zfs_file_attr_t *zfattr)
203{
204	struct thread *td;
205	struct stat sb;
206	int rc;
207
208	td = curthread;
209
210	rc = fo_stat(fp, &sb, td->td_ucred, td);
211	if (rc)
212		return (SET_ERROR(rc));
213	zfattr->zfa_size = sb.st_size;
214	zfattr->zfa_mode = sb.st_mode;
215
216	return (0);
217}
218
219static __inline int
220zfs_vop_fsync(vnode_t *vp)
221{
222	struct mount *mp;
223	int error;
224
225	if ((error = vn_start_write(vp, &mp, V_WAIT | PCATCH)) != 0)
226		goto drop;
227	vn_lock(vp, LK_EXCLUSIVE | LK_RETRY);
228	error = VOP_FSYNC(vp, MNT_WAIT, curthread);
229	VOP_UNLOCK1(vp);
230	vn_finished_write(mp);
231drop:
232	return (SET_ERROR(error));
233}
234
235int
236zfs_file_fsync(zfs_file_t *fp, int flags)
237{
238	if (fp->f_type != DTYPE_VNODE)
239		return (EINVAL);
240
241	return (zfs_vop_fsync(fp->f_vnode));
242}
243
244int
245zfs_file_get(int fd, zfs_file_t **fpp)
246{
247	struct file *fp;
248
249	if (fget(curthread, fd, &cap_no_rights, &fp))
250		return (SET_ERROR(EBADF));
251
252	*fpp = fp;
253	return (0);
254}
255
256void
257zfs_file_put(int fd)
258{
259	struct file *fp;
260
261	/* No CAP_ rights required, as we're only releasing. */
262	if (fget(curthread, fd, &cap_no_rights, &fp) == 0) {
263		fdrop(fp, curthread);
264		fdrop(fp, curthread);
265	}
266}
267
268loff_t
269zfs_file_off(zfs_file_t *fp)
270{
271	return (fp->f_offset);
272}
273
274void *
275zfs_file_private(zfs_file_t *fp)
276{
277	file_t *tmpfp;
278	void *data;
279	int error;
280
281	tmpfp = curthread->td_fpop;
282	curthread->td_fpop = fp;
283	error = devfs_get_cdevpriv(&data);
284	curthread->td_fpop = tmpfp;
285	if (error != 0)
286		return (NULL);
287	return (data);
288}
289
290int
291zfs_file_unlink(const char *fnamep)
292{
293	zfs_uio_seg_t seg = UIO_SYSSPACE;
294	int rc;
295
296#if __FreeBSD_version >= 1300018
297	rc = kern_funlinkat(curthread, AT_FDCWD, fnamep, FD_NONE, seg, 0, 0);
298#elif __FreeBSD_version >= 1202504 || defined(AT_BENEATH)
299	rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep),
300	    seg, 0, 0);
301#else
302	rc = kern_unlinkat(curthread, AT_FDCWD, __DECONST(char *, fnamep),
303	    seg, 0);
304#endif
305	return (SET_ERROR(rc));
306}
307