static int zfs_replay_truncate(zfsvfs_t *zfsvfs, lr_truncate_t *lr, boolean_t byteswap) { #ifdef sun znode_t *zp; flock64_t fl; int error; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) return (error); bzero(&fl, sizeof (fl)); fl.l_type = F_WRLCK; fl.l_whence = 0; fl.l_start = lr->lr_offset; fl.l_len = lr->lr_length; error = VOP_SPACE(ZTOV(zp), F_FREESP, &fl, FWRITE | FOFFMAX, lr->lr_offset, kcred, NULL); VN_RELE(ZTOV(zp)); return (error); #else /* !sun */ ZFS_LOG(0, "Unexpected code path, report to [email protected]"); return (EOPNOTSUPP); #endif /* !sun */ }
static int zfs_replay_truncate(void *arg1, char *arg2, boolean_t byteswap) { zfsvfs_t *zfsvfs = (zfsvfs_t *)arg1; lr_truncate_t *lr = (lr_truncate_t *)arg2; znode_t *zp; flock64_t fl; int error; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) return (error); bzero(&fl, sizeof (fl)); fl.l_type = F_WRLCK; fl.l_whence = 0; fl.l_start = lr->lr_offset; fl.l_len = lr->lr_length; error = VOP_SPACE(ZTOV(zp), F_FREESP, &fl, FWRITE | FOFFMAX, lr->lr_offset, kcred, NULL); VN_RELE(ZTOV(zp)); return (error); }
static int zfs_replay_truncate(zfsvfs_t *zfsvfs, lr_truncate_t *lr, boolean_t byteswap) { znode_t *zp; flock64_t fl; int error; if (byteswap) byteswap_uint64_array(lr, sizeof (*lr)); if ((error = zfs_zget(zfsvfs, lr->lr_foid, &zp)) != 0) { /* * As we can log truncates out of order, it's possible the * file has been removed. In this case just drop the truncate * and return success. */ if (error == ENOENT) error = 0; return (error); } bzero(&fl, sizeof (fl)); fl.l_type = F_WRLCK; fl.l_whence = 0; fl.l_start = lr->lr_offset; fl.l_len = lr->lr_length; error = VOP_SPACE(ZTOV(zp), F_FREESP, &fl, FWRITE | FOFFMAX, lr->lr_offset, kcred, NULL); VN_RELE(ZTOV(zp)); return (error); }
static int zfsfuse_setattr(fuse_req_t req, fuse_ino_t ino, struct stat *attr, int to_set, struct fuse_file_info *fi) { vfs_t *vfs = (vfs_t *) fuse_req_userdata(req); zfsvfs_t *zfsvfs = vfs->vfs_data; ZFS_ENTER(zfsvfs); vnode_t *vp; boolean_t release; int error; cred_t cred; zfsfuse_getcred(req, &cred); if(fi == NULL) { znode_t *znode; error = zfs_zget(zfsvfs, ino, &znode, B_TRUE); if(error) { ZFS_EXIT(zfsvfs); /* If the inode we are trying to get was recently deleted dnode_hold_impl will return EEXIST instead of ENOENT */ return error == EEXIST ? ENOENT : error; } ASSERT(znode != NULL); vp = ZTOV(znode); release = B_TRUE; } else { file_info_t *info = (file_info_t *)(uintptr_t) fi->fh; vp = info->vp; release = B_FALSE; /* * Special treatment for ftruncate(). * This is needed because otherwise ftruncate() would * fail with permission denied on read-only files. * (Solaris calls VOP_SPACE instead of VOP_SETATTR on * ftruncate). */ if(to_set & FUSE_SET_ATTR_SIZE) { /* Check if file is opened for writing */ if((info->flags & FWRITE) == 0) { error = EBADF; goto out; } /* Sanity check */ if(vp->v_type != VREG) { error = EINVAL; goto out; } flock64_t bf; bf.l_whence = 0; /* beginning of file */ bf.l_start = attr->st_size; bf.l_type = F_WRLCK; bf.l_len = (off_t) 0; /* FIXME: check locks */ error = VOP_SPACE(vp, F_FREESP, &bf, info->flags, 0, &cred, NULL); if(error) goto out; to_set &= ~FUSE_SET_ATTR_SIZE; if(to_set == 0) goto out; } } ASSERT(vp != NULL); vattr_t vattr = { 0 }; if(to_set & FUSE_SET_ATTR_MODE) { vattr.va_mask |= AT_MODE; vattr.va_mode = attr->st_mode; } if(to_set & FUSE_SET_ATTR_UID) { vattr.va_mask |= AT_UID; vattr.va_uid = attr->st_uid; } if(to_set & FUSE_SET_ATTR_GID) { vattr.va_mask |= AT_GID; vattr.va_gid = attr->st_gid; } if(to_set & FUSE_SET_ATTR_SIZE) { vattr.va_mask |= AT_SIZE; vattr.va_size = attr->st_size; } if(to_set & FUSE_SET_ATTR_ATIME) { vattr.va_mask |= AT_ATIME; TIME_TO_TIMESTRUC(attr->st_atime, &vattr.va_atime); } if(to_set & FUSE_SET_ATTR_MTIME) { vattr.va_mask |= AT_MTIME; TIME_TO_TIMESTRUC(attr->st_mtime, &vattr.va_mtime); } int flags = (to_set & (FUSE_SET_ATTR_ATIME | FUSE_SET_ATTR_MTIME)) ? ATTR_UTIME : 0; error = VOP_SETATTR(vp, &vattr, flags, &cred, NULL); out: ; struct stat stat_reply; if(!error) error = zfsfuse_stat(vp, &stat_reply, &cred); /* Do not release if vp was an opened inode */ if(release) VN_RELE(vp); ZFS_EXIT(zfsvfs); if(!error) fuse_reply_attr(req, &stat_reply, 0.0); return error; }
/* * File control. */ int fcntl(int fdes, int cmd, intptr_t arg) { int iarg; int error = 0; int retval; proc_t *p; file_t *fp; vnode_t *vp; u_offset_t offset; u_offset_t start; struct vattr vattr; int in_crit; int flag; struct flock sbf; struct flock64 bf; struct o_flock obf; struct flock64_32 bf64_32; struct fshare fsh; struct shrlock shr; struct shr_locowner shr_own; offset_t maxoffset; model_t datamodel; int fdres; #if defined(_ILP32) && !defined(lint) && defined(_SYSCALL32) ASSERT(sizeof (struct flock) == sizeof (struct flock32)); ASSERT(sizeof (struct flock64) == sizeof (struct flock64_32)); #endif #if defined(_LP64) && !defined(lint) && defined(_SYSCALL32) ASSERT(sizeof (struct flock) == sizeof (struct flock64_64)); ASSERT(sizeof (struct flock64) == sizeof (struct flock64_64)); #endif /* * First, for speed, deal with the subset of cases * that do not require getf() / releasef(). */ switch (cmd) { case F_GETFD: if ((error = f_getfd_error(fdes, &flag)) == 0) retval = flag; goto out; case F_SETFD: error = f_setfd_error(fdes, (int)arg); retval = 0; goto out; case F_GETFL: if ((error = f_getfl(fdes, &flag)) == 0) retval = (flag & (FMASK | FASYNC)) + FOPEN; goto out; case F_GETXFL: if ((error = f_getfl(fdes, &flag)) == 0) retval = flag + FOPEN; goto out; case F_BADFD: if ((error = f_badfd(fdes, &fdres, (int)arg)) == 0) retval = fdres; goto out; } /* * Second, for speed, deal with the subset of cases that * require getf() / releasef() but do not require copyin. */ if ((fp = getf(fdes)) == NULL) { error = EBADF; goto out; } iarg = (int)arg; switch (cmd) { /* ONC_PLUS EXTRACT END */ case F_DUPFD: p = curproc; if ((uint_t)iarg >= p->p_fno_ctl) { if (iarg >= 0) fd_too_big(p); error = EINVAL; } else if ((retval = ufalloc_file(iarg, fp)) == -1) { error = EMFILE; } else { mutex_enter(&fp->f_tlock); fp->f_count++; mutex_exit(&fp->f_tlock); } goto done; case F_DUP2FD: p = curproc; if (fdes == iarg) { retval = iarg; } else if ((uint_t)iarg >= p->p_fno_ctl) { if (iarg >= 0) fd_too_big(p); error = EBADF; } else { /* * We can't hold our getf(fdes) across the call to * closeandsetf() because it creates a window for * deadlock: if one thread is doing dup2(a, b) while * another is doing dup2(b, a), each one will block * waiting for the other to call releasef(). The * solution is to increment the file reference count * (which we have to do anyway), then releasef(fdes), * then closeandsetf(). Incrementing f_count ensures * that fp won't disappear after we call releasef(). * When closeandsetf() fails, we try avoid calling * closef() because of all the side effects. */ mutex_enter(&fp->f_tlock); fp->f_count++; mutex_exit(&fp->f_tlock); releasef(fdes); if ((error = closeandsetf(iarg, fp)) == 0) { retval = iarg; } else { mutex_enter(&fp->f_tlock); if (fp->f_count > 1) { fp->f_count--; mutex_exit(&fp->f_tlock); } else { mutex_exit(&fp->f_tlock); (void) closef(fp); } } goto out; } goto done; case F_SETFL: vp = fp->f_vnode; flag = fp->f_flag; if ((iarg & (FNONBLOCK|FNDELAY)) == (FNONBLOCK|FNDELAY)) iarg &= ~FNDELAY; if ((error = VOP_SETFL(vp, flag, iarg, fp->f_cred)) == 0) { iarg &= FMASK; mutex_enter(&fp->f_tlock); fp->f_flag &= ~FMASK | (FREAD|FWRITE); fp->f_flag |= (iarg - FOPEN) & ~(FREAD|FWRITE); mutex_exit(&fp->f_tlock); } retval = 0; goto done; } /* * Finally, deal with the expensive cases. */ retval = 0; in_crit = 0; maxoffset = MAXOFF_T; datamodel = DATAMODEL_NATIVE; #if defined(_SYSCALL32_IMPL) if ((datamodel = get_udatamodel()) == DATAMODEL_ILP32) maxoffset = MAXOFF32_T; #endif vp = fp->f_vnode; flag = fp->f_flag; offset = fp->f_offset; switch (cmd) { /* ONC_PLUS EXTRACT START */ /* * The file system and vnode layers understand and implement * locking with flock64 structures. So here once we pass through * the test for compatibility as defined by LFS API, (for F_SETLK, * F_SETLKW, F_GETLK, F_GETLKW, F_FREESP) we transform * the flock structure to a flock64 structure and send it to the * lower layers. Similarly in case of GETLK the returned flock64 * structure is transformed to a flock structure if everything fits * in nicely, otherwise we return EOVERFLOW. */ case F_GETLK: case F_O_GETLK: case F_SETLK: case F_SETLKW: case F_SETLK_NBMAND: /* * Copy in input fields only. */ if (cmd == F_O_GETLK) { if (datamodel != DATAMODEL_ILP32) { error = EINVAL; break; } if (copyin((void *)arg, &obf, sizeof (obf))) { error = EFAULT; break; } bf.l_type = obf.l_type; bf.l_whence = obf.l_whence; bf.l_start = (off64_t)obf.l_start; bf.l_len = (off64_t)obf.l_len; bf.l_sysid = (int)obf.l_sysid; bf.l_pid = obf.l_pid; } else if (datamodel == DATAMODEL_NATIVE) { if (copyin((void *)arg, &sbf, sizeof (sbf))) { error = EFAULT; break; } /* * XXX In an LP64 kernel with an LP64 application * there's no need to do a structure copy here * struct flock == struct flock64. However, * we did it this way to avoid more conditional * compilation. */ bf.l_type = sbf.l_type; bf.l_whence = sbf.l_whence; bf.l_start = (off64_t)sbf.l_start; bf.l_len = (off64_t)sbf.l_len; bf.l_sysid = sbf.l_sysid; bf.l_pid = sbf.l_pid; } #if defined(_SYSCALL32_IMPL) else { struct flock32 sbf32; if (copyin((void *)arg, &sbf32, sizeof (sbf32))) { error = EFAULT; break; } bf.l_type = sbf32.l_type; bf.l_whence = sbf32.l_whence; bf.l_start = (off64_t)sbf32.l_start; bf.l_len = (off64_t)sbf32.l_len; bf.l_sysid = sbf32.l_sysid; bf.l_pid = sbf32.l_pid; } #endif /* _SYSCALL32_IMPL */ /* * 64-bit support: check for overflow for 32-bit lock ops */ if ((error = flock_check(vp, &bf, offset, maxoffset)) != 0) break; /* * Not all of the filesystems understand F_O_GETLK, and * there's no need for them to know. Map it to F_GETLK. */ if ((error = VOP_FRLOCK(vp, (cmd == F_O_GETLK) ? F_GETLK : cmd, &bf, flag, offset, NULL, fp->f_cred)) != 0) break; /* * If command is GETLK and no lock is found, only * the type field is changed. */ if ((cmd == F_O_GETLK || cmd == F_GETLK) && bf.l_type == F_UNLCK) { /* l_type always first entry, always a short */ if (copyout(&bf.l_type, &((struct flock *)arg)->l_type, sizeof (bf.l_type))) error = EFAULT; break; } if (cmd == F_O_GETLK) { /* * Return an SVR3 flock structure to the user. */ obf.l_type = (int16_t)bf.l_type; obf.l_whence = (int16_t)bf.l_whence; obf.l_start = (int32_t)bf.l_start; obf.l_len = (int32_t)bf.l_len; if (bf.l_sysid > SHRT_MAX || bf.l_pid > SHRT_MAX) { /* * One or both values for the above fields * is too large to store in an SVR3 flock * structure. */ error = EOVERFLOW; break; } obf.l_sysid = (int16_t)bf.l_sysid; obf.l_pid = (int16_t)bf.l_pid; if (copyout(&obf, (void *)arg, sizeof (obf))) error = EFAULT; } else if (cmd == F_GETLK) { /* * Copy out SVR4 flock. */ int i; if (bf.l_start > maxoffset || bf.l_len > maxoffset) { error = EOVERFLOW; break; } if (datamodel == DATAMODEL_NATIVE) { for (i = 0; i < 4; i++) sbf.l_pad[i] = 0; /* * XXX In an LP64 kernel with an LP64 * application there's no need to do a * structure copy here as currently * struct flock == struct flock64. * We did it this way to avoid more * conditional compilation. */ sbf.l_type = bf.l_type; sbf.l_whence = bf.l_whence; sbf.l_start = (off_t)bf.l_start; sbf.l_len = (off_t)bf.l_len; sbf.l_sysid = bf.l_sysid; sbf.l_pid = bf.l_pid; if (copyout(&sbf, (void *)arg, sizeof (sbf))) error = EFAULT; } #if defined(_SYSCALL32_IMPL) else { struct flock32 sbf32; if (bf.l_start > MAXOFF32_T || bf.l_len > MAXOFF32_T) { error = EOVERFLOW; break; } for (i = 0; i < 4; i++) sbf32.l_pad[i] = 0; sbf32.l_type = (int16_t)bf.l_type; sbf32.l_whence = (int16_t)bf.l_whence; sbf32.l_start = (off32_t)bf.l_start; sbf32.l_len = (off32_t)bf.l_len; sbf32.l_sysid = (int32_t)bf.l_sysid; sbf32.l_pid = (pid32_t)bf.l_pid; if (copyout(&sbf32, (void *)arg, sizeof (sbf32))) error = EFAULT; } #endif } break; /* ONC_PLUS EXTRACT END */ case F_CHKFL: /* * This is for internal use only, to allow the vnode layer * to validate a flags setting before applying it. User * programs can't issue it. */ error = EINVAL; break; case F_ALLOCSP: case F_FREESP: case F_ALLOCSP64: case F_FREESP64: if ((flag & FWRITE) == 0) { error = EBADF; break; } if (vp->v_type != VREG) { error = EINVAL; break; } if (datamodel != DATAMODEL_ILP32 && (cmd == F_ALLOCSP64 || cmd == F_FREESP64)) { error = EINVAL; break; } #if defined(_ILP32) || defined(_SYSCALL32_IMPL) if (datamodel == DATAMODEL_ILP32 && (cmd == F_ALLOCSP || cmd == F_FREESP)) { struct flock32 sbf32; /* * For compatibility we overlay an SVR3 flock on an SVR4 * flock. This works because the input field offsets * in "struct flock" were preserved. */ if (copyin((void *)arg, &sbf32, sizeof (sbf32))) { error = EFAULT; break; } else { bf.l_type = sbf32.l_type; bf.l_whence = sbf32.l_whence; bf.l_start = (off64_t)sbf32.l_start; bf.l_len = (off64_t)sbf32.l_len; bf.l_sysid = sbf32.l_sysid; bf.l_pid = sbf32.l_pid; } } #endif /* _ILP32 || _SYSCALL32_IMPL */ #if defined(_LP64) if (datamodel == DATAMODEL_LP64 && (cmd == F_ALLOCSP || cmd == F_FREESP)) { if (copyin((void *)arg, &bf, sizeof (bf))) { error = EFAULT; break; } } #endif /* defined(_LP64) */ #if !defined(_LP64) || defined(_SYSCALL32_IMPL) if (datamodel == DATAMODEL_ILP32 && (cmd == F_ALLOCSP64 || cmd == F_FREESP64)) { if (copyin((void *)arg, &bf64_32, sizeof (bf64_32))) { error = EFAULT; break; } else { /* * Note that the size of flock64 is different in * the ILP32 and LP64 models, due to the l_pad * field. We do not want to assume that the * flock64 structure is laid out the same in * ILP32 and LP64 environments, so we will * copy in the ILP32 version of flock64 * explicitly and copy it to the native * flock64 structure. */ bf.l_type = (short)bf64_32.l_type; bf.l_whence = (short)bf64_32.l_whence; bf.l_start = bf64_32.l_start; bf.l_len = bf64_32.l_len; bf.l_sysid = (int)bf64_32.l_sysid; bf.l_pid = (pid_t)bf64_32.l_pid; } } #endif /* !defined(_LP64) || defined(_SYSCALL32_IMPL) */ if (cmd == F_ALLOCSP || cmd == F_FREESP) error = flock_check(vp, &bf, offset, maxoffset); else if (cmd == F_ALLOCSP64 || cmd == F_FREESP64) error = flock_check(vp, &bf, offset, MAXOFFSET_T); if (error) break; if (vp->v_type == VREG && bf.l_len == 0 && bf.l_start > OFFSET_MAX(fp)) { error = EFBIG; break; } /* * Make sure that there are no conflicting non-blocking * mandatory locks in the region being manipulated. If * there are such locks then return EACCES. */ if ((error = flock_get_start(vp, &bf, offset, &start)) != 0) break; if (nbl_need_check(vp)) { u_offset_t begin; ssize_t length; nbl_start_crit(vp, RW_READER); in_crit = 1; vattr.va_mask = AT_SIZE; if ((error = VOP_GETATTR(vp, &vattr, 0, CRED())) != 0) break; begin = start > vattr.va_size ? vattr.va_size : start; length = vattr.va_size > start ? vattr.va_size - start : start - vattr.va_size; if (nbl_conflict(vp, NBL_WRITE, begin, length, 0)) { error = EACCES; break; } } if (cmd == F_ALLOCSP64) cmd = F_ALLOCSP; else if (cmd == F_FREESP64) cmd = F_FREESP; error = VOP_SPACE(vp, cmd, &bf, flag, offset, fp->f_cred, NULL); break; #if !defined(_LP64) || defined(_SYSCALL32_IMPL) /* ONC_PLUS EXTRACT START */ case F_GETLK64: case F_SETLK64: case F_SETLKW64: case F_SETLK64_NBMAND: /* * Large Files: Here we set cmd as *LK and send it to * lower layers. *LK64 is only for the user land. * Most of the comments described above for F_SETLK * applies here too. * Large File support is only needed for ILP32 apps! */ if (datamodel != DATAMODEL_ILP32) { error = EINVAL; break; } if (cmd == F_GETLK64) cmd = F_GETLK; else if (cmd == F_SETLK64) cmd = F_SETLK; else if (cmd == F_SETLKW64) cmd = F_SETLKW; else if (cmd == F_SETLK64_NBMAND) cmd = F_SETLK_NBMAND; /* * Note that the size of flock64 is different in the ILP32 * and LP64 models, due to the sucking l_pad field. * We do not want to assume that the flock64 structure is * laid out in the same in ILP32 and LP64 environments, so * we will copy in the ILP32 version of flock64 explicitly * and copy it to the native flock64 structure. */ if (copyin((void *)arg, &bf64_32, sizeof (bf64_32))) { error = EFAULT; break; } bf.l_type = (short)bf64_32.l_type; bf.l_whence = (short)bf64_32.l_whence; bf.l_start = bf64_32.l_start; bf.l_len = bf64_32.l_len; bf.l_sysid = (int)bf64_32.l_sysid; bf.l_pid = (pid_t)bf64_32.l_pid; if ((error = flock_check(vp, &bf, offset, MAXOFFSET_T)) != 0) break; if ((error = VOP_FRLOCK(vp, cmd, &bf, flag, offset, NULL, fp->f_cred)) != 0) break; if ((cmd == F_GETLK) && bf.l_type == F_UNLCK) { if (copyout(&bf.l_type, &((struct flock *)arg)->l_type, sizeof (bf.l_type))) error = EFAULT; break; } if (cmd == F_GETLK) { int i; /* * We do not want to assume that the flock64 structure * is laid out in the same in ILP32 and LP64 * environments, so we will copy out the ILP32 version * of flock64 explicitly after copying the native * flock64 structure to it. */ for (i = 0; i < 4; i++) bf64_32.l_pad[i] = 0; bf64_32.l_type = (int16_t)bf.l_type; bf64_32.l_whence = (int16_t)bf.l_whence; bf64_32.l_start = bf.l_start; bf64_32.l_len = bf.l_len; bf64_32.l_sysid = (int32_t)bf.l_sysid; bf64_32.l_pid = (pid32_t)bf.l_pid; if (copyout(&bf64_32, (void *)arg, sizeof (bf64_32))) error = EFAULT; } break; /* ONC_PLUS EXTRACT END */ #endif /* !defined(_LP64) || defined(_SYSCALL32_IMPL) */ /* ONC_PLUS EXTRACT START */ case F_SHARE: case F_SHARE_NBMAND: case F_UNSHARE: /* * Copy in input fields only. */ if (copyin((void *)arg, &fsh, sizeof (fsh))) { error = EFAULT; break; } /* * Local share reservations always have this simple form */ shr.s_access = fsh.f_access; shr.s_deny = fsh.f_deny; shr.s_sysid = 0; shr.s_pid = ttoproc(curthread)->p_pid; shr_own.sl_pid = shr.s_pid; shr_own.sl_id = fsh.f_id; shr.s_own_len = sizeof (shr_own); shr.s_owner = (caddr_t)&shr_own; error = VOP_SHRLOCK(vp, cmd, &shr, flag, fp->f_cred); /* ONC_PLUS EXTRACT END */ break; default: error = EINVAL; break; } if (in_crit) nbl_end_crit(vp); done: releasef(fdes); out: if (error) return (set_errno(error)); return (retval); }