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