Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F111851863
D28347.id84064.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
57 KB
Referenced Files
None
Subscribers
None
D28347.id84064.diff
View Options
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,152 @@
+.\"
+.\" 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 Feb 08, 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
+Deallocate the range specified by the
+.Fa range
+argument in the file.
+.El
+.Pp
+The
+.Fa flags
+argument may include one or more of the following:
+.Bl -tag -width SPACECTL_F_CANEXTEND
+.It Dv SPACECTL_F_CANEXTEND
+The operation can extend the file size if
+.Fa range->r_offset
++
+.Fa range->r_len
+is greater than the current file size.
+.El
+.Pp
+If signal is delivered to a thread calling this system call,
+the system call would either finish as if nothing has happened,
+or signal would be delivered after the operation finishes.
+.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 EFBIG
+The value of
+.Fa offset +
+.Fa len
+is greater than the maximum file size when
+.Dv SPACECTL_F_CANEXTEND
+is set.
+.It Bq Er EINTR
+A signal was caught during execution.
+.It Bq Er EINVAL
+Either the
+.Fa "range->r_offset"
+argument was less than zero, or the
+.Fa "range->r_len"
+argument was less than zero,
+or the operation is not supported by the file system.
+.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: 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,101 @@
+.\"
+.\" 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 Feb 10, 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
+.Fn VOP_DEALLOCATE "struct vnode *vp" "off_t *offset" "off_t *len" "int flags" "struct ucred *cred"
+.Sh DESCRIPTION
+This VOP call zeroes/deallocates storage for an offset range in a file.
+It is used to implement the
+.Xr fspacectl
+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 allocate storage for in the file.
+.It Fa len
+The length of the range to allocate storage for in the file.
+.It Fa flags
+The parameter to control the behavior of this call.
+.Nm .
+.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.
+The file's contents are not changed.
+.Sh LOCKS
+The file should be locked on entry and will still be locked on exit.
+In case
+.Dv SPACECTL_F_CANEXTEND
+is specified in
+.Fa flags ,
+the file should be locked exclusively.
+.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 EFBIG
+An attempt was made to write a file that exceeds the process's file size
+limit or the maximum file size when SPACECTL_F_CANEXPAND is set in
+.Fa flags.
+.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
===================================================================
--- /dev/null
+++ share/man/man9/vn_deallocate.9
@@ -0,0 +1,95 @@
+.\"
+.\" 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 July 9, 2001
+.Dt VN_DEALLOCATE 9
+.Os
+.Sh NAME
+.Nm vn_deallocate
+.Nd zero and/or deallocate storage from a file
+.Sh SYNOPSIS
+.In sys/param.h
+.In sys/vnode.h
+.Ft int
+.Fn vn_deallocate "struct vnode *vp" "off_t offset" "off_t len" "int flags" "int ioflg" "struct ucred *active_cred" "struct ucred *file_cred"
+.Sh DESCRIPTION
+The
+.Fn vn_deallocate
+function zeros and/or deallocate 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
+.It Fa flags
+The control flags argument of the operation.
+.It Fa ioflg
+The control flags argument 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 flags
+argument may be one or more of the following flags:
+.Bl -tag -width SPACECTL_F_CANEXTEND
+.It Dv SPACECTL_F_CANEXTEND
+The operation can extend the file size if
+.Fa offset
++
+.Fa len
+is greater than the current file size.
+.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 caller has granted vnode lock.
+.It Dv IO_RANGELOCKED
+The caller has granted vnode rangelock.
+.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
+.Nm
+and this manual page were developed by
+.An Ka Ho Ng Aq Mt khng@FreeBSDFoundation.org
+under sponsorship from the FreeBSD Foundation.
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/freebsd32_proto.h
===================================================================
--- sys/compat/freebsd32/freebsd32_proto.h
+++ sys/compat/freebsd32/freebsd32_proto.h
@@ -751,6 +751,12 @@
struct freebsd32_aio_readv_args {
char aiocbp_l_[PADL_(struct aiocb32 *)]; struct aiocb32 * aiocbp; char aiocbp_r_[PADR_(struct aiocb32 *)];
};
+struct freebsd32_fspacectl_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char cmd_l_[PADL_(int)]; int cmd; char cmd_r_[PADR_(int)];
+ char range_l_[PADL_(struct spacectl_range *)]; struct spacectl_range * range; char range_r_[PADR_(struct spacectl_range *)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
#if !defined(PAD64_REQUIRED) && !defined(__amd64__)
#define PAD64_REQUIRED
#endif
@@ -893,6 +899,7 @@
int freebsd32___sysctlbyname(struct thread *, struct freebsd32___sysctlbyname_args *);
int freebsd32_aio_writev(struct thread *, struct freebsd32_aio_writev_args *);
int freebsd32_aio_readv(struct thread *, struct freebsd32_aio_readv_args *);
+int freebsd32_fspacectl(struct thread *, struct freebsd32_fspacectl_args *);
#ifdef COMPAT_43
@@ -1476,6 +1483,7 @@
#define FREEBSD32_SYS_AUE_freebsd32___sysctlbyname AUE_SYSCTL
#define FREEBSD32_SYS_AUE_freebsd32_aio_writev AUE_AIO_WRITEV
#define FREEBSD32_SYS_AUE_freebsd32_aio_readv AUE_AIO_READV
+#define FREEBSD32_SYS_AUE_freebsd32_fspacectl AUE_FSPACECTL
#undef PAD_
#undef PADL_
Index: sys/compat/freebsd32/freebsd32_syscall.h
===================================================================
--- sys/compat/freebsd32/freebsd32_syscall.h
+++ sys/compat/freebsd32/freebsd32_syscall.h
@@ -506,4 +506,5 @@
#define FREEBSD32_SYS___specialfd 577
#define FREEBSD32_SYS_freebsd32_aio_writev 578
#define FREEBSD32_SYS_freebsd32_aio_readv 579
-#define FREEBSD32_SYS_MAXSYSCALL 580
+#define FREEBSD32_SYS_freebsd32_fspacectl 580
+#define FREEBSD32_SYS_MAXSYSCALL 581
Index: sys/compat/freebsd32/freebsd32_syscalls.c
===================================================================
--- sys/compat/freebsd32/freebsd32_syscalls.c
+++ sys/compat/freebsd32/freebsd32_syscalls.c
@@ -616,4 +616,5 @@
"__specialfd", /* 577 = __specialfd */
"freebsd32_aio_writev", /* 578 = freebsd32_aio_writev */
"freebsd32_aio_readv", /* 579 = freebsd32_aio_readv */
+ "freebsd32_fspacectl", /* 580 = freebsd32_fspacectl */
};
Index: sys/compat/freebsd32/freebsd32_sysent.c
===================================================================
--- sys/compat/freebsd32/freebsd32_sysent.c
+++ sys/compat/freebsd32/freebsd32_sysent.c
@@ -669,4 +669,5 @@
{ .sy_narg = AS(__specialfd_args), .sy_call = (sy_call_t *)sys___specialfd, .sy_auevent = AUE_SPECIALFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 577 = __specialfd */
{ .sy_narg = AS(freebsd32_aio_writev_args), .sy_call = (sy_call_t *)freebsd32_aio_writev, .sy_auevent = AUE_AIO_WRITEV, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 578 = freebsd32_aio_writev */
{ .sy_narg = AS(freebsd32_aio_readv_args), .sy_call = (sy_call_t *)freebsd32_aio_readv, .sy_auevent = AUE_AIO_READV, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 579 = freebsd32_aio_readv */
+ { .sy_narg = AS(freebsd32_fspacectl_args), .sy_call = (sy_call_t *)freebsd32_fspacectl, .sy_auevent = AUE_FSPACECTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 580 = freebsd32_fspacectl */
};
Index: sys/compat/freebsd32/freebsd32_systrace_args.c
===================================================================
--- sys/compat/freebsd32/freebsd32_systrace_args.c
+++ sys/compat/freebsd32/freebsd32_systrace_args.c
@@ -3407,6 +3407,16 @@
*n_args = 1;
break;
}
+ /* freebsd32_fspacectl */
+ case 580: {
+ struct freebsd32_fspacectl_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->cmd; /* int */
+ uarg[2] = (intptr_t) p->range; /* struct spacectl_range * */
+ iarg[3] = p->flags; /* int */
+ *n_args = 4;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9183,6 +9193,25 @@
break;
};
break;
+ /* freebsd32_fspacectl */
+ case 580:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "userland struct spacectl_range *";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11102,6 +11131,11 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* freebsd32_fspacectl */
+ case 580:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
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/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c
===================================================================
--- sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c
+++ sys/contrib/openzfs/module/os/freebsd/zfs/zfs_vnops_os.c
@@ -3797,6 +3797,49 @@
return (error);
}
+/*
+ * common code for zfs_space-related operations
+ *
+ * This function is called from zfs_space and zfs_deallocate.
+ */
+static int
+zfs_space_common(znode_t *zp, int cmd, off_t off, off_t len, int flag,
+ cred_t *cr)
+{
+ zfsvfs_t *zfsvfs = ZTOZSB(zp);
+ uint64_t uoff, ulen;
+ int error;
+
+ if (cmd != F_FREESP)
+ return (EINVAL);
+
+ /*
+ * Callers might not be able to detect properly that we are read-only,
+ * so check it explicitly here.
+ */
+ if (zfs_is_readonly(zfsvfs))
+ return (EROFS);
+
+ if (len < 0)
+ return (EINVAL);
+
+ /*
+ * Permissions aren't checked on Solaris because on this OS
+ * zfs_space() can only be called with an opened file handle.
+ * On Linux we can get here through truncate_range() which
+ * operates directly on inodes, so we need to check access rights.
+ */
+ if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr)))
+ return (error);
+
+ uoff = off;
+ ulen = len;
+
+ error = zfs_freesp(zp, uoff, ulen, flag, TRUE);
+
+ return (error);
+}
+
/*
* Free or allocate space in a file. Currently, this function only
* supports the `F_FREESP' command. However, this command is somewhat
@@ -3821,47 +3864,16 @@
offset_t offset, cred_t *cr)
{
zfsvfs_t *zfsvfs = ZTOZSB(zp);
- uint64_t off, len;
+ off_t off, len;
int error;
ZFS_ENTER(zfsvfs);
ZFS_VERIFY_ZP(zp);
- if (cmd != F_FREESP) {
- ZFS_EXIT(zfsvfs);
- return (SET_ERROR(EINVAL));
- }
-
- /*
- * Callers might not be able to detect properly that we are read-only,
- * so check it explicitly here.
- */
- if (zfs_is_readonly(zfsvfs)) {
- ZFS_EXIT(zfsvfs);
- return (SET_ERROR(EROFS));
- }
-
- if (bfp->l_len < 0) {
- ZFS_EXIT(zfsvfs);
- return (SET_ERROR(EINVAL));
- }
-
- /*
- * Permissions aren't checked on Solaris because on this OS
- * zfs_space() can only be called with an opened file handle.
- * On Linux we can get here through truncate_range() which
- * operates directly on inodes, so we need to check access rights.
- */
- if ((error = zfs_zaccess(zp, ACE_WRITE_DATA, 0, B_FALSE, cr))) {
- ZFS_EXIT(zfsvfs);
- return (error);
- }
-
off = bfp->l_start;
len = bfp->l_len; /* 0 means from off to end of file */
- error = zfs_freesp(zp, off, len, flag, TRUE);
-
+ error = zfs_space_common(zp, cmd, off, len, flag, cr);
ZFS_EXIT(zfsvfs);
return (error);
}
@@ -5189,6 +5201,9 @@
case _PC_NAME_MAX:
*ap->a_retval = NAME_MAX;
return (0);
+ case _PC_FDEALLOC_PRESENT:
+ *ap->a_retval = 1;
+ return (0);
case _PC_PIPE_BUF:
if (ap->a_vp->v_type == VDIR || ap->a_vp->v_type == VFIFO) {
*ap->a_retval = PIPE_BUF;
@@ -5781,6 +5796,51 @@
return (error);
}
+/*
+ * This is mostly the same as zfs_space except it also limits the range of
+ * operation to zp->z_size if SPACECTL_F_CANEXTEND is not specified. The log
+ * record of zfs_space during replay would be the same as ordinary zfs_space.
+ */
+static int
+zfs_deallocate(struct vop_deallocate_args *ap)
+{
+ vnode_t *vp;
+ znode_t *zp;
+ zfsvfs_t *zfsvfs;
+ off_t offset, len;
+ uint64_t file_sz;
+ int error;
+
+ vp = ap->a_vp;
+ zp = VTOZ(vp);
+ zfsvfs = ZTOZSB(zp);
+
+ if ((ap->a_offset < 0 || ap->a_len < 0) ||
+ (ap->a_flags & ~SPACECTL_F_SUPPORTED))
+ return (EINVAL);
+
+ ZFS_ENTER(zfsvfs);
+ ZFS_VERIFY_ZP(zp);
+
+ offset = *ap->a_offset;
+ len = *ap->a_len;
+
+ file_sz = zp->z_size;
+ if ((ap->a_flags & SPACECTL_F_CANEXTEND) == 0 &&
+ (uint64_t)offset + len > file_sz)
+ len = file_sz - offset;
+ if (len <= 0) {
+ error = 0;
+ goto out;
+ }
+ error = zfs_space_common(zp, F_FREESP, offset, len, O_RDWR, ap->a_cred);
+out:
+ if (__predict_true(error == 0))
+ *ap->a_len = 0;
+ ZFS_EXIT(zfsvfs);
+ return (error);
+}
+
struct vop_vector zfs_vnodeops;
struct vop_vector zfs_fifoops;
struct vop_vector zfs_shareops;
@@ -5798,6 +5858,7 @@
.vop_fplookup_symlink = zfs_freebsd_fplookup_symlink,
.vop_access = zfs_freebsd_access,
.vop_allocate = VOP_EINVAL,
+ .vop_deallocate = zfs_deallocate,
.vop_lookup = zfs_cache_lookup,
.vop_cachedlookup = zfs_freebsd_cachedlookup,
.vop_getattr = zfs_freebsd_getattr,
Index: sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c
===================================================================
--- sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c
+++ sys/contrib/openzfs/module/os/freebsd/zfs/zfs_znode.c
@@ -1449,14 +1449,8 @@
error = dmu_free_long_range(zfsvfs->z_os, zp->z_id, off, len);
- if (error == 0) {
- /*
- * In FreeBSD we cannot free block in the middle of a file,
- * but only at the end of a file, so this code path should
- * never happen.
- */
- vnode_pager_setsize(ZTOV(zp), off);
- }
+ if (error == 0)
+ vnode_pager_purge_range(ZTOV(zp), off, off + len);
zfs_rangelock_exit(lr);
Index: sys/kern/init_sysent.c
===================================================================
--- sys/kern/init_sysent.c
+++ sys/kern/init_sysent.c
@@ -635,4 +635,5 @@
{ .sy_narg = AS(__specialfd_args), .sy_call = (sy_call_t *)sys___specialfd, .sy_auevent = AUE_SPECIALFD, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 577 = __specialfd */
{ .sy_narg = AS(aio_writev_args), .sy_call = (sy_call_t *)sys_aio_writev, .sy_auevent = AUE_AIO_WRITEV, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 578 = aio_writev */
{ .sy_narg = AS(aio_readv_args), .sy_call = (sy_call_t *)sys_aio_readv, .sy_auevent = AUE_AIO_READV, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 579 = aio_readv */
+ { .sy_narg = AS(fspacectl_args), .sy_call = (sy_call_t *)sys_fspacectl, .sy_auevent = AUE_FSPACECTL, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 580 = fspacectl */
};
Index: sys/kern/sys_generic.c
===================================================================
--- sys/kern/sys_generic.c
+++ sys/kern/sys_generic.c
@@ -855,8 +855,62 @@
goto out;
}
- error = fo_fallocate(fp, offset, len, td);
- out:
+ error = fo_fspacectl(fp, SPACECTL_ALLOC, offset, len,
+ SPACECTL_F_CANEXTEND, td->td_ucred, td);
+out:
+ fdrop(fp, td);
+ 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_ALLOC && cmd != SPACECTL_DEALLOC) ||
+ (range->r_offset < 0 || range->r_len < 0) ||
+ (flags & ~SPACECTL_F_SUPPORTED))
+ 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);
}
Index: sys/kern/syscalls.c
===================================================================
--- sys/kern/syscalls.c
+++ sys/kern/syscalls.c
@@ -586,4 +586,5 @@
"__specialfd", /* 577 = __specialfd */
"aio_writev", /* 578 = aio_writev */
"aio_readv", /* 579 = aio_readv */
+ "fspacectl", /* 580 = fspacectl */
};
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/systrace_args.c
===================================================================
--- sys/kern/systrace_args.c
+++ sys/kern/systrace_args.c
@@ -3399,6 +3399,16 @@
*n_args = 1;
break;
}
+ /* fspacectl */
+ case 580: {
+ struct fspacectl_args *p = params;
+ iarg[0] = p->fd; /* int */
+ iarg[1] = p->cmd; /* int */
+ uarg[2] = (intptr_t) p->range; /* struct spacectl_range * */
+ iarg[3] = p->flags; /* int */
+ *n_args = 4;
+ break;
+ }
default:
*n_args = 0;
break;
@@ -9088,6 +9098,25 @@
break;
};
break;
+ /* fspacectl */
+ case 580:
+ switch(ndx) {
+ case 0:
+ p = "int";
+ break;
+ case 1:
+ p = "int";
+ break;
+ case 2:
+ p = "userland struct spacectl_range *";
+ break;
+ case 3:
+ p = "int";
+ break;
+ default:
+ break;
+ };
+ break;
default:
break;
};
@@ -11034,6 +11063,11 @@
if (ndx == 0 || ndx == 1)
p = "int";
break;
+ /* fspacectl */
+ case 580:
+ if (ndx == 0 || ndx == 1)
+ p = "int";
+ break;
default:
break;
};
Index: sys/kern/uipc_shm.c
===================================================================
--- sys/kern/uipc_shm.c
+++ sys/kern/uipc_shm.c
@@ -145,7 +145,7 @@
static fo_mmap_t shm_mmap;
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 = {
@@ -165,7 +165,7 @@
.fo_mmap = shm_mmap,
.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,
};
@@ -1875,7 +1875,56 @@
}
static int
-shm_fallocate(struct file *fp, off_t offset, off_t len, struct thread *td)
+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;
+ int error;
+
+ start = OFF_TO_IDX(offset);
+ start2 = OFF_TO_IDX(offset + PAGE_MASK);
+ end = OFF_TO_IDX(offset + len);
+ size = offset + len;
+ error = 0;
+
+ 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);
+ }
+ }
+ if (size > shmfd->shm_size && flags & SPACECTL_F_CANEXTEND)
+ error = shm_largepage(shmfd) ? shm_dotruncate_largepage(shmfd,
+ size, rl_cookie) : shm_dotruncate_locked(shmfd, size,
+ rl_cookie);
+
+ VM_OBJECT_WUNLOCK(shmfd->shm_object);
+ return (error);
+}
+
+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;
@@ -1887,6 +1936,14 @@
shmfd = fp->f_data;
size = offset + len;
+ if (cmd != SPACECTL_ALLOC && cmd != SPACECTL_DEALLOC)
+ return (EINVAL);
+ if (offset < 0 || len < 0 || (flags & ~SPACECTL_F_SUPPORTED) != 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
@@ -1898,8 +1955,17 @@
*/
rl_cookie = rangelock_wlock(&shmfd->shm_rl, offset, size,
&shmfd->shm_mtx);
- if (size > shmfd->shm_size)
- error = shm_dotruncate_cookie(shmfd, size, rl_cookie);
+ switch (cmd) {
+ case SPACECTL_ALLOC:
+ if (size > shmfd->shm_size && flags & SPACECTL_F_CANEXTEND)
+ error = shm_dotruncate_cookie(shmfd, size, rl_cookie);
+ break;
+ 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);
/* Translate to posix_fallocate(2) return value as needed. */
if (error == ENOMEM)
Index: sys/kern/vfs_default.c
===================================================================
--- sys/kern/vfs_default.c
+++ sys/kern/vfs_default.c
@@ -92,6 +92,7 @@
static int vop_stdgetpages_async(struct vop_getpages_async_args *ap);
static int vop_stdread_pgcache(struct vop_read_pgcache_args *ap);
static int vop_stdstat(struct vop_stat_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
@@ -116,6 +117,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,
@@ -501,6 +503,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;
@@ -1075,6 +1078,147 @@
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;
+ void *buf = NULL;
+ 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;
+ buf = malloc(iosize, M_TEMP, M_ZERO | M_WAITOK);
+
+ while (len > 0) {
+ int xfersize = iosize;
+ if (offset % iosize != 0)
+ xfersize -= offset % iosize;
+ if (xfersize > len)
+ xfersize = len;
+
+ aiov.iov_base = buf;
+ 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();
+ }
+
+ free(buf, M_TEMP);
+ *residp = len;
+ return (error);
+}
+
+static int
+vop_stddeallocate(struct vop_deallocate_args *ap)
+{
+ struct vnode *vp;
+ off_t offset, resid;
+ struct ucred *cred;
+ int error;
+ struct vattr va;
+ off_t noff, xfersize, rem;
+ bool extended;
+
+ vp = ap->a_vp;
+ offset = *ap->a_offset;
+ resid = *ap->a_len;
+ cred = ap->a_cred;
+ extended = false;
+
+ if ((offset < 0 || resid < 0) ||
+ (ap->a_flags & ~SPACECTL_F_SUPPORTED))
+ return (EINVAL);
+
+ error = VOP_GETATTR(vp, &va, cred);
+ if (error)
+ return (error);
+
+ resid = omin(OFF_MAX - offset, *ap->a_len);
+ /* Extend the file if requested */
+ if (resid != 0 && (ap->a_flags & SPACECTL_F_CANEXTEND) &&
+ (uint64_t)offset + resid > va.va_size) {
+ error = vn_truncate_locked(vp, offset + resid, false, cred);
+ if (error != 0)
+ goto out;
+
+ xfersize = offset + resid - va.va_size;
+ resid -= xfersize;
+ extended = true;
+ }
+ while (resid > 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
+ */
+ resid = 0;
+ error = 0;
+ break;
+ }
+ KASSERT(noff >= offset, ("FIOSEEKDATA going backward"));
+ if (noff != offset) {
+ xfersize = omin(noff - offset, resid);
+ resid -= xfersize;
+ offset += xfersize;
+ if (resid == 0)
+ break;
+ }
+ error = vn_bmap_seekhole_locked(vp, FIOSEEKHOLE, &noff, cred);
+ if (error)
+ goto out;
+
+ /* Fill zeroes */
+ xfersize = omin(noff - offset, resid);
+ error = vp_zerofill(vp, &va, offset, xfersize, &rem, cred);
+ if (error) {
+ resid -= xfersize - rem;
+ offset += xfersize - rem;
+ goto out;
+ }
+
+ resid -= xfersize;
+ offset += xfersize;
+ if (should_yield())
+ break;
+ }
+out:
+ if (error == 0) {
+ *ap->a_offset = offset;
+ *ap->a_len = resid;
+ } else if (extended)
+ vn_truncate_locked(vp, va.va_size, false, cred);
+ 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
@@ -105,7 +105,7 @@
static fo_stat_t vn_statfile;
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,
@@ -122,7 +122,7 @@
.fo_seek = vn_seek,
.fo_fill_kinfo = vn_fill_kinfo,
.fo_mmap = vn_mmap,
- .fo_fallocate = vn_fallocate,
+ .fo_fspacectl = vn_fspacectl,
.fo_flags = DFLAG_PASSABLE | DFLAG_SEEKABLE
};
@@ -2345,7 +2345,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;
@@ -2353,22 +2354,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 -
@@ -2376,14 +2372,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)
@@ -2391,13 +2387,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)
{
@@ -3283,7 +3293,8 @@
}
static int
-vn_fallocate(struct file *fp, off_t offset, off_t len, struct thread *td)
+vn_fallocate(struct file *fp, off_t offset, off_t len, int flags,
+ struct ucred *active_cred, struct thread *td)
{
struct mount *mp;
struct vnode *vp;
@@ -3294,8 +3305,15 @@
#endif
vp = fp->f_vnode;
+ error = 0;
+
+ if (offset < 0 || len < 0 || (flags & ~SPACECTL_F_SUPPORTED) != 0)
+ return (EINVAL);
if (vp->v_type != VREG)
return (ENODEV);
+ if (len == 0)
+ /* Degenerated case */
+ return (0);
/* Allocating blocks may take a long time, so iterate. */
for (;;) {
@@ -3319,7 +3337,7 @@
}
#endif
#ifdef MAC
- error = mac_vnode_check_write(td->td_ucred, fp->f_cred, vp);
+ error = mac_vnode_check_write(active_cred, fp->f_cred, vp);
if (error == 0)
#endif
error = VOP_ALLOCATE(vp, &offset, &len);
@@ -3339,6 +3357,131 @@
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 & ~SPACECTL_F_SUPPORTED) != 0)
+ return (EINVAL);
+ if (vp->v_type != VREG)
+ return (ENODEV);
+ if (len == 0)
+ /* Degenerated case */
+ return (0);
+
+ /* Take the maximum range if end offset overflows */
+ if (len > OFF_MAX - offset)
+ 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 ((flags & SPACECTL_F_CANEXTEND) == 0 &&
+ (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_ALLOC && cmd != SPACECTL_DEALLOC)
+ return (EINVAL);
+
+ switch (cmd) {
+ case SPACECTL_ALLOC:
+ error = vn_fallocate(fp, offset, len, flags, active_cred, td);
+ break;
+ 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
@@ -792,6 +792,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
@@ -1076,6 +1076,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
@@ -314,6 +314,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
@@ -343,6 +351,18 @@
* similar syscalls.
*/
#define FD_NONE -200
+
+/*
+ * Commands for fspacectl(2)
+ */
+#define SPACECTL_DEALLOC 0 /* deallocate space */
+#define SPACECTL_ALLOC 1 /* allocate space */
+
+/*
+ * fspacectl(2) flags
+ */
+#define SPACECTL_F_CANEXTEND (1)
+#define SPACECTL_F_SUPPORTED (SPACECTL_F_CANEXTEND)
#endif
#ifndef _KERNEL
@@ -360,6 +380,9 @@
int posix_fadvise(int, off_t, off_t, int);
int posix_fallocate(int, off_t, off_t);
#endif
+#if __BSD_VISIBLE
+int fspacectl(int, int, struct spacectl_range *, int);
+#endif
__END_DECLS
#endif
Index: sys/sys/file.h
===================================================================
--- sys/sys/file.h
+++ sys/sys/file.h
@@ -35,6 +35,7 @@
#ifndef _SYS_FILE_H_
#define _SYS_FILE_H_
+#include "sys/fcntl.h"
#ifndef _KERNEL
#include <sys/types.h> /* XXX */
#include <sys/fcntl.h>
@@ -127,8 +128,9 @@
typedef int fo_aio_queue_t(struct file *fp, struct kaiocb *job);
typedef int fo_add_seals_t(struct file *fp, int flags);
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 {
@@ -149,7 +151,7 @@
fo_aio_queue_t *fo_aio_queue;
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 */
};
@@ -461,15 +463,17 @@
return ((*fp->f_ops->fo_get_seals)(fp, seals));
}
-static __inline int
-fo_fallocate(struct file *fp, off_t offset, off_t len, struct thread *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_fallocate == NULL)
+ if (fp->f_ops->fo_fspacectl == NULL)
return (ENODEV);
- return ((*fp->f_ops->fo_fallocate)(fp, offset, len, td));
+ return ((*fp->f_ops->fo_fspacectl)(fp, cmd, offset, len, flags,
+ active_cred, td));
}
+
#endif /* _KERNEL */
#endif /* !SYS_FILE_H */
Index: sys/sys/syscall.h
===================================================================
--- sys/sys/syscall.h
+++ sys/sys/syscall.h
@@ -515,4 +515,5 @@
#define SYS___specialfd 577
#define SYS_aio_writev 578
#define SYS_aio_readv 579
-#define SYS_MAXSYSCALL 580
+#define SYS_fspacectl 580
+#define SYS_MAXSYSCALL 581
Index: sys/sys/syscall.mk
===================================================================
--- sys/sys/syscall.mk
+++ sys/sys/syscall.mk
@@ -420,4 +420,5 @@
rpctls_syscall.o \
__specialfd.o \
aio_writev.o \
- aio_readv.o
+ aio_readv.o \
+ fspacectl.o
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,10 @@
int advice);
int kern_posix_fallocate(struct thread *td, int fd, off_t offset,
off_t len);
+int kern_fdeallocate(struct thread *td, int fd, off_t offset, off_t len);
+int kern_fzerorange(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/sysproto.h
===================================================================
--- sys/sys/sysproto.h
+++ sys/sys/sysproto.h
@@ -1847,6 +1847,12 @@
struct aio_readv_args {
char aiocbp_l_[PADL_(struct aiocb *)]; struct aiocb * aiocbp; char aiocbp_r_[PADR_(struct aiocb *)];
};
+struct fspacectl_args {
+ char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)];
+ char cmd_l_[PADL_(int)]; int cmd; char cmd_r_[PADR_(int)];
+ char range_l_[PADL_(struct spacectl_range *)]; struct spacectl_range * range; char range_r_[PADR_(struct spacectl_range *)];
+ char flags_l_[PADL_(int)]; int flags; char flags_r_[PADR_(int)];
+};
int nosys(struct thread *, struct nosys_args *);
void sys_sys_exit(struct thread *, struct sys_exit_args *);
int sys_fork(struct thread *, struct fork_args *);
@@ -2241,6 +2247,7 @@
int sys___specialfd(struct thread *, struct __specialfd_args *);
int sys_aio_writev(struct thread *, struct aio_writev_args *);
int sys_aio_readv(struct thread *, struct aio_readv_args *);
+int sys_fspacectl(struct thread *, struct fspacectl_args *);
#ifdef COMPAT_43
@@ -3175,6 +3182,7 @@
#define SYS_AUE___specialfd AUE_SPECIALFD
#define SYS_AUE_aio_writev AUE_AIO_WRITEV
#define SYS_AUE_aio_readv AUE_AIO_READV
+#define SYS_AUE_fspacectl AUE_FSPACECTL
#undef PAD_
#undef PADL_
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
@@ -719,6 +719,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,
@@ -727,6 +730,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,303 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2021 The FreeBSD Foundation
+ * All rights reserved.
+ *
+ * This software was developed by Andrew Turner 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 <fcntl.h>
+#include <malloc.h>
+#include <sys/stat.h>
+
+#include <atf-c.h>
+
+static off_t file_max_blocks = 32;
+static size_t block_size = 131072;
+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;
+
+ error = 0;
+ buf = malloc(block_size);
+ if (buf == NULL)
+ return (1);
+
+ while (len > 0) {
+ blen = len < (off_t)block_size ? len : block_size;
+ 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 int
+check_content(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;
+
+ error = 0;
+ buf = malloc(block_size * 2);
+ if (buf == NULL)
+ return (1);
+ sblk = buf + block_size;
+
+ memset(sblk, 0, block_size);
+
+ /*
+ * Check hole is zeroed.
+ */
+ offset = hole_start;
+ resid = hole_len;
+ if ((uint64_t)offset + resid > (uint64_t)file_sz)
+ resid = file_sz - offset;
+ while (resid > 0) {
+ blen = resid < (off_t)block_size ? resid : block_size;
+ 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, block_size);
+
+ /*
+ * Check file region before hole is zeroed.
+ */
+ offset = 0;
+ resid = hole_start;
+ while (resid > 0) {
+ blen = resid < (off_t)block_size ? resid : block_size;
+ 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)block_size ? resid : block_size;
+ 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;
+ int fd;
+
+ range.r_offset = block_size;
+ range.r_len = (file_max_blocks - 1) * block_size -
+ 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ file_max_blocks * block_size) == 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;
+ int fd;
+
+ range.r_offset = block_size / 2;
+ range.r_len = (file_max_blocks - 1) * block_size + block_size / 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ file_max_blocks * block_size) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation and extending
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_canextend);
+ATF_TC_BODY(aligned_dealloc_canextend, tc)
+{
+ struct spacectl_range range;
+ int fd;
+
+ range.r_offset = block_size;
+ range.r_len = (file_max_blocks + 1) * block_size -
+ 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range,
+ SPACECTL_F_CANEXTEND) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ (file_max_blocks + 1) * block_size) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation and extending
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_canextend);
+ATF_TC_BODY(unaligned_dealloc_canextend, tc)
+{
+ struct spacectl_range range;
+ int fd;
+
+ range.r_offset = block_size / 2;
+ range.r_len = file_max_blocks * block_size + block_size / 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range,
+ SPACECTL_F_CANEXTEND) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ file_max_blocks * block_size + block_size / 2) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check aligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(aligned_dealloc_no_canextend);
+ATF_TC_BODY(aligned_dealloc_no_canextend, tc)
+{
+ struct spacectl_range range;
+ int fd;
+
+ range.r_offset = block_size;
+ range.r_len = (file_max_blocks + 1) * block_size -
+ 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ file_max_blocks * block_size) == 0);
+ ATF_REQUIRE(close(fd) == 0);
+}
+
+/*
+ * Check unaligned deallocation around EOF
+ */
+ATF_TC_WITHOUT_HEAD(unaligned_dealloc_no_canextend);
+ATF_TC_BODY(unaligned_dealloc_no_canextend, tc)
+{
+ struct spacectl_range range;
+ int fd;
+
+ range.r_offset = block_size / 2;
+ range.r_len = file_max_blocks * block_size + block_size / 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 * block_size) == 0);
+ ATF_CHECK(fspacectl(fd, SPACECTL_DEALLOC, &range, 0) == 0);
+ ATF_CHECK(check_content(fd, range.r_offset, range.r_len,
+ file_max_blocks * block_size) == 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_canextend);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_canextend);
+ ATF_TP_ADD_TC(tp, aligned_dealloc_no_canextend);
+ ATF_TP_ADD_TC(tp, unaligned_dealloc_no_canextend);
+ return atf_no_error();
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Mon, Mar 10, 7:45 AM (7 h, 1 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17081481
Default Alt Text
D28347.id84064.diff (57 KB)
Attached To
Mode
D28347: Add fspacectl(2), vn_deallocate(9) and VOP_DEALLOCATE(9).
Attached
Detach File
Event Timeline
Log In to Comment