Page MenuHomeFreeBSD

D28347.id87513.diff
No OneTemporary

D28347.id87513.diff

Index: lib/libc/sys/Makefile.inc
===================================================================
--- lib/libc/sys/Makefile.inc
+++ lib/libc/sys/Makefile.inc
@@ -189,6 +189,7 @@
fhreadlink.2 \
flock.2 \
fork.2 \
+ fspacectl.2 \
fsync.2 \
getdirentries.2 \
getdtablesize.2 \
Index: lib/libc/sys/Symbol.map
===================================================================
--- lib/libc/sys/Symbol.map
+++ lib/libc/sys/Symbol.map
@@ -410,6 +410,7 @@
fhlink;
fhlinkat;
fhreadlink;
+ fspacectl;
getfhat;
funlinkat;
memfd_create;
Index: lib/libc/sys/fspacectl.2
===================================================================
--- /dev/null
+++ lib/libc/sys/fspacectl.2
@@ -0,0 +1,149 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+.\"
+.\" Copyright (c) 2021 The FreeBSD Foundation
+.\"
+.\" This software was developed by Ka Ho Ng under sponsorship from
+.\" the FreeBSD Foundation.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 16, 2021
+.Dt FSPACECTL 2
+.Os
+.Sh NAME
+.Nm fspacectl
+.Nd space management in a file
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In fcntl.h
+.Ft int
+.Fn fspacectl "int fd" "int cmd" "struct spacectl_range *range" "int flags"
+.Sh DESCRIPTION
+.Nm
+is a system call performing space management in the file referenced by
+.Fa fd .
+.Fa cmd
+specifies the operation to take place in the file.
+.Fa range
+is the operation range.
+.Fa flags
+controls the behavior of the operation to take place.
+.Pp
+The
+.Fa range
+argument points to a structure defined as:
+.Bd -literal
+struct spacectl_range {
+ off_t r_offset;
+ off_t r_len;
+};
+.Ed
+.Pp
+The operation specified by the
+.Fa cmd
+argument may be one of:
+.Bl -tag -width SPACECTL_DEALLOC
+.It Dv SPACECTL_DEALLOC
+Zero a region in the file specified by the
+.Fa range
+argument.
+The
+.Va "range->r_offset"
+has to be a value greater than or equal to 0, and the
+.Va "range->r_len"
+has to be a value greater than 0.
+.Pp
+If the file system supports hole-punching,
+file system space deallocation may be performed in the given region.
+.El
+.Pp
+The
+.Fa flags
+argument needs to be the value 0 currently.
+.Pp
+The call does not return partial success if a signal is caught during execution.
+.Sh RETURN VALUES
+Upon successful completion, the value 0 is returned;
+otherwise the value -1 is returned and
+.Va errno
+is set to indicate the error.
+.Sh ERRORS
+Possible failure conditions:
+.Bl -tag -width Er
+.It Bq Er EBADF
+The
+.Fa fd
+argument is not a valid file descriptor.
+.It Bq Er EBADF
+The
+.Fa fd
+argument references a file that was opened without write permission.
+.It Bq Er EINTR
+A signal was caught during execution.
+.It Bq Er EINVAL
+If the operation is
+.Dv SPACECTL_DEALLOC ,
+either the
+.Fa "range->r_offset"
+argument was less than zero, or the
+.Fa "range->r_len"
+argument was less than or equal to zero,
+.It Bq Er EINVAL
+The operation is not supported by the file descriptor.
+.It Bq Er EIO
+An I/O error occurred while reading from or writing to a file system.
+.It Bq Er EINTEGRITY
+Corrupted data was detected while reading from the file system.
+.It Bq Er ENODEV
+The
+.Fa fd
+argument does not refer to a file that supports
+.Nm .
+.It Bq Er ENOSPC
+There is insufficient free space remaining on the file system storage
+media.
+.It Bq Er ENOTCAPABLE
+The file descriptor
+.Fa fd
+has insufficient rights.
+.It Bq Er ESPIPE
+The
+.Fa fd
+argument is associated with a pipe or FIFO.
+.El
+.Sh SEE ALSO
+.Xr creat 2 ,
+.Xr ftruncate 2 ,
+.Xr open 2 ,
+.Xr unlink 2
+.Sh HISTORY
+The
+.Nm
+system call appeared in
+.Fx 14.0 .
+.Sh AUTHORS
+.Nm
+and this manual page were developed by
+.An Ka Ho Ng Aq Mt khng@FreeBSDFoundation.org
+under sponsorship from the FreeBSD Foundation.
Index: lib/libc/sys/pathconf.2
===================================================================
--- lib/libc/sys/pathconf.2
+++ lib/libc/sys/pathconf.2
@@ -166,6 +166,9 @@
.It Li _PC_MIN_HOLE_SIZE
If a file system supports the reporting of holes (see
.Xr lseek 2 ) ,
+.It Li _PC_FDEALLOC_PRESENT
+If a file system supports hole-punching (see
+.Xr fspacectl 2 ) ,
.Fn pathconf
and
.Fn fpathconf
Index: share/man/man9/Makefile
===================================================================
--- share/man/man9/Makefile
+++ share/man/man9/Makefile
@@ -397,6 +397,7 @@
vm_page_wire.9 \
vm_set_page_size.9 \
vmem.9 \
+ vn_deallocate.9 \
vn_fullpath.9 \
vn_isdisk.9 \
vnet.9 \
@@ -412,6 +413,7 @@
VOP_BWRITE.9 \
VOP_COPY_FILE_RANGE.9 \
VOP_CREATE.9 \
+ VOP_DEALLOCATE.9 \
VOP_FSYNC.9 \
VOP_GETACL.9 \
VOP_GETEXTATTR.9 \
Index: share/man/man9/VOP_DEALLOCATE.9
===================================================================
--- /dev/null
+++ share/man/man9/VOP_DEALLOCATE.9
@@ -0,0 +1,99 @@
+.\"
+.\" SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+.\"
+.\" Copyright (c) 2021 The FreeBSD Foundation
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\" notice, this list of conditions and the following disclaimer in the
+.\" documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.Dd April 16, 2021
+.Dt VOP_DEALLOCATE 9
+.Os
+.Sh NAME
+.Nm VOP_DEALLOCATE
+.Nd zero and/or deallocate storage from a file
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/vnode.h
+.Ft int
+.Fo VOP_DEALLOCATE
+.Fa "struct vnode *vp"
+.Fa "off_t *offset"
+.Fa "off_t *len"
+.Fa "int flags"
+.Fa "struct ucred *cred"
+.Fc
+.Sh DESCRIPTION
+This VOP call zeroes/deallocates storage for an offset range in a file.
+It is used to implement the
+.Xr fspacectl 2
+system call.
+.Pp
+Its arguments are:
+.Bl -tag -width offset
+.It Fa vp
+The vnode of the file.
+.It Fa offset
+The start of the range to deallocate storage in the file.
+.It Fa len
+The length of the range to deallocate storage in the file.
+.It Fa flags
+The flags of this call.
+This should be set to 0 for now.
+.It Fa cred
+The credentials of the caller.
+.El
+.Pp
+The
+.Fa offset
+and
+.Fa len
+arguments are updated to reflect the portion of the range that
+still needs to be zeroed/deallocated on return.
+Partial result is considered a successful operation.
+.Sh LOCKS
+The vnode should be locked on entry and will still be locked on exit.
+.Sh RETURN VALUES
+Zero is returned if the call is successful, otherwise an appropriate
+error code is returned.
+.Sh ERRORS
+.Bl -tag -width Er
+.It Bq Er EINVAL
+Invalid
+.Fa offset , len
+or
+.Fa flags
+parameters are passed into this VOP call.
+.It Bq Er ENODEV
+The vnode type is not supported by this VOP call.
+.It Bq Er ENOSPC
+The file system is full.
+.It Bq Er EPERM
+An append-only flag is set on the file, but the caller is attempting to
+write before the current end of file.
+.El
+.Sh SEE ALSO
+.Xr vnode 9
+.Sh AUTHORS
+.Nm
+and this manual page were developed by
+.An Ka Ho Ng Aq Mt khng@FreeBSDFoundation.org
+under sponsorship from the FreeBSD Foundation.
Index: share/man/man9/vn_deallocate.9
===================================================================
--- share/man/man9/vn_deallocate.9
+++ share/man/man9/vn_deallocate.9
@@ -24,52 +24,70 @@
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd January 26, 2021
-.Dt VNODE_PAGER_PURGE_RANGE 9
+.Dd April 16, 2021
+.Dt VN_DEALLOCATE 9
.Os
.Sh NAME
-.Nm vnode_pager_purge_range
-.Nd "invalidate the content of the given range in bytes from cache"
+.Nm vn_deallocate
+.Nd zero and/or deallocate storage from a file
.Sh SYNOPSIS
.In sys/param.h
-.In vm/vm.h
-.In vm/vm_extern.h
-.Ft void
-.Fn vnode_pager_purge_range "struct vnode *vp" "vm_ooffset_t startoff" "vm_ooffset_t endoff"
+.In sys/vnode.h
+.Ft int
+.Fo vn_deallocate
+.Fa "struct vnode *vp"
+.Fa "off_t offset"
+.Fa "off_t len"
+.Fa "int flags"
+.Fa "int ioflg"
+.Fa "struct ucred *active_cred"
+.Fa "struct ucred *file_cred"
+.Fc
.Sh DESCRIPTION
-.Nm
-invalidates content of the cache covered by the given range from the
-specified vnode
-.Fa vp .
-The range to be purged is
-.Eo [
-.Fa startoff , endoff
-.Ec ) .
-Affected pages within the specified range will be tossed away.
-.Sh IMPLEMENTATION NOTES
-Within the specified range,
-in case
-.Fa startoff
-or
-.Fa endoff
-is not aligned to page boundaries,
-partial-page area will be zeroed.
-In partial-page area,
-for content occupying whole blocks within block
-boundaries,
-the dirty bits for the corresponding blocks will be cleared.
-.Sh LOCKING
-Writer lock of the VM object of
-.Fa vp
-will be held within the function.
-.Sh SEE ALSO
-.Xr vnode 9
-.Sh HISTORY
The
-.Nm
-manual page first appeared in
-.Fx 14 .
+.Fn vn_deallocate
+function zeros and/or deallocates backing storage space from a file.
+This function only works on vnodes with
+.Dv VREG
+type.
+.Pp
+The arguments are:
+.Bl -tag -width active_cred
+.It Fa vp
+The vnode of the file.
+.It Fa offset
+The starting offset of the operation range.
+.It Fa len
+The length of the operation range.
+This must be greater than 0.
+.It Fa flags
+The control flags of the operation.
+This should be set to 0 for now.
+.It Fa ioflg
+The control flags of vnode locking.
+.It Fa active_cred
+The user credentials of the calling thread.
+.It Fa file_cred
+The credentials installed on the file description pointing to the vnode or NOCRED.
+.El
+.Pp
+The
+.Fn ioflg
+argument may be one or more of the following flags:
+.Bl -tag -width IO_RANGELOCKED
+.It Dv IO_NODELOCKED
+The vnode was locked on entry to this function.
+.It Dv IO_RANGELOCKED
+Rangelock was owned around the call.
+.El
+.Sh RETURN VALUES
+Upon successful completion, the value 0 is returned; otherwise the
+appropriate error is returned.
+.Sh SEE ALSO
+.Xr vnode 9 ,
+.Xr VOP_DEALLOCATE 9
.Sh AUTHORS
-This
-manual page was written by
-.An Ka Ho Ng Aq Mt khng@freebsdfoundation.org .
+.Nm
+and this manual page were developed by
+.An Ka Ho Ng Aq Mt khng@FreeBSDFoundation.org
+under sponsorship from the FreeBSD Foundation.
Index: share/man/man9/vnode_pager_purge_range.9
===================================================================
--- share/man/man9/vnode_pager_purge_range.9
+++ share/man/man9/vnode_pager_purge_range.9
@@ -3,6 +3,9 @@
.\"
.\" Copyright (c) 2021 The FreeBSD Foundation
.\"
+.\" This software was developed by Ka Ho Ng under sponsorship from
+.\" the FreeBSD Foundation.
+.\"
.\" Redistribution and use in source and binary forms, with or without
.\" modification, are permitted provided that the following conditions
.\" are met:
Index: sys/bsm/audit_kevents.h
===================================================================
--- sys/bsm/audit_kevents.h
+++ sys/bsm/audit_kevents.h
@@ -662,6 +662,7 @@
#define AUE_SPECIALFD 43266 /* FreeBSD-specific. */
#define AUE_AIO_WRITEV 43267 /* FreeBSD-specific. */
#define AUE_AIO_READV 43268 /* FreeBSD-specific. */
+#define AUE_FSPACECTL 43269 /* FreeBSD-specific. */
/*
* Darwin BSM uses a number of AUE_O_* definitions, which are aliased to the
Index: sys/compat/freebsd32/freebsd32_misc.c
===================================================================
--- sys/compat/freebsd32/freebsd32_misc.c
+++ sys/compat/freebsd32/freebsd32_misc.c
@@ -3569,6 +3569,20 @@
return (kern_posix_error(td, error));
}
+int
+freebsd32_fspacectl(struct thread *td, struct freebsd32_fspacectl_args *uap)
+{
+ int error;
+ struct spacectl_range range;
+
+ error = copyin(uap->range, &range, sizeof(range));
+ if (error != 0)
+ return (error);
+
+ error = kern_fspacectl(td, uap->fd, uap->cmd, uap->range, uap->flags);
+ return (error);
+}
+
int
freebsd32_posix_fadvise(struct thread *td,
struct freebsd32_posix_fadvise_args *uap)
Index: sys/compat/freebsd32/syscalls.master
===================================================================
--- sys/compat/freebsd32/syscalls.master
+++ sys/compat/freebsd32/syscalls.master
@@ -1174,5 +1174,9 @@
struct aiocb32 *aiocbp); }
579 AUE_AIO_READV STD { int freebsd32_aio_readv( \
struct aiocb32 *aiocbp); }
+580 AUE_FSPACECTL STD { int freebsd32_fspacectl(int fd, \
+ int cmd, \
+ struct spacectl_range *range,\
+ int flags); }
; vim: syntax=off
Index: sys/kern/sys_generic.c
===================================================================
--- sys/kern/sys_generic.c
+++ sys/kern/sys_generic.c
@@ -861,6 +861,59 @@
return (error);
}
+int
+sys_fspacectl(struct thread *td, struct fspacectl_args *uap)
+{
+ struct spacectl_range range;
+ int error;
+
+ error = copyin(uap->range, &range, sizeof(range));
+ if (error != 0)
+ return (error);
+
+ error = kern_fspacectl(td, uap->fd, uap->cmd, &range, uap->flags);
+ return (error);
+}
+
+int
+kern_fspacectl(struct thread *td, int fd, int cmd, struct spacectl_range *range,
+ int flags)
+{
+ struct file *fp;
+ off_t offset;
+ int error;
+
+ offset = range->r_len;
+
+ AUDIT_ARG_FD(fd);
+ AUDIT_ARG_CMD(cmd);
+ AUDIT_ARG_FFLAGS(flags);
+
+ if (cmd != SPACECTL_DEALLOC ||
+ range->r_offset < 0 || range->r_len < 0 ||
+ (flags & ~SPACECTL_F_SUPPORTED) != 0)
+ return (EINVAL);
+
+ error = fget(td, fd, &cap_pwrite_rights, &fp);
+ if (error != 0)
+ return (error);
+ AUDIT_ARG_FILE(td->td_proc, fp);
+ if ((fp->f_ops->fo_flags & DFLAG_SEEKABLE) == 0) {
+ error = ESPIPE;
+ goto out;
+ }
+ if ((fp->f_flag & FWRITE) == 0) {
+ error = EBADF;
+ goto out;
+ }
+
+ error = fo_fspacectl(fp, cmd, range->r_offset, range->r_len, flags,
+ td->td_ucred, td);
+out:
+ fdrop(fp, td);
+ return (error);
+}
+
int
kern_specialfd(struct thread *td, int type, void *arg)
{
Index: sys/kern/syscalls.master
===================================================================
--- sys/kern/syscalls.master
+++ sys/kern/syscalls.master
@@ -3238,6 +3238,14 @@
_Inout_ struct aiocb *aiocbp
);
}
+580 AUE_FSPACECTL STD {
+ int fspacectl(
+ int fd,
+ int cmd,
+ _In_ struct spacectl_range *range,
+ int flags
+ );
+ }
; Please copy any additions and changes to the following compatability tables:
; sys/compat/freebsd32/syscalls.master
Index: sys/kern/uipc_shm.c
===================================================================
--- sys/kern/uipc_shm.c
+++ sys/kern/uipc_shm.c
@@ -146,6 +146,7 @@
static fo_get_seals_t shm_get_seals;
static fo_add_seals_t shm_add_seals;
static fo_fallocate_t shm_fallocate;
+static fo_fspacectl_t shm_fspacectl;
/* File descriptor operations. */
struct fileops shm_ops = {
@@ -166,6 +167,7 @@
.fo_get_seals = shm_get_seals,
.fo_add_seals = shm_add_seals,
.fo_fallocate = shm_fallocate,
+ .fo_fspacectl = shm_fspacectl,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE,
};
@@ -1874,6 +1876,93 @@
return (0);
}
+static int
+shm_deallocate(struct shmfd *shmfd, off_t offset, off_t len, int flags,
+ void *rl_cookie, struct thread *td)
+{
+ vm_pindex_t start, start2, end;
+ vm_ooffset_t size;
+ vm_page_t m;
+
+ start = OFF_TO_IDX(offset);
+ start2 = OFF_TO_IDX(offset + PAGE_MASK);
+ end = OFF_TO_IDX(offset + len);
+ size = offset + len;
+
+ VM_OBJECT_WLOCK(shmfd->shm_object);
+
+ if (start2 < end)
+ vm_object_page_remove(shmfd->shm_object, start2, end, 0);
+ if (len > OFF_MAX - offset)
+ len = OFF_MAX - offset;
+
+ if ((offset & PAGE_MASK) != offset) {
+ m = vm_page_grab(shmfd->shm_object, start, VM_ALLOC_NOCREAT);
+ if (m != NULL) {
+ pmap_zero_page_area(m, offset & PAGE_MASK,
+ PAGE_SIZE - (offset & PAGE_MASK));
+ vm_page_set_dirty(m);
+ vm_page_xunbusy(m);
+ }
+ }
+ if ((size & PAGE_MASK) != size) {
+ m = vm_page_grab(shmfd->shm_object, end, VM_ALLOC_NOCREAT);
+ if (m != NULL) {
+ pmap_zero_page_area(m, 0, offset + len & PAGE_MASK);
+ vm_page_set_dirty(m);
+ vm_page_xunbusy(m);
+ }
+ }
+
+ VM_OBJECT_WUNLOCK(shmfd->shm_object);
+ return (0);
+}
+
+static int
+shm_fspacectl(struct file *fp, int cmd, off_t offset, off_t len, int flags,
+ struct ucred *active_cred, struct thread *td)
+{
+ void *rl_cookie;
+ struct shmfd *shmfd;
+ size_t size;
+ int error;
+
+ /* This assumes that the caller already checked for overflow. */
+ error = 0;
+ shmfd = fp->f_data;
+ size = offset + len;
+
+ if (cmd != SPACECTL_DEALLOC)
+ return (EINVAL);
+ if (offset < 0 || len < 0 || flags != 0)
+ return (EINVAL);
+ if (len == 0)
+ /* Degenerated case */
+ return (0);
+
+ /*
+ * Just grab the rangelock for the range that we may be attempting to
+ * grow, rather than blocking read/write for regions we won't be
+ * touching while this (potential) resize is in progress. Other
+ * attempts to resize the shmfd will have to take a write lock from 0 to
+ * OFF_MAX, so this being potentially beyond the current usable range of
+ * the shmfd is not necessarily a concern. If other mechanisms are
+ * added to grow a shmfd, this may need to be re-evaluated.
+ */
+ rl_cookie = rangelock_wlock(&shmfd->shm_rl, offset, size,
+ &shmfd->shm_mtx);
+ switch (cmd) {
+ case SPACECTL_DEALLOC:
+ error = shm_deallocate(shmfd, offset, len, flags, rl_cookie, td);
+ break;
+ default:
+ panic("%s: unknown cmd %d", __func__, cmd);
+ }
+ rangelock_unlock(&shmfd->shm_rl, rl_cookie, &shmfd->shm_mtx);
+ return (error);
+}
+
+
static int
shm_fallocate(struct file *fp, off_t offset, off_t len, struct thread *td)
{
Index: sys/kern/vfs_default.c
===================================================================
--- sys/kern/vfs_default.c
+++ sys/kern/vfs_default.c
@@ -93,6 +93,7 @@
static int vop_stdread_pgcache(struct vop_read_pgcache_args *ap);
static int vop_stdstat(struct vop_stat_args *ap);
static int vop_stdvput_pair(struct vop_vput_pair_args *ap);
+static int vop_stddeallocate(struct vop_deallocate_args *ap);
/*
* This vnode table stores what we want to do if the filesystem doesn't
@@ -117,6 +118,7 @@
.vop_advlockasync = vop_stdadvlockasync,
.vop_advlockpurge = vop_stdadvlockpurge,
.vop_allocate = vop_stdallocate,
+ .vop_deallocate = vop_stddeallocate,
.vop_bmap = vop_stdbmap,
.vop_close = VOP_NULL,
.vop_fsync = VOP_NULL,
@@ -518,6 +520,7 @@
case _PC_ACL_EXTENDED:
case _PC_ACL_NFS4:
case _PC_CAP_PRESENT:
+ case _PC_FDEALLOC_PRESENT:
case _PC_INF_PRESENT:
case _PC_MAC_PRESENT:
*ap->a_retval = 0;
@@ -1069,6 +1072,127 @@
return (error);
}
+static int
+vp_zerofill(struct vnode *vp, struct vattr *vap, off_t offset, off_t len,
+ off_t *residp, struct ucred *cred)
+{
+ int iosize;
+ int error = 0;
+ struct iovec aiov;
+ struct uio auio;
+ struct thread *td;
+
+ iosize = vap->va_blocksize;
+ td = curthread;
+
+ if (iosize == 0)
+ iosize = BLKDEV_IOSIZE;
+ if (iosize > maxphys)
+ iosize = maxphys;
+ iosize = min(iosize, ZERO_REGION_SIZE);
+
+ while (len > 0) {
+ int xfersize = iosize;
+ if (offset % iosize != 0)
+ xfersize -= offset % iosize;
+ if (xfersize > len)
+ xfersize = len;
+
+ aiov.iov_base = __DECONST(void *, zero_region);
+ aiov.iov_len = xfersize;
+ auio.uio_iov = &aiov;
+ auio.uio_iovcnt = 1;
+ auio.uio_offset = offset;
+ auio.uio_resid = xfersize;
+ auio.uio_segflg = UIO_SYSSPACE;
+ auio.uio_rw = UIO_WRITE;
+ auio.uio_td = td;
+
+ error = VOP_WRITE(vp, &auio, 0, cred);
+ if (error != 0) {
+ len -= xfersize - auio.uio_resid;
+ break;
+ }
+
+ len -= xfersize;
+ offset += xfersize;
+
+ maybe_yield();
+ }
+
+ *residp = len;
+ return (error);
+}
+
+static int
+vop_stddeallocate(struct vop_deallocate_args *ap)
+{
+ struct vnode *vp;
+ off_t offset, len;
+ struct ucred *cred;
+ int error;
+ struct vattr va;
+ off_t noff, xfersize, rem;
+
+ vp = ap->a_vp;
+ offset = *ap->a_offset;
+ len = *ap->a_len;
+ cred = ap->a_cred;
+
+ error = VOP_GETATTR(vp, &va, cred);
+ if (error)
+ return (error);
+
+ len = omin(OFF_MAX - offset, *ap->a_len);
+ while (len > 0) {
+ noff = offset;
+ error = vn_bmap_seekhole_locked(vp, FIOSEEKDATA, &noff, cred);
+ if (error) {
+ if (error != ENXIO)
+ /* XXX: Is it okay to fallback further? */
+ goto out;
+
+ /*
+ * No more data region to be filled
+ */
+ len = 0;
+ error = 0;
+ break;
+ }
+ KASSERT(noff >= offset, ("FIOSEEKDATA going backward"));
+ if (noff != offset) {
+ xfersize = omin(noff - offset, len);
+ len -= xfersize;
+ offset += xfersize;
+ if (len == 0)
+ break;
+ }
+ error = vn_bmap_seekhole_locked(vp, FIOSEEKHOLE, &noff, cred);
+ if (error)
+ goto out;
+
+ /* Fill zeroes */
+ xfersize = omin(noff - offset, len);
+ error = vp_zerofill(vp, &va, offset, xfersize, &rem, cred);
+ if (error) {
+ len -= xfersize - rem;
+ offset += xfersize - rem;
+ goto out;
+ }
+
+ len -= xfersize;
+ offset += xfersize;
+ if (should_yield())
+ break;
+ }
+out:
+ if (error == 0) {
+ *ap->a_offset = offset;
+ *ap->a_len = len;
+ }
+ return (error);
+}
+
int
vop_stdadvise(struct vop_advise_args *ap)
{
Index: sys/kern/vfs_vnops.c
===================================================================
--- sys/kern/vfs_vnops.c
+++ sys/kern/vfs_vnops.c
@@ -106,6 +106,7 @@
static fo_close_t vn_closefile;
static fo_mmap_t vn_mmap;
static fo_fallocate_t vn_fallocate;
+static fo_fspacectl_t vn_fspacectl;
struct fileops vnops = {
.fo_read = vn_io_fault,
@@ -123,6 +124,7 @@
.fo_fill_kinfo = vn_fill_kinfo,
.fo_mmap = vn_mmap,
.fo_fallocate = vn_fallocate,
+ .fo_fspacectl = vn_fspacectl,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
@@ -2363,7 +2365,8 @@
}
int
-vn_bmap_seekhole(struct vnode *vp, u_long cmd, off_t *off, struct ucred *cred)
+vn_bmap_seekhole_locked(struct vnode *vp, u_long cmd, off_t *off,
+ struct ucred *cred)
{
struct vattr va;
daddr_t bn, bnp;
@@ -2371,22 +2374,17 @@
off_t noff;
int error;
- KASSERT(cmd == FIOSEEKHOLE || cmd == FIOSEEKDATA,
- ("Wrong command %lu", cmd));
-
- if (vn_lock(vp, LK_SHARED) != 0)
- return (EBADF);
if (vp->v_type != VREG) {
error = ENOTTY;
- goto unlock;
+ goto out;
}
error = VOP_GETATTR(vp, &va, cred);
if (error != 0)
- goto unlock;
+ goto out;
noff = *off;
if (noff >= va.va_size) {
error = ENXIO;
- goto unlock;
+ goto out;
}
bsize = vp->v_mount->mnt_stat.f_iosize;
for (bn = noff / bsize; noff < va.va_size; bn++, noff += bsize -
@@ -2394,14 +2392,14 @@
error = VOP_BMAP(vp, bn, NULL, &bnp, NULL, NULL);
if (error == EOPNOTSUPP) {
error = ENOTTY;
- goto unlock;
+ goto out;
}
if ((bnp == -1 && cmd == FIOSEEKHOLE) ||
(bnp != -1 && cmd == FIOSEEKDATA)) {
noff = bn * bsize;
if (noff < *off)
noff = *off;
- goto unlock;
+ goto out;
}
}
if (noff > va.va_size)
@@ -2409,13 +2407,27 @@
/* noff == va.va_size. There is an implicit hole at the end of file. */
if (cmd == FIOSEEKDATA)
error = ENXIO;
-unlock:
- VOP_UNLOCK(vp);
+out:
if (error == 0)
*off = noff;
return (error);
}
+int
+vn_bmap_seekhole(struct vnode *vp, u_long cmd, off_t *off, struct ucred *cred)
+{
+ int error;
+
+ KASSERT(cmd == FIOSEEKHOLE || cmd == FIOSEEKDATA,
+ ("Wrong command %lu", cmd));
+
+ if (vn_lock(vp, LK_SHARED) != 0)
+ return (EBADF);
+ error = vn_bmap_seekhole_locked(vp, cmd, off, cred);
+ VOP_UNLOCK(vp);
+ return (error);
+}
+
int
vn_seek(struct file *fp, off_t offset, int whence, struct thread *td)
{
@@ -3357,6 +3369,123 @@
return (error);
}
+static int
+vn_deallocate_impl(struct vnode *vp, off_t offset, off_t len, int flags, int ioflg,
+ bool may_audit, struct ucred *active_cred, struct ucred *file_cred,
+ struct thread *td)
+{
+ struct mount *mp;
+ void *rl_cookie;
+ int lock_flags;
+ int error;
+#ifdef AUDIT
+ int audited_vnode1 = 0;
+#endif
+
+ rl_cookie = NULL;
+ error = 0;
+
+ if (offset < 0 || len <= 0 || flags != 0)
+ return (EINVAL);
+ if (vp->v_type != VREG)
+ return (ENODEV);
+
+ /* Take the maximum range if end offset overflows */
+ len = omin(len, OFF_MAX - offset);
+
+ while (len > 0) {
+ /*
+ * Try to deallocate the longest range in one pass.
+ * In case a pass takes too long to be executed, it returns
+ * partial result. The residue will be proceeded in the next
+ * pass.
+ */
+
+ mp = NULL;
+
+ bwillwrite();
+
+ if ((ioflg & IO_NODELOCKED) == 0) {
+ if ((ioflg & IO_RANGELOCKED) == 0 &&
+ rl_cookie == NULL) {
+ rl_cookie = vn_rangelock_wlock(vp, offset,
+ offset + len);
+ if ((error = vn_start_write(vp, &mp,
+ V_WAIT | PCATCH)) != 0)
+ goto out;
+ }
+
+ if ((MNT_SHARED_WRITES(mp) ||
+ (mp == NULL && MNT_SHARED_WRITES(vp->v_mount))))
+ lock_flags = LK_SHARED;
+ else
+ lock_flags = LK_EXCLUSIVE;
+
+ vn_lock(vp, lock_flags | LK_RETRY);
+#ifdef AUDIT
+ if (may_audit && !audited_vnode1) {
+ AUDIT_ARG_VNODE1(vp);
+ audited_vnode1 = 1;
+ }
+#endif
+ }
+
+#ifdef MAC
+ if ((ioflg & IO_NOMACCHECK) == 0)
+ error = mac_vnode_check_write(active_cred, file_cred,
+ vp);
+#endif
+ if (error == 0)
+ error = VOP_DEALLOCATE(vp, &offset, &len, flags,
+ active_cred);
+
+ if ((ioflg & IO_NODELOCKED) == 0) {
+ VOP_UNLOCK(vp);
+ if (mp != NULL)
+ vn_finished_write(mp);
+ }
+
+ if (error != 0)
+ break;
+ }
+out:
+ if (rl_cookie != NULL)
+ vn_rangelock_unlock(vp, rl_cookie);
+ return (error);
+}
+
+int
+vn_deallocate(struct vnode *vp, off_t offset, off_t len, int flags, int ioflg,
+ struct ucred *active_cred, struct ucred *file_cred, struct thread *td)
+{
+ return (vn_deallocate_impl(vp, offset, len, flags, ioflg, false,
+ active_cred, file_cred, td));
+}
+
+static int
+vn_fspacectl(struct file *fp, int cmd, off_t offset, off_t len, int flags,
+ struct ucred *active_cred, struct thread *td)
+{
+ int error;
+ struct vnode *vp;
+
+ vp = fp->f_vnode;
+
+ if (cmd != SPACECTL_DEALLOC)
+ return (EINVAL);
+
+ switch (cmd) {
+ case SPACECTL_DEALLOC:
+ error = vn_deallocate_impl(vp, offset, len, flags, 0, true,
+ active_cred, fp->f_cred, td);
+ break;
+ default:
+ panic("vn_fspacectl: unknown cmd %d", cmd);
+ }
+
+ return (error);
+}
+
static u_long vn_lock_pair_pause_cnt;
SYSCTL_ULONG(_debug, OID_AUTO, vn_lock_pair_pause, CTLFLAG_RD,
&vn_lock_pair_pause_cnt, 0,
Index: sys/kern/vnode_if.src
===================================================================
--- sys/kern/vnode_if.src
+++ sys/kern/vnode_if.src
@@ -801,6 +801,17 @@
};
+%% deallocate vp L L L
+
+vop_deallocate {
+ IN struct vnode *vp;
+ INOUT off_t *offset;
+ INOUT off_t *len;
+ IN int flags;
+ IN struct ucred *cred;
+};
+
+
# The VOPs below are spares at the end of the table to allow new VOPs to be
# added in stable branches without breaking the KBI. New VOPs in HEAD should
# be added above these spares. When merging a new VOP to a stable branch,
Index: sys/security/audit/audit_bsm.c
===================================================================
--- sys/security/audit/audit_bsm.c
+++ sys/security/audit/audit_bsm.c
@@ -1091,6 +1091,18 @@
FD_VNODE1_TOKENS;
break;
+ case AUE_FSPACECTL:
+ if (ARG_IS_VALID(kar, ARG_CMD)) {
+ tok = au_to_arg32(2, "operation", ar->ar_arg_cmd);
+ kau_write(rec, tok);
+ }
+ if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
+ tok = au_to_arg32(4, "flags", ar->ar_arg_fflags);
+ kau_write(rec, tok);
+ }
+ FD_VNODE1_TOKENS;
+ break;
+
case AUE_RFORK:
if (ARG_IS_VALID(kar, ARG_FFLAGS)) {
tok = au_to_arg32(1, "flags", ar->ar_arg_fflags);
Index: sys/sys/fcntl.h
===================================================================
--- sys/sys/fcntl.h
+++ sys/sys/fcntl.h
@@ -313,6 +313,14 @@
short l_type; /* lock type: read/write, etc. */
short l_whence; /* type of l_start */
};
+
+/*
+ * Space control offset/length description
+ */
+struct spacectl_range {
+ off_t r_offset; /* starting offset */
+ off_t r_len; /* length */
+};
#endif
#if __BSD_VISIBLE
@@ -342,6 +350,16 @@
* similar syscalls.
*/
#define FD_NONE -200
+
+/*
+ * Commands for fspacectl(2)
+ */
+#define SPACECTL_DEALLOC 0 /* deallocate space */
+
+/*
+ * fspacectl(2) flags
+ */
+#define SPACECTL_F_SUPPORTED 0
#endif
#ifndef _KERNEL
@@ -351,6 +369,7 @@
int fcntl(int, int, ...);
#if __BSD_VISIBLE
int flock(int, int);
+int fspacectl(int, int, struct spacectl_range *, int);
#endif
#if __POSIX_VISIBLE >= 200809
int openat(int, const char *, int, ...);
Index: sys/sys/file.h
===================================================================
--- sys/sys/file.h
+++ sys/sys/file.h
@@ -129,6 +129,9 @@
typedef int fo_get_seals_t(struct file *fp, int *flags);
typedef int fo_fallocate_t(struct file *fp, off_t offset, off_t len,
struct thread *td);
+typedef int fo_fspacectl_t(struct file *fp, int cmd,
+ off_t offset, off_t len, int flags,
+ struct ucred *active_cred, struct thread *td);
typedef int fo_flags_t;
struct fileops {
@@ -150,6 +153,7 @@
fo_add_seals_t *fo_add_seals;
fo_get_seals_t *fo_get_seals;
fo_fallocate_t *fo_fallocate;
+ fo_fspacectl_t *fo_fspacectl;
fo_flags_t fo_flags; /* DFLAG_* below */
};
@@ -470,6 +474,17 @@
return ((*fp->f_ops->fo_fallocate)(fp, offset, len, td));
}
+static __inline int fo_fspacectl(struct file *fp, int cmd, off_t offset,
+ off_t len, int flags, struct ucred *active_cred, struct thread *td)
+{
+
+ if (fp->f_ops->fo_fspacectl == NULL)
+ return (ENODEV);
+ return ((*fp->f_ops->fo_fspacectl)(fp, cmd, offset, len, flags,
+ active_cred, td));
+}
+
+
#endif /* _KERNEL */
#endif /* !SYS_FILE_H */
Index: sys/sys/syscallsubr.h
===================================================================
--- sys/sys/syscallsubr.h
+++ sys/sys/syscallsubr.h
@@ -59,6 +59,7 @@
struct sched_param;
union semun;
struct sockaddr;
+struct spacectl_range;
struct stat;
struct thr_param;
struct timex;
@@ -230,6 +231,8 @@
int advice);
int kern_posix_fallocate(struct thread *td, int fd, off_t offset,
off_t len);
+int kern_fspacectl(struct thread *td, int fd, int cmd,
+ struct spacectl_range *, int flags);
int kern_procctl(struct thread *td, enum idtype idtype, id_t id, int com,
void *data);
int kern_pread(struct thread *td, int fd, void *buf, size_t nbyte,
Index: sys/sys/unistd.h
===================================================================
--- sys/sys/unistd.h
+++ sys/sys/unistd.h
@@ -156,6 +156,7 @@
#define _PC_INF_PRESENT 62
#define _PC_MAC_PRESENT 63
#define _PC_ACL_NFS4 64
+#define _PC_FDEALLOC_PRESENT 65
#endif
/* From OpenSolaris, used by SEEK_DATA/SEEK_HOLE. */
Index: sys/sys/vnode.h
===================================================================
--- sys/sys/vnode.h
+++ sys/sys/vnode.h
@@ -725,6 +725,9 @@
void vn_printf(struct vnode *vp, const char *fmt, ...) __printflike(2,3);
int vrecycle(struct vnode *vp);
int vrecyclel(struct vnode *vp);
+/* vn_bmap_seekhole_locked is not public KPI */
+int vn_bmap_seekhole_locked(struct vnode *vp, u_long cmd, off_t *off,
+ struct ucred *cred);
int vn_bmap_seekhole(struct vnode *vp, u_long cmd, off_t *off,
struct ucred *cred);
int vn_close(struct vnode *vp,
@@ -733,6 +736,9 @@
struct vnode *outvp, off_t *outoffp, size_t *lenp,
unsigned int flags, struct ucred *incred, struct ucred *outcred,
struct thread *fsize_td);
+int vn_deallocate(struct vnode *vp, off_t offset, off_t len, int flags,
+ int ioflg, struct ucred *active_cred, struct ucred *file_cred,
+ struct thread *td);
void vn_finished_write(struct mount *mp);
void vn_finished_secondary_write(struct mount *mp);
int vn_fsync_buf(struct vnode *vp, int waitfor);
Index: tests/sys/file/Makefile
===================================================================
--- tests/sys/file/Makefile
+++ tests/sys/file/Makefile
@@ -10,6 +10,7 @@
TAP_TESTS_SH+= flock_test
PLAIN_TESTS_C+= ftruncate_test
PLAIN_TESTS_C+= newfileops_on_fork_test
+ATF_TESTS_C+= fspacectl_test
PROGS+= flock_helper
Index: tests/sys/file/fspacectl_test.c
===================================================================
--- /dev/null
+++ tests/sys/file/fspacectl_test.c
@@ -0,0 +1,330 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Ka Ho Ng under sponsorship from
+ * the FreeBSD Foundation.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/param.h>
+#include <sys/mount.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+#include <fcntl.h>
+#include <malloc.h>
+
+static off_t file_max_blocks = 32;
+static const char byte_to_fill = 0x5f;
+
+static int
+fill(int fd, off_t offset, off_t len)
+{
+ int error;
+ size_t blen;
+ char *buf;
+ struct stat statbuf;
+ blksize_t blocksize;
+
+ if (fstat(fd, &statbuf) == -1)
+ return (1);
+ blocksize = statbuf.st_blksize;
+ error = 0;
+ buf = malloc(blocksize);
+ if (buf == NULL)
+ return (1);
+
+ while (len > 0) {
+ blen = len < (off_t)blocksize ? len : blocksize;
+ memset(buf, byte_to_fill, blen);
+ if (pwrite(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ len -= blen;
+ offset += blen;
+ }
+
+ free(buf);
+ return (error);
+}
+
+static blksize_t
+fd_get_blksize(void)
+{
+ struct statfs statfsbuf;
+
+ if (statfs(".", &statfsbuf) == -1)
+ return (-1);
+ return statfsbuf.f_iosize;
+}
+
+static int
+check_content_dealloc(int fd, off_t hole_start, off_t hole_len, off_t file_sz)
+{
+ int error;
+ size_t blen;
+ off_t offset, resid;
+ struct stat statbuf;
+ char *buf, *sblk;
+ blksize_t blocksize;
+
+ blocksize = fd_get_blksize();
+ if (blocksize == -1)
+ return (1);
+ error = 0;
+ buf = malloc(blocksize * 2);
+ if (buf == NULL)
+ return (1);
+ sblk = buf + blocksize;
+
+ memset(sblk, 0, blocksize);
+
+ if ((uint64_t)hole_start + hole_len > (uint64_t)file_sz)
+ hole_len = file_sz - hole_start;
+
+ /*
+ * Check hole is zeroed.
+ */
+ offset = hole_start;
+ resid = hole_len;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ memset(sblk, byte_to_fill, blocksize);
+
+ /*
+ * Check file region before hole is zeroed.
+ */
+ offset = 0;
+ resid = hole_start;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file region after hole is zeroed.
+ */
+ offset = hole_start + hole_len;
+ resid = file_sz - offset;
+ while (resid > 0) {
+ blen = resid < (off_t)blocksize ? resid : blocksize;
+ if (pread(fd, buf, blen, offset) != (ssize_t)blen) {
+ error = 1;
+ break;
+ }
+ if (memcmp(buf, sblk, blen) != 0) {
+ error = 1;
+ break;
+ }
+ resid -= blen;
+ offset += blen;
+ }
+
+ /*
+ * Check file size matches with expected file size.
+ */
+ if (fstat(fd, &statbuf) == -1)
+ error = -1;
+ if (statbuf.st_size != file_sz)
+ error = -1;
+
+ free(buf);
+ return (error);
+}
+
+/*
+ * Check aligned deallocation
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc);
+ATF_TC_BODY(aligned_dealloc, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize;
+ range.r_len = (file_max_blocks - 1) * blocksize - range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc);
+ATF_TC_BODY(unaligned_dealloc, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize / 2;
+ range.r_len = (file_max_blocks - 1) * blocksize + blocksize / 2 -
+ range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation from certain offset to OFF_MAX
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_offmax);
+ATF_TC_BODY(aligned_dealloc_offmax, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize;
+ range.r_len = OFF_MAX;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation from certain offset to OFF_MAX
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_offmax);
+ATF_TC_BODY(unaligned_dealloc_offmax, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize / 2;
+ range.r_len = OFF_MAX;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_eof);
+ATF_TC_BODY(aligned_dealloc_eof, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize;
+ range.r_len = (file_max_blocks + 1) * blocksize - range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_eof);
+ATF_TC_BODY(unaligned_dealloc_eof, tc)
+{
+ struct spacectl_range range;
+ blksize_t blocksize;
+ int fd;
+
+ ATF_REQUIRE((blocksize = fd_get_blksize()) != -1);
+ range.r_offset = blocksize / 2;
+ range.r_len = file_max_blocks * blocksize + blocksize / 2 -
+ range.r_offset;
+
+ ATF_REQUIRE((fd = open("sys_fspacectl_testfile",
+ O_CREAT | O_RDWR | O_TRUNC, 0600)) != -1);
+ ATF_REQUIRE(fill(fd, 0, file_max_blocks * blocksize) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content_dealloc(fd, range.r_offset, range.r_len,
+ file_max_blocks * blocksize) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+ATF_TP_ADD_TCS(tp)
+{
+ ATF_TP_ADD_TC(tp, aligned_dealloc);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc);
+ ATF_TP_ADD_TC(tp, aligned_dealloc_eof);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_eof);
+ ATF_TP_ADD_TC(tp, aligned_dealloc_offmax);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_offmax);
+
+ return atf_no_error();
+}

File Metadata

Mime Type
text/plain
Expires
Fri, Nov 21, 1:04 PM (18 h, 16 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
25766121
Default Alt Text
D28347.id87513.diff (42 KB)

Event Timeline