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