diff options
144 files changed, 4569 insertions, 1674 deletions
diff --git a/.clean_build_epoch b/.clean_build_epoch new file mode 100644 index 000000000000..74cb650f0015 --- /dev/null +++ b/.clean_build_epoch @@ -0,0 +1,7 @@ +# Add a new line containing a larger number than the last (e.g. YYYYMMDD) for +# every change that breaks ABI or requires a full rebuild for some other reason. +# Only the last entry is required, but having the full list may be useful for +# looking at frequency of ABI breakage, etc. + +# 4757b351ea9: openssl: Import version 3.5.1 +20250807 # All OpenSSL-using bits need rebuilt diff --git a/Makefile.inc1 b/Makefile.inc1 index 9128d1d8ee77..cf32248b6b9d 100644 --- a/Makefile.inc1 +++ b/Makefile.inc1 @@ -1021,8 +1021,7 @@ IMAKE_MTREE= MTREE_CMD="${MTREE_CMD} ${MTREEFLAGS}" .endif .if make(distributeworld) -CERTCTLDESTDIR= ${DESTDIR}/${DISTDIR} -CERTCTLFLAGS+= -d /base +CERTCTLDESTDIR= ${DESTDIR}/${DISTDIR}/base .else CERTCTLDESTDIR= ${DESTDIR} .endif @@ -1083,7 +1082,7 @@ _cleanobj_fast_depend_hack: .PHONY @echo ">>> Deleting stale dependencies..."; MACHINE=${MACHINE} MACHINE_ARCH=${MACHINE_ARCH} \ ALL_libcompats=${_ALL_libcompats:Q} \ - sh ${.CURDIR}/tools/build/depend-cleanup.sh ${OBJTOP} + sh ${.CURDIR}/tools/build/depend-cleanup.sh ${OBJTOP} ${SRCTOP} _cleanworldtmp: .PHONY .if ${MK_CLEAN} == "yes" @@ -1176,7 +1175,8 @@ _cleanobj: ${_+_}cd ${.CURDIR}; ${LIB${LIBCOMPAT}WMAKE} _NO_INCLUDE_COMPILERMK=t -f Makefile.inc1 ${CLEANDIR} .endfor .else - ${_+_}cd ${.CURDIR}; ${WMAKE} _NO_INCLUDE_COMPILERMK=t _cleanobj_fast_depend_hack + ${_+_}cd ${.CURDIR}; env CLEANMK="_NO_INCLUDE_COMPILERMK=t ${CLEANDIR}" \ + MAKE=${MAKE} ${WMAKE} _cleanobj_fast_depend_hack .endif # ${MK_CLEAN} == "yes" _obj: @echo @@ -1541,14 +1541,10 @@ distributeworld installworld stageworld: _installcheck_world .PHONY .endif # make(distributeworld) ${_+_}cd ${.CURDIR}; ${IMAKE} re${.TARGET:S/world$//}; \ ${IMAKEENV} rm -rf ${INSTALLTMP} -.if !make(packageworld) && ${MK_CAROOT} != "no" - @if which openssl>/dev/null; then \ - PATH=${TMPPATH:Q}:${PATH:Q} \ - LOCALBASE=${LOCALBASE:Q} \ - sh ${SRCTOP}/usr.sbin/certctl/certctl.sh ${CERTCTLFLAGS} rehash; \ - else \ - echo "No openssl on the host, not rehashing certificates target -- /etc/ssl may not be populated."; \ - fi +.if !make(packageworld) && ${MK_CAROOT} != "no" && ${MK_OPENSSL} != "no" + PATH=${TMPPATH:Q}:${PATH:Q} \ + LOCALBASE=${LOCALBASE:Q} \ + certctl ${CERTCTLFLAGS} rehash .endif .if make(distributeworld) .for dist in ${EXTRA_DISTRIBUTIONS} @@ -2712,6 +2708,11 @@ _basic_bootstrap_tools+=sbin/md5 _basic_bootstrap_tools+=usr.sbin/tzsetup .endif +# certctl is needed as an install tool +.if ${MK_CAROOT} != "no" && ${MK_OPENSSL} != "no" +_certctl=usr.sbin/certctl +.endif + .if defined(BOOTSTRAP_ALL_TOOLS) _other_bootstrap_tools+=${_basic_bootstrap_tools} .for _subdir _links in ${_basic_bootstrap_tools_multilink} @@ -2775,6 +2776,7 @@ bootstrap-tools: ${_bt}-links .PHONY ${_strfile} \ usr.bin/dtc \ ${_cat} \ + ${_certctl} \ ${_kbdcontrol} \ ${_elftoolchain_libs} \ ${_libkldelf} \ @@ -10,6 +10,12 @@ newline. Entries should be separated by a newline. Changes to this file should not be MFCed. +cce64f2e6851: + Add support for the NFSv4.2 Clone operation, which uses + block cloning to "copy on write" files on an NFS server. + This only works for exported ZFS file systems that have + block cloning enabled, at this time. + 7ac276298b72, 7b8c9de17448, 1271b1d747a7, 9dcb984251b3: Support for Secure RPC DES authentication has been removed. This includes publickey(5), keyserv(8) and the rpc_secure(3) routines which @@ -27,6 +27,18 @@ NOTE TO PEOPLE WHO THINK THAT FreeBSD 15.x IS SLOW: world, or to merely disable the most expensive debugging functionality at runtime, run "ln -s 'abort:false,junk:false' /etc/malloc.conf".) +20250813: + Commit cce64f2e6851 changed the internal KAPI between the NFS + modules. As such, all of them need to be rebuilt from sources. + __FreeBSD_version has been bumped to 1500060 already today, + so I will not bump it again for this. + +20250813: + The gpiobus_attach_bus() function has been removed. Drivers should now + use the gpiobus_add_bus() function instead. The difference being that + gpiobus_add_bus() doesn't call bus_attach_children(), calling it will + be the responsibility of the caller now. + 20250810: Support for Secure RPC DES authentication has been removed. The keyserv(8) daemon was already removed, but support for it in libc diff --git a/cddl/usr.sbin/zhack/Makefile b/cddl/usr.sbin/zhack/Makefile index c4fc25dcfb8f..69d561b3407b 100644 --- a/cddl/usr.sbin/zhack/Makefile +++ b/cddl/usr.sbin/zhack/Makefile @@ -1,8 +1,11 @@ -.PATH: ${SRCTOP}/sys/contrib/openzfs/cmd +ZFSTOP= ${SRCTOP}/sys/contrib/openzfs + +.PATH: ${ZFSTOP}/cmd +.PATH: ${ZFSTOP}/man/man1 PACKAGE= zfs PROG= zhack -MAN= +MAN= zhack.1 WARNS?= 0 CSTD= c99 diff --git a/crypto/openssh/uidswap.c b/crypto/openssh/uidswap.c index 0143f4994611..6ed3024d0180 100644 --- a/crypto/openssh/uidswap.c +++ b/crypto/openssh/uidswap.c @@ -14,9 +14,6 @@ #include "includes.h" -#ifdef __FreeBSD__ -#include <assert.h> -#endif #include <errno.h> #include <pwd.h> #include <string.h> @@ -124,20 +121,8 @@ temporarily_use_uid(struct passwd *pw) fatal("setgroups: %.100s", strerror(errno)); #ifndef SAVED_IDS_WORK_WITH_SETEUID /* Propagate the privileged gid to all of our gids. */ -#ifdef __FreeBSD__ - /* - * FreeBSD traditionally includes the egid as the first element. If we - * use getegid() here then we effectively propagate user_groups[0], - * which is probably pw->pw_gid. Fix it to work as intended by using - * the egid we already have stashed off. - */ - assert(saved_egroupslen > 0); - if (setgid(saved_egroups[0]) == -1) - debug("setgid %u: %.100s", (u_int) saved_egroups[0], strerror(errno)); -#else if (setgid(getegid()) == -1) debug("setgid %u: %.100s", (u_int) getegid(), strerror(errno)); -#endif /* Propagate the privileged uid to all of our uids. */ if (setuid(geteuid()) == -1) debug("setuid %u: %.100s", (u_int) geteuid(), strerror(errno)); diff --git a/etc/mtree/BSD.tests.dist b/etc/mtree/BSD.tests.dist index 2c25d9386032..e6a013f010de 100644 --- a/etc/mtree/BSD.tests.dist +++ b/etc/mtree/BSD.tests.dist @@ -1255,6 +1255,8 @@ .. .. usr.sbin + certctl + .. chown .. ctladm diff --git a/lib/libc/include/compat.h b/lib/libc/include/compat.h index 70fb8dcd97f3..97f22607ddd7 100644 --- a/lib/libc/include/compat.h +++ b/lib/libc/include/compat.h @@ -69,6 +69,9 @@ __sym_compat(kevent, freebsd11_kevent, FBSD_1.0); __sym_compat(swapoff, freebsd13_swapoff, FBSD_1.0); +__sym_compat(getgroups, freebsd14_getgroups, FBSD_1.0); +__sym_compat(setgroups, freebsd14_setgroups, FBSD_1.0); + #undef __sym_compat #define __weak_reference(sym,alias) \ diff --git a/lib/libc/stdlib/qsort.c b/lib/libc/stdlib/qsort.c index e0b06494cf98..400124593d07 100644 --- a/lib/libc/stdlib/qsort.c +++ b/lib/libc/stdlib/qsort.c @@ -106,13 +106,11 @@ local_qsort(void *a, size_t n, size_t es, cmp_t *cmp, void *thunk) char *pa, *pb, *pc, *pd, *pl, *pm, *pn; size_t d1, d2; int cmp_result; - int swap_cnt; /* if there are less than 2 elements, then sorting is not needed */ if (__predict_false(n < 2)) return; loop: - swap_cnt = 0; if (n < 7) { for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) for (pl = pm; @@ -141,7 +139,6 @@ loop: for (;;) { while (pb <= pc && (cmp_result = CMP(thunk, pb, a)) <= 0) { if (cmp_result == 0) { - swap_cnt = 1; swapfunc(pa, pb, es); pa += es; } @@ -149,7 +146,6 @@ loop: } while (pb <= pc && (cmp_result = CMP(thunk, pc, a)) >= 0) { if (cmp_result == 0) { - swap_cnt = 1; swapfunc(pc, pd, es); pd -= es; } @@ -158,18 +154,9 @@ loop: if (pb > pc) break; swapfunc(pb, pc, es); - swap_cnt = 1; pb += es; pc -= es; } - if (swap_cnt == 0) { /* Switch to insertion sort */ - for (pm = (char *)a + es; pm < (char *)a + n * es; pm += es) - for (pl = pm; - pl > (char *)a && CMP(thunk, pl - es, pl) > 0; - pl -= es) - swapfunc(pl, pl - es, es); - return; - } pn = (char *)a + n * es; d1 = MIN(pa - (char *)a, pb - pa); diff --git a/lib/libc/tests/stdlib/Makefile b/lib/libc/tests/stdlib/Makefile index 50726a5d8af6..9d84becfbd1f 100644 --- a/lib/libc/tests/stdlib/Makefile +++ b/lib/libc/tests/stdlib/Makefile @@ -14,6 +14,7 @@ ATF_TESTS_C+= qsort_b_test ATF_TESTS_C+= qsort_r_compat_test ATF_TESTS_C+= qsort_r_test ATF_TESTS_C+= qsort_s_test +ATF_TESTS_C+= qsort_bench ATF_TESTS_C+= set_constraint_handler_s_test ATF_TESTS_C+= strfmon_test ATF_TESTS_C+= tsearch_test diff --git a/lib/libc/tests/stdlib/qsort_bench.c b/lib/libc/tests/stdlib/qsort_bench.c new file mode 100644 index 000000000000..5f2cfae40140 --- /dev/null +++ b/lib/libc/tests/stdlib/qsort_bench.c @@ -0,0 +1,113 @@ +/*- + * Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <atf-c.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +/*- + * Measures qsort(3) runtime with pathological input and verify that it + * stays close to N * log2(N). + * + * Thanks to Vivian Hussey for the proof of concept. + * + * The input we construct is similar to a sweep from 0 to N where each + * half, except for the first element, has been reversed; for instance, + * with N = 8, we get { 0, 3, 2, 1, 4, 8, 7, 6 }. This triggers a bug in + * the BSD qsort(3) where it will switch to insertion sort if the pivots + * are sorted. + * + * This article goes into more detail about the bug and its origin: + * + * https://www.raygard.net/2022/02/26/Re-engineering-a-qsort-part-3 + * + * With this optimization (the `if (swap_cnt == 0)` block), qsort(3) needs + * roughly N * N / 4 comparisons to sort our pathological input. Without + * it, it needs only a little more than N * log2(N) comparisons. + */ + +/* we stop testing once a single takes longer than this */ +#define MAXRUNSECS 10 + +static bool debugging; + +static uintmax_t ncmp; + +static int +intcmp(const void *a, const void *b) +{ + ncmp++; + return ((*(int *)a > *(int *)b) - (*(int *)a < *(int *)b)); +} + +static void +qsort_bench(int log2n) +{ + uintmax_t n = 1LLU << log2n; + int *buf; + + /* fill an array with a pathological pattern */ + ATF_REQUIRE(buf = malloc(n * sizeof(*buf))); + buf[0] = 0; + buf[n / 2] = n / 2; + for (unsigned int i = 1; i < n / 2; i++) { + buf[i] = n / 2 - i; + buf[n / 2 + i] = n - i; + } + + ncmp = 0; + qsort(buf, n, sizeof(*buf), intcmp); + + /* check result and free array */ + if (debugging) { + for (unsigned int i = 1; i < n; i++) { + ATF_REQUIRE_MSG(buf[i] > buf[i - 1], + "array is not sorted"); + } + } + free(buf); + + /* check that runtime does not exceed N² */ + ATF_CHECK_MSG(ncmp / n < n, + "runtime %ju exceeds N² for N = %ju", ncmp, n); + + /* check that runtime does not exceed N log N by much */ + ATF_CHECK_MSG(ncmp / n <= log2n + 1, + "runtime %ju exceeds N log N for N = %ju", ncmp, n); +} + +ATF_TC_WITHOUT_HEAD(qsort_bench); +ATF_TC_BODY(qsort_bench, tc) +{ + struct timespec t0, t1; + uintmax_t tus; + + for (int i = 10; i <= 30; i++) { + clock_gettime(CLOCK_UPTIME, &t0); + qsort_bench(i); + clock_gettime(CLOCK_UPTIME, &t1); + tus = t1.tv_sec * 1000000 + t1.tv_nsec / 1000; + tus -= t0.tv_sec * 1000000 + t0.tv_nsec / 1000; + if (debugging) { + fprintf(stderr, "N = 2^%d in %ju.%06jus\n", + i, tus / 1000000, tus % 1000000); + } + /* stop once an individual run exceeds our limit */ + if (tus / 1000000 >= MAXRUNSECS) + break; + } +} + +ATF_TP_ADD_TCS(tp) +{ + debugging = !getenv("__RUNNING_INSIDE_ATF_RUN") && + isatty(STDERR_FILENO); + ATF_TP_ADD_TC(tp, qsort_bench); + return (atf_no_error()); +} diff --git a/lib/libsys/Symbol.sys.map b/lib/libsys/Symbol.sys.map index 45e0160100af..1a297f9df581 100644 --- a/lib/libsys/Symbol.sys.map +++ b/lib/libsys/Symbol.sys.map @@ -89,7 +89,6 @@ FBSD_1.0 { geteuid; getfh; getgid; - getgroups; getitimer; getpagesize; getpeername; @@ -204,7 +203,6 @@ FBSD_1.0 { setegid; seteuid; setgid; - setgroups; setitimer; setlogin; setpgid; @@ -380,11 +378,13 @@ FBSD_1.7 { FBSD_1.8 { exterrctl; fchroot; + getgroups; getrlimitusage; inotify_add_watch_at; inotify_rm_watch; kcmp; setcred; + setgroups; }; FBSDprivate_1.0 { diff --git a/lib/libsys/_libsys.h b/lib/libsys/_libsys.h index 2f89e8fea92b..34eebc1aa67a 100644 --- a/lib/libsys/_libsys.h +++ b/lib/libsys/_libsys.h @@ -121,8 +121,6 @@ typedef int (__sys_munmap_t)(void *, size_t); typedef int (__sys_mprotect_t)(void *, size_t, int); typedef int (__sys_madvise_t)(void *, size_t, int); typedef int (__sys_mincore_t)(const void *, size_t, char *); -typedef int (__sys_getgroups_t)(int, gid_t *); -typedef int (__sys_setgroups_t)(int, const gid_t *); typedef int (__sys_getpgrp_t)(void); typedef int (__sys_setpgid_t)(int, int); typedef int (__sys_setitimer_t)(int, const struct itimerval *, struct itimerval *); @@ -468,6 +466,8 @@ typedef int (__sys_setcred_t)(u_int, const struct setcred *, size_t); typedef int (__sys_exterrctl_t)(u_int, u_int, void *); typedef int (__sys_inotify_add_watch_at_t)(int, int, const char *, uint32_t); typedef int (__sys_inotify_rm_watch_t)(int, int); +typedef int (__sys_getgroups_t)(int, gid_t *); +typedef int (__sys_setgroups_t)(int, const gid_t *); _Noreturn void __sys__exit(int rval); int __sys_fork(void); @@ -525,8 +525,6 @@ int __sys_munmap(void * addr, size_t len); int __sys_mprotect(void * addr, size_t len, int prot); int __sys_madvise(void * addr, size_t len, int behav); int __sys_mincore(const void * addr, size_t len, char * vec); -int __sys_getgroups(int gidsetsize, gid_t * gidset); -int __sys_setgroups(int gidsetsize, const gid_t * gidset); int __sys_getpgrp(void); int __sys_setpgid(int pid, int pgid); int __sys_setitimer(int which, const struct itimerval * itv, struct itimerval * oitv); @@ -872,6 +870,8 @@ int __sys_setcred(u_int flags, const struct setcred * wcred, size_t size); int __sys_exterrctl(u_int op, u_int flags, void * ptr); int __sys_inotify_add_watch_at(int fd, int dfd, const char * path, uint32_t mask); int __sys_inotify_rm_watch(int fd, int wd); +int __sys_getgroups(int gidsetsize, gid_t * gidset); +int __sys_setgroups(int gidsetsize, const gid_t * gidset); __END_DECLS #endif /* __LIBSYS_H_ */ diff --git a/lib/libsys/getgroups.2 b/lib/libsys/getgroups.2 index 91cca2748ec2..37c8fbad7215 100644 --- a/lib/libsys/getgroups.2 +++ b/lib/libsys/getgroups.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 21, 2011 +.Dd August 1, 2025 .Dt GETGROUPS 2 .Os .Sh NAME @@ -41,8 +41,8 @@ The .Fn getgroups system call -gets the current group access list of the user process -and stores it in the array +gets the current supplementary groups of the user process and stores it in the +array .Fa gidset . The .Fa gidsetlen @@ -54,7 +54,7 @@ The system call returns the actual number of groups returned in .Fa gidset . -At least one and as many as {NGROUPS_MAX}+1 values may be returned. +As many as {NGROUPS_MAX} values may be returned. If .Fa gidsetlen is zero, @@ -102,3 +102,10 @@ The .Fn getgroups system call appeared in .Bx 4.2 . +.Pp +Before +.Fx 15.0 , +the +.Fn getgroups +system call always returned the effective group ID for the process as the first +element of the array, before the supplementary groups. diff --git a/lib/libsys/pathconf.2 b/lib/libsys/pathconf.2 index 79ac8310000d..5a983a3a13e2 100644 --- a/lib/libsys/pathconf.2 +++ b/lib/libsys/pathconf.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd July 5, 2025 +.Dd August 6, 2025 .Dt PATHCONF 2 .Os .Sh NAME @@ -187,6 +187,11 @@ and flags can be set by .Xr chflags 2 , otherwise 0. +.It Li _PC_CLONE_BLKSIZE +Returns the block size required for block cloning via +.Xr copy_file_range 2 +for a file system if block cloning is supported, +otherwise 0. .El .Sh RETURN VALUES If the call to @@ -264,6 +269,7 @@ Corrupted data was detected while reading from the file system. .El .Sh SEE ALSO .Xr chflags 2 , +.Xr copy_file_range 2 , .Xr lseek 2 , .Xr sysctl 3 .Sh HISTORY diff --git a/lib/libsys/setgroups.2 b/lib/libsys/setgroups.2 index a226aeafea96..451f63ba1266 100644 --- a/lib/libsys/setgroups.2 +++ b/lib/libsys/setgroups.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 19, 2018 +.Dd August 1, 2025 .Dt SETGROUPS 2 .Os .Sh NAME @@ -42,7 +42,7 @@ The .Fn setgroups system call -sets the group access list of the current user process +sets the supplementary group list of the current user process according to the array .Fa gidset . The @@ -50,26 +50,12 @@ The argument indicates the number of entries in the array and must be no more than -.Dv {NGROUPS_MAX}+1 . -.Pp -Only the super-user may set a new group list. +.Dv {NGROUPS_MAX} . +The +.Fa ngroups +argument may be set to 0 to clear the supplementary group list. .Pp -The first entry of the group array -.Pq Va gidset[0] -is used as the effective group-ID for the process. -This entry is over-written when a setgid program is run. -To avoid losing access to the privileges of the -.Va gidset[0] -entry, it should be duplicated later in the group array. -By convention, -this happens because the group value indicated -in the password file also appears in -.Pa /etc/group . -The group value in the password file is placed in -.Va gidset[0] -and that value then gets added a second time when the -.Pa /etc/group -file is scanned to create the group set. +Only the super-user may set a new supplementary group list. .Sh RETURN VALUES .Rv -std setgroups .Sh ERRORS @@ -99,3 +85,11 @@ The .Fn setgroups system call appeared in .Bx 4.2 . +.Pp +Before +.Fx 15.0 , +the +.Fn setgroups +system call would set the effective group ID for the process to the first +element of +.Fa gidset . diff --git a/lib/libsys/shm_open.2 b/lib/libsys/shm_open.2 index 8bea939690ba..c3196d966e6b 100644 --- a/lib/libsys/shm_open.2 +++ b/lib/libsys/shm_open.2 @@ -26,7 +26,7 @@ .\" OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd January 30, 2023 +.Dd August 4, 2025 .Dt SHM_OPEN 2 .Os .Sh NAME @@ -74,8 +74,9 @@ must be included in The optional flags .Dv O_CREAT , .Dv O_EXCL , +.Dv O_TRUNC , and -.Dv O_TRUNC +.Dv O_CLOFORK may also be specified. .Pp If diff --git a/lib/libsys/syscalls.map b/lib/libsys/syscalls.map index dbb011343c8f..4cf80a2ffc69 100644 --- a/lib/libsys/syscalls.map +++ b/lib/libsys/syscalls.map @@ -117,10 +117,6 @@ FBSDprivate_1.0 { __sys_madvise; _mincore; __sys_mincore; - _getgroups; - __sys_getgroups; - _setgroups; - __sys_setgroups; _getpgrp; __sys_getpgrp; _setpgid; @@ -813,4 +809,8 @@ FBSDprivate_1.0 { __sys_inotify_add_watch_at; _inotify_rm_watch; __sys_inotify_rm_watch; + _getgroups; + __sys_getgroups; + _setgroups; + __sys_setgroups; }; diff --git a/lib/libutil++/Makefile b/lib/libutil++/Makefile index 729a336ddb3a..56b64bbf358c 100644 --- a/lib/libutil++/Makefile +++ b/lib/libutil++/Makefile @@ -1,4 +1,3 @@ -PACKAGE= clibs LIB_CXX= util++ INTERNALLIB= true SHLIB_MAJOR= 1 diff --git a/lib/msun/src/e_fmodf.c b/lib/msun/src/e_fmodf.c index a7d1a0c22acd..ada969db44c7 100644 --- a/lib/msun/src/e_fmodf.c +++ b/lib/msun/src/e_fmodf.c @@ -27,7 +27,7 @@ static const float one = 1.0, Zero[] = {0.0, -0.0,}; float fmodf(float x, float y) { - int32_t n,hx,hy,hz,ix,iy,sx,i; + int32_t hx, hy, hz, ix, iy, n, sx; GET_FLOAT_WORD(hx,x); GET_FLOAT_WORD(hy,y); @@ -44,14 +44,16 @@ fmodf(float x, float y) return Zero[(u_int32_t)sx>>31]; /* |x|=|y| return x*0*/ /* determine ix = ilogb(x) */ - if(hx<0x00800000) { /* subnormal x */ - for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; - } else ix = (hx>>23)-127; + if(hx<0x00800000) + ix = subnormal_ilogbf(hx); + else + ix = (hx>>23)-127; /* determine iy = ilogb(y) */ - if(hy<0x00800000) { /* subnormal y */ - for (iy = -126,i=(hy<<8); i>=0; i<<=1) iy -=1; - } else iy = (hy>>23)-127; + if(hy<0x00800000) + iy = subnormal_ilogbf(hy); + else + iy = (hy>>23)-127; /* set up {hx,lx}, {hy,ly} and align y to x */ if(ix >= -126) diff --git a/lib/msun/src/math_private.h b/lib/msun/src/math_private.h index 0711f2f33c41..fbd84e246ca7 100644 --- a/lib/msun/src/math_private.h +++ b/lib/msun/src/math_private.h @@ -740,7 +740,7 @@ irintl(long double x) } while (0) /* - * For a double entity split into high and low parts, compute ilogb. + * For a subnormal double entity split into high and low parts, compute ilogb. */ static inline int32_t subnormal_ilogb(int32_t hi, int32_t lo) @@ -760,6 +760,20 @@ subnormal_ilogb(int32_t hi, int32_t lo) return (j); } +/* + * For a subnormal float entity represented as an int32_t, compute ilogb. + */ +static inline int32_t +subnormal_ilogbf(int32_t hx) +{ + int32_t j; + uint32_t i; + i = (uint32_t) hx << 8; + for (j = -126; i < 0x7fffffff; i <<= 1) j -=1; + + return (j); +} + #ifdef DEBUG #if defined(__amd64__) || defined(__i386__) #define breakpoint() asm("int $3") diff --git a/lib/msun/src/s_ilogbf.c b/lib/msun/src/s_ilogbf.c index e0f8fee26ac8..5195e2331af3 100644 --- a/lib/msun/src/s_ilogbf.c +++ b/lib/msun/src/s_ilogbf.c @@ -18,7 +18,8 @@ #include "math.h" #include "math_private.h" - int ilogbf(float x) +int +ilogbf(float x) { int32_t hx,ix; @@ -28,7 +29,7 @@ if(hx==0) return FP_ILOGB0; else /* subnormal x */ - for (ix = -126,hx<<=8; hx>0; hx<<=1) ix -=1; + ix = subnormal_ilogbf(hx); return ix; } else if (hx<0x7f800000) return (hx>>23)-127; diff --git a/lib/msun/src/s_remquof.c b/lib/msun/src/s_remquof.c index 9cd148586796..e4adb8321559 100644 --- a/lib/msun/src/s_remquof.c +++ b/lib/msun/src/s_remquof.c @@ -25,7 +25,7 @@ static const float Zero[] = {0.0, -0.0,}; float remquof(float x, float y, int *quo) { - int32_t n,hx,hy,hz,ix,iy,sx,i; + int32_t hx, hy, hz, ix, iy, n, sx; u_int32_t q,sxy; GET_FLOAT_WORD(hx,x); @@ -47,14 +47,16 @@ remquof(float x, float y, int *quo) } /* determine ix = ilogb(x) */ - if(hx<0x00800000) { /* subnormal x */ - for (ix = -126,i=(hx<<8); i>0; i<<=1) ix -=1; - } else ix = (hx>>23)-127; + if(hx<0x00800000) + ix = subnormal_ilogbf(hx); + else + ix = (hx>>23)-127; /* determine iy = ilogb(y) */ - if(hy<0x00800000) { /* subnormal y */ - for (iy = -126,i=(hy<<8); i>0; i<<=1) iy -=1; - } else iy = (hy>>23)-127; + if(hy<0x00800000) + iy = subnormal_ilogbf(hy); + else + iy = (hy>>23)-127; /* set up {hx,lx}, {hy,ly} and align y to x */ if(ix >= -126) diff --git a/sbin/hastd/subr.c b/sbin/hastd/subr.c index 2a26482b3727..284fb0d07647 100644 --- a/sbin/hastd/subr.c +++ b/sbin/hastd/subr.c @@ -207,10 +207,8 @@ drop_privs(const struct hast_resource *res) } } PJDLOG_VERIFY(chdir("/") == 0); - gidset[0] = pw->pw_gid; - if (setgroups(1, gidset) == -1) { - pjdlog_errno(LOG_ERR, "Unable to set groups to gid %u", - (unsigned int)pw->pw_gid); + if (setgroups(0, NULL) == -1) { + pjdlog_errno(LOG_ERR, "Unable to drop supplementary groups"); return (-1); } if (setgid(pw->pw_gid) == -1) { @@ -287,8 +285,7 @@ drop_privs(const struct hast_resource *res) PJDLOG_VERIFY(egid == pw->pw_gid); PJDLOG_VERIFY(sgid == pw->pw_gid); PJDLOG_VERIFY(getgroups(0, NULL) == 1); - PJDLOG_VERIFY(getgroups(1, gidset) == 1); - PJDLOG_VERIFY(gidset[0] == pw->pw_gid); + PJDLOG_VERIFY(getgroups(1, gidset) == 0); pjdlog_debug(1, "Privileges successfully dropped using %s%s+setgid+setuid.", diff --git a/share/man/man4/sa.4 b/share/man/man4/sa.4 index 699a940a34d1..6c948a0f21ab 100644 --- a/share/man/man4/sa.4 +++ b/share/man/man4/sa.4 @@ -82,21 +82,19 @@ the case where a control mode device is opened. In the latter case, exclusive access is only sought when needed (e.g., to set parameters). .Sh SUB-MODES -Bits 0 and 1 of the minor number are interpreted as -.Sq sub-modes . The sub-modes differ in the action taken when the device is closed: .Bl -tag -width XXXX -.It 00 +.It Pa /dev/sa* A close will rewind the device; if the tape has been written, then a file mark will be written before the rewind is requested. The device is unmounted. -.It 01 +.It Pa /dev/nsa* A close will leave the tape mounted. If the tape was written to, a file mark will be written. No other head positioning takes place. Any further reads or writes will occur directly after the last read, or the written file mark. -.It 10 +.It Pa /dev/esa* A close will rewind the device. If the tape has been written, then a file mark will be written before the rewind is requested. diff --git a/share/misc/committers-src.dot b/share/misc/committers-src.dot index 0f9f8242c5c2..73d142b875f2 100644 --- a/share/misc/committers-src.dot +++ b/share/misc/committers-src.dot @@ -51,6 +51,7 @@ eik [label="Oliver Eikemeier\neik@FreeBSD.org\n2004/05/20\n2008/11/10"] furuta [label="Atsushi Furuta\nfuruta@FreeBSD.org\n2000/06/21\n2003/03/08"] gj [label="Gary L. Jennejohn\ngj@FreeBSD.org\n1994/??/??\n2006/04/28"] groudier [label="Gerard Roudier\ngroudier@FreeBSD.org\n1999/12/30\n2006/04/06"] +hselasky [label="Hans Petter Selasky\nhselasky@FreeBSD.org\n2023/06/23"] jake [label="Jake Burkholder\njake@FreeBSD.org\n2000/05/16\n2008/11/10"] jayanth [label="Jayanth Vijayaraghavan\njayanth@FreeBSD.org\n2000/05/08\n2008/11/10"] jb [label="John Birrell\njb@FreeBSD.org\n1997/03/27\n2009/12/15"] @@ -60,6 +61,7 @@ jkh [label="Jordan K. Hubbard\njkh@FreeBSD.org\n1993/06/12\n2008/06/13"] jlemon [label="Jonathan Lemon\njlemon@FreeBSD.org\n1997/08/14\n2008/11/10"] joe [label="Josef Karthauser\njoe@FreeBSD.org\n1999/10/22\n2008/08/10"] jtc [label="J.T. Conklin\njtc@FreeBSD.org\n1993/06/12\n????/??/??"] +karels [label="Mike Karels\nkarels@FreeBSD.org\n2016/06/09\n2024/06/02"] kargl [label="Steven G. Kargl\nkargl@FreeBSD.org\n2011/01/17\n2015/06/28"] kbyanc [label="Kelly Yancey\nkbyanc@FreeBSD.org\n2000/07/11\n2006/07/25"] keichii [label="Michael Wu\nkeichii@FreeBSD.org\n2001/03/07\n2006/04/28"] @@ -203,7 +205,6 @@ gshapiro [label="Gregory Shapiro\ngshapiro@FreeBSD.org\n2000/07/12"] harti [label="Hartmut Brandt\nharti@FreeBSD.org\n2003/01/29"] hiren [label="Hiren Panchasara\nhiren@FreeBSD.org\n2013/04/12"] hmp [label="Hiten Pandya\nhmp@FreeBSD.org\n2004/03/23"] -hselasky [label="Hans Petter Selasky\nhselasky@FreeBSD.org\n"] ian [label="Ian Lepore\nian@FreeBSD.org\n2013/01/07"] iedowse [label="Ian Dowse\niedowse@FreeBSD.org\n2000/12/01"] igoro [label="Igor Ostapenko\nigoro@FreeBSD.org\n2024/08/22"] @@ -245,7 +246,6 @@ jwd [label="John De Boskey\njwd@FreeBSD.org\n2000/05/19"] kaiw [label="Kai Wang\nkaiw@FreeBSD.org\n2007/09/26"] kaktus [label="Pawel Biernacki\nkaktus@FreeBSD.org\n2019/09/26"] kan [label="Alexander Kabaev\nkan@FreeBSD.org\n2002/07/21"] -karels [label="Mike Karels\nkarels@FreeBSD.org\n2016/06/09"] kbowling [label="Kevin Bowling\nkbowling@FreeBSD.org\n2024/10/15"] kd [label="Kornel DulÄ™ba\nkd@FreeBSD.org\n2022/06/22"] ken [label="Ken Merry\nken@FreeBSD.org\n1998/09/08"] diff --git a/share/mk/src.opts.mk b/share/mk/src.opts.mk index 6000da865332..f146a4b24424 100644 --- a/share/mk/src.opts.mk +++ b/share/mk/src.opts.mk @@ -296,9 +296,9 @@ __DEFAULT_NO_OPTIONS+=LLVM_TARGET_BPF LLVM_TARGET_MIPS .include <bsd.compiler.mk> .if ${__T} == "i386" || ${__T} == "amd64" -__DEFAULT_NO_OPTIONS += FDT +__DEFAULT_NO_OPTIONS+=FDT .else -__DEFAULT_YES_OPTIONS += FDT +__DEFAULT_YES_OPTIONS+=FDT .endif .if ${__T:Marm*} == "" && ${__T:Mriscv64*} == "" @@ -519,8 +519,4 @@ MK_${vv:H}:= ${MK_${vv:T}} .endif .endfor -# -# Set defaults for the MK_*_SUPPORT variables. -# - .endif # !target(__<src.opts.mk>__) diff --git a/sys/amd64/vmm/io/vioapic.c b/sys/amd64/vmm/io/vioapic.c index 8869dc1383e6..7df6193d6dc0 100644 --- a/sys/amd64/vmm/io/vioapic.c +++ b/sys/amd64/vmm/io/vioapic.c @@ -130,6 +130,15 @@ vioapic_send_intr(struct vioapic *vioapic, int pin) vector = low & IOART_INTVEC; dest = high >> APIC_ID_SHIFT; + /* + * Ideally we'd just call lapic_intr_msi() here with the + * constructed MSI instead of interpreting it for ourselves. + * But until/unless we support emulated IOMMUs with interrupt + * remapping, interpretation is simple. We just need to mask + * in the Extended Destination ID bits for the 15-bit + * enlightenment (http://david.woodhou.se/ExtDestId.pdf) + */ + dest |= ((high & APIC_EXT_ID_MASK) >> APIC_EXT_ID_SHIFT) << 8; vlapic_deliver_intr(vioapic->vm, level, dest, phys, delmode, vector); } diff --git a/sys/amd64/vmm/vmm_lapic.c b/sys/amd64/vmm/vmm_lapic.c index fd511733492e..0cae01f172ec 100644 --- a/sys/amd64/vmm/vmm_lapic.c +++ b/sys/amd64/vmm/vmm_lapic.c @@ -115,6 +115,11 @@ lapic_intr_msi(struct vm *vm, uint64_t addr, uint64_t msg) * physical otherwise. */ dest = (addr >> 12) & 0xff; + /* + * Extended Destination ID support uses bits 5-11 of the address: + * http://david.woodhou.se/ExtDestId.pdf + */ + dest |= ((addr >> 5) & 0x7f) << 8; phys = ((addr & (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)) != (MSI_X86_ADDR_RH | MSI_X86_ADDR_LOG)); delmode = msg & APIC_DELMODE_MASK; diff --git a/sys/amd64/vmm/x86.c b/sys/amd64/vmm/x86.c index 366f1da9f850..2e2224595ab4 100644 --- a/sys/amd64/vmm/x86.c +++ b/sys/amd64/vmm/x86.c @@ -48,7 +48,12 @@ SYSCTL_DECL(_hw_vmm); static SYSCTL_NODE(_hw_vmm, OID_AUTO, topology, CTLFLAG_RD | CTLFLAG_MPSAFE, 0, NULL); -#define CPUID_VM_HIGH 0x40000000 +#define CPUID_VM_SIGNATURE 0x40000000 +#define CPUID_BHYVE_FEATURES 0x40000001 +#define CPUID_VM_HIGH CPUID_BHYVE_FEATURES + +/* Features advertised in CPUID_BHYVE_FEATURES %eax */ +#define CPUID_BHYVE_FEAT_EXT_DEST_ID (1UL << 0) /* MSI Extended Dest ID */ static const char bhyve_id[12] = "bhyve bhyve "; @@ -100,7 +105,7 @@ x86_emulate_cpuid(struct vcpu *vcpu, uint64_t *rax, uint64_t *rbx, if (cpu_exthigh != 0 && func >= 0x80000000) { if (func > cpu_exthigh) func = cpu_exthigh; - } else if (func >= 0x40000000) { + } else if (func >= CPUID_VM_SIGNATURE) { if (func > CPUID_VM_HIGH) func = CPUID_VM_HIGH; } else if (func > cpu_high) { @@ -601,13 +606,20 @@ x86_emulate_cpuid(struct vcpu *vcpu, uint64_t *rax, uint64_t *rbx, regs[3] = 0; break; - case 0x40000000: + case CPUID_VM_SIGNATURE: regs[0] = CPUID_VM_HIGH; bcopy(bhyve_id, ®s[1], 4); bcopy(bhyve_id + 4, ®s[2], 4); bcopy(bhyve_id + 8, ®s[3], 4); break; + case CPUID_BHYVE_FEATURES: + regs[0] = CPUID_BHYVE_FEAT_EXT_DEST_ID; + regs[1] = 0; + regs[2] = 0; + regs[3] = 0; + break; + default: default_leaf: /* diff --git a/sys/arm/allwinner/aw_gpio.c b/sys/arm/allwinner/aw_gpio.c index 2061e38a155f..f1b6f0bc9193 100644 --- a/sys/arm/allwinner/aw_gpio.c +++ b/sys/arm/allwinner/aw_gpio.c @@ -1162,11 +1162,12 @@ aw_gpio_attach(device_t dev) fdt_pinctrl_register(dev, "allwinner,pins"); fdt_pinctrl_configure_tree(dev); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) goto fail; config_intrhook_oneshot(aw_gpio_enable_bank_supply, sc); + bus_attach_children(dev); return (0); diff --git a/sys/arm/allwinner/axp209.c b/sys/arm/allwinner/axp209.c index 239ead02d0e0..ff999a0f9b9b 100644 --- a/sys/arm/allwinner/axp209.c +++ b/sys/arm/allwinner/axp209.c @@ -1322,7 +1322,7 @@ axp2xx_attach(device_t dev) case AXP209: sc->pins = axp209_pins; sc->npins = nitems(axp209_pins); - sc->gpiodev = gpiobus_attach_bus(dev); + sc->gpiodev = gpiobus_add_bus(dev); sc->sensors = axp209_sensors; sc->nsensors = nitems(axp209_sensors); @@ -1333,7 +1333,7 @@ axp2xx_attach(device_t dev) case AXP221: sc->pins = axp221_pins; sc->npins = nitems(axp221_pins); - sc->gpiodev = gpiobus_attach_bus(dev); + sc->gpiodev = gpiobus_add_bus(dev); sc->sensors = axp221_sensors; sc->nsensors = nitems(axp221_sensors); @@ -1374,6 +1374,7 @@ axp2xx_attach(device_t dev) } } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/allwinner/axp81x.c b/sys/arm/allwinner/axp81x.c index fc1a168595e5..71f0c8156a0d 100644 --- a/sys/arm/allwinner/axp81x.c +++ b/sys/arm/allwinner/axp81x.c @@ -1609,7 +1609,8 @@ axp8xx_attach(device_t dev) EVENTHANDLER_REGISTER(shutdown_final, axp8xx_shutdown, dev, SHUTDOWN_PRI_LAST); - sc->gpiodev = gpiobus_attach_bus(dev); + sc->gpiodev = gpiobus_add_bus(dev); + bus_attach_children(dev); return (0); } diff --git a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c index 48d1d2af5abc..93ee5d7c8bd3 100644 --- a/sys/arm/broadcom/bcm2835/bcm2835_gpio.c +++ b/sys/arm/broadcom/bcm2835/bcm2835_gpio.c @@ -840,10 +840,11 @@ bcm_gpio_attach(device_t dev) fdt_pinctrl_register(dev, "brcm,pins"); fdt_pinctrl_configure_tree(dev); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) goto fail; + bus_attach_children(dev); return (0); fail: diff --git a/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c b/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c index 5a0f5cf2b1b3..b286654c6f18 100644 --- a/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c +++ b/sys/arm/broadcom/bcm2835/raspberrypi_gpio.c @@ -404,10 +404,11 @@ rpi_fw_gpio_attach(device_t dev) } } free(names, M_OFWPROP); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) goto fail; + bus_attach_children(dev); return (0); fail: diff --git a/sys/arm/freescale/imx/imx_gpio.c b/sys/arm/freescale/imx/imx_gpio.c index 7610d28af90e..3b19ef1b5e67 100644 --- a/sys/arm/freescale/imx/imx_gpio.c +++ b/sys/arm/freescale/imx/imx_gpio.c @@ -861,13 +861,14 @@ imx51_gpio_attach(device_t dev) gpio_pic_register_isrcs(sc); intr_pic_register(dev, OF_xref_from_node(ofw_bus_get_node(dev))); #endif - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { imx51_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/freescale/vybrid/vf_gpio.c b/sys/arm/freescale/vybrid/vf_gpio.c index c81524a8a27e..b4e1ba9af586 100644 --- a/sys/arm/freescale/vybrid/vf_gpio.c +++ b/sys/arm/freescale/vybrid/vf_gpio.c @@ -147,13 +147,14 @@ vf_gpio_attach(device_t dev) "vf_gpio%d.%d", device_get_unit(dev), i); } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { bus_release_resources(dev, vf_gpio_spec, sc->res); mtx_destroy(&sc->sc_mtx); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/mv/a37x0_gpio.c b/sys/arm/mv/a37x0_gpio.c index 86110ff87ab1..754663d2991e 100644 --- a/sys/arm/mv/a37x0_gpio.c +++ b/sys/arm/mv/a37x0_gpio.c @@ -291,10 +291,11 @@ a37x0_gpio_attach(device_t dev) if (sc->sc_npins > sc->sc_max_pins) return (ENXIO); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) return (ENXIO); + bus_attach_children(dev); return (0); } diff --git a/sys/arm/mv/gpio.c b/sys/arm/mv/gpio.c index 934c00236153..b3c2314fb2d6 100644 --- a/sys/arm/mv/gpio.c +++ b/sys/arm/mv/gpio.c @@ -340,7 +340,7 @@ mv_gpio_attach(device_t dev) if (rv != 0) return (rv); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { mtx_destroy(&sc->mutex); bus_release_resource(dev, SYS_RES_IRQ, @@ -348,6 +348,7 @@ mv_gpio_attach(device_t dev) return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/mv/mvebu_gpio.c b/sys/arm/mv/mvebu_gpio.c index 7acdfff539dc..4cc9b7030a65 100644 --- a/sys/arm/mv/mvebu_gpio.c +++ b/sys/arm/mv/mvebu_gpio.c @@ -804,12 +804,13 @@ mvebu_gpio_attach(device_t dev) } } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { mvebu_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/nvidia/tegra_gpio.c b/sys/arm/nvidia/tegra_gpio.c index e37fd69a121e..aa34537352be 100644 --- a/sys/arm/nvidia/tegra_gpio.c +++ b/sys/arm/nvidia/tegra_gpio.c @@ -818,12 +818,13 @@ tegra_gpio_attach(device_t dev) return (ENXIO); } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { tegra_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/ti/ti_gpio.c b/sys/arm/ti/ti_gpio.c index aceb3d63204e..01b9597a4418 100644 --- a/sys/arm/ti/ti_gpio.c +++ b/sys/arm/ti/ti_gpio.c @@ -674,12 +674,13 @@ ti_gpio_attach(device_t dev) } } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { ti_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm/xilinx/zy7_gpio.c b/sys/arm/xilinx/zy7_gpio.c index 71b6fc3c0586..2434e43bf27c 100644 --- a/sys/arm/xilinx/zy7_gpio.c +++ b/sys/arm/xilinx/zy7_gpio.c @@ -441,12 +441,13 @@ zy7_gpio_attach(device_t dev) return (ENOMEM); } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { zy7_gpio_detach(dev); return (ENOMEM); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm64/apple/apple_pinctrl.c b/sys/arm64/apple/apple_pinctrl.c index ebaaccea1d99..c28b1c62d78c 100644 --- a/sys/arm64/apple/apple_pinctrl.c +++ b/sys/arm64/apple/apple_pinctrl.c @@ -171,12 +171,13 @@ apple_pinctrl_attach(device_t dev) OF_xref_from_node(ofw_bus_get_node(dev))); } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { device_printf(dev, "failed to attach gpiobus\n"); goto error; } + bus_attach_children(dev); return (0); error: mtx_destroy(&sc->sc_mtx); diff --git a/sys/arm64/rockchip/rk_gpio.c b/sys/arm64/rockchip/rk_gpio.c index 847bc7394dd0..61614f532634 100644 --- a/sys/arm64/rockchip/rk_gpio.c +++ b/sys/arm64/rockchip/rk_gpio.c @@ -371,12 +371,13 @@ rk_gpio_attach(device_t dev) sc->swporta_ddr = rk_gpio_read_4(sc, RK_GPIO_SWPORTA_DDR); RK_GPIO_UNLOCK(sc); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { rk_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/arm64/rockchip/rk_grf_gpio.c b/sys/arm64/rockchip/rk_grf_gpio.c index 6818bd85bb95..6ac419889614 100644 --- a/sys/arm64/rockchip/rk_grf_gpio.c +++ b/sys/arm64/rockchip/rk_grf_gpio.c @@ -181,11 +181,12 @@ rk_grf_gpio_attach(device_t dev) return (ENXIO); } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/cddl/boot/zfs/zfsimpl.h b/sys/cddl/boot/zfs/zfsimpl.h index 83d964360343..915aeeda3c9e 100644 --- a/sys/cddl/boot/zfs/zfsimpl.h +++ b/sys/cddl/boot/zfs/zfsimpl.h @@ -536,6 +536,12 @@ typedef struct zio_gbh { offsetof(vdev_label_t, vl_uberblock[(n) << VDEV_UBERBLOCK_SHIFT(vd)]) #define VDEV_UBERBLOCK_SIZE(vd) (1ULL << VDEV_UBERBLOCK_SHIFT(vd)) +#define ASHIFT_UBERBLOCK_SHIFT(ashift) \ + MIN(MAX(ashift, UBERBLOCK_SHIFT), \ + MAX_UBERBLOCK_SHIFT) +#define ASHIFT_UBERBLOCK_SIZE(ashift) \ + (1ULL << ASHIFT_UBERBLOCK_SHIFT(ashift)) + typedef struct vdev_phys { char vp_nvlist[VDEV_PHYS_SIZE - sizeof (zio_eck_t)]; zio_eck_t vp_zbt; diff --git a/sys/compat/freebsd32/freebsd32_syscall.h b/sys/compat/freebsd32/freebsd32_syscall.h index 971510ebb6b6..90cd21a80923 100644 --- a/sys/compat/freebsd32/freebsd32_syscall.h +++ b/sys/compat/freebsd32/freebsd32_syscall.h @@ -83,8 +83,8 @@ /* 76 is obsolete vhangup */ /* 77 is obsolete vlimit */ #define FREEBSD32_SYS_mincore 78 -#define FREEBSD32_SYS_getgroups 79 -#define FREEBSD32_SYS_setgroups 80 +#define FREEBSD32_SYS_freebsd14_getgroups 79 +#define FREEBSD32_SYS_freebsd14_setgroups 80 #define FREEBSD32_SYS_getpgrp 81 #define FREEBSD32_SYS_setpgid 82 #define FREEBSD32_SYS_freebsd32_setitimer 83 @@ -513,4 +513,6 @@ #define FREEBSD32_SYS_exterrctl 592 #define FREEBSD32_SYS_inotify_add_watch_at 593 #define FREEBSD32_SYS_inotify_rm_watch 594 -#define FREEBSD32_SYS_MAXSYSCALL 595 +#define FREEBSD32_SYS_getgroups 595 +#define FREEBSD32_SYS_setgroups 596 +#define FREEBSD32_SYS_MAXSYSCALL 597 diff --git a/sys/compat/freebsd32/freebsd32_syscalls.c b/sys/compat/freebsd32/freebsd32_syscalls.c index 79c414b675b1..f0f8d26554b5 100644 --- a/sys/compat/freebsd32/freebsd32_syscalls.c +++ b/sys/compat/freebsd32/freebsd32_syscalls.c @@ -84,8 +84,8 @@ const char *freebsd32_syscallnames[] = { "obs_vhangup", /* 76 = obsolete vhangup */ "obs_vlimit", /* 77 = obsolete vlimit */ "mincore", /* 78 = mincore */ - "getgroups", /* 79 = getgroups */ - "setgroups", /* 80 = setgroups */ + "compat14.getgroups", /* 79 = freebsd14 getgroups */ + "compat14.setgroups", /* 80 = freebsd14 setgroups */ "getpgrp", /* 81 = getpgrp */ "setpgid", /* 82 = setpgid */ "freebsd32_setitimer", /* 83 = freebsd32_setitimer */ @@ -600,4 +600,6 @@ const char *freebsd32_syscallnames[] = { "exterrctl", /* 592 = exterrctl */ "inotify_add_watch_at", /* 593 = inotify_add_watch_at */ "inotify_rm_watch", /* 594 = inotify_rm_watch */ + "getgroups", /* 595 = getgroups */ + "setgroups", /* 596 = setgroups */ }; diff --git a/sys/compat/freebsd32/freebsd32_sysent.c b/sys/compat/freebsd32/freebsd32_sysent.c index 1a4b0d87722c..12f1a346c3e9 100644 --- a/sys/compat/freebsd32/freebsd32_sysent.c +++ b/sys/compat/freebsd32/freebsd32_sysent.c @@ -146,8 +146,8 @@ struct sysent freebsd32_sysent[] = { { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 76 = obsolete vhangup */ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 77 = obsolete vlimit */ { .sy_narg = AS(mincore_args), .sy_call = (sy_call_t *)sys_mincore, .sy_auevent = AUE_MINCORE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 78 = mincore */ - { .sy_narg = AS(getgroups_args), .sy_call = (sy_call_t *)sys_getgroups, .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 79 = getgroups */ - { .sy_narg = AS(setgroups_args), .sy_call = (sy_call_t *)sys_setgroups, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 80 = setgroups */ + { compat14(AS(freebsd14_getgroups_args),getgroups), .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 79 = freebsd14 getgroups */ + { compat14(AS(freebsd14_setgroups_args),setgroups), .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 80 = freebsd14 setgroups */ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_getpgrp, .sy_auevent = AUE_GETPGRP, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 81 = getpgrp */ { .sy_narg = AS(setpgid_args), .sy_call = (sy_call_t *)sys_setpgid, .sy_auevent = AUE_SETPGRP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 82 = setpgid */ { .sy_narg = AS(freebsd32_setitimer_args), .sy_call = (sy_call_t *)freebsd32_setitimer, .sy_auevent = AUE_SETITIMER, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 83 = freebsd32_setitimer */ @@ -662,4 +662,6 @@ struct sysent freebsd32_sysent[] = { { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */ { .sy_narg = AS(inotify_add_watch_at_args), .sy_call = (sy_call_t *)sys_inotify_add_watch_at, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 593 = inotify_add_watch_at */ { .sy_narg = AS(inotify_rm_watch_args), .sy_call = (sy_call_t *)sys_inotify_rm_watch, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 594 = inotify_rm_watch */ + { .sy_narg = AS(getgroups_args), .sy_call = (sy_call_t *)sys_getgroups, .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 595 = getgroups */ + { .sy_narg = AS(setgroups_args), .sy_call = (sy_call_t *)sys_setgroups, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 596 = setgroups */ }; diff --git a/sys/compat/freebsd32/freebsd32_systrace_args.c b/sys/compat/freebsd32/freebsd32_systrace_args.c index f9dc514bee7d..e471c5148021 100644 --- a/sys/compat/freebsd32/freebsd32_systrace_args.c +++ b/sys/compat/freebsd32/freebsd32_systrace_args.c @@ -457,22 +457,6 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 3; break; } - /* getgroups */ - case 79: { - struct getgroups_args *p = params; - iarg[a++] = p->gidsetsize; /* int */ - uarg[a++] = (intptr_t)p->gidset; /* gid_t * */ - *n_args = 2; - break; - } - /* setgroups */ - case 80: { - struct setgroups_args *p = params; - iarg[a++] = p->gidsetsize; /* int */ - uarg[a++] = (intptr_t)p->gidset; /* const gid_t * */ - *n_args = 2; - break; - } /* getpgrp */ case 81: { *n_args = 0; @@ -3413,6 +3397,22 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 2; break; } + /* getgroups */ + case 595: { + struct getgroups_args *p = params; + iarg[a++] = p->gidsetsize; /* int */ + uarg[a++] = (intptr_t)p->gidset; /* gid_t * */ + *n_args = 2; + break; + } + /* setgroups */ + case 596: { + struct setgroups_args *p = params; + iarg[a++] = p->gidsetsize; /* int */ + uarg[a++] = (intptr_t)p->gidset; /* const gid_t * */ + *n_args = 2; + break; + } default: *n_args = 0; break; @@ -4112,32 +4112,6 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; - /* getgroups */ - case 79: - switch (ndx) { - case 0: - p = "int"; - break; - case 1: - p = "userland gid_t *"; - break; - default: - break; - }; - break; - /* setgroups */ - case 80: - switch (ndx) { - case 0: - p = "int"; - break; - case 1: - p = "userland const gid_t *"; - break; - default: - break; - }; - break; /* getpgrp */ case 81: break; @@ -9222,6 +9196,32 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* getgroups */ + case 595: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland gid_t *"; + break; + default: + break; + }; + break; + /* setgroups */ + case 596: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const gid_t *"; + break; + default: + break; + }; + break; default: break; }; @@ -9488,16 +9488,6 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; - /* getgroups */ - case 79: - if (ndx == 0 || ndx == 1) - p = "int"; - break; - /* setgroups */ - case 80: - if (ndx == 0 || ndx == 1) - p = "int"; - break; /* getpgrp */ case 81: /* setpgid */ @@ -11130,6 +11120,16 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; + /* getgroups */ + case 595: + if (ndx == 0 || ndx == 1) + p = "int"; + break; + /* setgroups */ + case 596: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/contrib/openzfs/module/zfs/dsl_deadlist.c b/sys/contrib/openzfs/module/zfs/dsl_deadlist.c index 475db3c89508..41ac72bf1c16 100644 --- a/sys/contrib/openzfs/module/zfs/dsl_deadlist.c +++ b/sys/contrib/openzfs/module/zfs/dsl_deadlist.c @@ -1049,7 +1049,8 @@ dsl_livelist_iterate(void *arg, const blkptr_t *bp, boolean_t bp_freed, ASSERT3U(BP_GET_PSIZE(bp), ==, BP_GET_PSIZE(&found->le_bp)); ASSERT3U(BP_GET_CHECKSUM(bp), ==, BP_GET_CHECKSUM(&found->le_bp)); - ASSERT3U(BP_GET_BIRTH(bp), ==, BP_GET_BIRTH(&found->le_bp)); + ASSERT3U(BP_GET_PHYSICAL_BIRTH(bp), ==, + BP_GET_PHYSICAL_BIRTH(&found->le_bp)); } if (bp_freed) { if (found == NULL) { diff --git a/sys/dev/amdgpio/amdgpio.c b/sys/dev/amdgpio/amdgpio.c index f39006d95805..2bd455c612b8 100644 --- a/sys/dev/amdgpio/amdgpio.c +++ b/sys/dev/amdgpio/amdgpio.c @@ -408,12 +408,13 @@ amdgpio_attach(device_t dev) GPIO_PIN_OUTPUT : GPIO_PIN_INPUT; } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { device_printf(dev, "could not attach gpiobus\n"); goto err_bus; } + bus_attach_children(dev); return (0); err_bus: diff --git a/sys/dev/bhnd/cores/chipc/chipc_gpio.c b/sys/dev/bhnd/cores/chipc/chipc_gpio.c index a110bdda5fa7..429de0fc1fd8 100644 --- a/sys/dev/bhnd/cores/chipc/chipc_gpio.c +++ b/sys/dev/bhnd/cores/chipc/chipc_gpio.c @@ -173,11 +173,13 @@ chipc_gpio_attach(device_t dev) if (CC_GPIO_QUIRK(sc, NO_GPIOC)) { sc->gpiobus = NULL; } else { - if ((sc->gpiobus = gpiobus_attach_bus(dev)) == NULL) { + if ((sc->gpiobus = gpiobus_add_bus(dev)) == NULL) { device_printf(dev, "failed to attach gpiobus\n"); error = ENXIO; goto failed; } + + bus_attach_children(dev); } /* Register as the bus GPIO provider */ diff --git a/sys/dev/ftgpio/ftgpio.c b/sys/dev/ftgpio/ftgpio.c index 7acfdd5b900e..68787b54bb16 100644 --- a/sys/dev/ftgpio/ftgpio.c +++ b/sys/dev/ftgpio/ftgpio.c @@ -398,12 +398,13 @@ ftgpio_attach(device_t dev) FTGPIO_VERBOSE_PRINTF(sc->dev, "groups GPIO1..GPIO6 enabled\n"); GPIO_UNLOCK(sc); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { GPIO_LOCK_DESTROY(sc); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/gpio/acpi_gpiobus.c b/sys/dev/gpio/acpi_gpiobus.c index 94f4e5771266..170f23615416 100644 --- a/sys/dev/gpio/acpi_gpiobus.c +++ b/sys/dev/gpio/acpi_gpiobus.c @@ -383,7 +383,8 @@ acpi_gpiobus_detach(device_t dev) } static int -acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, uintptr_t *result) +acpi_gpiobus_read_ivar(device_t dev, device_t child, int which, + uintptr_t *result) { struct acpi_gpiobus_ivar *devi = device_get_ivars(child); diff --git a/sys/dev/gpio/bytgpio.c b/sys/dev/gpio/bytgpio.c index f7b2a73ec6cb..5d685c155a03 100644 --- a/sys/dev/gpio/bytgpio.c +++ b/sys/dev/gpio/bytgpio.c @@ -608,7 +608,7 @@ bytgpio_attach(device_t dev) sc->sc_pad_funcs[pin] = val & BYTGPIO_PCONF0_FUNC_MASK; } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { BYTGPIO_LOCK_DESTROY(sc); bus_release_resource(dev, SYS_RES_MEMORY, @@ -616,6 +616,7 @@ bytgpio_attach(device_t dev) return (ENXIO); } + bus_attach_children(dev); return (0); error: diff --git a/sys/dev/gpio/chvgpio.c b/sys/dev/gpio/chvgpio.c index 199ad4d6f373..3273aad9242b 100644 --- a/sys/dev/gpio/chvgpio.c +++ b/sys/dev/gpio/chvgpio.c @@ -441,7 +441,7 @@ chvgpio_attach(device_t dev) bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_MASK, 0); bus_write_4(sc->sc_mem_res, CHVGPIO_INTERRUPT_STATUS, 0xffff); - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { CHVGPIO_LOCK_DESTROY(sc); bus_release_resource(dev, SYS_RES_MEMORY, @@ -451,6 +451,7 @@ chvgpio_attach(device_t dev) return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/gpio/dwgpio/dwgpio.c b/sys/dev/gpio/dwgpio/dwgpio.c index 5acb99ca591e..3908113d5fd4 100644 --- a/sys/dev/gpio/dwgpio/dwgpio.c +++ b/sys/dev/gpio/dwgpio/dwgpio.c @@ -167,12 +167,13 @@ dwgpio_attach(device_t dev) snprintf(sc->gpio_pins[i].gp_name, GPIOMAXNAME, "dwgpio%d.%d", device_get_unit(dev), i); } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { mtx_destroy(&sc->sc_mtx); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/gpio/gpiobus.c b/sys/dev/gpio/gpiobus.c index 764bcb7e6ee8..c25c41f43042 100644 --- a/sys/dev/gpio/gpiobus.c +++ b/sys/dev/gpio/gpiobus.c @@ -330,24 +330,6 @@ gpiobus_add_bus(device_t dev) return (busdev); } -/* - * Attach a gpiobus child. - * Note that the controller is expected - * to be fully initialized at this point. - */ -device_t -gpiobus_attach_bus(device_t dev) -{ - device_t busdev; - - busdev = gpiobus_add_bus(dev); - if (busdev == NULL) - return (NULL); - - bus_attach_children(dev); - return (busdev); -} - int gpiobus_detach_bus(device_t dev) { diff --git a/sys/dev/gpio/gpiobusvar.h b/sys/dev/gpio/gpiobusvar.h index 7f504236a774..0528efe45525 100644 --- a/sys/dev/gpio/gpiobusvar.h +++ b/sys/dev/gpio/gpiobusvar.h @@ -171,7 +171,6 @@ struct resource *gpio_alloc_intr_resource(device_t consumer_dev, int *rid, int gpio_check_flags(uint32_t, uint32_t); device_t gpiobus_add_bus(device_t); -device_t gpiobus_attach_bus(device_t); int gpiobus_detach_bus(device_t); #endif /* __GPIOBUS_H__ */ diff --git a/sys/dev/gpio/pl061.c b/sys/dev/gpio/pl061.c index 87d4310a6396..32109e5982bc 100644 --- a/sys/dev/gpio/pl061.c +++ b/sys/dev/gpio/pl061.c @@ -495,13 +495,14 @@ pl061_attach(device_t dev) goto free_isrc; } - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { device_printf(dev, "couldn't attach gpio bus\n"); PL061_LOCK_DESTROY(sc); goto free_isrc; } + bus_attach_children(dev); return (0); free_isrc: diff --git a/sys/dev/gpio/qoriq_gpio.c b/sys/dev/gpio/qoriq_gpio.c index 8b44cd256c79..d11868a23751 100644 --- a/sys/dev/gpio/qoriq_gpio.c +++ b/sys/dev/gpio/qoriq_gpio.c @@ -379,12 +379,13 @@ qoriq_gpio_attach(device_t dev) OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { qoriq_gpio_detach(dev); return (ENOMEM); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/iicbus/gpio/pcf8574.c b/sys/dev/iicbus/gpio/pcf8574.c index ab6e2bc07d1f..bf60dec67557 100644 --- a/sys/dev/iicbus/gpio/pcf8574.c +++ b/sys/dev/iicbus/gpio/pcf8574.c @@ -142,12 +142,13 @@ pcf8574_attach(device_t dev) (void)pcf8574_write(sc, 0xff); sx_init(&sc->lock, "pcf8574"); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "Could not create busdev child\n"); sx_destroy(&sc->lock); return (ENXIO); } + bus_attach_children(dev); return (0); } @@ -158,9 +159,7 @@ pcf8574_detach(device_t dev) sc = device_get_softc(dev); - if (sc->busdev != NULL) - gpiobus_detach_bus(sc->busdev); - + gpiobus_detach_bus(dev); sx_destroy(&sc->lock); return (0); } diff --git a/sys/dev/iicbus/gpio/tca64xx.c b/sys/dev/iicbus/gpio/tca64xx.c index cd011ae9be75..ab8fedd3f8fd 100644 --- a/sys/dev/iicbus/gpio/tca64xx.c +++ b/sys/dev/iicbus/gpio/tca64xx.c @@ -262,7 +262,7 @@ tca64xx_attach(device_t dev) mtx_init(&sc->mtx, "tca64xx gpio", "gpio", MTX_DEF); OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "Could not create busdev child\n"); return (ENXIO); @@ -281,6 +281,7 @@ tca64xx_attach(device_t dev) } #endif + bus_attach_children(dev); return (0); } @@ -291,9 +292,7 @@ tca64xx_detach(device_t dev) sc = device_get_softc(dev); - if (sc->busdev != NULL) - gpiobus_detach_bus(sc->busdev); - + gpiobus_detach_bus(dev); mtx_destroy(&sc->mtx); return (0); diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c b/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c index 6e24395b5577..c45f02cdaf42 100644 --- a/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_flow_table.c @@ -1783,8 +1783,8 @@ mlx5e_add_vxlan_rule(struct mlx5e_priv *priv, sa_family_t family, u_int port) el->refcount++; if (el->installed) return (0); - } - el = mlx5e_vxlan_alloc_db_el(priv, proto, port); + } else + el = mlx5e_vxlan_alloc_db_el(priv, proto, port); if ((if_getcapenable(priv->ifp) & IFCAP_VXLAN_HWCSUM) != 0) { err = mlx5e_add_vxlan_rule_from_db(priv, el); diff --git a/sys/dev/nctgpio/nctgpio.c b/sys/dev/nctgpio/nctgpio.c index 75ea1fbdba17..ddc2ceef7dfb 100644 --- a/sys/dev/nctgpio/nctgpio.c +++ b/sys/dev/nctgpio/nctgpio.c @@ -1258,13 +1258,14 @@ nct_attach(device_t dev) GPIO_UNLOCK(sc); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "failed to attach to gpiobus\n"); GPIO_LOCK_DESTROY(sc); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/p2sb/lewisburg_gpio.c b/sys/dev/p2sb/lewisburg_gpio.c index b45d7767602c..3be777ab9524 100644 --- a/sys/dev/p2sb/lewisburg_gpio.c +++ b/sys/dev/p2sb/lewisburg_gpio.c @@ -217,10 +217,11 @@ lbggpio_attach(device_t dev) } /* support gpio */ - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) return (ENXIO); + bus_attach_children(dev); return (0); } diff --git a/sys/dev/qcom_tlmm/qcom_tlmm_ipq4018.c b/sys/dev/qcom_tlmm/qcom_tlmm_ipq4018.c index 2d390cd449af..50f54b896748 100644 --- a/sys/dev/qcom_tlmm/qcom_tlmm_ipq4018.c +++ b/sys/dev/qcom_tlmm/qcom_tlmm_ipq4018.c @@ -346,13 +346,14 @@ qcom_tlmm_ipq4018_attach(device_t dev) fdt_pinctrl_register(dev, NULL); fdt_pinctrl_configure_by_name(dev, "default"); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "%s: failed to attach bus\n", __func__); qcom_tlmm_ipq4018_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/rccgpio/rccgpio.c b/sys/dev/rccgpio/rccgpio.c index b2b775b879ad..dafd0b511fa9 100644 --- a/sys/dev/rccgpio/rccgpio.c +++ b/sys/dev/rccgpio/rccgpio.c @@ -308,7 +308,7 @@ rcc_gpio_attach(device_t dev) RCC_WRITE(sc, RCC_GPIO_GP_LVL, sc->sc_output); /* Attach the gpiobus. */ - sc->sc_busdev = gpiobus_attach_bus(dev); + sc->sc_busdev = gpiobus_add_bus(dev); if (sc->sc_busdev == NULL) { bus_release_resource(dev, SYS_RES_IOPORT, sc->sc_io_rid, sc->sc_io_res); @@ -316,6 +316,7 @@ rcc_gpio_attach(device_t dev) return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/dev/uart/uart_cpu_acpi.c b/sys/dev/uart/uart_cpu_acpi.c index 7382c47a8db6..da77603f0093 100644 --- a/sys/dev/uart/uart_cpu_acpi.c +++ b/sys/dev/uart/uart_cpu_acpi.c @@ -44,23 +44,15 @@ #include <contrib/dev/acpica/include/accommon.h> #include <contrib/dev/acpica/include/actables.h> -static struct acpi_uart_compat_data * +static struct acpi_spcr_compat_data * uart_cpu_acpi_scan(uint8_t interface_type) { - struct acpi_uart_compat_data **cd, *curcd; + struct acpi_spcr_compat_data **cd, *curcd; int i; - SET_FOREACH(cd, uart_acpi_class_and_device_set) { + SET_FOREACH(cd, uart_acpi_spcr_class_set) { curcd = *cd; - for (i = 0; curcd[i].cd_hid != NULL; i++) { - if (curcd[i].cd_port_subtype == interface_type) - return (&curcd[i]); - } - } - - SET_FOREACH(cd, uart_acpi_class_set) { - curcd = *cd; - for (i = 0; curcd[i].cd_hid != NULL; i++) { + for (i = 0; curcd[i].cd_class != NULL; i++) { if (curcd[i].cd_port_subtype == interface_type) return (&curcd[i]); } @@ -143,7 +135,7 @@ uart_cpu_acpi_spcr(int devtype, struct uart_devinfo *di) { vm_paddr_t spcr_physaddr; ACPI_TABLE_SPCR *spcr; - struct acpi_uart_compat_data *cd; + struct acpi_spcr_compat_data *cd; struct uart_class *class; int error = ENXIO; @@ -237,7 +229,7 @@ uart_cpu_acpi_dbg2(struct uart_devinfo *di) ACPI_TABLE_DBG2 *dbg2; ACPI_DBG2_DEVICE *dbg2_dev; ACPI_GENERIC_ADDRESS *base_address; - struct acpi_uart_compat_data *cd; + struct acpi_spcr_compat_data *cd; struct uart_class *class; int error; bool found; diff --git a/sys/dev/uart/uart_cpu_acpi.h b/sys/dev/uart/uart_cpu_acpi.h index 94329e1f1349..218f643c7621 100644 --- a/sys/dev/uart/uart_cpu_acpi.h +++ b/sys/dev/uart/uart_cpu_acpi.h @@ -35,11 +35,18 @@ struct uart_class; +struct acpi_spcr_compat_data { + struct uart_class *cd_class; + uint16_t cd_port_subtype; +}; +SET_DECLARE(uart_acpi_spcr_class_set, struct acpi_spcr_compat_data); +#define UART_ACPI_SPCR_CLASS(data) \ + DATA_SET(uart_acpi_spcr_class_set, data) + struct acpi_uart_compat_data { const char *cd_hid; struct uart_class *cd_class; - uint16_t cd_port_subtype; int cd_regshft; int cd_regiowidth; int cd_rclk; @@ -56,14 +63,6 @@ SET_DECLARE(uart_acpi_class_and_device_set, struct acpi_uart_compat_data); #define UART_ACPI_CLASS_AND_DEVICE(data) \ DATA_SET(uart_acpi_class_and_device_set, data) -/* - * If your UART driver implements uart_class and custom device layer, - * then use UART_ACPI_CLASS for its declaration - */ -SET_DECLARE(uart_acpi_class_set, struct acpi_uart_compat_data); -#define UART_ACPI_CLASS(data) \ - DATA_SET(uart_acpi_class_set, data) - /* Try to initialize UART device from ACPI tables */ int uart_cpu_acpi_setup(int devtype, struct uart_devinfo *di); diff --git a/sys/dev/uart/uart_dev_ns8250.c b/sys/dev/uart/uart_dev_ns8250.c index 0f19ede6d9df..c38d50e54ad8 100644 --- a/sys/dev/uart/uart_dev_ns8250.c +++ b/sys/dev/uart/uart_dev_ns8250.c @@ -492,24 +492,32 @@ UART_CLASS(uart_ns8250_class); * XXX -- refactor out ACPI and FDT ifdefs */ #ifdef DEV_ACPI +static struct acpi_spcr_compat_data acpi_spcr_compat_data[] = { + { &uart_ns8250_class, ACPI_DBG2_16550_COMPATIBLE }, + { &uart_ns8250_class, ACPI_DBG2_16550_SUBSET }, + { &uart_ns8250_class, ACPI_DBG2_16550_WITH_GAS }, + { NULL, 0 }, +}; +UART_ACPI_SPCR_CLASS(acpi_spcr_compat_data); + static struct acpi_uart_compat_data acpi_compat_data[] = { - {"AMD0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, - {"AMDI0020", &uart_ns8250_class, 0, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, - {"APMC0D08", &uart_ns8250_class, ACPI_DBG2_16550_COMPATIBLE, 2, 4, 0, 0, "APM compatible UART"}, - {"MRVL0001", &uart_ns8250_class, ACPI_DBG2_16550_SUBSET, 2, 0, 200000000, UART_F_BUSY_DETECT, "Marvell / Synopsys Designware UART"}, - {"SCX0006", &uart_ns8250_class, 0, 2, 0, 62500000, UART_F_BUSY_DETECT, "SynQuacer / Synopsys Designware UART"}, - {"HISI0031", &uart_ns8250_class, 0, 2, 0, 200000000, UART_F_BUSY_DETECT, "HiSilicon / Synopsys Designware UART"}, - {"INTC1006", &uart_ns8250_class, 0, 2, 0, 25000000, 0, "Intel ARM64 UART"}, - {"NXP0018", &uart_ns8250_class, 0, 0, 0, 350000000, UART_F_BUSY_DETECT, "NXP / Synopsys Designware UART"}, - {"PNP0500", &uart_ns8250_class, 0, 0, 0, 0, 0, "Standard PC COM port"}, - {"PNP0501", &uart_ns8250_class, 0, 0, 0, 0, 0, "16550A-compatible COM port"}, - {"PNP0502", &uart_ns8250_class, 0, 0, 0, 0, 0, "Multiport serial device (non-intelligent 16550)"}, - {"PNP0510", &uart_ns8250_class, 0, 0, 0, 0, 0, "Generic IRDA-compatible device"}, - {"PNP0511", &uart_ns8250_class, 0, 0, 0, 0, 0, "Generic IRDA-compatible device"}, - {"WACF004", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet PC Screen"}, - {"WACF00E", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet PC Screen 00e"}, - {"FUJ02E5", &uart_ns8250_class, 0, 0, 0, 0, 0, "Wacom Tablet at FuS Lifebook T"}, - {NULL, NULL, 0, 0 , 0, 0, 0, NULL}, + {"AMD0020", &uart_ns8250_class, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, + {"AMDI0020", &uart_ns8250_class, 2, 0, 48000000, UART_F_BUSY_DETECT, "AMD / Synopsys Designware UART"}, + {"APMC0D08", &uart_ns8250_class, 2, 4, 0, 0, "APM compatible UART"}, + {"MRVL0001", &uart_ns8250_class, 2, 0, 200000000, UART_F_BUSY_DETECT, "Marvell / Synopsys Designware UART"}, + {"SCX0006", &uart_ns8250_class, 2, 0, 62500000, UART_F_BUSY_DETECT, "SynQuacer / Synopsys Designware UART"}, + {"HISI0031", &uart_ns8250_class, 2, 0, 200000000, UART_F_BUSY_DETECT, "HiSilicon / Synopsys Designware UART"}, + {"INTC1006", &uart_ns8250_class, 2, 0, 25000000, 0, "Intel ARM64 UART"}, + {"NXP0018", &uart_ns8250_class, 0, 0, 350000000, UART_F_BUSY_DETECT, "NXP / Synopsys Designware UART"}, + {"PNP0500", &uart_ns8250_class, 0, 0, 0, 0, "Standard PC COM port"}, + {"PNP0501", &uart_ns8250_class, 0, 0, 0, 0, "16550A-compatible COM port"}, + {"PNP0502", &uart_ns8250_class, 0, 0, 0, 0, "Multiport serial device (non-intelligent 16550)"}, + {"PNP0510", &uart_ns8250_class, 0, 0, 0, 0, "Generic IRDA-compatible device"}, + {"PNP0511", &uart_ns8250_class, 0, 0, 0, 0, "Generic IRDA-compatible device"}, + {"WACF004", &uart_ns8250_class, 0, 0, 0, 0, "Wacom Tablet PC Screen"}, + {"WACF00E", &uart_ns8250_class, 0, 0, 0, 0, "Wacom Tablet PC Screen 00e"}, + {"FUJ02E5", &uart_ns8250_class, 0, 0, 0, 0, "Wacom Tablet at FuS Lifebook T"}, + {NULL, NULL, 0 , 0, 0, 0, NULL}, }; UART_ACPI_CLASS_AND_DEVICE(acpi_compat_data); #endif diff --git a/sys/dev/uart/uart_dev_pl011.c b/sys/dev/uart/uart_dev_pl011.c index a0d5a5b1c7e2..6afc693cd347 100644 --- a/sys/dev/uart/uart_dev_pl011.c +++ b/sys/dev/uart/uart_dev_pl011.c @@ -391,11 +391,19 @@ UART_FDT_CLASS_AND_DEVICE(fdt_compat_data); #endif #ifdef DEV_ACPI +static struct acpi_spcr_compat_data acpi_spcr_compat_data[] = { + { &uart_pl011_class, ACPI_DBG2_ARM_PL011 }, + { &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC }, + { &uart_pl011_class, ACPI_DBG2_ARM_SBSA_32BIT }, + { NULL, 0 }, +}; +UART_ACPI_SPCR_CLASS(acpi_spcr_compat_data); + static struct acpi_uart_compat_data acpi_compat_data[] = { - {"ARMH0011", &uart_pl011_class, ACPI_DBG2_ARM_PL011, 2, 0, 0, 0, "uart pl011"}, - {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_GENERIC, 2, 0, 0, 0, "uart pl011"}, - {"ARMHB000", &uart_pl011_class, ACPI_DBG2_ARM_SBSA_32BIT, 2, 0, 0, 0, "uart pl011"}, - {NULL, NULL, 0, 0, 0, 0, 0, NULL}, + {"ARMH0011", &uart_pl011_class, 2, 0, 0, 0, "uart pl011"}, + {"ARMHB000", &uart_pl011_class, 2, 0, 0, 0, "uart pl011"}, + {"ARMHB000", &uart_pl011_class, 2, 0, 0, 0, "uart pl011"}, + {NULL, NULL, 0, 0, 0, 0, NULL}, }; UART_ACPI_CLASS_AND_DEVICE(acpi_compat_data); #endif diff --git a/sys/dev/usb/misc/cp2112.c b/sys/dev/usb/misc/cp2112.c index d4776ca342cb..201a3ec51ce4 100644 --- a/sys/dev/usb/misc/cp2112.c +++ b/sys/dev/usb/misc/cp2112.c @@ -708,11 +708,12 @@ cp2112gpio_attach(device_t dev) } } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { - device_printf(dev, "gpiobus_attach_bus failed\n"); + device_printf(dev, "gpiobus_add_bus failed\n"); goto detach; } + bus_attach_children(dev); return (0); detach: diff --git a/sys/dev/usb/net/if_ipheth.c b/sys/dev/usb/net/if_ipheth.c index f70113c53eb4..cfa800707391 100644 --- a/sys/dev/usb/net/if_ipheth.c +++ b/sys/dev/usb/net/if_ipheth.c @@ -55,6 +55,7 @@ #include <net/if_var.h> #include <dev/usb/usb.h> +#include <dev/usb/usb_cdc.h> #include <dev/usb/usbdi.h> #include <dev/usb/usbdi_util.h> #include "usbdevs.h" @@ -81,6 +82,9 @@ static uether_fn_t ipheth_start; static uether_fn_t ipheth_setmulti; static uether_fn_t ipheth_setpromisc; +static ipheth_consumer_t ipheth_consume_read; +static ipheth_consumer_t ipheth_consume_read_ncm; + #ifdef USB_DEBUG static int ipheth_debug = 0; @@ -96,7 +100,31 @@ static const struct usb_config ipheth_config[IPHETH_N_TRANSFER] = { .direction = UE_DIR_RX, .frames = IPHETH_RX_FRAMES_MAX, .bufsize = (IPHETH_RX_FRAMES_MAX * MCLBYTES), - .flags = {.short_frames_ok = 1,.short_xfer_ok = 1,.ext_buffer = 1,}, + .flags = {.short_frames_ok = 1, .short_xfer_ok = 1, .ext_buffer = 1,}, + .callback = ipheth_bulk_read_callback, + .timeout = 0, /* no timeout */ + }, + + [IPHETH_BULK_TX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_TX, + .frames = IPHETH_TX_FRAMES_MAX, + .bufsize = (IPHETH_TX_FRAMES_MAX * IPHETH_BUF_SIZE), + .flags = {.force_short_xfer = 1,}, + .callback = ipheth_bulk_write_callback, + .timeout = IPHETH_TX_TIMEOUT, + }, +}; + +static const struct usb_config ipheth_config_ncm[IPHETH_N_TRANSFER] = { + [IPHETH_BULK_RX] = { + .type = UE_BULK, + .endpoint = UE_ADDR_ANY, + .direction = UE_DIR_RX, + .frames = 1, + .bufsize = IPHETH_RX_NCM_BUF_SIZE, + .flags = {.short_frames_ok = 1, .short_xfer_ok = 1,}, .callback = ipheth_bulk_read_callback, .timeout = 0, /* no timeout */ }, @@ -204,6 +232,21 @@ ipheth_get_mac_addr(struct ipheth_softc *sc) return (0); } +static bool +ipheth_enable_ncm(struct ipheth_softc *sc) +{ + struct usb_device_request req; + + req.bmRequestType = UT_WRITE_VENDOR_INTERFACE; + req.bRequest = IPHETH_CMD_ENABLE_NCM; + USETW(req.wValue, 0); + req.wIndex[0] = sc->sc_iface_no; + req.wIndex[1] = 0; + USETW(req.wLength, 0); + + return (usbd_do_request(sc->sc_ue.ue_udev, NULL, &req, NULL) == 0); +} + static int ipheth_probe(device_t dev) { @@ -221,6 +264,7 @@ ipheth_attach(device_t dev) struct ipheth_softc *sc = device_get_softc(dev); struct usb_ether *ue = &sc->sc_ue; struct usb_attach_arg *uaa = device_get_ivars(dev); + const struct usb_config *config; int error; sc->sc_iface_no = uaa->info.bIfaceIndex; @@ -235,18 +279,29 @@ ipheth_attach(device_t dev) device_printf(dev, "Cannot set alternate setting\n"); goto detach; } - error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, - sc->sc_xfer, ipheth_config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); - if (error) { - device_printf(dev, "Cannot setup USB transfers\n"); - goto detach; - } + ue->ue_sc = sc; ue->ue_dev = dev; ue->ue_udev = uaa->device; ue->ue_mtx = &sc->sc_mtx; ue->ue_methods = &ipheth_ue_methods; + if (ipheth_enable_ncm(sc)) { + config = ipheth_config_ncm; + sc->is_ncm = true; + sc->consume = &ipheth_consume_read_ncm; + } else { + config = ipheth_config; + sc->consume = &ipheth_consume_read; + } + + error = usbd_transfer_setup(uaa->device, &sc->sc_iface_no, sc->sc_xfer, + config, IPHETH_N_TRANSFER, sc, &sc->sc_mtx); + if (error) { + device_printf(dev, "Cannot setup USB transfers\n"); + goto detach; + } + error = ipheth_get_mac_addr(sc); if (error) { device_printf(dev, "Cannot get MAC address\n"); @@ -389,12 +444,9 @@ ipheth_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error) int actlen; int aframes; - usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); - - DPRINTFN(1, "\n"); - switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); DPRINTFN(11, "transfer complete: %u bytes in %u frames\n", actlen, aframes); @@ -471,53 +523,40 @@ ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) uint8_t x; int actlen; int aframes; - int len; - - usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); switch (USB_GET_STATE(xfer)) { case USB_ST_TRANSFERRED: - + usbd_xfer_status(xfer, &actlen, NULL, &aframes, NULL); DPRINTF("received %u bytes in %u frames\n", actlen, aframes); - for (x = 0; x != aframes; x++) { - m = sc->sc_rx_buf[x]; - sc->sc_rx_buf[x] = NULL; - len = usbd_xfer_frame_len(xfer, x); - - if (len < (int)(sizeof(struct ether_header) + - IPHETH_RX_ADJ)) { - m_freem(m); - continue; - } - - m_adj(m, IPHETH_RX_ADJ); - - /* queue up mbuf */ - uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); - } + for (x = 0; x != aframes; x++) + sc->consume(xfer, x); /* FALLTHROUGH */ case USB_ST_SETUP: - - for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { - if (sc->sc_rx_buf[x] == NULL) { - m = uether_newbuf(); - if (m == NULL) - goto tr_stall; - - /* cancel alignment for ethernet */ - m_adj(m, ETHER_ALIGN); - - sc->sc_rx_buf[x] = m; - } else { - m = sc->sc_rx_buf[x]; + if (!sc->is_ncm) { + for (x = 0; x != IPHETH_RX_FRAMES_MAX; x++) { + if (sc->sc_rx_buf[x] == NULL) { + m = uether_newbuf(); + if (m == NULL) + goto tr_stall; + + /* cancel alignment for ethernet */ + m_adj(m, ETHER_ALIGN); + + sc->sc_rx_buf[x] = m; + } else { + m = sc->sc_rx_buf[x]; + } + usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); } - - usbd_xfer_set_frame_data(xfer, x, m->m_data, m->m_len); + usbd_xfer_set_frames(xfer, x); + } else { + usbd_xfer_set_frame_len(xfer, 0, + IPHETH_RX_NCM_BUF_SIZE); + usbd_xfer_set_frames(xfer, 1); } - /* set number of frames and start hardware */ - usbd_xfer_set_frames(xfer, x); + usbd_transfer_submit(xfer); /* flush any received frames */ uether_rxflush(&sc->sc_ue); @@ -539,3 +578,86 @@ ipheth_bulk_read_callback(struct usb_xfer *xfer, usb_error_t error) break; } } + +static void +ipheth_consume_read(struct usb_xfer *xfer, int x) +{ + struct ipheth_softc *sc = usbd_xfer_softc(xfer); + struct mbuf *m = sc->sc_rx_buf[x]; + int len; + + sc->sc_rx_buf[x] = NULL; + len = usbd_xfer_frame_len(xfer, x); + + if (len < (int)(sizeof(struct ether_header) + IPHETH_RX_ADJ)) { + m_freem(m); + return; + } + + m_adj(m, IPHETH_RX_ADJ); + + /* queue up mbuf */ + uether_rxmbuf(&sc->sc_ue, m, len - IPHETH_RX_ADJ); +} + +static void +ipheth_consume_read_ncm(struct usb_xfer *xfer, int x) +{ + struct ipheth_softc *sc = usbd_xfer_softc(xfer); + struct usb_page_cache *pc = usbd_xfer_get_frame(xfer, 0); + struct ncm_data_cache ncm; + if_t ifp = uether_getifp(&sc->sc_ue); + struct mbuf *new_buf; + int i, actlen; + uint16_t dp_offset, dp_len; + + usbd_xfer_status(xfer, &actlen, NULL, NULL, NULL); + + if (actlen < IPHETH_NCM_HEADER_SIZE) + return; + + usbd_copy_out(pc, 0, &ncm.hdr, sizeof(ncm.hdr)); + + if (UGETDW(ncm.hdr.dwSignature) != 0x484D434E) + return; + + /* Dpt follows the hdr on iOS */ + if (UGETW(ncm.hdr.wDptIndex) != (int)(sizeof(struct usb_ncm16_hdr))) + return; + + usbd_copy_out(pc, UGETW(ncm.hdr.wDptIndex), &ncm.dpt, sizeof(ncm.dpt)); + + if (UGETDW(ncm.dpt.dwSignature) != 0x304D434E) + return; + + usbd_copy_out(pc, UGETW(ncm.hdr.wDptIndex) + sizeof(ncm.dpt), &ncm.dp, + sizeof(ncm.dp)); + + for (i = 0; i < IPHETH_NCM_DPT_DP_NUM; ++i) { + dp_offset = UGETW(ncm.dp[i].wFrameIndex); + dp_len = UGETW(ncm.dp[i].wFrameLength); + + /* (3.3.1 USB CDC NCM spec v1.0) */ + if (dp_offset == 0 && dp_len == 0) + break; + + if (dp_offset < IPHETH_NCM_HEADER_SIZE || dp_offset >= actlen || + actlen < (dp_len + dp_offset) || + dp_len < sizeof(struct ether_header)) { + if_inc_counter(ifp, IFCOUNTER_IERRORS, 1); + continue; + } + if (dp_len > (MCLBYTES - ETHER_ALIGN)) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + continue; + } + + new_buf = uether_newbuf(); + if (new_buf == NULL) { + if_inc_counter(ifp, IFCOUNTER_IQDROPS, 1); + continue; + } + usbd_copy_out(pc, dp_offset, new_buf->m_data, dp_len); + uether_rxmbuf(&sc->sc_ue, new_buf, dp_len); + } +} diff --git a/sys/dev/usb/net/if_iphethvar.h b/sys/dev/usb/net/if_iphethvar.h index 203bb96b6f22..d637e8f67d01 100644 --- a/sys/dev/usb/net/if_iphethvar.h +++ b/sys/dev/usb/net/if_iphethvar.h @@ -41,6 +41,7 @@ #define IPHETH_BUF_SIZE 1514 #define IPHETH_TX_TIMEOUT 5000 /* ms */ +#define IPHETH_RX_NCM_BUF_SIZE 65536 #define IPHETH_RX_FRAMES_MAX 1 #define IPHETH_TX_FRAMES_MAX 8 @@ -55,10 +56,20 @@ #define IPHETH_CTRL_TIMEOUT 5000 /* ms */ #define IPHETH_CMD_GET_MACADDR 0x00 +#define IPHETH_CMD_ENABLE_NCM 0x04 #define IPHETH_CMD_CARRIER_CHECK 0x45 #define IPHETH_CARRIER_ON 0x04 +#define IPHETH_NCM_DPT_DP_NUM 22 +#define IPHETH_NCM_DPT_HEADER_SIZE \ + (sizeof(struct usb_ncm16_dpt) + \ + IPHETH_NCM_DPT_DP_NUM * sizeof(struct usb_ncm16_dp)) +#define IPHETH_NCM_HEADER_SIZE \ + (sizeof(struct usb_ncm16_hdr) + IPHETH_NCM_DPT_HEADER_SIZE) + +typedef void (ipheth_consumer_t)(struct usb_xfer *xfer, int idx); + enum { IPHETH_BULK_TX, IPHETH_BULK_RX, @@ -76,6 +87,16 @@ struct ipheth_softc { uint8_t sc_data[IPHETH_CTRL_BUF_SIZE]; uint8_t sc_iface_no; uint8_t sc_carrier_on; + + bool is_ncm; + + ipheth_consumer_t *consume; +}; + +struct ncm_data_cache { + struct usb_ncm16_hdr hdr; + struct usb_ncm16_dpt dpt; + struct usb_ncm16_dp dp[IPHETH_NCM_DPT_DP_NUM]; }; #define IPHETH_LOCK(_sc) mtx_lock(&(_sc)->sc_mtx) diff --git a/sys/dev/watchdog/watchdog.c b/sys/dev/watchdog/watchdog.c index e6b6dc1eac70..e1b2e08c3f10 100644 --- a/sys/dev/watchdog/watchdog.c +++ b/sys/dev/watchdog/watchdog.c @@ -50,11 +50,20 @@ #include <sys/syscallsubr.h> /* kern_clock_gettime() */ -static int wd_set_pretimeout(int newtimeout, int disableiftoolong); +#ifdef COMPAT_FREEBSD14 +#define WDIOCPATPAT_14 _IOW('W', 42, u_int) /* pat the watchdog */ +#define WDIOC_SETTIMEOUT_14 _IOW('W', 43, int) /* set/reset the timer */ +#define WDIOC_GETTIMEOUT_14 _IOR('W', 44, int) /* get total timeout */ +#define WDIOC_GETTIMELEFT_14 _IOR('W', 45, int) /* get time left */ +#define WDIOC_GETPRETIMEOUT_14 _IOR('W', 46, int) /* get the pre-timeout */ +#define WDIOC_SETPRETIMEOUT_14 _IOW('W', 47, int) /* set the pre-timeout */ +#endif + +static int wd_set_pretimeout(sbintime_t newtimeout, int disableiftoolong); static void wd_timeout_cb(void *arg); static struct callout wd_pretimeo_handle; -static int wd_pretimeout; +static sbintime_t wd_pretimeout; static int wd_pretimeout_act = WD_SOFT_LOG; static struct callout wd_softtimeo_handle; @@ -63,6 +72,8 @@ static int wd_softtimer; /* true = use softtimer instead of hardware static int wd_softtimeout_act = WD_SOFT_LOG; /* action for the software timeout */ static struct cdev *wd_dev; +static volatile sbintime_t wd_last_sbt; /* last timeout value (sbt) */ +static sbintime_t wd_last_sbt_sysctl; /* last timeout value (sbt) */ static volatile u_int wd_last_u; /* last timeout value set by kern_do_pat */ static u_int wd_last_u_sysctl; /* last timeout value set by kern_do_pat */ static u_int wd_last_u_sysctl_secs; /* wd_last_u in seconds */ @@ -73,6 +84,8 @@ SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u, CTLFLAG_RD, &wd_last_u_sysctl, 0, "Watchdog last update time"); SYSCTL_UINT(_hw_watchdog, OID_AUTO, wd_last_u_secs, CTLFLAG_RD, &wd_last_u_sysctl_secs, 0, "Watchdog last update time"); +SYSCTL_SBINTIME_MSEC(_hw_watchdog, OID_AUTO, wd_last_msecs, CTLFLAG_RD, + &wd_last_sbt_sysctl, "Watchdog last update time (milliseconds)"); static int wd_lastpat_valid = 0; static time_t wd_lastpat = 0; /* when the watchdog was last patted */ @@ -80,105 +93,94 @@ static time_t wd_lastpat = 0; /* when the watchdog was last patted */ /* Hook for external software watchdog to register for use if needed */ void (*wdog_software_attach)(void); -static void -pow2ns_to_ts(int pow2ns, struct timespec *ts) +/* Legacy interface to watchdog. */ +int +wdog_kern_pat(u_int utim) { - uint64_t ns; + sbintime_t sbt; - ns = 1ULL << pow2ns; - ts->tv_sec = ns / 1000000000ULL; - ts->tv_nsec = ns % 1000000000ULL; -} + if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0) + return (EINVAL); -static int -pow2ns_to_ticks(int pow2ns) -{ - struct timeval tv; - struct timespec ts; + if ((utim & WD_LASTVAL) != 0) { + return (wdog_control(WD_CTRL_RESET)); + } - pow2ns_to_ts(pow2ns, &ts); - TIMESPEC_TO_TIMEVAL(&tv, &ts); - return (tvtohz(&tv)); + utim &= WD_INTERVAL; + if (utim == WD_TO_NEVER) + sbt = 0; + else + sbt = nstosbt(1 << utim); + + return (wdog_kern_pat_sbt(sbt)); } -static int -seconds_to_pow2ns(int seconds) +int +wdog_control(int ctrl) { - uint64_t power; - uint64_t ns; - uint64_t shifted; - - ns = ((uint64_t)seconds) * 1000000000ULL; - power = flsll(ns); - shifted = 1ULL << power; - if (shifted <= ns) { - power++; + /* Disable takes precedence */ + if (ctrl == WD_CTRL_DISABLE) { + wdog_kern_pat(0); } - return (power); + + if ((ctrl & WD_CTRL_RESET) != 0) { + wdog_kern_pat_sbt(wd_last_sbt); + } else if ((ctrl & WD_CTRL_ENABLE) != 0) { + wdog_kern_pat_sbt(wd_last_sbt); + } + + return (0); } int -wdog_kern_pat(u_int utim) +wdog_kern_pat_sbt(sbintime_t sbt) { - int error; - static int first = 1; - - if ((utim & WD_LASTVAL) != 0 && (utim & WD_INTERVAL) > 0) - return (EINVAL); - - if ((utim & WD_LASTVAL) != 0) { - /* - * if WD_LASTVAL is set, fill in the bits for timeout - * from the saved value in wd_last_u. - */ - MPASS((wd_last_u & ~WD_INTERVAL) == 0); - utim &= ~WD_LASTVAL; - utim |= wd_last_u; - } else { - /* - * Otherwise save the new interval. - * This can be zero (to disable the watchdog) - */ - wd_last_u = (utim & WD_INTERVAL); + sbintime_t error_sbt = 0; + int pow2ns = 0; + int error = 0; + static bool first = true; + + /* legacy uses power-of-2-nanoseconds time. */ + if (sbt != 0) { + pow2ns = flsl(sbttons(sbt)); + } + if (wd_last_sbt != sbt) { + wd_last_u = pow2ns; wd_last_u_sysctl = wd_last_u; - wd_last_u_sysctl_secs = pow2ns_to_ticks(wd_last_u) / hz; + wd_last_u_sysctl_secs = sbt / SBT_1S; + + wd_last_sbt = sbt; } - if ((utim & WD_INTERVAL) == WD_TO_NEVER) { - utim = 0; - /* Assume all is well; watchdog signals failure. */ - error = 0; - } else { - /* Assume no watchdog available; watchdog flags success */ + if (sbt != 0) error = EOPNOTSUPP; - } + if (wd_softtimer) { - if (utim == 0) { + if (sbt == 0) { callout_stop(&wd_softtimeo_handle); } else { - (void) callout_reset(&wd_softtimeo_handle, - pow2ns_to_ticks(utim), wd_timeout_cb, "soft"); + (void) callout_reset_sbt(&wd_softtimeo_handle, + sbt, 0, wd_timeout_cb, "soft", 0); } error = 0; } else { - EVENTHANDLER_INVOKE(watchdog_list, utim, &error); + EVENTHANDLER_INVOKE(watchdog_sbt_list, sbt, &error_sbt, &error); + EVENTHANDLER_INVOKE(watchdog_list, pow2ns, &error); } /* - * If we no hardware watchdog responded, we have not tried to + * If no hardware watchdog responded, we have not tried to * attach an external software watchdog, and one is available, * attach it now and retry. */ - if (error == EOPNOTSUPP && first && *wdog_software_attach != NULL) { + if (error == EOPNOTSUPP && first && wdog_software_attach != NULL) { (*wdog_software_attach)(); - EVENTHANDLER_INVOKE(watchdog_list, utim, &error); + EVENTHANDLER_INVOKE(watchdog_sbt_list, sbt, &error_sbt, &error); + EVENTHANDLER_INVOKE(watchdog_list, pow2ns, &error); } - first = 0; + first = false; + /* TODO: Print a (rate limited?) warning if error_sbt is too far away */ wd_set_pretimeout(wd_pretimeout, true); - /* - * If we were able to arm/strobe the watchdog, then - * update the last time it was strobed for WDIOC_GETTIMELEFT - */ if (!error) { struct timespec ts; @@ -189,6 +191,7 @@ wdog_kern_pat(u_int utim) wd_lastpat_valid = 1; } } + return (error); } @@ -265,16 +268,14 @@ wd_timeout_cb(void *arg) * current actual watchdog timeout. */ static int -wd_set_pretimeout(int newtimeout, int disableiftoolong) +wd_set_pretimeout(sbintime_t newtimeout, int disableiftoolong) { - u_int utime; - struct timespec utime_ts; - int timeout_ticks; + sbintime_t utime; + sbintime_t timeout_left; - utime = wdog_kern_last_timeout(); - pow2ns_to_ts(utime, &utime_ts); + utime = wdog_kern_last_timeout_sbt(); /* do not permit a pre-timeout >= than the timeout. */ - if (newtimeout >= utime_ts.tv_sec) { + if (newtimeout >= utime) { /* * If 'disableiftoolong' then just fall through * so as to disable the pre-watchdog @@ -292,7 +293,7 @@ wd_set_pretimeout(int newtimeout, int disableiftoolong) return 0; } - timeout_ticks = pow2ns_to_ticks(utime) - (hz*newtimeout); + timeout_left = utime - newtimeout; #if 0 printf("wd_set_pretimeout: " "newtimeout: %d, " @@ -306,8 +307,8 @@ wd_set_pretimeout(int newtimeout, int disableiftoolong) #endif /* We determined the value is sane, so reset the callout */ - (void) callout_reset(&wd_pretimeo_handle, - timeout_ticks, wd_timeout_cb, "pre"); + (void) callout_reset_sbt(&wd_pretimeo_handle, + timeout_left, 0, wd_timeout_cb, "pre", 0); wd_pretimeout = newtimeout; return 0; } @@ -316,6 +317,7 @@ static int wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, int flags __unused, struct thread *td) { + sbintime_t sb; u_int u; time_t timeleft; int error; @@ -351,29 +353,55 @@ wd_ioctl(struct cdev *dev __unused, u_long cmd, caddr_t data, error = EINVAL; } break; - case WDIOC_GETPRETIMEOUT: - *(int *)data = (int)wd_pretimeout; +#ifdef COMPAT_FREEBSD14 + case WDIOC_GETPRETIMEOUT_14: + *(int *)data = (int)(wd_pretimeout / SBT_1S); break; - case WDIOC_SETPRETIMEOUT: - error = wd_set_pretimeout(*(int *)data, false); + case WDIOC_SETPRETIMEOUT_14: + error = wd_set_pretimeout(*(int *)data * SBT_1S, false); break; - case WDIOC_GETTIMELEFT: + case WDIOC_GETTIMELEFT_14: error = wd_get_time_left(td, &timeleft); if (error) break; *(int *)data = (int)timeleft; break; - case WDIOC_SETTIMEOUT: + case WDIOC_SETTIMEOUT_14: u = *(u_int *)data; - error = wdog_kern_pat(seconds_to_pow2ns(u)); + error = wdog_kern_pat_sbt(mstosbt(u * 1000ULL)); break; - case WDIOC_GETTIMEOUT: + case WDIOC_GETTIMEOUT_14: u = wdog_kern_last_timeout(); *(u_int *)data = u; break; - case WDIOCPATPAT: + case WDIOCPATPAT_14: error = wd_ioctl_patpat(data); break; +#endif + + /* New API */ + case WDIOC_CONTROL: + wdog_control(*(int *)data); + break; + case WDIOC_SETTIMEOUT: + sb = *(sbintime_t *)data; + error = wdog_kern_pat_sbt(sb); + break; + case WDIOC_GETTIMEOUT: + *(sbintime_t *)data = wdog_kern_last_timeout_sbt(); + break; + case WDIOC_GETTIMELEFT: + error = wd_get_time_left(td, &timeleft); + if (error) + break; + *(sbintime_t *)data = (sbintime_t)timeleft * SBT_1S; + break; + case WDIOC_GETPRETIMEOUT: + *(sbintime_t *)data = wd_pretimeout; + break; + case WDIOC_SETPRETIMEOUT: + error = wd_set_pretimeout(*(sbintime_t *)data, false); + break; default: error = ENOIOCTL; break; @@ -392,6 +420,12 @@ wdog_kern_last_timeout(void) return (wd_last_u); } +sbintime_t +wdog_kern_last_timeout_sbt(void) +{ + return (wd_last_sbt); +} + static struct cdevsw wd_cdevsw = { .d_version = D_VERSION, .d_ioctl = wd_ioctl, diff --git a/sys/fs/nfs/nfs_commonkrpc.c b/sys/fs/nfs/nfs_commonkrpc.c index 0ae3b94bef89..1e4e8506790f 100644 --- a/sys/fs/nfs/nfs_commonkrpc.c +++ b/sys/fs/nfs/nfs_commonkrpc.c @@ -239,6 +239,7 @@ static bool nfscl_use_gss[NFSV42_NPROCS] = { true, true, true, + true, }; /* diff --git a/sys/fs/nfs/nfs_commonsubs.c b/sys/fs/nfs/nfs_commonsubs.c index 67e33193ecec..7f5b29ca2085 100644 --- a/sys/fs/nfs/nfs_commonsubs.c +++ b/sys/fs/nfs/nfs_commonsubs.c @@ -187,7 +187,7 @@ struct nfsv4_opflag nfsv4_opflag[NFSV42_NOPS] = { { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Read Plus */ { 0, 1, 0, 0, LK_SHARED, 1, 0 }, /* Seek */ { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Write Same */ - { 0, 0, 0, 0, LK_EXCLUSIVE, 1, 1 }, /* Clone */ + { 2, 1, 1, 0, LK_SHARED, 1, 0 }, /* Clone */ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Getxattr */ { 0, 1, 1, 1, LK_EXCLUSIVE, 1, 1 }, /* Setxattr */ { 0, 1, 0, 0, LK_SHARED, 1, 1 }, /* Listxattrs */ @@ -219,7 +219,7 @@ NFSD_VNET_DEFINE_STATIC(u_char *, nfsrv_dnsname) = NULL; static int nfs_bigreply[NFSV42_NPROCS] = { 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 0, 0, 1, 0, 0, 0, 0, 0, 0 }; + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0 }; /* local functions */ static int nfsrv_skipace(struct nfsrv_descript *nd, int *acesizep); @@ -310,6 +310,7 @@ static struct { { NFSV4OP_LAYOUTERROR, 1, "LayoutError", 11, }, { NFSV4OP_VERIFY, 3, "AppendWrite", 11, }, { NFSV4OP_OPENATTR, 3, "OpenAttr", 8, }, + { NFSV4OP_SAVEFH, 5, "Clone", 5, }, }; /* @@ -319,7 +320,7 @@ static int nfs_bigrequest[NFSV42_NPROCS] = { 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, - 0, 1, 0 + 0, 1, 0, 0 }; /* @@ -648,7 +649,7 @@ nfscl_fillsattr(struct nfsrv_descript *nd, struct vattr *vap, NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_TIMECREATE); (void) nfsv4_fillattr(nd, vp->v_mount, vp, NULL, vap, NULL, 0, &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL, - false, false, false); + false, false, false, 0); break; } } @@ -1302,7 +1303,7 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp, struct nfsv3_pathconf *pc, struct statfs *sbp, struct nfsstatfs *sfp, struct nfsfsinfo *fsp, NFSACL_T *aclp, int compare, int *retcmpp, u_int32_t *leasep, u_int32_t *rderrp, bool *has_namedattrp, - NFSPROC_T *p, struct ucred *cred) + uint32_t *clone_blksizep, NFSPROC_T *p, struct ucred *cred) { u_int32_t *tl; int i = 0, j, k, l = 0, m, bitpos, attrsum = 0; @@ -1437,6 +1438,13 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp, NFSCLRBIT_ATTRBIT(&checkattrbits, NFSATTRBIT_SYSTEM); } + /* Some filesystems do not support block cloning */ + if (vp == NULL || VOP_PATHCONF(vp, + _PC_CLONE_BLKSIZE, &has_pathconf) != 0) + has_pathconf = 0; + if (has_pathconf == 0) + NFSCLRBIT_ATTRBIT(&checkattrbits, + NFSATTRBIT_CLONEBLKSIZE); if (!NFSEQUAL_ATTRBIT(&retattrbits, &checkattrbits) || retnotsup) *retcmpp = NFSERR_NOTSAME; @@ -2374,6 +2382,23 @@ nfsv4_loadattr(struct nfsrv_descript *nd, vnode_t vp, if (compare && !(*retcmpp) && i != nfs_srvmaxio) *retcmpp = NFSERR_NOTSAME; break; + case NFSATTRBIT_CLONEBLKSIZE: + NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); + if (compare) { + if (!(*retcmpp)) { + if (vp == NULL || VOP_PATHCONF(vp, + _PC_CLONE_BLKSIZE, &has_pathconf) + != 0) + has_pathconf = 0; + if (has_pathconf != + fxdr_unsigned(uint32_t, *tl)) + *retcmpp = NFSERR_NOTSAME; + } + } else if (clone_blksizep != NULL) { + *clone_blksizep = fxdr_unsigned(uint32_t, *tl); + } + attrsum += NFSX_UNSIGNED; + break; case NFSATTRBIT_CHANGEATTRTYPE: NFSM_DISSECT(tl, uint32_t *, NFSX_UNSIGNED); if (compare) { @@ -2648,7 +2673,7 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp, nfsattrbit_t *attrbitp, struct ucred *cred, NFSPROC_T *p, int isdgram, int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno, struct statfs *pnfssf, bool xattrsupp, bool has_hiddensystem, - bool has_namedattr) + bool has_namedattr, uint32_t clone_blksize) { int bitpos, retnum = 0; u_int32_t *tl; @@ -2771,6 +2796,9 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp, NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_HIDDEN); NFSCLRBIT_ATTRBIT(&attrbits, NFSATTRBIT_SYSTEM); } + if (clone_blksize == 0) + NFSCLRBIT_ATTRBIT(&attrbits, + NFSATTRBIT_CLONEBLKSIZE); retnum += nfsrv_putattrbit(nd, &attrbits); break; case NFSATTRBIT_TYPE: @@ -3249,6 +3277,11 @@ nfsv4_fillattr(struct nfsrv_descript *nd, struct mount *mp, vnode_t vp, } retnum += NFSX_UNSIGNED; break; + case NFSATTRBIT_CLONEBLKSIZE: + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(clone_blksize); + retnum += NFSX_UNSIGNED; + break; default: printf("EEK! Bad V4 attribute bitpos=%d\n", bitpos); } diff --git a/sys/fs/nfs/nfs_var.h b/sys/fs/nfs/nfs_var.h index 54f60a753c50..61083ecf2d66 100644 --- a/sys/fs/nfs/nfs_var.h +++ b/sys/fs/nfs/nfs_var.h @@ -286,6 +286,8 @@ int nfsrvd_deallocate(struct nfsrv_descript *, int, vnode_t, struct nfsexstuff *); int nfsrvd_copy_file_range(struct nfsrv_descript *, int, vnode_t, vnode_t, struct nfsexstuff *, struct nfsexstuff *); +int nfsrvd_clone(struct nfsrv_descript *, int, + vnode_t, vnode_t, struct nfsexstuff *, struct nfsexstuff *); int nfsrvd_seek(struct nfsrv_descript *, int, vnode_t, struct nfsexstuff *); int nfsrvd_getxattr(struct nfsrv_descript *, int, @@ -341,7 +343,8 @@ int nfsv4_loadattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, struct nfsfh **, fhandle_t *, int, struct nfsv3_pathconf *, struct statfs *, struct nfsstatfs *, struct nfsfsinfo *, NFSACL_T *, - int, int *, u_int32_t *, u_int32_t *, bool *, NFSPROC_T *, struct ucred *); + int, int *, u_int32_t *, u_int32_t *, bool *, uint32_t *, NFSPROC_T *, + struct ucred *); int nfsv4_lock(struct nfsv4lock *, int, int *, struct mtx *, struct mount *); void nfsv4_unlock(struct nfsv4lock *, int); void nfsv4_relref(struct nfsv4lock *); @@ -397,7 +400,7 @@ void nfsrv_wcc(struct nfsrv_descript *, int, struct nfsvattr *, int, int nfsv4_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, NFSACL_T *, struct vattr *, fhandle_t *, int, nfsattrbit_t *, struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, struct statfs *, bool, bool, - bool); + bool, uint32_t); void nfsrv_fillattr(struct nfsrv_descript *, struct nfsvattr *); struct mbuf *nfsrv_adj(struct mbuf *, int, int); void nfsrv_postopattr(struct nfsrv_descript *, int, struct nfsvattr *); @@ -517,10 +520,10 @@ int nfsrpc_lock(struct nfsrv_descript *, struct nfsmount *, vnode_t, u_int8_t *, int, struct nfscllockowner *, int, int, u_int64_t, u_int64_t, short, struct ucred *, NFSPROC_T *, int); int nfsrpc_statfs(vnode_t, struct nfsstatfs *, struct nfsfsinfo *, uint32_t *, - struct ucred *, NFSPROC_T *, struct nfsvattr *, int *); + uint32_t *, struct ucred *, NFSPROC_T *, struct nfsvattr *, int *); int nfsrpc_fsinfo(vnode_t, struct nfsfsinfo *, struct ucred *, NFSPROC_T *, struct nfsvattr *, int *); -int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *, bool *, +int nfsrpc_pathconf(vnode_t, struct nfsv3_pathconf *, bool *, uint32_t *, struct ucred *, NFSPROC_T *, struct nfsvattr *, int *); int nfsrpc_renew(struct nfsclclient *, struct nfsclds *, struct ucred *, NFSPROC_T *); @@ -562,6 +565,8 @@ int nfsrpc_deallocate(vnode_t, off_t, off_t, struct nfsvattr *, int *, int nfsrpc_copy_file_range(vnode_t, off_t *, vnode_t, off_t *, size_t *, unsigned int, int *, struct nfsvattr *, int *, struct nfsvattr *, struct ucred *, bool, bool *); +int nfsrpc_clone(vnode_t, off_t *, vnode_t, off_t *, size_t *, bool, + int *, struct nfsvattr *, int *, struct nfsvattr *, struct ucred *); int nfsrpc_seek(vnode_t, off_t *, bool *, int, struct ucred *, struct nfsvattr *, int *); int nfsrpc_getextattr(vnode_t, const char *, struct uio *, ssize_t *, @@ -668,7 +673,7 @@ int nfscl_nget(mount_t, vnode_t, struct nfsfh *, NFSPROC_T *nfscl_getparent(NFSPROC_T *); void nfscl_start_renewthread(struct nfsclclient *); void nfscl_loadsbinfo(struct nfsmount *, struct nfsstatfs *, void *); -void nfscl_loadfsinfo (struct nfsmount *, struct nfsfsinfo *); +void nfscl_loadfsinfo(struct nfsmount *, struct nfsfsinfo *, uint32_t); void nfscl_delegreturn(struct nfscldeleg *, int, struct nfsmount *, struct ucred *, NFSPROC_T *); void nfsrvd_cbinit(int); @@ -737,7 +742,7 @@ int nfsvno_updfilerev(vnode_t, struct nfsvattr *, struct nfsrv_descript *, int nfsvno_fillattr(struct nfsrv_descript *, struct mount *, vnode_t, struct nfsvattr *, fhandle_t *, int, nfsattrbit_t *, struct ucred *, NFSPROC_T *, int, int, int, int, uint64_t, bool, bool, - bool); + bool, uint32_t); int nfsrv_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *, NFSACL_T *, NFSPROC_T *); int nfsv4_sattr(struct nfsrv_descript *, vnode_t, struct nfsvattr *, nfsattrbit_t *, diff --git a/sys/fs/nfs/nfsport.h b/sys/fs/nfs/nfsport.h index c30b46261df0..bd6107187966 100644 --- a/sys/fs/nfs/nfsport.h +++ b/sys/fs/nfs/nfsport.h @@ -442,10 +442,13 @@ /* Do a NFSv4 Openattr. */ #define NFSPROC_OPENATTR 70 +/* Do a NFSv4.2 Clone. */ +#define NFSPROC_CLONE 71 + /* * Must be defined as one higher than the last NFSv4.2 Proc# above. */ -#define NFSV42_NPROCS 71 +#define NFSV42_NPROCS 72 /* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */ #define NFSV42_OLDNPROCS 69 @@ -477,7 +480,7 @@ struct nfsstatsv1 { uint64_t readlink_bios; uint64_t biocache_readdirs; uint64_t readdir_bios; - uint64_t rpccnt[NFSV42_NPROCS + 9]; + uint64_t rpccnt[NFSV42_NPROCS + 8]; uint64_t rpcretries; uint64_t srvrpccnt[NFSV42_NOPS + NFSV4OP_FAKENOPS + 15]; uint64_t srvlayouts; diff --git a/sys/fs/nfs/nfsproto.h b/sys/fs/nfs/nfsproto.h index cb5a80e8df73..d628108bdc1a 100644 --- a/sys/fs/nfs/nfsproto.h +++ b/sys/fs/nfs/nfsproto.h @@ -411,10 +411,13 @@ /* Do a NFSv4 Openattr. */ #define NFSPROC_OPENATTR 70 +/* Do a NFSv4.2 Clone. */ +#define NFSPROC_CLONE 71 + /* * Must be defined as one higher than the last NFSv4.2 Proc# above. */ -#define NFSV42_NPROCS 71 +#define NFSV42_NPROCS 72 /* Value of NFSV42_NPROCS for old nfsstats structure. (Always 69) */ #define NFSV42_OLDNPROCS 69 @@ -1194,6 +1197,7 @@ struct nfsv3_sattr { NFSATTRBM_LAYOUTBLKSIZE | \ NFSATTRBM_LAYOUTALIGNMENT | \ NFSATTRBM_SUPPATTREXCLCREAT | \ + NFSATTRBM_CLONEBLKSIZE | \ NFSATTRBM_CHANGEATTRTYPE | \ NFSATTRBM_XATTRSUPPORT) @@ -1242,7 +1246,8 @@ struct nfsv3_sattr { * NFSATTRBIT_NFSV42 - Attributes only supported by NFSv4.2. */ #define NFSATTRBIT_NFSV42_2 \ - (NFSATTRBM_CHANGEATTRTYPE | \ + (NFSATTRBM_CLONEBLKSIZE | \ + NFSATTRBM_CHANGEATTRTYPE | \ NFSATTRBM_XATTRSUPPORT | \ NFSATTRBM_MODEUMASK) @@ -1415,7 +1420,7 @@ struct nfsv3_sattr { /* * NFSGETATTRBIT_STATFS2 - bits 64<->95 */ -#define NFSGETATTRBIT_STATFS2 0 +#define NFSGETATTRBIT_STATFS2 (NFSATTRBM_CLONEBLKSIZE) /* * Set of attributes for the equivalent of an nfsv3 pathconf rpc. @@ -1438,7 +1443,7 @@ struct nfsv3_sattr { /* * NFSGETATTRBIT_PATHCONF2 - bits 64<->95 */ -#define NFSGETATTRBIT_PATHCONF2 0 +#define NFSGETATTRBIT_PATHCONF2 (NFSATTRBM_CLONEBLKSIZE) /* * Sets of attributes required by readdir and readdirplus. diff --git a/sys/fs/nfsclient/nfs_clcomsubs.c b/sys/fs/nfsclient/nfs_clcomsubs.c index bca0bdcd0df1..05963074e53d 100644 --- a/sys/fs/nfsclient/nfs_clcomsubs.c +++ b/sys/fs/nfsclient/nfs_clcomsubs.c @@ -272,7 +272,7 @@ nfsm_loadattr(struct nfsrv_descript *nd, struct nfsvattr *nap) if (nd->nd_flag & ND_NFSV4) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, - NULL); + NULL, NULL); } else if (nd->nd_flag & ND_NFSV3) { NFSM_DISSECT(fp, struct nfs_fattr *, NFSX_V3FATTR); nap->na_type = nfsv34tov_type(fp->fa_type); diff --git a/sys/fs/nfsclient/nfs_clport.c b/sys/fs/nfsclient/nfs_clport.c index b25d967982a1..704aeeeabdf2 100644 --- a/sys/fs/nfsclient/nfs_clport.c +++ b/sys/fs/nfsclient/nfs_clport.c @@ -828,7 +828,7 @@ nfscl_wcc_data(struct nfsrv_descript *nd, struct vnode *vp, == (ND_NFSV4 | ND_V4WCCATTR)) { error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (error) return (error); /* @@ -963,7 +963,8 @@ nfscl_loadsbinfo(struct nfsmount *nmp, struct nfsstatfs *sfp, void *statfs) * Use the fsinfo stuff to update the mount point. */ void -nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp) +nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp, + uint32_t clone_blksize) { if ((nmp->nm_wsize == 0 || fsp->fs_wtpref < nmp->nm_wsize) && @@ -1003,6 +1004,14 @@ nfscl_loadfsinfo(struct nfsmount *nmp, struct nfsfsinfo *fsp) fsp->fs_maxfilesize < nmp->nm_maxfilesize) nmp->nm_maxfilesize = fsp->fs_maxfilesize; nmp->nm_mountp->mnt_stat.f_iosize = newnfs_iosize(nmp); + + /* + * Although ZFS reports a clone_blksize of 16Mbytes, + * 128Kbytes usually works, so set it to that. + */ + if (clone_blksize > 128 * 1024) + clone_blksize = 128 * 1024; + nmp->nm_cloneblksize = clone_blksize; nmp->nm_state |= NFSSTA_GOTFSINFO; } diff --git a/sys/fs/nfsclient/nfs_clrpcops.c b/sys/fs/nfsclient/nfs_clrpcops.c index 920fcf7b8c61..4ec621de2eff 100644 --- a/sys/fs/nfsclient/nfs_clrpcops.c +++ b/sys/fs/nfsclient/nfs_clrpcops.c @@ -225,6 +225,9 @@ static int nfsrpc_layoutgetres(struct nfsmount *, vnode_t, uint8_t *, static int nfsrpc_copyrpc(vnode_t, off_t, vnode_t, off_t, size_t *, nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *, struct nfsvattr *, int *, bool, int *, struct ucred *, NFSPROC_T *); +static int nfsrpc_clonerpc(vnode_t, off_t, vnode_t, off_t, size_t *, bool, + nfsv4stateid_t *, nfsv4stateid_t *, struct nfsvattr *, int *, + struct nfsvattr *, int *, struct ucred *, NFSPROC_T *); static int nfsrpc_seekrpc(vnode_t, off_t *, nfsv4stateid_t *, bool *, int, struct nfsvattr *, int *, struct ucred *); static struct mbuf *nfsm_split(struct mbuf *, uint64_t); @@ -696,7 +699,7 @@ nfsrpc_openrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, int fhlen, ("nfsrpc_openrpc: Getattr repstat")); error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, NULL, NULL, p, cred); + NULL, NULL, NULL, NULL, NULL, p, cred); if (error) goto nfsmout; } @@ -1355,7 +1358,7 @@ nfsrpc_getattrnovp(struct nfsmount *nmp, u_int8_t *fhp, int fhlen, int syscred, if ((nd->nd_flag & ND_NFSV4) != 0) error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, leasep, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); else error = nfsm_loadattr(nd, nap); } else @@ -3597,7 +3600,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, nfsva.na_mntonfileno = UINT64_MAX; error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, NULL, NULL, p, cred); + NULL, NULL, NULL, NULL, NULL, p, cred); if (error) { dotdotfileid = dotfileid; } else if (gotmnton) { @@ -3847,7 +3850,7 @@ nfsrpc_readdir(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, nfsva.na_mntonfileno = UINT64_MAX; error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, &rderr, NULL, p, cred); + NULL, NULL, &rderr, NULL, NULL, p, cred); if (error) goto nfsmout; NFSM_DISSECT(tl, u_int32_t *, NFSX_UNSIGNED); @@ -4072,7 +4075,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, nfsva.na_mntonfileno = UINT64_MAX; error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, NULL, NULL, p, cred); + NULL, NULL, NULL, NULL, NULL, p, cred); if (error) { dotdotfileid = dotfileid; } else if (gotmnton) { @@ -4346,7 +4349,7 @@ nfsrpc_readdirplus(vnode_t vp, struct uio *uiop, nfsuint64 *cookiep, nfsva.na_mntonfileno = 0xffffffff; error = nfsv4_loadattr(nd, NULL, &nfsva, &nfhp, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, &rderr, NULL, p, cred); + NULL, NULL, &rderr, NULL, NULL, p, cred); if (error) goto nfsmout; } @@ -4981,8 +4984,8 @@ nfsmout: */ int nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp, - uint32_t *leasep, struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, - int *attrflagp) + uint32_t *leasep, uint32_t *cloneblksizep, struct ucred *cred, NFSPROC_T *p, + struct nfsvattr *nap, int *attrflagp) { u_int32_t *tl = NULL; struct nfsrv_descript nfsd, *nd = &nfsd; @@ -4991,6 +4994,8 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp, int error; *attrflagp = 0; + if (cloneblksizep != NULL) + *cloneblksizep = 0; nmp = VFSTONFS(vp->v_mount); if (NFSHASNFSV4(nmp)) { /* @@ -5009,7 +5014,7 @@ nfsrpc_statfs(vnode_t vp, struct nfsstatfs *sbp, struct nfsfsinfo *fsp, if (nd->nd_repstat == 0) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, sbp, fsp, NULL, 0, NULL, leasep, NULL, - NULL, p, cred); + NULL, cloneblksizep, p, cred); if (!error) { nmp->nm_fsid[0] = nap->na_filesid[0]; nmp->nm_fsid[1] = nap->na_filesid[1]; @@ -5063,7 +5068,8 @@ nfsmout: */ int nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp, - struct ucred *cred, NFSPROC_T *p, struct nfsvattr *nap, int *attrflagp) + uint32_t *clone_blksizep, struct ucred *cred, NFSPROC_T *p, + struct nfsvattr *nap, int *attrflagp) { struct nfsrv_descript nfsd, *nd = &nfsd; struct nfsmount *nmp; @@ -5074,6 +5080,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp, *has_namedattrp = false; *attrflagp = 0; + *clone_blksizep = 0; nmp = VFSTONFS(vp->v_mount); if (NFSHASNFSV4(nmp)) { np = VTONFS(vp); @@ -5100,7 +5107,7 @@ nfsrpc_pathconf(vnode_t vp, struct nfsv3_pathconf *pc, bool *has_namedattrp, if (nd->nd_repstat == 0) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, pc, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, - has_namedattrp, p, cred); + has_namedattrp, clone_blksizep, p, cred); if (!error) *attrflagp = 1; } else { @@ -5395,7 +5402,8 @@ nfsrpc_getacl(vnode_t vp, struct ucred *cred, NFSPROC_T *p, struct acl *aclp) return (error); if (!nd->nd_repstat) error = nfsv4_loadattr(nd, vp, NULL, NULL, NULL, 0, NULL, - NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, p, cred); + NULL, NULL, NULL, aclp, 0, NULL, NULL, NULL, NULL, NULL, p, + cred); else error = nd->nd_repstat; m_freem(nd->nd_mrep); @@ -5437,7 +5445,7 @@ nfsrpc_setaclrpc(vnode_t vp, struct ucred *cred, NFSPROC_T *p, NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_ACL); (void) nfsv4_fillattr(nd, vp->v_mount, vp, aclp, NULL, NULL, 0, &attrbits, NULL, NULL, 0, 0, 0, 0, (uint64_t)0, NULL, false, false, - false); + false, 0); error = nfscl_request(nd, vp, p, cred); if (error) return (error); @@ -8496,7 +8504,7 @@ nfsrpc_openlayoutrpc(struct nfsmount *nmp, vnode_t vp, u_int8_t *nfhp, if (*++tl == 0) { error = nfsv4_loadattr(nd, NULL, &nfsva, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, - NULL, NULL, NULL, NULL, p, cred); + NULL, NULL, NULL, NULL, NULL, p, cred); if (error != 0) goto nfsmout; if (ndp != NULL) { @@ -9168,6 +9176,199 @@ nfsmout: } /* + * nfs clone operation. + */ +int +nfsrpc_clone(vnode_t invp, off_t *inoffp, vnode_t outvp, + off_t *outoffp, size_t *lenp, bool toeof, int *inattrflagp, + struct nfsvattr *innap, int *outattrflagp, struct nfsvattr *outnap, + struct ucred *cred) +{ + int error, expireret = 0, retrycnt; + uint32_t clidrev = 0; + struct nfsmount *nmp = VFSTONFS(invp->v_mount); + struct nfsfh *innfhp = NULL, *outnfhp = NULL; + nfsv4stateid_t instateid, outstateid; + void *inlckp, *outlckp; + + if (nmp->nm_clp != NULL) + clidrev = nmp->nm_clp->nfsc_clientidrev; + innfhp = VTONFS(invp)->n_fhp; + outnfhp = VTONFS(outvp)->n_fhp; + retrycnt = 0; + do { + /* Get both stateids. */ + inlckp = NULL; + nfscl_getstateid(invp, innfhp->nfh_fh, innfhp->nfh_len, + NFSV4OPEN_ACCESSREAD, 0, NULL, curthread, &instateid, + &inlckp); + outlckp = NULL; + nfscl_getstateid(outvp, outnfhp->nfh_fh, outnfhp->nfh_len, + NFSV4OPEN_ACCESSWRITE, 0, NULL, curthread, &outstateid, + &outlckp); + + error = nfsrpc_clonerpc(invp, *inoffp, outvp, *outoffp, lenp, + toeof, &instateid, &outstateid, innap, inattrflagp, outnap, + outattrflagp, cred, curthread); + if (error == 0) { + *inoffp += *lenp; + *outoffp += *lenp; + } else if (error == NFSERR_STALESTATEID) + nfscl_initiate_recovery(nmp->nm_clp); + if (inlckp != NULL) + nfscl_lockderef(inlckp); + if (outlckp != NULL) + nfscl_lockderef(outlckp); + if (error == NFSERR_GRACE || error == NFSERR_STALESTATEID || + error == NFSERR_STALEDONTRECOVER || error == NFSERR_DELAY || + error == NFSERR_OLDSTATEID || error == NFSERR_BADSESSION) { + (void) nfs_catnap(PZERO, error, "nfs_cfr"); + } else if ((error == NFSERR_EXPIRED || (!NFSHASINT(nmp) && + error == NFSERR_BADSTATEID)) && clidrev != 0) { + expireret = nfscl_hasexpired(nmp->nm_clp, clidrev, + curthread); + } else if (error == NFSERR_BADSTATEID && NFSHASINT(nmp)) { + error = EIO; + } + retrycnt++; + } while (error == NFSERR_GRACE || error == NFSERR_DELAY || + error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION || + error == NFSERR_STALEDONTRECOVER || + (error == NFSERR_OLDSTATEID && retrycnt < 20) || + ((error == NFSERR_EXPIRED || error == NFSERR_BADSTATEID) && + expireret == 0 && clidrev != 0 && retrycnt < 4)); + if (error != 0 && (retrycnt >= 4 || + error == NFSERR_STALESTATEID || error == NFSERR_BADSESSION || + error == NFSERR_STALEDONTRECOVER)) + error = EIO; + return (error); +} + +/* + * The clone RPC. + */ +static int +nfsrpc_clonerpc(vnode_t invp, off_t inoff, vnode_t outvp, off_t outoff, + size_t *lenp, bool toeof, nfsv4stateid_t *instateidp, + nfsv4stateid_t *outstateidp, struct nfsvattr *innap, int *inattrflagp, + struct nfsvattr *outnap, int *outattrflagp, struct ucred *cred, + NFSPROC_T *p) +{ + uint32_t *tl, *opcntp; + int error; + struct nfsrv_descript nfsd; + struct nfsrv_descript *nd = &nfsd; + struct nfsmount *nmp; + nfsattrbit_t attrbits; + struct vattr va; + uint64_t len; + + nmp = VFSTONFS(invp->v_mount); + *inattrflagp = *outattrflagp = 0; + len = *lenp; + if (len == 0) + return (0); + if (toeof) + len = 0; + nfscl_reqstart(nd, NFSPROC_CLONE, nmp, VTONFS(invp)->n_fhp->nfh_fh, + VTONFS(invp)->n_fhp->nfh_len, &opcntp, NULL, 0, 0, cred); + /* + * First do a Setattr of atime to the server's clock + * time. The FreeBSD "collective" was of the opinion + * that setting atime was necessary for this syscall. + * Do the Setattr before the Clone, so that it can be + * handled well if the server replies NFSERR_DELAY to + * the Setattr operation. + */ + if ((nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) { + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_SETATTR); + nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID); + VATTR_NULL(&va); + va.va_atime.tv_sec = va.va_atime.tv_nsec = 0; + va.va_vaflags = VA_UTIMES_NULL; + nfscl_fillsattr(nd, &va, invp, 0, 0); + /* Bump opcnt from 7 to 8. */ + *opcntp = txdr_unsigned(8); + } + + /* Now Getattr the invp attributes. */ + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_GETATTR); + NFSGETATTR_ATTRBIT(&attrbits); + nfsrv_putattrbit(nd, &attrbits); + + /* Set outvp. */ + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_PUTFH); + (void)nfsm_fhtom(nmp, nd, VTONFS(outvp)->n_fhp->nfh_fh, + VTONFS(outvp)->n_fhp->nfh_len, 0); + + /* Do the Clone. */ + NFSM_BUILD(tl, uint32_t *, NFSX_UNSIGNED); + *tl = txdr_unsigned(NFSV4OP_CLONE); + nfsm_stateidtom(nd, instateidp, NFSSTATEID_PUTSTATEID); + nfsm_stateidtom(nd, outstateidp, NFSSTATEID_PUTSTATEID); + NFSM_BUILD(tl, uint32_t *, 3 * NFSX_HYPER + NFSX_UNSIGNED); + txdr_hyper(inoff, tl); tl += 2; + txdr_hyper(outoff, tl); tl += 2; + txdr_hyper(len, tl); tl += 2; + + /* Get the outvp attributes. */ + *tl = txdr_unsigned(NFSV4OP_GETATTR); + NFSWRITEGETATTR_ATTRBIT(&attrbits); + nfsrv_putattrbit(nd, &attrbits); + + error = nfscl_request(nd, invp, p, cred); + if (error != 0) + return (error); + /* Skip over the Setattr reply. */ + if ((nd->nd_flag & ND_NOMOREDATA) == 0 && + (nmp->nm_mountp->mnt_flag & MNT_NOATIME) == 0) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) == 0) { + error = nfsrv_getattrbits(nd, &attrbits, NULL, NULL); + if (error != 0) + goto nfsmout; + } else + nd->nd_flag |= ND_NOMOREDATA; + } + if ((nd->nd_flag & ND_NOMOREDATA) == 0) { + /* Get the input file's attributes. */ + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*(tl + 1) == 0) { + error = nfsm_loadattr(nd, innap); + if (error != 0) + goto nfsmout; + *inattrflagp = 1; + } else + nd->nd_flag |= ND_NOMOREDATA; + } + /* Skip over return stat for PutFH. */ + if ((nd->nd_flag & ND_NOMOREDATA) == 0) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (*++tl != 0) + nd->nd_flag |= ND_NOMOREDATA; + } + /* Skip over return stat for Clone. */ + if ((nd->nd_flag & ND_NOMOREDATA) == 0) + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + if (nd->nd_repstat == 0) { + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); + error = nfsm_loadattr(nd, outnap); + if (error == 0) + *outattrflagp = NFS_LATTR_NOSHRINK; + } else { + *lenp = 0; + } + if (error == 0) + error = nd->nd_repstat; +nfsmout: + m_freem(nd->nd_mrep); + return (error); +} + +/* * Seek operation. */ int @@ -9724,13 +9925,13 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td) struct nfsstatfs sb; struct mount *mp; struct nfsmount *nmp; - uint32_t lease; + uint32_t clone_blksize, lease; int attrflag, error; mp = vp->v_mount; nmp = VFSTONFS(mp); - error = nfsrpc_statfs(vp, &sb, &fs, &lease, cred, td, &nfsva, - &attrflag); + error = nfsrpc_statfs(vp, &sb, &fs, &lease, &clone_blksize, cred, td, + &nfsva, &attrflag); if (attrflag != 0) (void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1); if (error == 0) { @@ -9739,7 +9940,7 @@ nfscl_statfs(struct vnode *vp, struct ucred *cred, NFSPROC_T *td) nmp->nm_clp->nfsc_renew = NFSCL_RENEW(lease); NFSUNLOCKCLSTATE(); mtx_lock(&nmp->nm_mtx); - nfscl_loadfsinfo(nmp, &fs); + nfscl_loadfsinfo(nmp, &fs, clone_blksize); nfscl_loadsbinfo(nmp, &sb, &mp->mnt_stat); mp->mnt_stat.f_iosize = newnfs_iosize(nmp); mtx_unlock(&nmp->nm_mtx); diff --git a/sys/fs/nfsclient/nfs_clstate.c b/sys/fs/nfsclient/nfs_clstate.c index 99a781640c53..aa9d01fc4632 100644 --- a/sys/fs/nfsclient/nfs_clstate.c +++ b/sys/fs/nfsclient/nfs_clstate.c @@ -3701,7 +3701,7 @@ nfscl_docb(struct nfsrv_descript *nd, NFSPROC_T *p) if (!error) (void) nfsv4_fillattr(nd, NULL, NULL, NULL, &va, NULL, 0, &rattrbits, NULL, p, 0, 0, 0, 0, - (uint64_t)0, NULL, false, false, false); + (uint64_t)0, NULL, false, false, false, 0); break; case NFSV4OP_CBRECALL: NFSCL_DEBUG(4, "cbrecall\n"); diff --git a/sys/fs/nfsclient/nfs_clvfsops.c b/sys/fs/nfsclient/nfs_clvfsops.c index 0bd05c03885b..5ea7eab07632 100644 --- a/sys/fs/nfsclient/nfs_clvfsops.c +++ b/sys/fs/nfsclient/nfs_clvfsops.c @@ -292,8 +292,10 @@ nfs_statfs(struct mount *mp, struct statfs *sbp) int error = 0, attrflag, gotfsinfo = 0, ret; struct nfsnode *np; char *fakefh; + uint32_t clone_blksize; td = curthread; + clone_blksize = 0; error = vfs_busy(mp, MBF_NOWAIT); if (error) @@ -337,8 +339,8 @@ nfs_statfs(struct mount *mp, struct statfs *sbp) } else mtx_unlock(&nmp->nm_mtx); if (!error) - error = nfsrpc_statfs(vp, &sb, &fs, NULL, td->td_ucred, td, - &nfsva, &attrflag); + error = nfsrpc_statfs(vp, &sb, &fs, NULL, &clone_blksize, + td->td_ucred, td, &nfsva, &attrflag); if ((nmp->nm_privflag & NFSMNTP_FAKEROOTFH) != 0 && error == NFSERR_WRONGSEC) { /* Cannot get new stats, so return what is in mnt_stat. */ @@ -375,7 +377,7 @@ nfs_statfs(struct mount *mp, struct statfs *sbp) if (!error) { mtx_lock(&nmp->nm_mtx); if (gotfsinfo || (nmp->nm_flag & NFSMNT_NFSV4)) - nfscl_loadfsinfo(nmp, &fs); + nfscl_loadfsinfo(nmp, &fs, clone_blksize); nfscl_loadsbinfo(nmp, &sb, sbp); sbp->f_iosize = newnfs_iosize(nmp); mtx_unlock(&nmp->nm_mtx); @@ -408,7 +410,7 @@ ncl_fsinfo(struct nfsmount *nmp, struct vnode *vp, struct ucred *cred, if (attrflag) (void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1); mtx_lock(&nmp->nm_mtx); - nfscl_loadfsinfo(nmp, &fs); + nfscl_loadfsinfo(nmp, &fs, 0); mtx_unlock(&nmp->nm_mtx); } return (error); diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index fa451887e73e..6824ee6ef13d 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -4027,31 +4027,51 @@ nfs_copy_file_range(struct vop_copy_file_range_args *ap) struct vattr va, *vap; struct uio io; struct nfsmount *nmp; + struct nfsnode *np; size_t len, len2; ssize_t r; int error, inattrflag, outattrflag, ret, ret2, invp_lock; off_t inoff, outoff; - bool consecutive, must_commit, tryoutcred; + bool consecutive, must_commit, onevp, toeof, tryclone, tryoutcred; + bool mustclone; /* * NFSv4.2 Copy is not permitted for infile == outfile. + * The NFSv4.2 Clone operation does work on non-overlapping + * byte ranges in the same file, but only if offsets + * (and len if not to EOF) are aligned properly. * TODO: copy_file_range() between multiple NFS mountpoints + * --> This is not possible now, since each mount appears to + * the NFSv4.n server as a separate client. */ - if (invp == outvp || invp->v_mount != outvp->v_mount) { + if ((invp == outvp && (ap->a_flags & COPY_FILE_RANGE_CLONE) == 0) || + (invp != outvp && invp->v_mount != outvp->v_mount)) { generic_copy: return (ENOSYS); } - - invp_lock = LK_SHARED; + if (invp == outvp) { + onevp = true; + invp_lock = LK_EXCLUSIVE; + } else { + onevp = false; + invp_lock = LK_SHARED; + } + mustclone = false; + if (onevp || (ap->a_flags & COPY_FILE_RANGE_CLONE) != 0) + mustclone = true; relock: + inoff = *ap->a_inoffp; + outoff = *ap->a_outoffp; - /* Lock both vnodes, avoiding risk of deadlock. */ + /* Lock vnode(s), avoiding risk of deadlock. */ do { mp = NULL; error = vn_start_write(outvp, &mp, V_WAIT); if (error == 0) { error = vn_lock(outvp, LK_EXCLUSIVE); if (error == 0) { + if (onevp) + break; error = vn_lock(invp, invp_lock | LK_NOWAIT); if (error == 0) break; @@ -4071,16 +4091,24 @@ relock: return (error); /* - * More reasons to avoid nfs copy: not NFSv4.2, or explicitly - * disabled. + * More reasons to avoid nfs copy/clone: not NFSv4.2, explicitly + * disabled or requires cloning and unable to clone. + * Only clone if the clone_blksize attribute is supported + * and the clone_blksize is greater than 0. + * Alignment of offsets and length will be checked later. */ nmp = VFSTONFS(invp->v_mount); + np = VTONFS(invp); mtx_lock(&nmp->nm_mtx); + if ((nmp->nm_privflag & NFSMNTP_NOCOPY) != 0) + mustclone = true; if (!NFSHASNFSV4(nmp) || nmp->nm_minorvers < NFSV42_MINORVERSION || - (nmp->nm_privflag & NFSMNTP_NOCOPY) != 0) { + (mustclone && (!NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, + NFSATTRBIT_CLONEBLKSIZE) || nmp->nm_cloneblksize == 0))) { mtx_unlock(&nmp->nm_mtx); VOP_UNLOCK(invp); - VOP_UNLOCK(outvp); + if (!onevp) + VOP_UNLOCK(outvp); /* For onevp, same as invp. */ if (mp != NULL) vn_finished_write(mp); goto generic_copy; @@ -4111,6 +4139,8 @@ relock: invp_obj = invp->v_object; if (invp_obj != NULL && vm_object_mightbedirty(invp_obj)) { if (invp_lock != LK_EXCLUSIVE) { + KASSERT(!onevp, ("nfs_copy_file_range: " + "invp_lock LK_SHARED for onevp")); invp_lock = LK_EXCLUSIVE; VOP_UNLOCK(invp); VOP_UNLOCK(outvp); @@ -4134,10 +4164,10 @@ relock: else consecutive = false; mtx_unlock(&nmp->nm_mtx); - inoff = *ap->a_inoffp; - outoff = *ap->a_outoffp; tryoutcred = true; must_commit = false; + toeof = false; + if (error == 0) { vap = &VTONFS(invp)->n_vattr.na_vattr; error = VOP_GETATTR(invp, vap, ap->a_incred); @@ -4169,29 +4199,63 @@ relock: if (error == 0 && ret != 0) error = ret; } - } else if (inoff + len > vap->va_size) + } else if (inoff + len >= vap->va_size) { + toeof = true; *ap->a_lenp = len = vap->va_size - inoff; + } } else error = 0; } /* + * For cloning, the offsets must be clone blksize aligned and + * the len must be blksize aligned unless it goes to EOF on + * the input file. + */ + tryclone = false; + if (len > 0) { + if (error == 0 && NFSISSET_ATTRBIT(&np->n_vattr.na_suppattr, + NFSATTRBIT_CLONEBLKSIZE) && nmp->nm_cloneblksize != 0 && + (inoff % nmp->nm_cloneblksize) == 0 && + (outoff % nmp->nm_cloneblksize) == 0 && + (toeof || (len % nmp->nm_cloneblksize) == 0)) + tryclone = true; + else if (mustclone) + error = ENOSYS; + } + + /* * len will be set to 0 upon a successful Copy RPC. - * As such, this only loops when the Copy RPC needs to be retried. + * As such, this only loops when the Copy/Clone RPC needs to be retried. */ while (len > 0 && error == 0) { inattrflag = outattrflag = 0; len2 = len; - if (tryoutcred) - error = nfsrpc_copy_file_range(invp, ap->a_inoffp, - outvp, ap->a_outoffp, &len2, ap->a_flags, - &inattrflag, &innfsva, &outattrflag, &outnfsva, - ap->a_outcred, consecutive, &must_commit); - else - error = nfsrpc_copy_file_range(invp, ap->a_inoffp, - outvp, ap->a_outoffp, &len2, ap->a_flags, - &inattrflag, &innfsva, &outattrflag, &outnfsva, - ap->a_incred, consecutive, &must_commit); + if (tryclone) { + if (tryoutcred) + error = nfsrpc_clone(invp, ap->a_inoffp, outvp, + ap->a_outoffp, &len2, toeof, &inattrflag, + &innfsva, &outattrflag, &outnfsva, + ap->a_outcred); + else + error = nfsrpc_clone(invp, ap->a_inoffp, outvp, + ap->a_outoffp, &len2, toeof, &inattrflag, + &innfsva, &outattrflag, &outnfsva, + ap->a_incred); + } else { + if (tryoutcred) + error = nfsrpc_copy_file_range(invp, + ap->a_inoffp, outvp, ap->a_outoffp, &len2, + ap->a_flags, &inattrflag, &innfsva, + &outattrflag, &outnfsva, + ap->a_outcred, consecutive, &must_commit); + else + error = nfsrpc_copy_file_range(invp, + ap->a_inoffp, outvp, ap->a_outoffp, &len2, + ap->a_flags, &inattrflag, &innfsva, + &outattrflag, &outnfsva, + ap->a_incred, consecutive, &must_commit); + } if (inattrflag != 0) ret = nfscl_loadattrcache(&invp, &innfsva, NULL, 0, 1); if (outattrflag != 0) @@ -4230,6 +4294,13 @@ relock: /* Try again with incred. */ tryoutcred = false; error = 0; + } else if (tryclone && error != 0) { + if (mustclone) { + error = ENOSYS; + } else { + tryclone = false; + error = 0; + } } if (error == NFSERR_STALEWRITEVERF) { /* @@ -4243,11 +4314,12 @@ relock: } } VOP_UNLOCK(invp); - VOP_UNLOCK(outvp); + if (!onevp) + VOP_UNLOCK(outvp); /* For onevp, same as invp. */ if (mp != NULL) vn_finished_write(mp); if (error == NFSERR_NOTSUPP || error == NFSERR_OFFLOADNOREQS || - error == NFSERR_ACCES) { + error == NFSERR_ACCES || error == ENOSYS) { /* * Unlike the NFSv4.2 Copy, vn_generic_copy_file_range() can * use a_incred for the read and a_outcred for the write, so @@ -4255,7 +4327,7 @@ relock: * For NFSERR_NOTSUPP and NFSERR_OFFLOADNOREQS, the Copy can * never succeed, so disable it. */ - if (error != NFSERR_ACCES) { + if (error != NFSERR_ACCES && error != ENOSYS) { /* Can never do Copy on this mount. */ mtx_lock(&nmp->nm_mtx); nmp->nm_privflag |= NFSMNTP_NOCOPY; @@ -4596,6 +4668,7 @@ nfs_pathconf(struct vop_pathconf_args *ap) struct nfsmount *nmp; struct thread *td = curthread; off_t off; + uint32_t clone_blksize; bool eof, has_namedattr, named_enabled; int attrflag, error; struct nfsnode *np; @@ -4604,19 +4677,22 @@ nfs_pathconf(struct vop_pathconf_args *ap) np = VTONFS(vp); named_enabled = false; has_namedattr = false; + clone_blksize = 0; if ((NFS_ISV34(vp) && (ap->a_name == _PC_LINK_MAX || ap->a_name == _PC_NAME_MAX || ap->a_name == _PC_CHOWN_RESTRICTED || ap->a_name == _PC_NO_TRUNC)) || (NFS_ISV4(vp) && (ap->a_name == _PC_ACL_NFS4 || - ap->a_name == _PC_HAS_NAMEDATTR))) { + ap->a_name == _PC_HAS_NAMEDATTR || + ap->a_name == _PC_CLONE_BLKSIZE))) { /* * Since only the above 4 a_names are returned by the NFSv3 * Pathconf RPC, there is no point in doing it for others. * For NFSv4, the Pathconf RPC (actually a Getattr Op.) can - * be used for _PC_ACL_NFS4 and _PC_HAS_NAMEDATTR as well. + * be used for _PC_ACL_NFS4, _PC_HAS_NAMEDATTR and + * _PC_CLONE_BLKSIZE as well. */ - error = nfsrpc_pathconf(vp, &pc, &has_namedattr, td->td_ucred, - td, &nfsva, &attrflag); + error = nfsrpc_pathconf(vp, &pc, &has_namedattr, &clone_blksize, + td->td_ucred, td, &nfsva, &attrflag); if (attrflag != 0) (void) nfscl_loadattrcache(&vp, &nfsva, NULL, 0, 1); if (error != 0) @@ -4771,6 +4847,9 @@ nfs_pathconf(struct vop_pathconf_args *ap) else *ap->a_retval = 0; break; + case _PC_CLONE_BLKSIZE: + *ap->a_retval = clone_blksize; + break; default: error = vop_stdpathconf(ap); diff --git a/sys/fs/nfsclient/nfsmount.h b/sys/fs/nfsclient/nfsmount.h index 37b84a015dab..ef876dd30e59 100644 --- a/sys/fs/nfsclient/nfsmount.h +++ b/sys/fs/nfsclient/nfsmount.h @@ -87,6 +87,7 @@ struct nfsmount { /* unclipped, wraps to 0 */ struct __rpc_client *nm_aconn[NFS_MAXNCONN - 1]; /* Additional nconn */ /* Locked via nm_sockreq.nr_mtx */ + uint32_t nm_cloneblksize; /* Block cloning alignment */ u_int16_t nm_krbnamelen; /* Krb5 host principal, if any */ u_int16_t nm_dirpathlen; /* and mount dirpath, for V4 */ u_int16_t nm_srvkrbnamelen; /* and the server's target name */ diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index 8c427c66c156..496cac263fa0 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -2113,7 +2113,8 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp, struct nfsvattr *nvap, fhandle_t *fhp, int rderror, nfsattrbit_t *attrbitp, struct ucred *cred, struct thread *p, int isdgram, int reterr, int supports_nfsv4acls, int at_root, uint64_t mounted_on_fileno, - bool xattrsupp, bool has_hiddensystem, bool has_namedattr) + bool xattrsupp, bool has_hiddensystem, bool has_namedattr, + uint32_t clone_blksize) { struct statfs *sf; int error; @@ -2130,9 +2131,11 @@ nfsvno_fillattr(struct nfsrv_descript *nd, struct mount *mp, struct vnode *vp, sf = NULL; } } + error = nfsv4_fillattr(nd, mp, vp, NULL, &nvap->na_vattr, fhp, rderror, attrbitp, cred, p, isdgram, reterr, supports_nfsv4acls, at_root, - mounted_on_fileno, sf, xattrsupp, has_hiddensystem, has_namedattr); + mounted_on_fileno, sf, xattrsupp, has_hiddensystem, has_namedattr, + clone_blksize); free(sf, M_TEMP); NFSEXITCODE2(0, nd); return (error); @@ -2441,7 +2444,7 @@ nfsrvd_readdirplus(struct nfsrv_descript *nd, int isdgram, struct vnode *vp, struct nfsexstuff *exp) { struct dirent *dp; - u_int32_t *tl; + uint32_t clone_blksize, *tl; int dirlen; char *cpos, *cend, *rbuf; struct vnode *nvp; @@ -2943,6 +2946,7 @@ again: xattrsupp = false; has_hiddensystem = false; has_namedattr = false; + clone_blksize = 0; if (nvp != NULL) { supports_nfsv4acls = nfs_supportsnfsv4acls(nvp); @@ -2966,6 +2970,11 @@ again: &pathval) != 0) pathval = 0; has_namedattr = pathval > 0; + pathval = 0; + if (VOP_PATHCONF(nvp, _PC_CLONE_BLKSIZE, + &pathval) != 0) + pathval = 0; + clone_blksize = pathval; NFSVOPUNLOCK(nvp); } else supports_nfsv4acls = 0; @@ -2986,14 +2995,16 @@ again: nd->nd_cred, p, isdgram, 0, supports_nfsv4acls, at_root, mounted_on_fileno, xattrsupp, - has_hiddensystem, has_namedattr); + has_hiddensystem, has_namedattr, + clone_blksize); } else { dirlen += nfsvno_fillattr(nd, new_mp, nvp, nvap, &nfh, r, &attrbits, nd->nd_cred, p, isdgram, 0, supports_nfsv4acls, at_root, mounted_on_fileno, xattrsupp, - has_hiddensystem, has_namedattr); + has_hiddensystem, has_namedattr, + clone_blksize); } if (nvp != NULL) vrele(nvp); @@ -5690,7 +5701,8 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len, if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) == (ND_NFSV4 | ND_V4WCCATTR)) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, - NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, + NULL); NFSD_DEBUG(4, "nfsrv_writedsdorpc: wcc attr=%d\n", error); if (error != 0) goto nfsmout; @@ -5721,7 +5733,8 @@ nfsrv_writedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, int len, if (error == 0) { NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, - NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, + NULL); } NFSD_DEBUG(4, "nfsrv_writedsdorpc: aft loadattr=%d\n", error); nfsmout: @@ -5887,7 +5900,8 @@ nfsrv_allocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, if (nd->nd_repstat == 0) { NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, - NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, + NULL); } else error = nd->nd_repstat; NFSD_DEBUG(4, "nfsrv_allocatedsdorpc: aft loadattr=%d\n", error); @@ -6054,7 +6068,8 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) == (ND_NFSV4 | ND_V4WCCATTR)) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, - NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, + NULL); NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: wcc attr=%d\n", error); if (error != 0) goto nfsmout; @@ -6068,7 +6083,8 @@ nfsrv_deallocatedsdorpc(struct nfsmount *nmp, fhandle_t *fhp, off_t off, if (nd->nd_repstat == 0) { NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, - NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL, + NULL); } else error = nd->nd_repstat; NFSD_DEBUG(4, "nfsrv_deallocatedsdorpc: aft loadattr=%d\n", error); @@ -6216,7 +6232,8 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p, if ((nd->nd_flag & (ND_NOMOREDATA | ND_NFSV4 | ND_V4WCCATTR)) == (ND_NFSV4 | ND_V4WCCATTR)) { error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL, - NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL); + NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, + NULL, NULL); NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: wcc attr=%d\n", error); if (error != 0) goto nfsmout; @@ -6241,7 +6258,7 @@ nfsrv_setattrdsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p, NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_UNSIGNED); error = nfsv4_loadattr(nd, NULL, dsnap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, - NULL); + NULL, NULL); } NFSD_DEBUG(4, "nfsrv_setattrdsdorpc: aft setattr loadattr=%d\n", error); nfsmout: @@ -6386,7 +6403,7 @@ nfsrv_setacldsdorpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p, * the same type (VREG). */ nfsv4_fillattr(nd, NULL, vp, aclp, NULL, NULL, 0, &attrbits, NULL, - NULL, 0, 0, 0, 0, 0, NULL, false, false, false); + NULL, 0, 0, 0, 0, 0, NULL, false, false, false, 0); error = newnfs_request(nd, nmp, NULL, &nmp->nm_sockreq, NULL, p, cred, NFS_PROG, NFS_VER4, NULL, 1, NULL, NULL); if (error != 0) { @@ -6530,7 +6547,7 @@ nfsrv_getattrdsrpc(fhandle_t *fhp, struct ucred *cred, NFSPROC_T *p, if (nd->nd_repstat == 0) { error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, - NULL, NULL, NULL); + NULL, NULL, NULL, NULL); /* * We can only save the updated values in the extended * attribute if the vp is exclusively locked. diff --git a/sys/fs/nfsserver/nfs_nfsdserv.c b/sys/fs/nfsserver/nfs_nfsdserv.c index 9eebcda548c6..a881968b03be 100644 --- a/sys/fs/nfsserver/nfs_nfsdserv.c +++ b/sys/fs/nfsserver/nfs_nfsdserv.c @@ -253,6 +253,7 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram, size_t atsiz; long pathval; bool has_hiddensystem, has_namedattr, xattrsupp; + uint32_t clone_blksize; if (nd->nd_repstat) goto out; @@ -330,6 +331,11 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram, &pathval) != 0) pathval = 0; has_namedattr = pathval > 0; + pathval = 0; + if (VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE, + &pathval) != 0) + pathval = 0; + clone_blksize = pathval; mp = vp->v_mount; if (nfsrv_enable_crossmntpt != 0 && vp->v_type == VDIR && @@ -365,7 +371,7 @@ nfsrvd_getattr(struct nfsrv_descript *nd, int isdgram, isdgram, 1, supports_nfsv4acls, at_root, mounted_on_fileno, xattrsupp, has_hiddensystem, - has_namedattr); + has_namedattr, clone_blksize); vfs_unbusy(mp); } vrele(vp); @@ -4347,7 +4353,7 @@ nfsrvd_verify(struct nfsrv_descript *nd, int isdgram, if (!nd->nd_repstat) { nfsvno_getfs(&fs, isdgram); error = nfsv4_loadattr(nd, vp, &nva, NULL, &fh, fhsize, NULL, - sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, NULL, p, + sf, NULL, &fs, NULL, 1, &ret, NULL, NULL, NULL, NULL, p, nd->nd_cred); if (!error) { if (nd->nd_procnum == NFSV4OP_NVERIFY) { @@ -6011,6 +6017,212 @@ nfsmout: } /* + * nfs clone service + */ +int +nfsrvd_clone(struct nfsrv_descript *nd, __unused int isdgram, + vnode_t vp, vnode_t tovp, struct nfsexstuff *exp, struct nfsexstuff *toexp) +{ + uint32_t *tl; + struct nfsvattr at; + int error = 0, ret; + off_t inoff, outoff; + uint64_t len; + size_t xfer; + struct nfsstate inst, outst, *instp = &inst, *outstp = &outst; + struct nfslock inlo, outlo, *inlop = &inlo, *outlop = &outlo; + nfsquad_t clientid; + nfsv4stateid_t stateid; + nfsattrbit_t attrbits; + void *rl_rcookie, *rl_wcookie; + long pathval; + + rl_rcookie = rl_wcookie = NULL; + pathval = 0; + if (nfsrv_maxcopyrange == 0 || nfsrv_devidcnt > 0 || + VOP_PATHCONF(vp, _PC_CLONE_BLKSIZE, &pathval) != 0 || + pathval == 0) { + /* + * For a pNFS server, reply NFSERR_NOTSUPP so that the client + * will not do the clone and will do I/O on the DS(s). + * If vfs.nfsd.maxcopyrange set to 0, disable Clone. + */ + nd->nd_repstat = NFSERR_NOTSUPP; + goto nfsmout; + } + NFSM_DISSECT(tl, uint32_t *, 2 * NFSX_STATEID + 3 * NFSX_HYPER); + instp->ls_flags = (NFSLCK_CHECK | NFSLCK_READACCESS); + inlop->lo_flags = NFSLCK_READ; + instp->ls_ownerlen = 0; + instp->ls_op = NULL; + instp->ls_uid = nd->nd_cred->cr_uid; + instp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++); + clientid.lval[0] = instp->ls_stateid.other[0] = *tl++; + clientid.lval[1] = instp->ls_stateid.other[1] = *tl++; + if ((nd->nd_flag & ND_IMPLIEDCLID) != 0) + clientid.qval = nd->nd_clientid.qval; + instp->ls_stateid.other[2] = *tl++; + outstp->ls_flags = (NFSLCK_CHECK | NFSLCK_WRITEACCESS); + outlop->lo_flags = NFSLCK_WRITE; + outstp->ls_ownerlen = 0; + outstp->ls_op = NULL; + outstp->ls_uid = nd->nd_cred->cr_uid; + outstp->ls_stateid.seqid = fxdr_unsigned(uint32_t, *tl++); + outstp->ls_stateid.other[0] = *tl++; + outstp->ls_stateid.other[1] = *tl++; + outstp->ls_stateid.other[2] = *tl++; + inoff = fxdr_hyper(tl); tl += 2; + inlop->lo_first = inoff; + outoff = fxdr_hyper(tl); tl += 2; + outlop->lo_first = outoff; + len = fxdr_hyper(tl); + if (len == 0) { + /* len == 0 means to EOF. */ + inlop->lo_end = OFF_MAX; + outlop->lo_end = OFF_MAX; + } else { + inlop->lo_end = inlop->lo_first + len; + outlop->lo_end = outlop->lo_first + len; + } + + if ((inoff > OFF_MAX || outoff > OFF_MAX || + inlop->lo_end > OFF_MAX || outlop->lo_end > OFF_MAX || + inlop->lo_end < inlop->lo_first || outlop->lo_end < + outlop->lo_first)) + nd->nd_repstat = NFSERR_INVAL; + + if (nd->nd_repstat == 0 && vp->v_type != VREG) + nd->nd_repstat = NFSERR_WRONGTYPE; + + /* Check permissions for the input file. */ + NFSZERO_ATTRBIT(&attrbits); + NFSSETBIT_ATTRBIT(&attrbits, NFSATTRBIT_OWNER); + ret = nfsvno_getattr(vp, &at, nd, curthread, 1, &attrbits); + if (nd->nd_repstat == 0) + nd->nd_repstat = ret; + if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid || + NFSVNO_EXSTRICTACCESS(exp))) + nd->nd_repstat = nfsvno_accchk(vp, VREAD, nd->nd_cred, exp, + curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, + NULL); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_lockctrl(vp, &instp, &inlop, NULL, + clientid, &stateid, exp, nd, curthread); + if (vp != tovp) { + NFSVOPUNLOCK(vp); + if (nd->nd_repstat != 0) + goto out; + + error = NFSVOPLOCK(tovp, LK_SHARED); + if (error != 0) + goto out; + pathval = 0; + if (VOP_PATHCONF(tovp, _PC_CLONE_BLKSIZE, &pathval) != 0 || + pathval == 0) + nd->nd_repstat = NFSERR_NOTSUPP; + else if (tovp->v_type != VREG) + nd->nd_repstat = NFSERR_WRONGTYPE; + } + + /* For the output file, we only need the Owner attribute. */ + ret = nfsvno_getattr(tovp, &at, nd, curthread, 1, &attrbits); + if (nd->nd_repstat == 0) + nd->nd_repstat = ret; + if (nd->nd_repstat == 0 && (at.na_uid != nd->nd_cred->cr_uid || + NFSVNO_EXSTRICTACCESS(exp))) + nd->nd_repstat = nfsvno_accchk(tovp, VWRITE, nd->nd_cred, toexp, + curthread, NFSACCCHK_ALLOWOWNER, NFSACCCHK_VPISLOCKED, + NULL); + if (nd->nd_repstat == 0) + nd->nd_repstat = nfsrv_lockctrl(tovp, &outstp, &outlop, NULL, + clientid, &stateid, toexp, nd, curthread); + NFSVOPUNLOCK(tovp); + + /* Range lock the byte ranges for both invp and outvp. */ + if (nd->nd_repstat == 0) { + for (;;) { + if (len == 0) + rl_wcookie = vn_rangelock_wlock(tovp, outoff, + OFF_MAX); + else + rl_wcookie = vn_rangelock_wlock(tovp, outoff, + outoff + len); + if (vp != tovp) { + if (len == 0) + rl_rcookie = vn_rangelock_tryrlock(vp, + inoff, OFF_MAX); + else + rl_rcookie = vn_rangelock_tryrlock(vp, + inoff, inoff + len); + if (rl_rcookie != NULL) + break; + } else { + rl_rcookie = NULL; + break; + } + vn_rangelock_unlock(tovp, rl_wcookie); + if (len == 0) + rl_rcookie = vn_rangelock_rlock(vp, inoff, + OFF_MAX); + else + rl_rcookie = vn_rangelock_rlock(vp, inoff, + inoff + len); + vn_rangelock_unlock(vp, rl_rcookie); + } + + error = NFSVOPLOCK(vp, LK_SHARED); + if (error == 0) { + ret = nfsvno_getattr(vp, &at, nd, curthread, 1, NULL); + if (ret == 0) { + /* + * Since invp is range locked, na_size should + * not change. + */ + if (len == 0 && at.na_size > inoff) + len = SSIZE_MAX; /* To EOF. */ + else if (inoff + len > at.na_size) + nd->nd_repstat = NFSERR_INVAL; + } + NFSVOPUNLOCK(vp); + if (ret != 0 && nd->nd_repstat == 0) + nd->nd_repstat = ret; + } else if (nd->nd_repstat == 0) + nd->nd_repstat = error; + } + + /* + * Do the actual copy to an upper limit of vfs.nfsd.maxcopyrange. + * This size limit can be set to limit the time a copy RPC will + * take. + */ + xfer = len; + if (nd->nd_repstat == 0) { + nd->nd_repstat = vn_copy_file_range(vp, &inoff, tovp, &outoff, + &xfer, COPY_FILE_RANGE_CLONE, nd->nd_cred, nd->nd_cred, + NULL); + if (nd->nd_repstat == ENOSYS) + nd->nd_repstat = NFSERR_INVAL; + } + + /* Unlock the ranges. */ + if (rl_rcookie != NULL) + vn_rangelock_unlock(vp, rl_rcookie); + if (rl_wcookie != NULL) + vn_rangelock_unlock(tovp, rl_wcookie); + +out: + vrele(vp); + vrele(tovp); + NFSEXITCODE2(error, nd); + return (error); +nfsmout: + vput(vp); + vrele(tovp); + NFSEXITCODE2(error, nd); + return (error); +} + +/* * nfs seek service */ int diff --git a/sys/fs/nfsserver/nfs_nfsdsocket.c b/sys/fs/nfsserver/nfs_nfsdsocket.c index d6832b4f74be..636c4735a131 100644 --- a/sys/fs/nfsserver/nfs_nfsdsocket.c +++ b/sys/fs/nfsserver/nfs_nfsdsocket.c @@ -371,7 +371,7 @@ int (*nfsrv4_ops2[NFSV42_NOPS])(struct nfsrv_descript *, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, - (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, + nfsrvd_clone, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, (int (*)(struct nfsrv_descript *, int, vnode_t , vnode_t , struct nfsexstuff *, struct nfsexstuff *))0, diff --git a/sys/fs/nfsserver/nfs_nfsdstate.c b/sys/fs/nfsserver/nfs_nfsdstate.c index 2e27817389dd..36e3ce7b7142 100644 --- a/sys/fs/nfsserver/nfs_nfsdstate.c +++ b/sys/fs/nfsserver/nfs_nfsdstate.c @@ -4675,7 +4675,7 @@ errout: } else if (error == 0 && procnum == NFSV4OP_CBGETATTR) error = nfsv4_loadattr(nd, NULL, nap, NULL, NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, NULL, NULL, - NULL, p, NULL); + NULL, NULL, p, NULL); m_freem(nd->nd_mrep); } NFSLOCKSTATE(); diff --git a/sys/kern/init_sysent.c b/sys/kern/init_sysent.c index c0a5479c9634..fcd232cde21e 100644 --- a/sys/kern/init_sysent.c +++ b/sys/kern/init_sysent.c @@ -145,8 +145,8 @@ struct sysent sysent[] = { { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 76 = obsolete vhangup */ { .sy_narg = 0, .sy_call = (sy_call_t *)nosys, .sy_auevent = AUE_NULL, .sy_flags = 0, .sy_thrcnt = SY_THR_ABSENT }, /* 77 = obsolete vlimit */ { .sy_narg = AS(mincore_args), .sy_call = (sy_call_t *)sys_mincore, .sy_auevent = AUE_MINCORE, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 78 = mincore */ - { .sy_narg = AS(getgroups_args), .sy_call = (sy_call_t *)sys_getgroups, .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 79 = getgroups */ - { .sy_narg = AS(setgroups_args), .sy_call = (sy_call_t *)sys_setgroups, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 80 = setgroups */ + { compat14(AS(freebsd14_getgroups_args),getgroups), .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 79 = freebsd14 getgroups */ + { compat14(AS(freebsd14_setgroups_args),setgroups), .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 80 = freebsd14 setgroups */ { .sy_narg = 0, .sy_call = (sy_call_t *)sys_getpgrp, .sy_auevent = AUE_GETPGRP, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 81 = getpgrp */ { .sy_narg = AS(setpgid_args), .sy_call = (sy_call_t *)sys_setpgid, .sy_auevent = AUE_SETPGRP, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 82 = setpgid */ { .sy_narg = AS(setitimer_args), .sy_call = (sy_call_t *)sys_setitimer, .sy_auevent = AUE_SETITIMER, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 83 = setitimer */ @@ -661,4 +661,6 @@ struct sysent sysent[] = { { .sy_narg = AS(exterrctl_args), .sy_call = (sy_call_t *)sys_exterrctl, .sy_auevent = AUE_NULL, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 592 = exterrctl */ { .sy_narg = AS(inotify_add_watch_at_args), .sy_call = (sy_call_t *)sys_inotify_add_watch_at, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 593 = inotify_add_watch_at */ { .sy_narg = AS(inotify_rm_watch_args), .sy_call = (sy_call_t *)sys_inotify_rm_watch, .sy_auevent = AUE_INOTIFY, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 594 = inotify_rm_watch */ + { .sy_narg = AS(getgroups_args), .sy_call = (sy_call_t *)sys_getgroups, .sy_auevent = AUE_GETGROUPS, .sy_flags = SYF_CAPENABLED, .sy_thrcnt = SY_THR_STATIC }, /* 595 = getgroups */ + { .sy_narg = AS(setgroups_args), .sy_call = (sy_call_t *)sys_setgroups, .sy_auevent = AUE_SETGROUPS, .sy_flags = 0, .sy_thrcnt = SY_THR_STATIC }, /* 596 = setgroups */ }; diff --git a/sys/kern/kern_prot.c b/sys/kern/kern_prot.c index 2cd5b7069023..beab30a9d157 100644 --- a/sys/kern/kern_prot.c +++ b/sys/kern/kern_prot.c @@ -310,6 +310,39 @@ sys_getegid(struct thread *td, struct getegid_args *uap) return (0); } +#ifdef COMPAT_FREEBSD14 +int +freebsd14_getgroups(struct thread *td, struct freebsd14_getgroups_args *uap) +{ + struct ucred *cred; + int ngrp, error; + + cred = td->td_ucred; + + /* + * For FreeBSD < 15.0, we account for the egid being placed at the + * beginning of the group list prior to all supplementary groups. + */ + ngrp = cred->cr_ngroups + 1; + if (uap->gidsetsize == 0) { + error = 0; + goto out; + } else if (uap->gidsetsize < ngrp) { + return (EINVAL); + } + + error = copyout(&cred->cr_gid, uap->gidset, sizeof(gid_t)); + if (error != 0) + error = copyout(cred->cr_groups, uap->gidset + 1, + (ngrp - 1) * sizeof(gid_t)); + +out: + td->td_retval[0] = ngrp; + return (error); + +} +#endif /* COMPAT_FREEBSD14 */ + #ifndef _SYS_SYSPROTO_H_ struct getgroups_args { int gidsetsize; @@ -320,18 +353,11 @@ int sys_getgroups(struct thread *td, struct getgroups_args *uap) { struct ucred *cred; - gid_t *ugidset; int ngrp, error; cred = td->td_ucred; - /* - * cr_gid has been moved out of cr_groups, but we'll continue exporting - * the egid as groups[0] for the time being until we audit userland for - * any surprises. - */ - ngrp = cred->cr_ngroups + 1; - + ngrp = cred->cr_ngroups; if (uap->gidsetsize == 0) { error = 0; goto out; @@ -339,14 +365,7 @@ sys_getgroups(struct thread *td, struct getgroups_args *uap) if (uap->gidsetsize < ngrp) return (EINVAL); - ugidset = uap->gidset; - error = copyout(&cred->cr_gid, ugidset, sizeof(*ugidset)); - if (error != 0) - goto out; - - if (ngrp > 1) - error = copyout(cred->cr_groups, ugidset + 1, - (ngrp - 1) * sizeof(*ugidset)); + error = copyout(cred->cr_groups, uap->gidset, ngrp * sizeof(gid_t)); out: td->td_retval[0] = ngrp; return (error); @@ -1186,6 +1205,44 @@ fail: return (error); } +#ifdef COMPAT_FREEBSD14 +int +freebsd14_setgroups(struct thread *td, struct freebsd14_setgroups_args *uap) +{ + gid_t smallgroups[CRED_SMALLGROUPS_NB]; + gid_t *groups; + int gidsetsize, error; + + /* + * Before FreeBSD 15.0, we allow one more group to be supplied to + * account for the egid appearing before the supplementary groups. This + * may technically allow one more supplementary group for systems that + * did use the default NGROUPS_MAX if we round it back up to 1024. + */ + gidsetsize = uap->gidsetsize; + if (gidsetsize > ngroups_max + 1 || gidsetsize < 0) + return (EINVAL); + + if (gidsetsize > CRED_SMALLGROUPS_NB) + groups = malloc(gidsetsize * sizeof(gid_t), M_TEMP, M_WAITOK); + else + groups = smallgroups; + + error = copyin(uap->gidset, groups, gidsetsize * sizeof(gid_t)); + if (error == 0) { + int ngroups = gidsetsize > 0 ? gidsetsize - 1 /* egid */ : 0; + + error = kern_setgroups(td, &ngroups, groups + 1); + if (error == 0 && gidsetsize > 0) + td->td_proc->p_ucred->cr_gid = groups[0]; + } + + if (groups != smallgroups) + free(groups, M_TEMP); + return (error); +} +#endif /* COMPAT_FREEBSD14 */ + #ifndef _SYS_SYSPROTO_H_ struct setgroups_args { int gidsetsize; @@ -1210,8 +1267,7 @@ sys_setgroups(struct thread *td, struct setgroups_args *uap) * setgroups() differ. */ gidsetsize = uap->gidsetsize; - /* XXXKE Limit to ngroups_max when we change the userland interface. */ - if (gidsetsize > ngroups_max + 1 || gidsetsize < 0) + if (gidsetsize > ngroups_max || gidsetsize < 0) return (EINVAL); if (gidsetsize > CRED_SMALLGROUPS_NB) @@ -1238,35 +1294,17 @@ kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups) struct proc *p = td->td_proc; struct ucred *newcred, *oldcred; int ngrp, error; - gid_t egid; ngrp = *ngrpp; /* Sanity check size. */ - /* XXXKE Limit to ngroups_max when we change the userland interface. */ - if (ngrp < 0 || ngrp > ngroups_max + 1) + if (ngrp < 0 || ngrp > ngroups_max) return (EINVAL); AUDIT_ARG_GROUPSET(groups, ngrp); - /* - * setgroups(0, NULL) is a legitimate way of clearing the groups vector - * on non-BSD systems (which generally do not have the egid in the - * groups[0]). We risk security holes when running non-BSD software if - * we do not do the same. So we allow and treat 0 for 'ngrp' specially - * below (twice). - */ - if (ngrp != 0) { - /* - * To maintain userland compat for now, we use the first group - * as our egid and we'll use the rest as our supplemental - * groups. - */ - egid = groups[0]; - ngrp--; - groups++; - groups_normalize(&ngrp, groups); - *ngrpp = ngrp; - } + groups_normalize(&ngrp, groups); + *ngrpp = ngrp; + newcred = crget(); crextend(newcred, ngrp); PROC_LOCK(p); @@ -1289,15 +1327,7 @@ kern_setgroups(struct thread *td, int *ngrpp, gid_t *groups) if (error) goto fail; - /* - * If some groups were passed, the first one is currently the desired - * egid. This code is to be removed (along with some commented block - * above) when setgroups() is changed to take only supplementary groups. - */ - if (ngrp != 0) - newcred->cr_gid = egid; crsetgroups_internal(newcred, ngrp, groups); - setsugid(p); proc_set_cred(p, newcred); PROC_UNLOCK(p); diff --git a/sys/kern/syscalls.c b/sys/kern/syscalls.c index 09bf4d519927..4122f9261871 100644 --- a/sys/kern/syscalls.c +++ b/sys/kern/syscalls.c @@ -84,8 +84,8 @@ const char *syscallnames[] = { "obs_vhangup", /* 76 = obsolete vhangup */ "obs_vlimit", /* 77 = obsolete vlimit */ "mincore", /* 78 = mincore */ - "getgroups", /* 79 = getgroups */ - "setgroups", /* 80 = setgroups */ + "compat14.getgroups", /* 79 = freebsd14 getgroups */ + "compat14.setgroups", /* 80 = freebsd14 setgroups */ "getpgrp", /* 81 = getpgrp */ "setpgid", /* 82 = setpgid */ "setitimer", /* 83 = setitimer */ @@ -600,4 +600,6 @@ const char *syscallnames[] = { "exterrctl", /* 592 = exterrctl */ "inotify_add_watch_at", /* 593 = inotify_add_watch_at */ "inotify_rm_watch", /* 594 = inotify_rm_watch */ + "getgroups", /* 595 = getgroups */ + "setgroups", /* 596 = setgroups */ }; diff --git a/sys/kern/syscalls.master b/sys/kern/syscalls.master index 53b5d3cbbba9..fa64597d14a5 100644 --- a/sys/kern/syscalls.master +++ b/sys/kern/syscalls.master @@ -552,13 +552,13 @@ _Out_writes_bytes_(len/PAGE_SIZE) char *vec ); } -79 AUE_GETGROUPS STD|CAPENABLED { +79 AUE_GETGROUPS STD|CAPENABLED|COMPAT14 { int getgroups( int gidsetsize, _Out_writes_opt_(gidsetsize) gid_t *gidset ); } -80 AUE_SETGROUPS STD { +80 AUE_SETGROUPS STD|COMPAT14 { int setgroups( int gidsetsize, _In_reads_(gidsetsize) const gid_t *gidset @@ -3371,5 +3371,17 @@ int wd ); } +595 AUE_GETGROUPS STD|CAPENABLED { + int getgroups( + int gidsetsize, + _Out_writes_opt_(gidsetsize) gid_t *gidset + ); + } +596 AUE_SETGROUPS STD { + int setgroups( + int gidsetsize, + _In_reads_(gidsetsize) const gid_t *gidset + ); + } ; vim: syntax=off diff --git a/sys/kern/systrace_args.c b/sys/kern/systrace_args.c index 4dfc63924da9..2b1ea9eed8d4 100644 --- a/sys/kern/systrace_args.c +++ b/sys/kern/systrace_args.c @@ -454,22 +454,6 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 3; break; } - /* getgroups */ - case 79: { - struct getgroups_args *p = params; - iarg[a++] = p->gidsetsize; /* int */ - uarg[a++] = (intptr_t)p->gidset; /* gid_t * */ - *n_args = 2; - break; - } - /* setgroups */ - case 80: { - struct setgroups_args *p = params; - iarg[a++] = p->gidsetsize; /* int */ - uarg[a++] = (intptr_t)p->gidset; /* const gid_t * */ - *n_args = 2; - break; - } /* getpgrp */ case 81: { *n_args = 0; @@ -3500,6 +3484,22 @@ systrace_args(int sysnum, void *params, uint64_t *uarg, int *n_args) *n_args = 2; break; } + /* getgroups */ + case 595: { + struct getgroups_args *p = params; + iarg[a++] = p->gidsetsize; /* int */ + uarg[a++] = (intptr_t)p->gidset; /* gid_t * */ + *n_args = 2; + break; + } + /* setgroups */ + case 596: { + struct setgroups_args *p = params; + iarg[a++] = p->gidsetsize; /* int */ + uarg[a++] = (intptr_t)p->gidset; /* const gid_t * */ + *n_args = 2; + break; + } default: *n_args = 0; break; @@ -4199,32 +4199,6 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; - /* getgroups */ - case 79: - switch (ndx) { - case 0: - p = "int"; - break; - case 1: - p = "userland gid_t *"; - break; - default: - break; - }; - break; - /* setgroups */ - case 80: - switch (ndx) { - case 0: - p = "int"; - break; - case 1: - p = "userland const gid_t *"; - break; - default: - break; - }; - break; /* getpgrp */ case 81: break; @@ -9367,6 +9341,32 @@ systrace_entry_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) break; }; break; + /* getgroups */ + case 595: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland gid_t *"; + break; + default: + break; + }; + break; + /* setgroups */ + case 596: + switch (ndx) { + case 0: + p = "int"; + break; + case 1: + p = "userland const gid_t *"; + break; + default: + break; + }; + break; default: break; }; @@ -9633,16 +9633,6 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; - /* getgroups */ - case 79: - if (ndx == 0 || ndx == 1) - p = "int"; - break; - /* setgroups */ - case 80: - if (ndx == 0 || ndx == 1) - p = "int"; - break; /* getpgrp */ case 81: /* setpgid */ @@ -11365,6 +11355,16 @@ systrace_return_setargdesc(int sysnum, int ndx, char *desc, size_t descsz) if (ndx == 0 || ndx == 1) p = "int"; break; + /* getgroups */ + case 595: + if (ndx == 0 || ndx == 1) + p = "int"; + break; + /* setgroups */ + case 596: + if (ndx == 0 || ndx == 1) + p = "int"; + break; default: break; }; diff --git a/sys/kern/uipc_shm.c b/sys/kern/uipc_shm.c index 85fe48ddd466..eb1327f7f2de 100644 --- a/sys/kern/uipc_shm.c +++ b/sys/kern/uipc_shm.c @@ -1160,7 +1160,8 @@ kern_shm_open2(struct thread *td, const char *userpath, int flags, mode_t mode, if ((flags & O_ACCMODE) != O_RDONLY && (flags & O_ACCMODE) != O_RDWR) return (EINVAL); - if ((flags & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC)) != 0) + if ((flags & ~(O_ACCMODE | O_CREAT | O_EXCL | O_TRUNC | O_CLOEXEC | + O_CLOFORK)) != 0) return (EINVAL); largepage = (shmflags & SHM_LARGEPAGE) != 0; diff --git a/sys/kern/vfs_inotify.c b/sys/kern/vfs_inotify.c index 746a5a39208e..b265a5ff3a62 100644 --- a/sys/kern/vfs_inotify.c +++ b/sys/kern/vfs_inotify.c @@ -801,6 +801,7 @@ vn_inotify_add_watch(struct vnode *vp, struct inotify_softc *sc, uint32_t mask, vn_lock(vp, LK_SHARED | LK_RETRY); if (error != 0) break; + NDFREE_PNBUF(&nd); vn_irflag_set_cond(nd.ni_vp, VIRF_INOTIFY_PARENT); vrele(nd.ni_vp); } diff --git a/sys/netinet/icmp6.h b/sys/netinet/icmp6.h index 7845b682f3e4..2ca5b3433e47 100644 --- a/sys/netinet/icmp6.h +++ b/sys/netinet/icmp6.h @@ -713,9 +713,6 @@ void icmp6_redirect_input(struct mbuf *, int); void icmp6_redirect_output(struct mbuf *, struct nhop_object *); int icmp6_ratelimit(const struct in6_addr *, const int, const int); -struct ip6ctlparam; -void icmp6_mtudisc_update(struct ip6ctlparam *, int); - /* XXX: is this the right place for these macros? */ #define icmp6_ifstat_inc(ifp, tag) \ do { \ diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index 1fce7c591639..2e039ebbfdd2 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -3205,7 +3205,7 @@ tcp6_next_pmtu(const struct icmp6_hdr *icmp6) * small, set to the min. */ if (mtu < IPV6_MMTU) - mtu = IPV6_MMTU - 8; /* XXXNP: what is the adjustment for? */ + mtu = IPV6_MMTU; return (mtu); } diff --git a/sys/netinet/tcp_timewait.c b/sys/netinet/tcp_timewait.c index c095fc8f7765..ce63fcf9ffc0 100644 --- a/sys/netinet/tcp_timewait.c +++ b/sys/netinet/tcp_timewait.c @@ -101,7 +101,7 @@ sysctl_net_inet_tcp_nolocaltimewait(SYSCTL_HANDLER_ARGS) if (error == 0 && req->newptr) { V_nolocaltimewait = new; gone_in(16, "net.inet.tcp.nolocaltimewait is obsolete." - " Use net.inet.tcp.local_msl instead.\n"); + " Use net.inet.tcp.msl_local instead.\n"); } return (error); } diff --git a/sys/netinet6/icmp6.c b/sys/netinet6/icmp6.c index d89515d7eda5..f98381499b2d 100644 --- a/sys/netinet6/icmp6.c +++ b/sys/netinet6/icmp6.c @@ -159,6 +159,7 @@ static int ni6_addrs(struct icmp6_nodeinfo *, struct mbuf *, static int ni6_store_addrs(struct icmp6_nodeinfo *, struct icmp6_nodeinfo *, struct ifnet *, int); static int icmp6_notify_error(struct mbuf **, int, int); +static void icmp6_mtudisc_update(struct ip6ctlparam *ip6cp); /* * Kernel module interface for updating icmp6stat. The argument is an index @@ -1115,7 +1116,7 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len) if (icmp6type == ICMP6_PACKET_TOO_BIG) { notifymtu = ntohl(icmp6->icmp6_mtu); ip6cp.ip6c_cmdarg = (void *)¬ifymtu; - icmp6_mtudisc_update(&ip6cp, 1); /*XXX*/ + icmp6_mtudisc_update(&ip6cp); } if (ip6_ctlprotox[nxt] != NULL) @@ -1130,47 +1131,18 @@ icmp6_notify_error(struct mbuf **mp, int off, int icmp6len) return (-1); } -void -icmp6_mtudisc_update(struct ip6ctlparam *ip6cp, int validated) +static void +icmp6_mtudisc_update(struct ip6ctlparam *ip6cp) { struct in6_addr *dst = &ip6cp->ip6c_finaldst->sin6_addr; struct icmp6_hdr *icmp6 = ip6cp->ip6c_icmp6; - struct mbuf *m = ip6cp->ip6c_m; /* will be necessary for scope issue */ + struct mbuf *m = ip6cp->ip6c_m; u_int mtu = ntohl(icmp6->icmp6_mtu); struct in_conninfo inc; uint32_t max_mtu; -#if 0 - /* - * RFC2460 section 5, last paragraph. - * even though minimum link MTU for IPv6 is IPV6_MMTU, - * we may see ICMPv6 too big with mtu < IPV6_MMTU - * due to packet translator in the middle. - * see ip6_output() and ip6_getpmtu() "alwaysfrag" case for - * special handling. - */ if (mtu < IPV6_MMTU) return; -#endif - - /* - * we reject ICMPv6 too big with abnormally small value. - * XXX what is the good definition of "abnormally small"? - */ - if (mtu < sizeof(struct ip6_hdr) + sizeof(struct ip6_frag) + 8) - return; - - if (!validated) - return; - - /* - * In case the suggested mtu is less than IPV6_MMTU, we - * only need to remember that it was for above mentioned - * "alwaysfrag" case. - * Try to be as close to the spec as possible. - */ - if (mtu < IPV6_MMTU) - mtu = IPV6_MMTU - 8; bzero(&inc, sizeof(inc)); inc.inc_fibnum = M_GETFIB(m); diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index ed71c58fffbe..6299ce6e146b 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -145,11 +145,10 @@ static int ip6_insertfraghdr(struct mbuf *, struct mbuf *, int, struct ip6_frag **); static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); -static int ip6_getpmtu(struct route_in6 *, int, - struct ifnet *, const struct in6_addr *, u_long *, int *, u_int, - u_int); -static int ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long, - u_long *, int *, u_int); +static void ip6_getpmtu(struct route_in6 *, int, + struct ifnet *, const struct in6_addr *, u_long *, u_int, u_int); +static void ip6_calcmtu(struct ifnet *, const struct in6_addr *, u_long, + u_long *, u_int); static int ip6_getpmtu_ctl(u_int, const struct in6_addr *, u_long *); static int copypktopts(struct ip6_pktopts *, struct ip6_pktopts *, int); @@ -418,7 +417,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, int vlan_pcp = -1; struct in6_ifaddr *ia = NULL; u_long mtu; - int alwaysfrag, dontfrag; + int dontfrag; u_int32_t optlen, plen = 0, unfragpartlen; struct ip6_exthdrs exthdrs; struct in6_addr src0, dst0; @@ -939,12 +938,10 @@ nonh6lookup: *ifpp = ifp; /* Determine path MTU. */ - if ((error = ip6_getpmtu(ro_pmtu, ro != ro_pmtu, ifp, &ip6->ip6_dst, - &mtu, &alwaysfrag, fibnum, *nexthdrp)) != 0) - goto bad; - KASSERT(mtu > 0, ("%s:%d: mtu %ld, ro_pmtu %p ro %p ifp %p " - "alwaysfrag %d fibnum %u\n", __func__, __LINE__, mtu, ro_pmtu, ro, - ifp, alwaysfrag, fibnum)); + ip6_getpmtu(ro_pmtu, ro != ro_pmtu, ifp, &ip6->ip6_dst, &mtu, fibnum, + *nexthdrp); + KASSERT(mtu > 0, ("%s:%d: mtu %ld, ro_pmtu %p ro %p ifp %p fibnum %u", + __func__, __LINE__, mtu, ro_pmtu, ro, ifp, fibnum)); /* * The caller of this function may specify to use the minimum MTU @@ -1121,20 +1118,13 @@ passout: * Send the packet to the outgoing interface. * If necessary, do IPv6 fragmentation before sending. * - * The logic here is rather complex: - * 1: normal case (dontfrag == 0, alwaysfrag == 0) + * 1: normal case (dontfrag == 0) * 1-a: send as is if tlen <= path mtu * 1-b: fragment if tlen > path mtu * * 2: if user asks us not to fragment (dontfrag == 1) * 2-a: send as is if tlen <= interface mtu * 2-b: error if tlen > interface mtu - * - * 3: if we always need to attach fragment header (alwaysfrag == 1) - * always fragment - * - * 4: if dontfrag == 1 && alwaysfrag == 1 - * error, as we cannot handle this conflicting request. */ sw_csum = m->m_pkthdr.csum_flags; if (!hdrsplit) { @@ -1157,14 +1147,9 @@ passout: dontfrag = 1; else dontfrag = 0; - if (dontfrag && alwaysfrag) { /* Case 4. */ - /* Conflicting request - can't transmit. */ - error = EMSGSIZE; - goto bad; - } if (dontfrag && tlen > IN6_LINKMTU(ifp) && !tso) { /* Case 2-b. */ /* - * Even if the DONTFRAG option is specified, we cannot send the + * If the DONTFRAG option is specified, we cannot send the * packet when the data length is larger than the MTU of the * outgoing interface. * Notify the error by sending IPV6_PATHMTU ancillary data if @@ -1178,7 +1163,7 @@ passout: } /* Transmit packet without fragmentation. */ - if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* Cases 1-a and 2-a. */ + if (dontfrag || tlen <= mtu) { /* Cases 1-a and 2-a. */ struct in6_ifaddr *ia6; ip6 = mtod(m, struct ip6_hdr *); @@ -1194,7 +1179,7 @@ passout: goto done; } - /* Try to fragment the packet. Cases 1-b and 3. */ + /* Try to fragment the packet. Case 1-b. */ if (mtu < IPV6_MMTU) { /* Path MTU cannot be less than IPV6_MMTU. */ error = EMSGSIZE; @@ -1478,9 +1463,10 @@ ip6_getpmtu_ctl(u_int fibnum, const struct in6_addr *dst, u_long *mtup) NET_EPOCH_ENTER(et); nh = fib6_lookup(fibnum, &kdst, scopeid, NHR_NONE, 0); - if (nh != NULL) - error = ip6_calcmtu(nh->nh_ifp, dst, nh->nh_mtu, mtup, NULL, 0); - else + if (nh != NULL) { + ip6_calcmtu(nh->nh_ifp, dst, nh->nh_mtu, mtup, 0); + error = 0; + } else error = EHOSTUNREACH; NET_EPOCH_EXIT(et); @@ -1494,13 +1480,12 @@ ip6_getpmtu_ctl(u_int fibnum, const struct in6_addr *dst, u_long *mtup) * inside @ro_pmtu to avoid subsequent route lookups after packet * filter processing. * - * Stores mtu and always-frag value into @mtup and @alwaysfragp. - * Returns 0 on success. + * Stores mtu into @mtup. */ -static int +static void ip6_getpmtu(struct route_in6 *ro_pmtu, int do_lookup, struct ifnet *ifp, const struct in6_addr *dst, u_long *mtup, - int *alwaysfragp, u_int fibnum, u_int proto) + u_int fibnum, u_int proto) { struct nhop_object *nh; struct in6_addr kdst; @@ -1544,65 +1529,41 @@ ip6_getpmtu(struct route_in6 *ro_pmtu, int do_lookup, if (ro_pmtu != NULL && ro_pmtu->ro_nh != NULL) mtu = ro_pmtu->ro_nh->nh_mtu; - return (ip6_calcmtu(ifp, dst, mtu, mtup, alwaysfragp, proto)); + ip6_calcmtu(ifp, dst, mtu, mtup, proto); } /* * Calculate MTU based on transmit @ifp, route mtu @rt_mtu and * hostcache data for @dst. - * Stores mtu and always-frag value into @mtup and @alwaysfragp. - * - * Returns 0 on success. + * Stores mtu into @mtup. */ -static int +static void ip6_calcmtu(struct ifnet *ifp, const struct in6_addr *dst, u_long rt_mtu, - u_long *mtup, int *alwaysfragp, u_int proto) + u_long *mtup, u_int proto) { u_long mtu = 0; - int alwaysfrag = 0; - int error = 0; if (rt_mtu > 0) { - u_int32_t ifmtu; - struct in_conninfo inc; - - bzero(&inc, sizeof(inc)); - inc.inc_flags |= INC_ISIPV6; - inc.inc6_faddr = *dst; - - ifmtu = IN6_LINKMTU(ifp); + /* Skip the hostcache if the protocol handles PMTU changes. */ + if (proto != IPPROTO_TCP && proto != IPPROTO_SCTP) { + struct in_conninfo inc = { + .inc_flags = INC_ISIPV6, + .inc6_faddr = *dst, + }; - /* TCP is known to react to pmtu changes so skip hc */ - if (proto != IPPROTO_TCP) mtu = tcp_hc_getmtu(&inc); + } if (mtu) mtu = min(mtu, rt_mtu); else mtu = rt_mtu; - if (mtu == 0) - mtu = ifmtu; - else if (mtu < IPV6_MMTU) { - /* - * RFC2460 section 5, last paragraph: - * if we record ICMPv6 too big message with - * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU - * or smaller, with framgent header attached. - * (fragment header is needed regardless from the - * packet size, for translators to identify packets) - */ - alwaysfrag = 1; - mtu = IPV6_MMTU; - } - } else if (ifp) { + } + + if (mtu == 0) mtu = IN6_LINKMTU(ifp); - } else - error = EHOSTUNREACH; /* XXX */ *mtup = mtu; - if (alwaysfragp) - *alwaysfragp = alwaysfrag; - return (error); } /* diff --git a/sys/powerpc/mpc85xx/mpc85xx_gpio.c b/sys/powerpc/mpc85xx/mpc85xx_gpio.c index cb96d768adef..7353ed7bac7b 100644 --- a/sys/powerpc/mpc85xx/mpc85xx_gpio.c +++ b/sys/powerpc/mpc85xx/mpc85xx_gpio.c @@ -228,12 +228,13 @@ mpc85xx_gpio_attach(device_t dev) OF_device_register_xref(OF_xref_from_node(ofw_bus_get_node(dev)), dev); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { mpc85xx_gpio_detach(dev); return (ENOMEM); } + bus_attach_children(dev); return (0); } diff --git a/sys/riscv/sifive/sifive_gpio.c b/sys/riscv/sifive/sifive_gpio.c index ef68d2b39da3..98bff2f72082 100644 --- a/sys/riscv/sifive/sifive_gpio.c +++ b/sys/riscv/sifive/sifive_gpio.c @@ -157,13 +157,14 @@ sfgpio_attach(device_t dev) sc->gpio_pins[i].gp_name[GPIOMAXNAME - 1] = '\0'; } - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "Cannot attach gpiobus\n"); error = ENXIO; goto fail; } + bus_attach_children(dev); return (0); fail: diff --git a/sys/riscv/starfive/jh7110_gpio.c b/sys/riscv/starfive/jh7110_gpio.c index 452a3306b4a1..1ed7d9f42259 100644 --- a/sys/riscv/starfive/jh7110_gpio.c +++ b/sys/riscv/starfive/jh7110_gpio.c @@ -321,13 +321,14 @@ jh7110_gpio_attach(device_t dev) JH7110_GPIO_WRITE(sc, GPIOE_1, 0); JH7110_GPIO_WRITE(sc, GPIOEN, 1); - sc->busdev = gpiobus_attach_bus(dev); + sc->busdev = gpiobus_add_bus(dev); if (sc->busdev == NULL) { device_printf(dev, "Cannot attach gpiobus\n"); jh7110_gpio_detach(dev); return (ENXIO); } + bus_attach_children(dev); return (0); } diff --git a/sys/sys/param.h b/sys/sys/param.h index b9942c7bc2fd..c410a6ee666f 100644 --- a/sys/sys/param.h +++ b/sys/sys/param.h @@ -74,7 +74,7 @@ * cannot include sys/param.h and should only be updated here. */ #undef __FreeBSD_version -#define __FreeBSD_version 1500059 +#define __FreeBSD_version 1500060 /* * __FreeBSD_kernel__ indicates that this system uses the kernel of FreeBSD, diff --git a/sys/sys/syscall.h b/sys/sys/syscall.h index d703a11fda01..2d6903967e15 100644 --- a/sys/sys/syscall.h +++ b/sys/sys/syscall.h @@ -85,8 +85,8 @@ /* 76 is obsolete vhangup */ /* 77 is obsolete vlimit */ #define SYS_mincore 78 -#define SYS_getgroups 79 -#define SYS_setgroups 80 +#define SYS_freebsd14_getgroups 79 +#define SYS_freebsd14_setgroups 80 #define SYS_getpgrp 81 #define SYS_setpgid 82 #define SYS_setitimer 83 @@ -533,4 +533,6 @@ #define SYS_exterrctl 592 #define SYS_inotify_add_watch_at 593 #define SYS_inotify_rm_watch 594 -#define SYS_MAXSYSCALL 595 +#define SYS_getgroups 595 +#define SYS_setgroups 596 +#define SYS_MAXSYSCALL 597 diff --git a/sys/sys/syscall.mk b/sys/sys/syscall.mk index b7ded62cacb4..d1172c2dc7bf 100644 --- a/sys/sys/syscall.mk +++ b/sys/sys/syscall.mk @@ -65,8 +65,8 @@ MIASM = \ mprotect.o \ madvise.o \ mincore.o \ - getgroups.o \ - setgroups.o \ + freebsd14_getgroups.o \ + freebsd14_setgroups.o \ getpgrp.o \ setpgid.o \ setitimer.o \ @@ -436,4 +436,6 @@ MIASM = \ setcred.o \ exterrctl.o \ inotify_add_watch_at.o \ - inotify_rm_watch.o + inotify_rm_watch.o \ + getgroups.o \ + setgroups.o diff --git a/sys/sys/sysproto.h b/sys/sys/sysproto.h index 8d666f9c8ee9..98311a6dbf94 100644 --- a/sys/sys/sysproto.h +++ b/sys/sys/sysproto.h @@ -273,14 +273,6 @@ struct mincore_args { char len_l_[PADL_(size_t)]; size_t len; char len_r_[PADR_(size_t)]; char vec_l_[PADL_(char *)]; char * vec; char vec_r_[PADR_(char *)]; }; -struct getgroups_args { - char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; - char gidset_l_[PADL_(gid_t *)]; gid_t * gidset; char gidset_r_[PADR_(gid_t *)]; -}; -struct setgroups_args { - char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; - char gidset_l_[PADL_(const gid_t *)]; const gid_t * gidset; char gidset_r_[PADR_(const gid_t *)]; -}; struct getpgrp_args { syscallarg_t dummy; }; @@ -1901,6 +1893,14 @@ struct inotify_rm_watch_args { char fd_l_[PADL_(int)]; int fd; char fd_r_[PADR_(int)]; char wd_l_[PADL_(int)]; int wd; char wd_r_[PADR_(int)]; }; +struct getgroups_args { + char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; + char gidset_l_[PADL_(gid_t *)]; gid_t * gidset; char gidset_r_[PADR_(gid_t *)]; +}; +struct setgroups_args { + char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; + char gidset_l_[PADL_(const gid_t *)]; const gid_t * gidset; char gidset_r_[PADR_(const gid_t *)]; +}; int sys__exit(struct thread *, struct _exit_args *); int sys_fork(struct thread *, struct fork_args *); int sys_read(struct thread *, struct read_args *); @@ -1957,8 +1957,6 @@ int sys_munmap(struct thread *, struct munmap_args *); int sys_mprotect(struct thread *, struct mprotect_args *); int sys_madvise(struct thread *, struct madvise_args *); int sys_mincore(struct thread *, struct mincore_args *); -int sys_getgroups(struct thread *, struct getgroups_args *); -int sys_setgroups(struct thread *, struct setgroups_args *); int sys_getpgrp(struct thread *, struct getpgrp_args *); int sys_setpgid(struct thread *, struct setpgid_args *); int sys_setitimer(struct thread *, struct setitimer_args *); @@ -2305,6 +2303,8 @@ int sys_setcred(struct thread *, struct setcred_args *); int sys_exterrctl(struct thread *, struct exterrctl_args *); int sys_inotify_add_watch_at(struct thread *, struct inotify_add_watch_at_args *); int sys_inotify_rm_watch(struct thread *, struct inotify_rm_watch_args *); +int sys_getgroups(struct thread *, struct getgroups_args *); +int sys_setgroups(struct thread *, struct setgroups_args *); #ifdef COMPAT_43 @@ -2799,6 +2799,16 @@ int freebsd13_swapoff(struct thread *, struct freebsd13_swapoff_args *); #ifdef COMPAT_FREEBSD14 +struct freebsd14_getgroups_args { + char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; + char gidset_l_[PADL_(gid_t *)]; gid_t * gidset; char gidset_r_[PADR_(gid_t *)]; +}; +struct freebsd14_setgroups_args { + char gidsetsize_l_[PADL_(int)]; int gidsetsize; char gidsetsize_r_[PADR_(int)]; + char gidset_l_[PADL_(const gid_t *)]; const gid_t * gidset; char gidset_r_[PADR_(const gid_t *)]; +}; +int freebsd14_getgroups(struct thread *, struct freebsd14_getgroups_args *); +int freebsd14_setgroups(struct thread *, struct freebsd14_setgroups_args *); #endif /* COMPAT_FREEBSD14 */ @@ -2873,8 +2883,8 @@ int freebsd13_swapoff(struct thread *, struct freebsd13_swapoff_args *); #define SYS_AUE_mprotect AUE_MPROTECT #define SYS_AUE_madvise AUE_MADVISE #define SYS_AUE_mincore AUE_MINCORE -#define SYS_AUE_getgroups AUE_GETGROUPS -#define SYS_AUE_setgroups AUE_SETGROUPS +#define SYS_AUE_freebsd14_getgroups AUE_GETGROUPS +#define SYS_AUE_freebsd14_setgroups AUE_SETGROUPS #define SYS_AUE_getpgrp AUE_GETPGRP #define SYS_AUE_setpgid AUE_SETPGRP #define SYS_AUE_setitimer AUE_SETITIMER @@ -3289,6 +3299,8 @@ int freebsd13_swapoff(struct thread *, struct freebsd13_swapoff_args *); #define SYS_AUE_exterrctl AUE_NULL #define SYS_AUE_inotify_add_watch_at AUE_INOTIFY #define SYS_AUE_inotify_rm_watch AUE_INOTIFY +#define SYS_AUE_getgroups AUE_GETGROUPS +#define SYS_AUE_setgroups AUE_SETGROUPS #undef PAD_ #undef PADL_ diff --git a/sys/sys/watchdog.h b/sys/sys/watchdog.h index 4a16b18509f5..8401d343a6b7 100644 --- a/sys/sys/watchdog.h +++ b/sys/sys/watchdog.h @@ -32,15 +32,16 @@ #define _SYS_WATCHDOG_H #include <sys/ioccom.h> +#include <sys/_types.h> #define _PATH_WATCHDOG "fido" -#define WDIOCPATPAT _IOW('W', 42, u_int) /* pat the watchdog */ -#define WDIOC_SETTIMEOUT _IOW('W', 43, int) /* set/reset the timer */ -#define WDIOC_GETTIMEOUT _IOR('W', 44, int) /* get total timeout */ -#define WDIOC_GETTIMELEFT _IOR('W', 45, int) /* get time left */ -#define WDIOC_GETPRETIMEOUT _IOR('W', 46, int) /* get the pre-timeout */ -#define WDIOC_SETPRETIMEOUT _IOW('W', 47, int) /* set the pre-timeout */ +#define WDIOC_PATPAT _IOW('W', 52, sbintime_t) /* pat the watchdog */ +#define WDIOC_SETTIMEOUT _IOW('W', 53, sbintime_t) /* set/reset the timer */ +#define WDIOC_GETTIMEOUT _IOR('W', 54, sbintime_t) /* get total timeout */ +#define WDIOC_GETTIMELEFT _IOR('W', 55, sbintime_t) /* get time left */ +#define WDIOC_GETPRETIMEOUT _IOR('W', 56, sbintime_t) /* get the pre-timeout */ +#define WDIOC_SETPRETIMEOUT _IOW('W', 57, sbintime_t) /* set the pre-timeout */ /* set the action when a pre-timeout occurs see: WD_SOFT_* */ #define WDIOC_SETPRETIMEOUTACT _IOW('W', 48, int) @@ -48,6 +49,8 @@ #define WDIOC_SETSOFT _IOW('W', 49, int) #define WDIOC_SETSOFTTIMEOUTACT _IOW('W', 50, int) +#define WDIOC_CONTROL _IOW('W', 51, int) /* configure watchdog */ + #define WD_ACTIVE 0x8000000 /* * Watchdog reset, timeout set to value in WD_INTERVAL field. @@ -93,6 +96,11 @@ #define WD_TO_64SEC 36 #define WD_TO_128SEC 37 +/* Control options for WDIOC_CONTROL */ +#define WD_CTRL_DISABLE 0x00000000 +#define WD_CTRL_ENABLE 0x00000001 +#define WD_CTRL_RESET 0x00000002 + /* action on pre-timeout trigger */ #define WD_SOFT_PANIC 0x01 /* panic */ #define WD_SOFT_DDB 0x02 /* enter debugger */ @@ -105,11 +113,16 @@ #include <sys/_eventhandler.h> typedef void (*watchdog_fn)(void *, u_int, int *); +typedef void (*watchdog_sbt_fn)(void *, sbintime_t, sbintime_t *, int *); EVENTHANDLER_DECLARE(watchdog_list, watchdog_fn); +EVENTHANDLER_DECLARE(watchdog_sbt_list, watchdog_sbt_fn); u_int wdog_kern_last_timeout(void); int wdog_kern_pat(u_int utim); +sbintime_t wdog_kern_last_timeout_sbt(void); +int wdog_kern_pat_sbt(sbintime_t utim); +int wdog_control(int ctrl); /* * The following function pointer is used to attach a software watchdog diff --git a/sys/x86/include/apicreg.h b/sys/x86/include/apicreg.h index d610d7f11a1c..1252647fbab3 100644 --- a/sys/x86/include/apicreg.h +++ b/sys/x86/include/apicreg.h @@ -296,6 +296,8 @@ typedef struct IOAPIC ioapic_t; /* constants relating to APIC ID registers */ #define APIC_ID_MASK 0xff000000 #define APIC_ID_SHIFT 24 +#define APIC_EXT_ID_MASK 0x00fe0000 +#define APIC_EXT_ID_SHIFT 17 #define APIC_ID_CLUSTER 0xf0 #define APIC_ID_CLUSTER_ID 0x0f #define APIC_MAX_CLUSTER 0xe diff --git a/tests/ci/Makefile b/tests/ci/Makefile index 48e638fdb79c..bc45c6acdfb4 100644 --- a/tests/ci/Makefile +++ b/tests/ci/Makefile @@ -73,6 +73,7 @@ CIIMAGE= ci-${OSRELEASE}-${GITREV}-${KERNCONF}.${FORMAT} CIDISK?= ${.OBJDIR}/${CIIMAGE} VMSIZE?= 6g CITYPE?= full +KYUA_TEST_FILTERS?= META_TAR!=mktemp /tmp/meta.XXXXXX META_DIR!=mktemp -d /tmp/meta.XXXXXX META_DIROUT!=mktemp -d /tmp/meta.XXXXXX diff --git a/tests/ci/tools/ci.conf b/tests/ci/tools/ci.conf index a9998a3e5373..1d2921ab75f3 100644 --- a/tests/ci/tools/ci.conf +++ b/tests/ci/tools/ci.conf @@ -11,10 +11,32 @@ export VM_RC_LIST="auditd freebsdci" if [ "${CITYPE}" != "smoke" ]; then -export VM_EXTRA_PACKAGES="coreutils devel/py-pytest gdb jq ksh93 net/py-dpkt net/scapy nist-kat nmap perl5 python python3 sudo sysutils/porch tcptestsuite" +export VM_EXTRA_PACKAGES=" +archivers/gtar +devel/git +devel/gdb +devel/py-pytest +perl5 +lang/python +lang/python3 +net/isc-dhcp44-server +net/ndisc6 +net/py-dpkt +net/scapy +net/tcptestsuite +security/nist-kat +security/nmap +security/openvpn +security/sudo +shells/ksh93 +sysutils/coreutils +sysutils/porch +sysutils/sg3_utils +textproc/jq +" if [ "${TARGET}" = "amd64" ]; then - export VM_EXTRA_PACKAGES="${VM_EXTRA_PACKAGES} linux-c7-ltp" + export VM_EXTRA_PACKAGES="${VM_EXTRA_PACKAGES} devel/linux-ltp" fi fi @@ -39,22 +61,35 @@ test_suites.FreeBSD.disks = '/dev/vtbd2 /dev/vtbd3 /dev/vtbd4 /dev/vtbd5 /dev/vt EOF cat << EOF >> ${DESTDIR}/etc/rc.conf kld_list="" # Load modules needed by tests -kld_list="${kld_list} blake2" # sys/opencrypto -kld_list="${kld_list} cryptodev" # sys/opencrypto -kld_list="${kld_list} dummymbuf" # sys/netpfil -kld_list="${kld_list} fusefs" # sys/fs/fusefs -kld_list="${kld_list} ipsec" # sys/netipsec -kld_list="${kld_list} mac_portacl" # sys/mac/portacl -kld_list="${kld_list} mqueuefs" # sys/kern/mqueue_test -kld_list="${kld_list} pfsync" # sys/netpfil/pf (loads pf) -kld_list="${kld_list} pflog" # sys/netpfil/pf -kld_list="${kld_list} ipl" # sys/sbin/ipf (loads ipfilter) -kld_list="${kld_list} ipfw" # sys/netpfil/ipfw (loads ipfw) -kld_list="${kld_list} ipfw_nat" # sys/netpfil/ipfw (loads ipfw_nat) -kld_list="${kld_list} ipdivert" # sys/netinet (loads ipdivert) -kld_list="${kld_list} dummynet" # sys/netpfil/common -kld_list="${kld_list} carp" # sys/netinet/carp -kld_list="${kld_list} if_stf" # sys/net/if_stf +kld_list="\${kld_list} accf_data" # sys/kern/socket_accf +kld_list="\${kld_list} accf_dns" # sys/kern/socket_accf +kld_list="\${kld_list} accf_http" # sys/kern/socket_accf +kld_list="\${kld_list} accf_tls" # sys/kern/socket_accf +kld_list="\${kld_list} blake2" # sys/opencrypto +kld_list="\${kld_list} carp" # sys/netinet/carp +kld_list="\${kld_list} cryptodev" # sys/opencrypto +kld_list="\${kld_list} dummymbuf" # sys/netpfil +kld_list="\${kld_list} dummynet" # sys/netpfil/common +kld_list="\${kld_list} fusefs" # sys/fs/fusefs +kld_list="\${kld_list} if_bridge" # sys/net/if_bridge_test +kld_list="\${kld_list} if_enc" # sys/netpfil/pf +kld_list="\${kld_list} if_epair" # sys/net/if_epair_test +kld_list="\${kld_list} if_ovpn" # sys/net/if_ovpn +kld_list="\${kld_list} if_stf" # sys/net/if_stf +kld_list="\${kld_list} ipdivert" # sys/netinet (loads ipdivert) +kld_list="\${kld_list} ipfw" # sys/netpfil/ipfw (loads ipfw) +kld_list="\${kld_list} ipfw_nat" # sys/netpfil/ipfw (loads ipfw_nat) +kld_list="\${kld_list} ipl" # sys/sbin/ipf (loads ipfilter) +kld_list="\${kld_list} ipsec" # sys/netipsec +kld_list="\${kld_list} mac_portacl" # sys/mac/portacl +kld_list="\${kld_list} mqueuefs" # sys/kern/mqueue_test +kld_list="\${kld_list} pf" # sys/netpfil/pf +kld_list="\${kld_list} pflog" # sys/netpfil/pf +kld_list="\${kld_list} pflow" # sys/netpfil/pf +kld_list="\${kld_list} pfsync" # sys/netpfil/pf (loads pf) +kld_list="\${kld_list} sctp" # sys/netpfil/pf +kld_list="\${kld_list} tarfs" # sys/fs/tarfs +kld_list="\${kld_list} tcpmd5" # sys/netinet background_fsck="NO" sendmail_enable="NONE" cron_enable="NO" @@ -68,6 +103,7 @@ EOF elif [ "${CITYPE}" = "full" ]; then cat << EOF >> ${DESTDIR}/etc/rc.conf freebsdci_type="full" +freebsdci_test_filters="${KYUA_TEST_FILTERS}" EOF fi cat << EOF >> ${DESTDIR}/etc/sysctl.conf diff --git a/tests/ci/tools/freebsdci b/tests/ci/tools/freebsdci index 51bd19e2967d..42c565a45055 100755 --- a/tests/ci/tools/freebsdci +++ b/tests/ci/tools/freebsdci @@ -39,6 +39,7 @@ istar=$(file -s ${tardev} | grep "POSIX tar archive" | wc -l) load_rc_config $name : ${freebsdci_enable:="NO"} : ${freebsdci_type:="full"} +: ${freebsdci_test_filters:=""} PATH="${PATH}:/usr/local/sbin:/usr/local/bin" auto_shutdown() @@ -77,7 +78,9 @@ full_tests() tar xvf ${tardev} -C ${metadir} cd /usr/tests set +e - kyua -v parallelism=${parallelism} test + kyua \ + -v parallelism=${parallelism} \ + test ${freebsdci_test_filters} rc=$? set -e if [ ${rc} -ne 0 ] && [ ${rc} -ne 1 ]; then diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 336e73f29835..9044b1e7e4f2 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -8,6 +8,7 @@ TESTSRC= ${SRCTOP}/contrib/netbsd-tests/kernel TESTSDIR= ${TESTSBASE}/sys/kern ATF_TESTS_C+= basic_signal +ATF_TESTS_C+= copy_file_range .if ${MACHINE_ARCH} != "i386" && ${MACHINE_ARCH} != "powerpc" && \ ${MACHINE_ARCH} != "powerpcspe" # No support for atomic_load_64 on i386 or (32-bit) powerpc @@ -81,6 +82,7 @@ PROGS+= coredump_phnum_helper PROGS+= pdeathsig_helper PROGS+= sendfile_helper +LIBADD.copy_file_range+= md LIBADD.jail_lookup_root+= jail util CFLAGS.sys_getrandom+= -I${SRCTOP}/sys/contrib/zstd/lib LIBADD.sys_getrandom+= zstd diff --git a/tests/sys/kern/copy_file_range.c b/tests/sys/kern/copy_file_range.c new file mode 100644 index 000000000000..ca52eaf668e3 --- /dev/null +++ b/tests/sys/kern/copy_file_range.c @@ -0,0 +1,231 @@ +/* + * Copyright (c) 2025 Mark Johnston <markj@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/mman.h> +#include <sys/stat.h> + +#include <errno.h> +#include <fcntl.h> +#include <limits.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +#include <atf-c.h> +#include <sha256.h> + +/* + * Create a file with random data and size between 1B and 32MB. Return a file + * descriptor for the file. + */ +static int +genfile(void) +{ + char buf[256], file[NAME_MAX]; + size_t sz; + int fd; + + sz = (random() % (32 * 1024 * 1024ul)) + 1; + + snprintf(file, sizeof(file), "testfile.XXXXXX"); + fd = mkstemp(file); + ATF_REQUIRE(fd != -1); + + while (sz > 0) { + ssize_t n; + int error; + + error = getentropy(buf, sizeof(buf)); + ATF_REQUIRE(error == 0); + n = write(fd, buf, sizeof(buf) < sz ? sizeof(buf) : sz); + ATF_REQUIRE(n > 0); + + sz -= n; + } + + ATF_REQUIRE(lseek(fd, 0, SEEK_SET) == 0); + return (fd); +} + +/* + * Return true if the file data in the two file descriptors is the same, + * false otherwise. + */ +static bool +cmpfile(int fd1, int fd2) +{ + struct stat st1, st2; + void *addr1, *addr2; + size_t sz; + int res; + + ATF_REQUIRE(fstat(fd1, &st1) == 0); + ATF_REQUIRE(fstat(fd2, &st2) == 0); + if (st1.st_size != st2.st_size) + return (false); + + sz = st1.st_size; + addr1 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd1, 0); + ATF_REQUIRE(addr1 != MAP_FAILED); + addr2 = mmap(NULL, sz, PROT_READ, MAP_PRIVATE, fd2, 0); + ATF_REQUIRE(addr2 != MAP_FAILED); + + res = memcmp(addr1, addr2, sz); + + ATF_REQUIRE(munmap(addr1, sz) == 0); + ATF_REQUIRE(munmap(addr2, sz) == 0); + + return (res == 0); +} + +/* + * Exercise a few error paths in the copy_file_range() syscall. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_invalid); +ATF_TC_BODY(copy_file_range_invalid, tc) +{ + off_t off1, off2; + int fd1, fd2; + + fd1 = genfile(); + fd2 = genfile(); + + /* Can't copy a file to itself without explicit offsets. */ + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, NULL, fd1, NULL, SSIZE_MAX, 0) == -1); + + /* When copying a file to itself, ranges cannot overlap. */ + off1 = off2 = 0; + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, &off1, fd1, &off2, 1, 0) == -1); + + /* Negative offsets are not allowed. */ + off1 = -1; + off2 = 0; + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd1, &off1, fd2, &off2, 42, 0) == -1); + ATF_REQUIRE_ERRNO(EINVAL, + copy_file_range(fd2, &off2, fd1, &off1, 42, 0) == -1); +} + +/* + * Make sure that copy_file_range() updates the file offsets passed to it. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_offset); +ATF_TC_BODY(copy_file_range_offset, tc) +{ + struct stat sb; + off_t off1, off2; + ssize_t n; + int fd1, fd2; + + off1 = off2 = 0; + + fd1 = genfile(); + fd2 = open("copy", O_RDWR | O_CREAT, 0644); + ATF_REQUIRE(fd2 != -1); + + ATF_REQUIRE(fstat(fd1, &sb) == 0); + + ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0); + ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0); + + do { + off_t ooff1, ooff2; + + ooff1 = off1; + ooff2 = off2; + n = copy_file_range(fd1, &off1, fd2, &off2, sb.st_size, 0); + ATF_REQUIRE(n >= 0); + ATF_REQUIRE_EQ(off1, ooff1 + n); + ATF_REQUIRE_EQ(off2, ooff2 + n); + } while (n != 0); + + /* Offsets should have been adjusted by copy_file_range(). */ + ATF_REQUIRE_EQ(off1, sb.st_size); + ATF_REQUIRE_EQ(off2, sb.st_size); + /* Seek offsets should have been left alone. */ + ATF_REQUIRE(lseek(fd1, 0, SEEK_CUR) == 0); + ATF_REQUIRE(lseek(fd2, 0, SEEK_CUR) == 0); + /* Make sure the file contents are the same. */ + ATF_REQUIRE_MSG(cmpfile(fd1, fd2), "file contents differ"); + + ATF_REQUIRE(close(fd1) == 0); + ATF_REQUIRE(close(fd2) == 0); +} + +/* + * Make sure that copying to a larger file doesn't cause it to be truncated. + */ +ATF_TC_WITHOUT_HEAD(copy_file_range_truncate); +ATF_TC_BODY(copy_file_range_truncate, tc) +{ + struct stat sb, sb1, sb2; + char digest1[65], digest2[65]; + off_t off; + ssize_t n; + int fd1, fd2; + + fd1 = genfile(); + fd2 = genfile(); + + ATF_REQUIRE(fstat(fd1, &sb1) == 0); + ATF_REQUIRE(fstat(fd2, &sb2) == 0); + + /* fd1 refers to the smaller file. */ + if (sb1.st_size > sb2.st_size) { + int tmp; + + tmp = fd1; + fd1 = fd2; + fd2 = tmp; + ATF_REQUIRE(fstat(fd1, &sb1) == 0); + ATF_REQUIRE(fstat(fd2, &sb2) == 0); + } + + /* + * Compute a hash of the bytes in the larger file which lie beyond the + * length of the smaller file. + */ + SHA256_FdChunk(fd2, digest1, sb1.st_size, sb2.st_size - sb1.st_size); + ATF_REQUIRE(lseek(fd2, 0, SEEK_SET) == 0); + + do { + n = copy_file_range(fd1, NULL, fd2, NULL, SSIZE_MAX, 0); + ATF_REQUIRE(n >= 0); + } while (n != 0); + + /* Validate file offsets after the copy. */ + off = lseek(fd1, 0, SEEK_CUR); + ATF_REQUIRE(off == sb1.st_size); + off = lseek(fd2, 0, SEEK_CUR); + ATF_REQUIRE(off == sb1.st_size); + + /* The larger file's size should remain the same. */ + ATF_REQUIRE(fstat(fd2, &sb) == 0); + ATF_REQUIRE(sb.st_size == sb2.st_size); + + /* The bytes beyond the end of the copy should be unchanged. */ + SHA256_FdChunk(fd2, digest2, sb1.st_size, sb2.st_size - sb1.st_size); + ATF_REQUIRE_MSG(strcmp(digest1, digest2) == 0, + "trailing file contents differ after copy_file_range()"); + + /* + * Verify that the copy actually replicated bytes from the smaller file. + */ + ATF_REQUIRE(ftruncate(fd2, sb1.st_size) == 0); + ATF_REQUIRE(cmpfile(fd1, fd2)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, copy_file_range_invalid); + ATF_TP_ADD_TC(tp, copy_file_range_offset); + ATF_TP_ADD_TC(tp, copy_file_range_truncate); + + return (atf_no_error()); +} diff --git a/tools/build/depend-cleanup.sh b/tools/build/depend-cleanup.sh index 33ca8ecb709f..cd51c59ff0e1 100755 --- a/tools/build/depend-cleanup.sh +++ b/tools/build/depend-cleanup.sh @@ -63,6 +63,10 @@ # if [ "$MACHINE_ARCH" = "amd64" ]; then # clean_dep lib/libc bcmp c # fi +# +# We also have a big hammer at the top of the tree, .clean_build_epoch, to be +# used in severe cases where we can't surgically remove just the parts that +# need rebuilt. This should be used sparingly. set -e set -u @@ -80,7 +84,7 @@ err() usage() { - echo "usage: $(basename $0) [-v] [-n] objtop" >&2 + echo "usage: $(basename $0) [-v] [-n] objtop srctop" >&2 } VERBOSE= @@ -101,17 +105,31 @@ while getopts vn o; do done shift $((OPTIND-1)) -if [ $# -ne 1 ]; then +if [ $# -ne 2 ]; then usage exit 1 fi OBJTOP=$1 shift +SRCTOP=$1 +shift + if [ ! -d "$OBJTOP" ]; then err "$OBJTOP: Not a directory" fi +if [ ! -d "$SRCTOP" -o ! -f "$SRCTOP/Makefile.inc1" ]; then + err "$SRCTOP: Not the root of a src tree" +fi + +: ${CLEANMK=""} +if [ -n "$CLEANMK" ]; then + if [ -z "${MAKE+set}" ]; then + err "MAKE not set" + fi +fi + if [ -z "${MACHINE+set}" ]; then err "MACHINE not set" fi @@ -151,6 +169,69 @@ clean_dep() done } +extract_epoch() +{ + [ -s "$1" ] || return 0 + + awk 'int($1) > 0 { epoch = $1 } END { print epoch }' "$1" +} + +clean_world() +{ + local buildepoch="$1" + + # The caller may set CLEANMK in the environment to make target(s) that + # should be invoked instead of just destroying everything. This is + # generally used after legacy/bootstrap tools to avoid over-cleansing + # since we're generally in the temporary tree's ancestor. + if [ -n "$CLEANMK" ]; then + echo "Cleaning up the object tree" + run $MAKE -C "$SRCTOP" -f "$SRCTOP"/Makefile.inc1 $CLEANMK + else + echo "Cleaning up the temporary build tree" + run rm -rf "$OBJTOP" + fi + + # We don't assume that all callers will have grabbed the build epoch, so + # we'll do it here as needed. This will be useful if we add other + # non-epoch reasons to force clean. + if [ -z "$buildepoch" ]; then + buildepoch=$(extract_epoch "$SRCTOP"/.clean_build_epoch) + fi + + mkdir -p "$OBJTOP" + echo "$buildepoch" > "$OBJTOP"/.clean_build_epoch + + exit 0 +} + +check_epoch() +{ + local srcepoch objepoch + + srcepoch=$(extract_epoch "$SRCTOP"/.clean_build_epoch) + if [ -z "$srcepoch" ]; then + err "Malformed .clean_build_epoch; please validate the last line" + fi + + # We don't discriminate between the varying degrees of difference + # between epochs. If it went backwards we could be bisecting across + # epochs, in which case the original need to clean likely still stands. + objepoch=$(extract_epoch "$OBJTOP"/.clean_build_epoch) + if [ -z "$objepoch" ] || [ "$srcepoch" -ne "$objepoch" ]; then + if [ "$VERBOSE" ]; then + echo "Cleaning - src epoch: $srcepoch, objdir epoch: ${objepoch:-unknown}" + fi + + clean_world "$srcepoch" + # NORETURN + fi +} + +check_epoch + +#### Typical dependency cleanup begins here. + # Date Rev Description # 20220326 fbc002cb72d2 move from bcmp.c to bcmp.S @@ -360,3 +441,6 @@ if [ -f "$OBJTOP"/lib/clang/liblldb/liblldb.a ]; then echo "Removing old static liblldb library" run rm -f "$OBJTOP"/lib/clang/liblldb/liblldb.a fi + +# 20250813 4f766afc1ca0 tcopy converted to C++ +clean_dep usr.bin/tcopy tcopy c diff --git a/tools/tools/nanobsd/defaults.sh b/tools/tools/nanobsd/defaults.sh index 65bace957f71..59ae8d92f4af 100755 --- a/tools/tools/nanobsd/defaults.sh +++ b/tools/tools/nanobsd/defaults.sh @@ -79,7 +79,12 @@ CONF_BUILD=' ' CONF_INSTALL=' ' # Options to put in make.conf during both build- & installworld. -CONF_WORLD=' ' +CONF_WORLD=' +WITHOUT_DEBUG_FILES=true +WITHOUT_LIB32=true +WITHOUT_KERNEL_SYMBOLS=true +WITHOUT_TESTS=true +' # Kernel config file to use NANO_KERNEL=GENERIC @@ -104,7 +109,7 @@ NANO_NEWFS="-b 4096 -f 512 -i 8192 -U" NANO_DRIVE=ada0 # Target media size in 512 bytes sectors -NANO_MEDIASIZE=2000000 +NANO_MEDIASIZE=4000000 # Number of code images on media (1 or 2) NANO_IMAGES=2 diff --git a/usr.bin/netstat/inet.c b/usr.bin/netstat/inet.c index 3c4d6d4b62f9..7014f02032c2 100644 --- a/usr.bin/netstat/inet.c +++ b/usr.bin/netstat/inet.c @@ -904,7 +904,7 @@ void udp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) { struct udpstat udpstat; - uint64_t delivered; + uint64_t delivered, noportbmcast; #ifdef INET6 if (udp_done != 0) @@ -937,8 +937,11 @@ udp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) "{N:/with no checksum}\n"); p1a(udps_noport, "{:dropped-no-socket/%ju} " "{N:/dropped due to no socket}\n"); - p(udps_noportbcast, "{:dropped-broadcast-multicast/%ju} " - "{N:/broadcast\\/multicast datagram%s undelivered}\n"); + noportbmcast = udpstat.udps_noportmcast + udpstat.udps_noportbcast; + if (noportbmcast || sflag <= 1) + xo_emit("\t{:dropped-broadcast-multicast/%ju} " + "{N:/broadcast\\/multicast datagram%s undelivered}\n", + (uintmax_t)noportbmcast, plural(noportbmcast)); p1a(udps_fullsock, "{:dropped-full-socket-buffer/%ju} " "{N:/dropped due to full socket buffers}\n"); p1a(udpps_pcbhashmiss, "{:not-for-hashed-pcb/%ju} " @@ -951,7 +954,7 @@ udp_stats(u_long off, const char *name, int af1 __unused, int proto __unused) udpstat.udps_fullsock; if (delivered || sflag <= 1) xo_emit("\t{:delivered-packets/%ju} {N:/delivered}\n", - delivered); + (uintmax_t)delivered); p(udps_opackets, "{:output-packets/%ju} {N:/datagram%s output}\n"); /* the next statistic is cumulative in udps_noportbcast */ p(udps_filtermcast, "{:multicast-source-filter-matches/%ju} " diff --git a/usr.bin/newgrp/newgrp.c b/usr.bin/newgrp/newgrp.c index f1da1c8cb1f5..0971f4d13b49 100644 --- a/usr.bin/newgrp/newgrp.c +++ b/usr.bin/newgrp/newgrp.c @@ -186,7 +186,7 @@ addgroup(const char *grpname) } } - ngrps_max = sysconf(_SC_NGROUPS_MAX) + 1; + ngrps_max = sysconf(_SC_NGROUPS_MAX); if ((grps = malloc(sizeof(gid_t) * ngrps_max)) == NULL) err(1, "malloc"); if ((ngrps = getgroups(ngrps_max, (gid_t *)grps)) < 0) { @@ -194,7 +194,12 @@ addgroup(const char *grpname) goto end; } - /* Remove requested gid from supp. list if it exists. */ + /* + * Remove requested gid from supp. list if it exists and doesn't match + * our prior egid -- this exception is to avoid providing the user a + * means to get rid of a group that could be used for, e.g., negative + * permissions. + */ if (grp->gr_gid != egid && inarray(grp->gr_gid, grps, ngrps)) { for (i = 0; i < ngrps; i++) if (grps[i] == grp->gr_gid) @@ -217,10 +222,9 @@ addgroup(const char *grpname) goto end; } PRIV_END; - grps[0] = grp->gr_gid; /* Add old effective gid to supp. list if it does not exist. */ - if (egid != grp->gr_gid && !inarray(egid, grps, ngrps)) { + if (!inarray(egid, grps, ngrps)) { if (ngrps == ngrps_max) warnx("too many groups"); else { diff --git a/usr.bin/quota/quota.c b/usr.bin/quota/quota.c index b5d28fd7c184..9ad4076cec40 100644 --- a/usr.bin/quota/quota.c +++ b/usr.bin/quota/quota.c @@ -100,8 +100,7 @@ static char *filename = NULL; int main(int argc, char *argv[]) { - int ngroups; - gid_t mygid, gidset[NGROUPS]; + int ngroups; int i, ch, gflag = 0, uflag = 0, errflag = 0; while ((ch = getopt(argc, argv, "f:ghlrquv")) != -1) { @@ -142,11 +141,15 @@ main(int argc, char *argv[]) if (uflag) errflag += showuid(getuid()); if (gflag) { + gid_t mygid, myegid, gidset[NGROUPS_MAX]; + mygid = getgid(); - ngroups = getgroups(NGROUPS, gidset); + errflag += showgid(mygid); + myegid = getegid(); + errflag += showgid(myegid); + ngroups = getgroups(NGROUPS_MAX, gidset); if (ngroups < 0) err(1, "getgroups"); - errflag += showgid(mygid); for (i = 0; i < ngroups; i++) if (gidset[i] != mygid) errflag += showgid(gidset[i]); diff --git a/usr.bin/systat/ip.c b/usr.bin/systat/ip.c index 6cb3787b3f91..344b74011e99 100644 --- a/usr.bin/systat/ip.c +++ b/usr.bin/systat/ip.c @@ -82,9 +82,10 @@ static struct stat curstat, initstat, oldstat; 13999999999 packets forwarded 999999999 - no checksum 14999999999 - unreachable dests 999999999 - invalid length 15999999999 - redirects generated 999999999 - no socket for dest port -16999999999 option errors 999999999 - no socket for broadcast -17999999999 unwanted multicasts 999999999 - socket buffer full -18999999999 delivered to upper layer 999999999 total output packets +16999999999 option errors 999999999 - no socket for broadcast +17999999999 unwanted multicasts 999999999 - no socket for multicast +18999999999 delivered to upper layer 999999999 - socket buffer full +19999999999 999999999 total output packets --0123456789012345678901234567890123456789012345678901234567890123456789012345 --0 1 2 3 4 5 6 7 */ @@ -127,9 +128,10 @@ labelip(void) L(13, "packets forwarded"); R(13, "- no checksum"); L(14, "- unreachable dests"); R(14, "- invalid length"); L(15, "- redirects generated"); R(15, "- no socket for dest port"); - L(16, "option errors"); R(16, "- no socket for broadcast"); - L(17, "unwanted multicasts"); R(17, "- socket buffer full"); - L(18, "delivered to upper layer"); R(18, "total output packets"); + L(16, "option errors"); R(16, " - no socket for broadcast"); + L(17, "unwanted multicasts"); R(17, " - no socket for multicast"); + L(18, "delivered to upper layer"); R(18, "- socket buffer full"); + R(19, "total output packets"); #undef L #undef R } @@ -189,6 +191,7 @@ domode(struct stat *ret) DO(u.udps_badlen); DO(u.udps_noport); DO(u.udps_noportbcast); + DO(u.udps_noportmcast); DO(u.udps_fullsock); DO(u.udps_opackets); #undef DO @@ -237,9 +240,10 @@ showip(void) DO(i.ips_badoptions, 16, 0); DO(u.udps_noportbcast, 16, 35); DO(i.ips_notmember, 17, 0); - DO(u.udps_fullsock, 17, 35); + DO(u.udps_noportmcast, 17, 35); DO(i.ips_delivered, 18, 0); - DO(u.udps_opackets, 18, 35); + DO(u.udps_fullsock, 18, 35); + DO(u.udps_opackets, 19, 35); #undef DO } diff --git a/usr.bin/tcopy/Makefile b/usr.bin/tcopy/Makefile index 73dcd45b2e0f..831de7625db8 100644 --- a/usr.bin/tcopy/Makefile +++ b/usr.bin/tcopy/Makefile @@ -1,4 +1,4 @@ -PROG= tcopy -LIBADD= util +PROG_CXX= tcopy +LIBADD= util .include <bsd.prog.mk> diff --git a/usr.bin/tcopy/tcopy.1 b/usr.bin/tcopy/tcopy.1 index 3f12a807e41e..f74a29ba1173 100644 --- a/usr.bin/tcopy/tcopy.1 +++ b/usr.bin/tcopy/tcopy.1 @@ -30,65 +30,189 @@ .Os .Sh NAME .Nm tcopy -.Nd copy and/or verify mag tapes +.Nd read, write, copy and verify tapes .Sh SYNOPSIS .Nm -.Op Fl cvx +.Op Fl crvx +.Op Fl l Ar logfile .Op Fl s Ar maxblk .Oo Ar src Op Ar dest .Oc .Sh DESCRIPTION The .Nm -utility is designed to copy magnetic tapes. +utility is designed to read, write and copy tapes. +.Pp The only assumption made about the tape layout is that there are two sequential EOF marks at the end. -By default, the -.Nm -utility will print -information about the sizes of records and files found -on the +.Pp +The +.Ar src +argument can be a tape device and defaults to .Pa /dev/sa0 -tape, or on the tape specified by the +or it can be data in SIMH-TAP format. +If +.Ar src +is +.Dq Cm - +the standard input is read. +.Pp +If the +.Ar dest +argument is also specified, a copy of the .Ar src -argument. -If a destination tape is also specified by the +will be made onto the +.Ar dest . +If +.Ar dest +is +.Dq Cm - +standard output will be written to. +.Pp +If .Ar dest -argument, a copy of the source tape will be made. -The blocking on the -destination tape will be identical to that used on the source tape. -Copying -a tape will yield the same program output as if just printing the sizes. +is a tape device, the file and record structure will be the same. +.Pp +If +.Ar dest +is a filename ending in +.Dq Cm .000 +the contents each file on +.Ar src +will be written to sequentially numbered files +.Dq Cm .000 , +.Dq Cm .001 , +.Dq Cm .002 +etc. +Information about record sizes will be lost. +.Pp +If the +.Fl r +flag is specified, only the data will be written, information about +file and record layout is lost. +.Pp +Otherwise the data, file and record structure of +.Ar src +will be written in SIMH-TAP format. +.Pp +The +.Nm +utility will report information about the layout of +.Ar src +like this on standard output: +.Bd -literal -offset indent +file 0: block size 80: 6 records +file 0: eof after 6 records: 480 bytes +file 1: block size 3072: records 0 to 262 +file 1: block size 612: record 262 +file 1: eof after 263 records: 805476 bytes +[…] +eot +total length: 972851280 bytes time: 41 s rate: 22934.8 kB/s +.Ed +.Pp +If +.Ar dest +is +.Dq Cm - +or if +.Fl x +is specified this goes to standard error instead, +and can also be redirected with +.Fl l Ar logfile , +in which case the final total line will also be reported on standard error. +.Pp +If +.Nm +receives a +.Dv SIGINFO +signal, current counts are reported on standard error. .Pp The following options are available: .Bl -tag -width ".Fl s Ar maxblk" .It Fl c -Copy +Rewind both tapes, copy .Ar src to -.Ar dest -and then verify that the two tapes are identical. +.Ar dest , +rewind again and verify that the two tapes are now identical. +.It Fl l Ar logfile +Output all informational messages to +.Ar logfile . +.It Fl r +Write only the contents of all data blocks to the output. +The file and record structure of the input will be lost. .It Fl s Ar maxblk Specify a maximum block size, .Ar maxblk . +The default is +.Va kern.maxphys . .It Fl v -Given the two tapes +Verify that .Ar src and -.Ar dest , -verify that they are identical. +.Ar dest +are identical. +Note that the tapes are not rewound prior to the comparison. .It Fl x Output all informational messages to the standard error instead of the standard output. -This option is useful when +This option is automatic if .Ar dest is given as -.Pa /dev/stdout . +.Dq Cm - . .El +.Sh EXIT STATUS +Unfortunately all over the place, but zero always means succeess. +.Sh EXAMPLES +Verify that the tape in /dev/sa0 can be read and see the layout +of its content: +.Bd -literal -offset indent +tcopy +.Ed +.Pp +Copy a tape using two tape drives: +.Bd -literal -offset indent +tcopy /dev/sa0 /dev/sa1 +.Ed +.Pp +Copy a tape using only a single tape drive, and verify the result: +.Bd -literal -offset indent +tcopy /dev/sa0 /tmp/temp.tapfile +# change tape +tcopy -c /tmp/temp.tapfile /dev/sa0 +.Ed +.Pp +Make a cryptographic hash of both the contents and the layout of the tape in +/dev/sa1: +.Pp +.Bd -literal -offset indent +tcopy /dev/sa1 - | sha256 +.Ed +.Pp +Copy a tape to a tape drive on another machine: +.Bd -literal -offset indent +tcopy /dev/sa0 - | ssh otherhost tcopy - /dev/sa0 +.Ed +.Pp +Extract the tape files into individual files: +.Bd -literal -offset indent +tcopy /dev/sa0 /tmp/_.tape.000 +.Ed +.Pp +Ignore all structure on the tape and feed all data to +.Xr tar 1 : +.Bd -literal -offset indent +tcopy -l /dev/null -r /dev/sa0 - | tar tvf - +.Ed .Sh SEE ALSO .Xr mt 1 , +.Xr sa 4 , .Xr mtio 4 +.Sh STANDARDS +The SIMH-TAP format is documented in the open-simh github repos: +.Pa https://github.com/open-simh/simh/blob/master/doc/simh_magtape.doc .Sh HISTORY The .Nm @@ -99,19 +223,16 @@ command appeared in .It Modern tape drives may return a SCSI "Incorrect Length Indicator (ILI)" for each read with a different block size that what is on the -tape, and that slows things down a lot. +tape, and that slows +.Nm +down a lot. This can be disabled with the .Xr mt 1 command: .Bd -literal -offset indent -$ mt param sili -s 1 +mt param sili -s 1 .Ed .It -Writing an image of a tape to a file does not preserve much more than -the raw data. -Block size(s) and tape EOF marks are lost which would -otherwise be preserved in a tape-to-tape copy. -.It End of data (EOD) is determined by two sequential EOF marks with no data between them. There used to be old systems which typically wrote three EOF's between tape @@ -120,13 +241,7 @@ The .Nm utility will erroneously stop copying early in this case. .It -When using the copy/verify option -.Fl c , -.Nm -does not rewind the tapes prior to start. -A rewind is performed -after writing, prior to the verification stage. -If one does not start -at the beginning-of-tape (BOT) then the comparison -may not be of the intended data. +With +.Fl c +the tape drives are not rewound at the same time, but one after the other. .El diff --git a/usr.bin/tcopy/tcopy.c b/usr.bin/tcopy/tcopy.c deleted file mode 100644 index 39eae4126324..000000000000 --- a/usr.bin/tcopy/tcopy.c +++ /dev/null @@ -1,338 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-3-Clause - * - * Copyright (c) 1985, 1987, 1993 - * The Regents of the University of California. All rights reserved. - * - * 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. - * 3. Neither the name of the University nor the names of its contributors - * may be used to endorse or promote products derived from this software - * without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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/types.h> -#include <sys/stat.h> -#include <sys/ioctl.h> -#include <sys/mtio.h> - -#include <err.h> -#include <errno.h> -#include <fcntl.h> -#include <libutil.h> -#include <paths.h> -#include <sys/sysctl.h> -#include <signal.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <unistd.h> - -#define MAXREC (64 * 1024) -#define NOCOUNT (-2) - -static int filen, guesslen, maxblk = MAXREC; -static uint64_t lastrec, record, size, tsize; -static FILE *msg; - -static void *getspace(int); -static void intr(int); -static void usage(void) __dead2; -static void verify(int, int, char *); -static void writeop(int, int); -static void rewind_tape(int); - -int -main(int argc, char *argv[]) -{ - int lastnread, nread, nw, inp, outp; - enum {READ, VERIFY, COPY, COPYVERIFY} op = READ; - sig_t oldsig; - int ch, needeof; - char *buff; - const char *inf; - unsigned long maxphys = 0; - size_t l_maxphys = sizeof maxphys; - uint64_t tmp; - - if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0)) - maxblk = maxphys; - - msg = stdout; - guesslen = 1; - outp = -1; - while ((ch = getopt(argc, argv, "cs:vx")) != -1) - switch((char)ch) { - case 'c': - op = COPYVERIFY; - break; - case 's': - if (expand_number(optarg, &tmp)) { - warnx("illegal block size"); - usage(); - } - maxblk = tmp; - if (maxblk == 0) { - warnx("illegal block size"); - usage(); - } - guesslen = 0; - break; - case 'v': - op = VERIFY; - break; - case 'x': - msg = stderr; - break; - case '?': - default: - usage(); - } - argc -= optind; - argv += optind; - - switch(argc) { - case 0: - if (op != READ) - usage(); - inf = _PATH_DEFTAPE; - break; - case 1: - if (op != READ) - usage(); - inf = argv[0]; - break; - case 2: - if (op == READ) - op = COPY; - inf = argv[0]; - if ((outp = open(argv[1], op == VERIFY ? O_RDONLY : - op == COPY ? O_WRONLY : O_RDWR, DEFFILEMODE)) < 0) - err(3, "%s", argv[1]); - break; - default: - usage(); - } - - if ((inp = open(inf, O_RDONLY, 0)) < 0) - err(1, "%s", inf); - - buff = getspace(maxblk); - - if (op == VERIFY) { - verify(inp, outp, buff); - exit(0); - } - - if ((oldsig = signal(SIGINT, SIG_IGN)) != SIG_IGN) - (void) signal(SIGINT, intr); - - needeof = 0; - for (lastnread = NOCOUNT;;) { - if ((nread = read(inp, buff, maxblk)) == -1) { - while (errno == EINVAL && (maxblk -= 1024)) { - nread = read(inp, buff, maxblk); - if (nread >= 0) - goto r1; - } - err(1, "read error, file %d, record %ju", filen, (intmax_t)record); - } else if (nread != lastnread) { - if (lastnread != 0 && lastnread != NOCOUNT) { - if (lastrec == 0 && nread == 0) - fprintf(msg, "%ju records\n", (intmax_t)record); - else if (record - lastrec > 1) - fprintf(msg, "records %ju to %ju\n", - (intmax_t)lastrec, (intmax_t)record); - else - fprintf(msg, "record %ju\n", (intmax_t)lastrec); - } - if (nread != 0) - fprintf(msg, "file %d: block size %d: ", - filen, nread); - (void) fflush(stdout); - lastrec = record; - } -r1: guesslen = 0; - if (nread > 0) { - if (op == COPY || op == COPYVERIFY) { - if (needeof) { - writeop(outp, MTWEOF); - needeof = 0; - } - nw = write(outp, buff, nread); - if (nw != nread) { - if (nw == -1) { - warn("write error, file %d, record %ju", filen, - (intmax_t)record); - } else { - warnx("write error, file %d, record %ju", filen, - (intmax_t)record); - warnx("write (%d) != read (%d)", nw, nread); - } - errx(5, "copy aborted"); - } - } - size += nread; - record++; - } else { - if (lastnread <= 0 && lastnread != NOCOUNT) { - fprintf(msg, "eot\n"); - break; - } - fprintf(msg, - "file %d: eof after %ju records: %ju bytes\n", - filen, (intmax_t)record, (intmax_t)size); - needeof = 1; - filen++; - tsize += size; - size = record = lastrec = 0; - lastnread = 0; - } - lastnread = nread; - } - fprintf(msg, "total length: %ju bytes\n", (intmax_t)tsize); - (void)signal(SIGINT, oldsig); - if (op == COPY || op == COPYVERIFY) { - writeop(outp, MTWEOF); - writeop(outp, MTWEOF); - if (op == COPYVERIFY) { - rewind_tape(outp); - rewind_tape(inp); - verify(inp, outp, buff); - } - } - exit(0); -} - -static void -verify(int inp, int outp, char *outb) -{ - int eot, inmaxblk, inn, outmaxblk, outn; - char *inb; - - inb = getspace(maxblk); - inmaxblk = outmaxblk = maxblk; - for (eot = 0;; guesslen = 0) { - if ((inn = read(inp, inb, inmaxblk)) == -1) { - if (guesslen) - while (errno == EINVAL && (inmaxblk -= 1024)) { - inn = read(inp, inb, inmaxblk); - if (inn >= 0) - goto r1; - } - warn("read error"); - break; - } -r1: if ((outn = read(outp, outb, outmaxblk)) == -1) { - if (guesslen) - while (errno == EINVAL && (outmaxblk -= 1024)) { - outn = read(outp, outb, outmaxblk); - if (outn >= 0) - goto r2; - } - warn("read error"); - break; - } -r2: if (inn != outn) { - fprintf(msg, - "%s: tapes have different block sizes; %d != %d.\n", - "tcopy", inn, outn); - break; - } - if (!inn) { - if (eot++) { - fprintf(msg, "tcopy: tapes are identical.\n"); - free(inb); - return; - } - } else { - if (bcmp(inb, outb, inn)) { - fprintf(msg, - "tcopy: tapes have different data.\n"); - break; - } - eot = 0; - } - } - exit(1); -} - -static void -intr(int signo __unused) -{ - if (record) { - if (record - lastrec > 1) - fprintf(msg, "records %ju to %ju\n", (intmax_t)lastrec, (intmax_t)record); - else - fprintf(msg, "record %ju\n", (intmax_t)lastrec); - } - fprintf(msg, "interrupt at file %d: record %ju\n", filen, (intmax_t)record); - fprintf(msg, "total length: %ju bytes\n", (uintmax_t)(tsize + size)); - exit(1); -} - -static void * -getspace(int blk) -{ - void *bp; - - if ((bp = malloc((size_t)blk)) == NULL) - errx(11, "no memory"); - return (bp); -} - -static void -writeop(int fd, int type) -{ - struct mtop op; - - op.mt_op = type; - op.mt_count = (daddr_t)1; - if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) - err(6, "tape op"); -} - -static void -usage(void) -{ - fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); - exit(1); -} - -static void -rewind_tape(int fd) -{ - struct stat sp; - - if(fstat(fd, &sp)) - errx(12, "fstat in rewind"); - - /* - * don't want to do tape ioctl on regular files: - */ - if( S_ISREG(sp.st_mode) ) { - if( lseek(fd, 0, SEEK_SET) == -1 ) - errx(13, "lseek"); - } else - /* assume its a tape */ - writeop(fd, MTREW); -} diff --git a/usr.bin/tcopy/tcopy.cc b/usr.bin/tcopy/tcopy.cc new file mode 100644 index 000000000000..891c37f871e5 --- /dev/null +++ b/usr.bin/tcopy/tcopy.cc @@ -0,0 +1,837 @@ +/*- + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 Poul-Henning Kamp, <phk@FreeBSD.org> + * Copyright (c) 1985, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * 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 REGENTS 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 REGENTS 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 <assert.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sysexits.h> +#include <unistd.h> + +#include <sys/endian.h> +#include <sys/mtio.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/uio.h> + +#include <libutil.h> + +#define MAXREC (1024 * 1024) +#define NOCOUNT (-2) + +enum operation {READ, VERIFY, COPY, COPYVERIFY}; + +// Stuff the tape_devs need to know about +static int filen; +static uint64_t record; + +//--------------------------------------------------------------------- + +class tape_dev { + size_t max_read_size; +public: + int fd; + char *name; + enum direction {SRC, DST} direction; + + tape_dev(int file_handle, const char *spec, bool destination); + + virtual ssize_t read_blk(void *dst, size_t len); + virtual ssize_t verify_blk(void *dst, size_t len, size_t expected); + virtual void write_blk(const void *src, size_t len); + virtual void file_mark(void); + virtual void rewind(void); +}; + +tape_dev::tape_dev(int file_handle, const char *spec, bool destination) +{ + assert(file_handle >= 0); + fd = file_handle; + name = strdup(spec); + assert(name != NULL); + direction = destination ? DST : SRC; + max_read_size = 0; +} + +ssize_t +tape_dev::read_blk(void *dst, size_t len) +{ + ssize_t retval = -1; + + if (max_read_size == 0) { + max_read_size = len; + while (max_read_size > 0) { + retval = read(fd, dst, max_read_size); + if (retval >= 0 || (errno != EINVAL && errno != EFBIG)) + break; + if (max_read_size < 512) + errx(1, "Cannot find a sane max blocksize"); + + // Reduce to next lower power of two + int i = flsl((long)max_read_size - 1L); + max_read_size = 1UL << (i - 1); + } + } else { + retval = read(fd, dst, (size_t)max_read_size); + } + if (retval < 0) { + err(1, "read error, %s, file %d, record %ju", + name, filen, (uintmax_t)record); + } + return (retval); +} + +ssize_t +tape_dev::verify_blk(void *dst, size_t len, size_t expected) +{ + (void)expected; + return read_blk(dst, len); +} + +void +tape_dev::write_blk(const void *src, size_t len) +{ + assert(len > 0); + ssize_t nwrite = write(fd, src, len); + if (nwrite < 0 || (size_t) nwrite != len) { + if (nwrite == -1) { + warn("write error, file %d, record %ju", + filen, (intmax_t)record); + } else { + warnx("write error, file %d, record %ju", + filen, (intmax_t)record); + warnx("write (%zd) != read (%zd)", nwrite, len); + } + errx(5, "copy aborted"); + } + return; +} + +void +tape_dev::file_mark(void) +{ + struct mtop op; + + op.mt_op = MTWEOF; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op (write file mark)"); +} + +void +tape_dev::rewind(void) +{ + struct mtop op; + + op.mt_op = MTREW; + op.mt_count = (daddr_t)1; + if (ioctl(fd, MTIOCTOP, (char *)&op) < 0) + err(6, "tape op (rewind)"); +} + +//--------------------------------------------------------------------- + +class tap_file: public tape_dev { +public: + tap_file(int file_handle, const char *spec, bool dst) : + tape_dev(file_handle, spec, dst) {}; + ssize_t read_blk(void *dst, size_t len); + void write_blk(const void *src, size_t len); + void file_mark(void); + virtual void rewind(void); +}; + +static +ssize_t full_read(int fd, void *dst, size_t len) +{ + // Input may be a socket which returns partial reads + + ssize_t retval = read(fd, dst, len); + if (retval <= 0 || (size_t)retval == len) + return (retval); + + char *ptr = (char *)dst + retval; + size_t left = len - (size_t)retval; + while (left > 0) { + retval = read(fd, ptr, left); + if (retval <= 0) + return (retval); + left -= (size_t)retval; + ptr += retval; + } + return ((ssize_t)len); +} + +ssize_t +tap_file::read_blk(void *dst, size_t len) +{ + char lbuf[4]; + + ssize_t nread = full_read(fd, lbuf, sizeof lbuf); + if (nread == 0) + return (0); + + if ((size_t)nread != sizeof lbuf) + err(EX_DATAERR, "Corrupt tap-file, read hdr1=%zd", nread); + + uint32_t u = le32dec(lbuf); + if (u == 0 || (u >> 24) == 0xff) + return(0); + + if (u > len) + err(17, "tapfile blocksize too big, 0x%08x", u); + + size_t alen = (u + 1) & ~1; + assert (alen <= len); + + ssize_t retval = full_read(fd, dst, alen); + if (retval < 0 || (size_t)retval != alen) + err(EX_DATAERR, "Corrupt tap-file, read data=%zd", retval); + + nread = full_read(fd, lbuf, sizeof lbuf); + if ((size_t)nread != sizeof lbuf) + err(EX_DATAERR, "Corrupt tap-file, read hdr2=%zd", nread); + + uint32_t v = le32dec(lbuf); + if (u == v) + return (u); + err(EX_DATAERR, + "Corrupt tap-file, headers differ (0x%08x != 0x%08x)", u, v); +} + +void +tap_file::write_blk(const void *src, size_t len) +{ + struct iovec iov[4]; + uint8_t zero = 0; + int niov = 0; + size_t expect = 0; + char tbuf[4]; + + assert((len & ~0xffffffffULL) == 0); + le32enc(tbuf, (uint32_t)len); + + iov[niov].iov_base = tbuf; + iov[niov].iov_len = sizeof tbuf; + expect += iov[niov].iov_len; + niov += 1; + + iov[niov].iov_base = (void*)(uintptr_t)src; + iov[niov].iov_len = len; + expect += iov[niov].iov_len; + niov += 1; + + if (len & 1) { + iov[niov].iov_base = &zero; + iov[niov].iov_len = 1; + expect += iov[niov].iov_len; + niov += 1; + } + + iov[niov].iov_base = tbuf; + iov[niov].iov_len = sizeof tbuf; + expect += iov[niov].iov_len; + niov += 1; + + ssize_t nwrite = writev(fd, iov, niov); + if (nwrite < 0 || (size_t)nwrite != expect) + errx(17, "write error (%zd != %zd)", nwrite, expect); +} + +void +tap_file::file_mark(void) +{ + char tbuf[4]; + le32enc(tbuf, 0); + ssize_t nwrite = write(fd, tbuf, sizeof tbuf); + if ((size_t)nwrite != sizeof tbuf) + errx(17, "write error (%zd != %zd)", nwrite, sizeof tbuf); +} + +void +tap_file::rewind(void) +{ + off_t where; + if (direction == DST) { + char tbuf[4]; + le32enc(tbuf, 0xffffffff); + ssize_t nwrite = write(fd, tbuf, sizeof tbuf); + if ((size_t)nwrite != sizeof tbuf) + errx(17, + "write error (%zd != %zd)", nwrite, sizeof tbuf); + } + where = lseek(fd, 0L, SEEK_SET); + if (where != 0 && errno == ESPIPE) + err(EX_USAGE, "Cannot rewind sockets and pipes"); + if (where != 0) + err(17, "lseek(0) failed"); +} + +//--------------------------------------------------------------------- + +class file_set: public tape_dev { +public: + file_set(int file_handle, const char *spec, bool dst) : + tape_dev(file_handle, spec, dst) {}; + ssize_t read_blk(void *dst, size_t len); + ssize_t verify_blk(void *dst, size_t len, size_t expected); + void write_blk(const void *src, size_t len); + void file_mark(void); + void rewind(void); + void open_next(bool increment); +}; + +void +file_set::open_next(bool increment) +{ + if (fd >= 0) { + assert(close(fd) >= 0); + fd = -1; + } + if (increment) { + char *p = strchr(name, '\0') - 3; + if (++p[2] == '9') { + p[2] = '0'; + if (++p[1] == '9') { + p[1] = '0'; + if (++p[0] == '9') { + errx(EX_USAGE, + "file-set sequence overflow"); + } + } + } + } + if (direction == DST) { + fd = open(name, O_RDWR|O_CREAT, DEFFILEMODE); + if (fd < 0) + err(1, "Could not open %s", name); + } else { + fd = open(name, O_RDONLY, 0); + } +} + +ssize_t +file_set::read_blk(void *dst, size_t len) +{ + (void)dst; + (void)len; + errx(EX_SOFTWARE, "That was not supposed to happen"); +} + +ssize_t +file_set::verify_blk(void *dst, size_t len, size_t expected) +{ + (void)len; + if (fd < 0) + open_next(true); + if (fd < 0) + return (0); + ssize_t retval = read(fd, dst, expected); + if (retval == 0) { + assert(close(fd) >= 0); + fd = -1; + } + return (retval); +} + +void +file_set::write_blk(const void *src, size_t len) +{ + if (fd < 0) + open_next(true); + ssize_t nwrite = write(fd, src, len); + if (nwrite < 0 || (size_t)nwrite != len) + errx(17, "write error (%zd != %zd)", nwrite, len); +} + +void +file_set::file_mark(void) +{ + if (fd < 0) + return; + + off_t where = lseek(fd, 0UL, SEEK_CUR); + + int i = ftruncate(fd, where); + if (i < 0) + errx(17, "truncate error, %s to %jd", name, (intmax_t)where); + assert(close(fd) >= 0); + fd = -1; +} + +void +file_set::rewind(void) +{ + char *p = strchr(name, '\0') - 3; + p[0] = '0'; + p[1] = '0'; + p[2] = '0'; + open_next(false); +} + +//--------------------------------------------------------------------- + +class flat_file: public tape_dev { +public: + flat_file(int file_handle, const char *spec, bool dst) : + tape_dev(file_handle, spec, dst) {}; + ssize_t read_blk(void *dst, size_t len); + ssize_t verify_blk(void *dst, size_t len, size_t expected); + void write_blk(const void *src, size_t len); + void file_mark(void); + virtual void rewind(void); +}; + +ssize_t +flat_file::read_blk(void *dst, size_t len) +{ + (void)dst; + (void)len; + errx(EX_SOFTWARE, "That was not supposed to happen"); +} + +ssize_t +flat_file::verify_blk(void *dst, size_t len, size_t expected) +{ + (void)len; + return (read(fd, dst, expected)); +} + +void +flat_file::write_blk(const void *src, size_t len) +{ + ssize_t nwrite = write(fd, src, len); + if (nwrite < 0 || (size_t)nwrite != len) + errx(17, "write error (%zd != %zd)", nwrite, len); +} + +void +flat_file::file_mark(void) +{ + return; +} + +void +flat_file::rewind(void) +{ + errx(EX_SOFTWARE, "That was not supposed to happen"); +} + +//--------------------------------------------------------------------- + +enum e_how {H_INPUT, H_OUTPUT, H_VERIFY}; + +static tape_dev * +open_arg(const char *arg, enum e_how how, int rawfile) +{ + int fd; + + if (!strcmp(arg, "-") && how == H_OUTPUT) + fd = STDOUT_FILENO; + else if (!strcmp(arg, "-")) + fd = STDIN_FILENO; + else if (how == H_OUTPUT) + fd = open(arg, O_RDWR|O_CREAT, DEFFILEMODE); + else + fd = open(arg, O_RDONLY); + + if (fd < 0) + err(EX_NOINPUT, "Cannot open %s:", arg); + + struct mtop mt; + mt.mt_op = MTNOP; + mt.mt_count = 1; + int i = ioctl(fd, MTIOCTOP, &mt); + + if (i >= 0) + return (new tape_dev(fd, arg, how == H_OUTPUT)); + + size_t alen = strlen(arg); + if (alen >= 5 && !strcmp(arg + (alen - 4), ".000")) { + if (how == H_INPUT) + errx(EX_USAGE, + "File-sets files cannot be used as source"); + return (new file_set(fd, arg, how == H_OUTPUT)); + } + + if (how != H_INPUT && rawfile) + return (new flat_file(fd, arg, how == H_OUTPUT)); + + return (new tap_file(fd, arg, how == H_OUTPUT)); +} + +//--------------------------------------------------------------------- + +static tape_dev *input; +static tape_dev *output; + +static size_t maxblk = MAXREC; +static uint64_t lastrec, fsize, tsize; +static FILE *msg; +static ssize_t lastnread; +static struct timespec t_start, t_end; + +static void +report_total(FILE *file) +{ + double dur = (t_end.tv_nsec - t_start.tv_nsec) * 1e-9; + dur += t_end.tv_sec - t_start.tv_sec; + uintmax_t tot = tsize + fsize; + fprintf(file, "total length: %ju bytes", tot); + fprintf(file, " time: %.0f s", dur); + tot /= 1024; + fprintf(file, " rate: %.1f kB/s", (double)tot/dur); + fprintf(file, "\n"); +} + +static void +sigintr(int signo __unused) +{ + (void)signo; + (void)clock_gettime(CLOCK_MONOTONIC, &t_end); + if (record) { + if (record - lastrec > 1) + fprintf(msg, "records %ju to %ju\n", + (intmax_t)lastrec, (intmax_t)record); + else + fprintf(msg, "record %ju\n", (intmax_t)lastrec); + } + fprintf(msg, "interrupt at file %d: record %ju\n", + filen, (uintmax_t)record); + report_total(msg); + exit(1); +} + +#ifdef SIGINFO +static volatile sig_atomic_t want_info; + +static void +siginfo(int signo) +{ + (void)signo; + want_info = 1; +} + +static void +check_want_info(void) +{ + if (want_info) { + (void)clock_gettime(CLOCK_MONOTONIC, &t_end); + fprintf(stderr, "tcopy: file %d record %ju ", + filen, (uintmax_t)record); + report_total(stderr); + want_info = 0; + } +} + +#else /* !SIGINFO */ + +static void +check_want_info(void) +{ +} + +#endif + +static char * +getspace(size_t blk) +{ + void *bp; + + assert(blk > 0); + if ((bp = malloc(blk)) == NULL) + errx(11, "no memory"); + return ((char *)bp); +} + +static void +usage(void) +{ + fprintf(stderr, "usage: tcopy [-cvx] [-s maxblk] [src [dest]]\n"); + exit(1); +} + +static void +progress(ssize_t nread) +{ + if (nread != lastnread) { + if (lastnread != 0 && lastnread != NOCOUNT) { + if (lastrec == 0 && nread == 0) + fprintf(msg, "%ju records\n", + (uintmax_t)record); + else if (record - lastrec > 1) + fprintf(msg, "records %ju to %ju\n", + (uintmax_t)lastrec, + (uintmax_t)record); + else + fprintf(msg, "record %ju\n", + (uintmax_t)lastrec); + } + if (nread != 0) + fprintf(msg, + "file %d: block size %zd: ", filen, nread); + (void) fflush(msg); + lastrec = record; + } + if (nread > 0) { + fsize += (size_t)nread; + record++; + } else { + if (lastnread <= 0 && lastnread != NOCOUNT) { + fprintf(msg, "eot\n"); + return; + } + fprintf(msg, + "file %d: eof after %ju records: %ju bytes\n", + filen, (uintmax_t)record, (uintmax_t)fsize); + filen++; + tsize += fsize; + fsize = record = lastrec = 0; + lastnread = 0; + } + lastnread = nread; +} + +static void +read_or_copy(void) +{ + int needeof; + ssize_t nread, prev_read; + char *buff = getspace(maxblk); + + (void)clock_gettime(CLOCK_MONOTONIC, &t_start); + needeof = 0; + for (prev_read = NOCOUNT;;) { + check_want_info(); + nread = input->read_blk(buff, maxblk); + progress(nread); + if (nread > 0) { + if (output != NULL) { + if (needeof) { + output->file_mark(); + needeof = 0; + } + output->write_blk(buff, (size_t)nread); + } + } else { + if (prev_read <= 0 && prev_read != NOCOUNT) { + break; + } + needeof = 1; + } + prev_read = nread; + } + (void)clock_gettime(CLOCK_MONOTONIC, &t_end); + report_total(msg); + free(buff); +} + +static void +verify(void) +{ + char *buf1 = getspace(maxblk); + char *buf2 = getspace(maxblk); + int eot = 0; + ssize_t nread1, nread2; + filen = 0; + tsize = 0; + + assert(output != NULL); + (void)clock_gettime(CLOCK_MONOTONIC, &t_start); + + while (1) { + check_want_info(); + nread1 = input->read_blk(buf1, (size_t)maxblk); + nread2 = output->verify_blk(buf2, maxblk, (size_t)nread1); + progress(nread1); + if (nread1 != nread2) { + fprintf(msg, + "tcopy: tapes have different block sizes; " + "%zd != %zd.\n", nread1, nread2); + exit(1); + } + if (nread1 > 0 && memcmp(buf1, buf2, (size_t)nread1)) { + fprintf(msg, "tcopy: tapes have different data.\n"); + exit(1); + } else if (nread1 > 0) { + eot = 0; + } else if (eot++) { + break; + } + } + (void)clock_gettime(CLOCK_MONOTONIC, &t_end); + report_total(msg); + fprintf(msg, "tcopy: tapes are identical.\n"); + fprintf(msg, "rewinding\n"); + input->rewind(); + output->rewind(); + + free(buf1); + free(buf2); +} + +int +main(int argc, char *argv[]) +{ + enum operation op = READ; + int ch; + unsigned long maxphys = 0; + size_t l_maxphys = sizeof maxphys; + int64_t tmp; + int rawfile = 0; + + setbuf(stderr, NULL); + + if (!sysctlbyname("kern.maxphys", &maxphys, &l_maxphys, NULL, 0UL)) + maxblk = maxphys; + + msg = stdout; + while ((ch = getopt(argc, argv, "cl:rs:vx")) != -1) + switch((char)ch) { + case 'c': + op = COPYVERIFY; + break; + case 'l': + msg = fopen(optarg, "w"); + if (msg == NULL) + errx(EX_CANTCREAT, "Cannot open %s", optarg); + setbuf(msg, NULL); + break; + case 'r': + rawfile = 1; + break; + case 's': + if (expand_number(optarg, &tmp)) { + warnx("illegal block size"); + usage(); + } + if (maxblk <= 0) { + warnx("illegal block size"); + usage(); + } + maxblk = tmp; + break; + case 'v': + op = VERIFY; + break; + case 'x': + if (msg == stdout) + msg = stderr; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + switch(argc) { + case 0: + if (op != READ) + usage(); + break; + case 1: + if (op != READ) + usage(); + break; + case 2: + if (op == READ) + op = COPY; + if (!strcmp(argv[1], "-")) { + if (op == COPYVERIFY) + errx(EX_USAGE, + "Cannot copy+verify with '-' destination"); + if (msg == stdout) + msg = stderr; + } + if (op == VERIFY) + output = open_arg(argv[1], H_VERIFY, 0); + else + output = open_arg(argv[1], H_OUTPUT, rawfile); + break; + default: + usage(); + } + + if (argc == 0) { + input = open_arg(_PATH_DEFTAPE, H_INPUT, 0); + } else { + input = open_arg(argv[0], H_INPUT, 0); + } + + if ((signal(SIGINT, SIG_IGN)) != SIG_IGN) + (void) signal(SIGINT, sigintr); + +#ifdef SIGINFO + (void)signal(SIGINFO, siginfo); +#endif + + if (op != VERIFY) { + if (op == COPYVERIFY) { + assert(output != NULL); + fprintf(msg, "rewinding\n"); + input->rewind(); + output->rewind(); + } + + read_or_copy(); + + if (op == COPY || op == COPYVERIFY) { + assert(output != NULL); + output->file_mark(); + output->file_mark(); + } + } + + if (op == VERIFY || op == COPYVERIFY) { + + if (op == COPYVERIFY) { + assert(output != NULL); + fprintf(msg, "rewinding\n"); + input->rewind(); + output->rewind(); + input->direction = tape_dev::SRC; + output->direction = tape_dev::SRC; + } + + verify(); + } + + if (msg != stderr && msg != stdout) + report_total(stderr); + + return(0); +} diff --git a/usr.sbin/autofs/common.c b/usr.sbin/autofs/common.c index 6b98214162ae..2dd7c290cebc 100644 --- a/usr.sbin/autofs/common.c +++ b/usr.sbin/autofs/common.c @@ -153,6 +153,7 @@ create_directory(const char *path) } } + free(partial); free(tofree); } diff --git a/usr.sbin/certctl/Makefile b/usr.sbin/certctl/Makefile index 88c024daf7e6..6900f0ce3b65 100644 --- a/usr.sbin/certctl/Makefile +++ b/usr.sbin/certctl/Makefile @@ -1,5 +1,14 @@ +.include <src.opts.mk> + PACKAGE= certctl -SCRIPTS=certctl.sh +PROG= certctl MAN= certctl.8 +LIBADD= crypto +HAS_TESTS= +SUBDIR.${MK_TESTS}= tests + +.ifdef BOOTSTRAPPING +CFLAGS+=-DBOOTSTRAPPING +.endif .include <bsd.prog.mk> diff --git a/usr.sbin/certctl/certctl.8 b/usr.sbin/certctl/certctl.8 index 7e49bb89e2ac..97bdc840c359 100644 --- a/usr.sbin/certctl/certctl.8 +++ b/usr.sbin/certctl/certctl.8 @@ -24,7 +24,7 @@ .\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE .\" POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 17, 2025 +.Dd August 11, 2025 .Dt CERTCTL 8 .Os .Sh NAME @@ -32,63 +32,83 @@ .Nd "tool for managing trusted and untrusted TLS certificates" .Sh SYNOPSIS .Nm -.Op Fl v +.Op Fl lv .Ic list .Nm -.Op Fl v +.Op Fl lv .Ic untrusted .Nm -.Op Fl cnUv +.Op Fl BnUv .Op Fl D Ar destdir .Op Fl M Ar metalog .Ic rehash .Nm -.Op Fl cnv -.Ic untrust Ar file +.Op Fl nv +.Ic untrust Ar .Nm -.Op Fl cnv -.Ic trust Ar file +.Op Fl nv +.Ic trust Ar .Sh DESCRIPTION The .Nm utility manages the list of TLS Certificate Authorities that are trusted by applications that use OpenSSL. .Pp -Flags: +The following options are available: .Bl -tag -width 4n -.It Fl c -Copy certificates instead of linking to them. +.It Fl B +Do not generate a bundle. +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl D Ar destdir Specify the DESTDIR (overriding values from the environment). -.It Fl d Ar distbase -Specify the DISTBASE (overriding values from the environment). +.It Fl l +When listing installed (trusted or untrusted) certificates, show the +full path and distinguished name for each certificate. .It Fl M Ar metalog -Specify the path of the METALOG file (default: $DESTDIR/METALOG). +Specify the path of the METALOG file +.Po +default: +.Pa ${DESTDIR}/METALOG +.Pc . +This option is only valid in conjunction with the +.Ic rehash +command. .It Fl n -No-Op mode, do not actually perform any actions. +Dry-run mode. +Do not actually perform any actions except write the metalog. .It Fl v -Be verbose, print details about actions before performing them. +Verbose mode. +Print detailed information about each action taken. .It Fl U -Unprivileged mode, do not change the ownership of created links. -Do record the ownership in the METALOG file. +Unprivileged mode. +Do not attempt to set the ownership of created files. +This option is only valid in conjunction with the +.Fl M +option and the +.Ic rehash +command. .El .Pp Primary command functions: .Bl -tag -width untrusted .It Ic list -List all currently trusted certificate authorities. +List all currently trusted certificates. .It Ic untrusted List all currently untrusted certificates. .It Ic rehash -Rebuild the list of trusted certificate authorities by scanning all directories +Rebuild the list of trusted certificates by scanning all directories in .Ev TRUSTPATH and all untrusted certificates in .Ev UNTRUSTPATH . -A symbolic link to each trusted certificate is placed in +A copy of each trusted certificate is placed in .Ev CERTDESTDIR and each untrusted certificate in .Ev UNTRUSTDESTDIR . +In addition, a bundle containing the trusted certificates is placed in +.Ev BUNDLEFILE . .It Ic untrust Add the specified file to the untrusted list. .It Ic trust @@ -98,8 +118,6 @@ Remove the specified file from the untrusted list. .Bl -tag -width UNTRUSTDESTDIR .It Ev DESTDIR Alternate destination directory to operate on. -.It Ev DISTBASE -Additional path component to include when operating on certificate directories. .It Ev LOCALBASE Location for local programs. Defaults to the value of the user.localbase sysctl which is usually @@ -107,32 +125,34 @@ Defaults to the value of the user.localbase sysctl which is usually .It Ev TRUSTPATH List of paths to search for trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/trusted -.Pa <DESTDIR><DISTBASE>/usr/local/share/certs -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/certs +.Pa ${DESTDIR}/usr/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/trusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs .It Ev UNTRUSTPATH List of paths to search for untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/usr/share/certs/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/untrusted -.Pa <DESTDIR><DISTBASE><LOCALBASE>/etc/ssl/blacklisted -.It Ev CERTDESTDIR +.Pa ${DESTDIR}/usr/share/certs/untrusted +.Pa ${DESTDIR}${LOCALBASE}/share/certs/untrusted +.It Ev TRUSTDESTDIR Destination directory for symbolic links to trusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/certs +.Pa ${DESTDIR}/etc/ssl/certs .It Ev UNTRUSTDESTDIR Destination directory for symbolic links to untrusted certificates. Default: -.Pa <DESTDIR><DISTBASE>/etc/ssl/untrusted -.It Ev EXTENSIONS -List of file extensions to read as certificate files. -Default: *.pem *.crt *.cer *.crl *.0 +.Pa ${DESTDIR}/etc/ssl/untrusted +.It Ev BUNDLE +File name of bundle to produce. .El .Sh SEE ALSO .Xr openssl 1 .Sh HISTORY .Nm first appeared in -.Fx 12.2 +.Fx 12.2 . .Sh AUTHORS -.An Allan Jude Aq Mt allanjude@freebsd.org +.An -nosplit +The original shell implementation was written by +.An Allan Jude Aq Mt allanjude@FreeBSD.org . +The current C implementation was written by +.An Dag-Erling Sm\(/orgrav Aq Mt des@FreeBSD.org . diff --git a/usr.sbin/certctl/certctl.c b/usr.sbin/certctl/certctl.c new file mode 100644 index 000000000000..365870167aeb --- /dev/null +++ b/usr.sbin/certctl/certctl.c @@ -0,0 +1,1065 @@ +/*- + * Copyright (c) 2023-2025 Dag-Erling Smørgrav <des@FreeBSD.org> + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/sysctl.h> +#include <sys/stat.h> +#include <sys/tree.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <paths.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include <openssl/ssl.h> + +#define info(fmt, ...) \ + do { \ + if (verbose) \ + fprintf(stderr, fmt "\n", ##__VA_ARGS__); \ + } while (0) + +static char * +xasprintf(const char *fmt, ...) +{ + va_list ap; + char *str; + int ret; + + va_start(ap, fmt); + ret = vasprintf(&str, fmt, ap); + va_end(ap); + if (ret < 0 || str == NULL) + err(1, NULL); + return (str); +} + +static char * +xstrdup(const char *str) +{ + char *dup; + + if ((dup = strdup(str)) == NULL) + err(1, NULL); + return (dup); +} + +static void usage(void); + +static bool dryrun; +static bool longnames; +static bool nobundle; +static bool unprivileged; +static bool verbose; + +static const char *localbase; +static const char *destdir; +static const char *metalog; + +static const char *uname = "root"; +static const char *gname = "wheel"; + +static const char *const default_trusted_paths[] = { + "/usr/share/certs/trusted", + "%L/share/certs/trusted", + "%L/share/certs", + NULL +}; +static char **trusted_paths; + +static const char *const default_untrusted_paths[] = { + "/usr/share/certs/untrusted", + "%L/share/certs/untrusted", + NULL +}; +static char **untrusted_paths; + +static char *trusted_dest; +static char *untrusted_dest; +static char *bundle_dest; + +#define SSL_PATH "/etc/ssl" +#define TRUSTED_DIR "certs" +#define TRUSTED_PATH SSL_PATH "/" TRUSTED_DIR +#define UNTRUSTED_DIR "untrusted" +#define UNTRUSTED_PATH SSL_PATH "/" UNTRUSTED_DIR +#define LEGACY_DIR "blacklisted" +#define LEGACY_PATH SSL_PATH "/" LEGACY_DIR +#define BUNDLE_FILE "cert.pem" +#define BUNDLE_PATH SSL_PATH "/" BUNDLE_FILE + +static FILE *mlf; + +/* + * Split a colon-separated list into a NULL-terminated array. + */ +static char ** +split_paths(const char *str) +{ + char **paths; + const char *p, *q; + unsigned int i, n; + + for (p = str, n = 1; *p; p++) { + if (*p == ':') + n++; + } + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (p = q = str, i = 0; i < n; i++, p = q + 1) { + q = strchrnul(p, ':'); + if ((paths[i] = strndup(p, q - p)) == NULL) + err(1, NULL); + } + return (paths); +} + +/* + * Expand %L into LOCALBASE and prefix DESTDIR. + */ +static char * +expand_path(const char *template) +{ + if (template[0] == '%' && template[1] == 'L') + return (xasprintf("%s%s%s", destdir, localbase, template + 2)); + return (xasprintf("%s%s", destdir, template)); +} + +/* + * Expand an array of paths. + */ +static char ** +expand_paths(const char *const *templates) +{ + char **paths; + unsigned int i, n; + + for (n = 0; templates[n] != NULL; n++) + continue; + if ((paths = calloc(n + 1, sizeof(*paths))) == NULL) + err(1, NULL); + for (i = 0; i < n; i++) + paths[i] = expand_path(templates[i]); + return (paths); +} + +/* + * If destdir is a prefix of path, returns a pointer to the rest of path, + * otherwise returns path. + */ +static const char * +unexpand_path(const char *path) +{ + const char *p = path; + const char *q = destdir; + + while (*p && *p == *q) { + p++; + q++; + } + return (*q == '\0' && *p == '/' ? p : path); +} + +/* + * X509 certificate in a rank-balanced tree. + */ +struct cert { + RB_ENTRY(cert) entry; + unsigned long hash; + char *name; + X509 *x509; + char *path; +}; + +static void +free_cert(struct cert *cert) +{ + free(cert->name); + X509_free(cert->x509); + free(cert->path); + free(cert); +} + +static int +certcmp(const struct cert *a, const struct cert *b) +{ + return (X509_cmp(a->x509, b->x509)); +} + +RB_HEAD(cert_tree, cert); +static struct cert_tree trusted = RB_INITIALIZER(&trusted); +static struct cert_tree untrusted = RB_INITIALIZER(&untrusted); +RB_GENERATE_STATIC(cert_tree, cert, entry, certcmp); + +static void +free_certs(struct cert_tree *tree) +{ + struct cert *cert, *tmp; + + RB_FOREACH_SAFE(cert, cert_tree, tree, tmp) { + RB_REMOVE(cert_tree, tree, cert); + free_cert(cert); + } +} + +static struct cert * +find_cert(struct cert_tree *haystack, X509 *x509) +{ + struct cert needle = { .x509 = x509 }; + + return (RB_FIND(cert_tree, haystack, &needle)); +} + +/* + * File containing a certificate in a rank-balanced tree sorted by + * certificate hash and disambiguating counter. This is needed because + * the certificate hash function is prone to collisions, necessitating a + * counter to distinguish certificates that hash to the same value. + */ +struct file { + RB_ENTRY(file) entry; + const struct cert *cert; + unsigned int c; +}; + +static int +filecmp(const struct file *a, const struct file *b) +{ + if (a->cert->hash > b->cert->hash) + return (1); + if (a->cert->hash < b->cert->hash) + return (-1); + return (a->c - b->c); +} + +RB_HEAD(file_tree, file); +RB_GENERATE_STATIC(file_tree, file, entry, filecmp); + +/* + * Lexicographical sort for scandir(). + */ +static int +lexisort(const struct dirent **d1, const struct dirent **d2) +{ + return (strcmp((*d1)->d_name, (*d2)->d_name)); +} + +/* + * Read certificate(s) from a single file and insert them into a tree. + * Ignore certificates that already exist in the tree. If exclude is not + * null, also ignore certificates that exist in exclude. + * + * Returns the number certificates added to the tree, or -1 on failure. + */ +static int +read_cert(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + FILE *f; + X509 *x509; + X509_NAME *name; + struct cert *cert; + unsigned long hash; + int ni, no; + + if ((f = fopen(path, "r")) == NULL) { + warn("%s", path); + return (-1); + } + for (ni = no = 0; + (x509 = PEM_read_X509(f, NULL, NULL, NULL)) != NULL; + ni++) { + hash = X509_subject_name_hash(x509); + if (exclude && find_cert(exclude, x509)) { + info("%08lx: excluded", hash); + X509_free(x509); + continue; + } + if (find_cert(tree, x509)) { + info("%08lx: duplicate", hash); + X509_free(x509); + continue; + } + if ((cert = calloc(1, sizeof(*cert))) == NULL) + err(1, NULL); + cert->x509 = x509; + name = X509_get_subject_name(x509); + cert->hash = X509_NAME_hash_ex(name, NULL, NULL, NULL); + cert->name = X509_NAME_oneline(name, NULL, 0); + cert->path = xstrdup(unexpand_path(path)); + if (RB_INSERT(cert_tree, tree, cert) != NULL) + errx(1, "unexpected duplicate"); + info("%08lx: %s", cert->hash, strrchr(cert->name, '=') + 1); + no++; + } + /* + * ni is the number of certificates we found in the file. + * no is the number of certificates that weren't already in our + * tree or on the exclusion list. + */ + if (ni == 0) + warnx("%s: no valid certificates found", path); + fclose(f); + return (no); +} + +/* + * Load all certificates found in the specified path into a tree, + * optionally excluding those that already exist in a different tree. + * + * Returns the number of certificates added to the tree, or -1 on failure. + */ +static int +read_certs(const char *path, struct cert_tree *tree, struct cert_tree *exclude) +{ + struct stat sb; + char *paths[] = { (char *)(uintptr_t)path, NULL }; + FTS *fts; + FTSENT *ent; + int fts_options = FTS_LOGICAL | FTS_NOCHDIR; + int ret, total = 0; + + if (stat(path, &sb) != 0) { + return (-1); + } else if (!S_ISDIR(sb.st_mode)) { + errno = ENOTDIR; + return (-1); + } + if ((fts = fts_open(paths, fts_options, NULL)) == NULL) + err(1, "fts_open()"); + while ((ent = fts_read(fts)) != NULL) { + if (ent->fts_info != FTS_F) { + if (ent->fts_info == FTS_ERR) + warnc(ent->fts_errno, "fts_read()"); + continue; + } + info("found %s", ent->fts_path); + ret = read_cert(ent->fts_path, tree, exclude); + if (ret > 0) + total += ret; + } + fts_close(fts); + return (total); +} + +/* + * Save the contents of a cert tree to disk. + * + * Returns 0 on success and -1 on failure. + */ +static int +write_certs(const char *dir, struct cert_tree *tree) +{ + struct file_tree files = RB_INITIALIZER(&files); + struct cert *cert; + struct file *file, *tmp; + struct dirent **dents, **ent; + char *path, *tmppath = NULL; + FILE *f; + mode_t mode = 0444; + int cmp, d, fd, ndents, ret = 0; + + /* + * Start by generating unambiguous file names for each certificate + * and storing them in lexicographical order + */ + RB_FOREACH(cert, cert_tree, tree) { + if ((file = calloc(1, sizeof(*file))) == NULL) + err(1, NULL); + file->cert = cert; + for (file->c = 0; file->c < INT_MAX; file->c++) + if (RB_INSERT(file_tree, &files, file) == NULL) + break; + if (file->c == INT_MAX) + errx(1, "unable to disambiguate %08lx", cert->hash); + free(cert->path); + cert->path = xasprintf("%08lx.%d", cert->hash, file->c); + } + /* + * Open and scan the directory. + */ + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0 || +#ifdef BOOTSTRAPPING + (ndents = scandir(dir, &dents, NULL, lexisort)) +#else + (ndents = fdscandir(d, &dents, NULL, lexisort)) +#endif + < 0) + err(1, "%s", dir); + /* + * Iterate over the directory listing and the certificate listing + * in parallel. If the directory listing gets ahead of the + * certificate listing, we need to write the current certificate + * and advance the certificate listing. If the certificate + * listing is ahead of the directory listing, we need to delete + * the current file and advance the directory listing. If they + * are neck and neck, we have a match and could in theory compare + * the two, but in practice it's faster to just replace the + * current file with the current certificate (and advance both). + */ + ent = dents; + file = RB_MIN(file_tree, &files); + for (;;) { + if (ent < dents + ndents) { + /* skip directories */ + if ((*ent)->d_type == DT_DIR) { + free(*ent++); + continue; + } + if (file != NULL) { + /* compare current dirent to current cert */ + path = file->cert->path; + cmp = strcmp((*ent)->d_name, path); + } else { + /* trailing files in directory */ + path = NULL; + cmp = -1; + } + } else { + if (file != NULL) { + /* trailing certificates */ + path = file->cert->path; + cmp = 1; + } else { + /* end of both lists */ + path = NULL; + break; + } + } + if (cmp < 0) { + /* a file on disk with no matching certificate */ + info("removing %s/%s", dir, (*ent)->d_name); + if (!dryrun) + (void)unlinkat(d, (*ent)->d_name, 0); + free(*ent++); + continue; + } + if (cmp == 0) { + /* a file on disk with a matching certificate */ + info("replacing %s/%s", dir, (*ent)->d_name); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_TRUNC, mode); + if (!unprivileged && fd >= 0) + (void)fchmod(fd, mode); + } + free(*ent++); + } else { + /* a certificate with no matching file */ + info("writing %s/%s", dir, path); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmppath = xasprintf(".%s", path); + fd = openat(d, tmppath, + O_CREAT | O_WRONLY | O_EXCL, mode); + } + } + /* write the certificate */ + if (fd < 0 || + (f = fdopen(fd, "w")) == NULL || + !PEM_write_X509(f, file->cert->x509)) { + if (tmppath != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmppath, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmppath ? tmppath : path); + } + /* rename temp file if applicable */ + if (tmppath != NULL) { + if (ret == 0 && renameat(d, tmppath, d, path) != 0) { + warn("%s/%s", dir, path); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmppath, 0); + free(tmppath); + tmppath = NULL; + } + /* emit metalog */ + if (mlf != NULL) { + fprintf(mlf, "%s/%s type=file " + "uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), path, + uname, gname, mode, ftell(f)); + } + fclose(f); + /* advance certificate listing */ + tmp = RB_NEXT(file_tree, &files, file); + RB_REMOVE(file_tree, &files, file); + free(file); + file = tmp; + } + free(dents); + close(d); + return (ret); +} + +/* + * Save all certs in a tree to a single file (bundle). + * + * Returns 0 on success and -1 on failure. + */ +static int +write_bundle(const char *dir, const char *file, struct cert_tree *tree) +{ + struct cert *cert; + char *tmpfile = NULL; + FILE *f; + int d, fd, ret = 0; + mode_t mode = 0444; + + if (dir != NULL) { + if ((d = open(dir, O_DIRECTORY | O_RDONLY)) < 0) + err(1, "%s", dir); + } else { + dir = "."; + d = AT_FDCWD; + } + info("writing %s/%s", dir, file); + if (dryrun) { + fd = open(_PATH_DEVNULL, O_WRONLY); + } else { + tmpfile = xasprintf(".%s", file); + fd = openat(d, tmpfile, O_WRONLY | O_CREAT | O_EXCL, mode); + } + if (fd < 0 || (f = fdopen(fd, "w")) == NULL) { + if (tmpfile != NULL && fd >= 0) { + int serrno = errno; + (void)unlinkat(d, tmpfile, 0); + errno = serrno; + } + err(1, "%s/%s", dir, tmpfile ? tmpfile : file); + } + RB_FOREACH(cert, cert_tree, tree) { + if (!PEM_write_X509(f, cert->x509)) { + warn("%s/%s", dir, tmpfile ? tmpfile : file); + ret = -1; + break; + } + } + if (tmpfile != NULL) { + if (ret == 0 && renameat(d, tmpfile, d, file) != 0) { + warn("%s/%s", dir, file); + ret = -1; + } + if (ret != 0) + (void)unlinkat(d, tmpfile, 0); + free(tmpfile); + } + if (ret == 0 && mlf != NULL) { + fprintf(mlf, + "%s/%s type=file uname=%s gname=%s mode=%#o size=%ld\n", + unexpand_path(dir), file, uname, gname, mode, ftell(f)); + } + fclose(f); + if (d != AT_FDCWD) + close(d); + return (ret); +} + +/* + * Load trusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_trusted(bool all, struct cert_tree *exclude) +{ + unsigned int i, n; + int ret; + + /* load external trusted certs */ + for (i = n = 0; all && trusted_paths[i] != NULL; i++) { + ret = read_certs(trusted_paths[i], &trusted, exclude); + if (ret > 0) + n += ret; + } + + /* load installed trusted certs */ + ret = read_certs(trusted_dest, &trusted, exclude); + if (ret > 0) + n += ret; + + info("%d trusted certificates found", n); + return (n); +} + +/* + * Load untrusted certificates. + * + * Returns the number of certificates loaded. + */ +static unsigned int +load_untrusted(bool all) +{ + char *path; + unsigned int i, n; + int ret; + + /* load external untrusted certs */ + for (i = n = 0; all && untrusted_paths[i] != NULL; i++) { + ret = read_certs(untrusted_paths[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + + /* load installed untrusted certs */ + ret = read_certs(untrusted_dest, &untrusted, NULL); + if (ret > 0) + n += ret; + + /* load legacy untrusted certs */ + path = expand_path(LEGACY_PATH); + ret = read_certs(path, &untrusted, NULL); + if (ret > 0) { + warnx("certificates found in legacy directory %s", + path); + n += ret; + } else if (ret == 0) { + warnx("legacy directory %s can safely be deleted", + path); + } + free(path); + + info("%d untrusted certificates found", n); + return (n); +} + +/* + * Save trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_trusted(void) +{ + int ret; + + /* save untrusted certs */ + ret = write_certs(trusted_dest, &trusted); + return (ret); +} + +/* + * Save untrusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_untrusted(void) +{ + int ret; + + ret = write_certs(untrusted_dest, &untrusted); + return (ret); +} + +/* + * Save certificate bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_bundle(void) +{ + char *dir, *file, *sep; + int ret; + + if ((sep = strrchr(bundle_dest, '/')) == NULL) { + dir = NULL; + file = bundle_dest; + } else { + dir = xasprintf("%.*s", (int)(sep - bundle_dest), bundle_dest); + file = sep + 1; + } + ret = write_bundle(dir, file, &trusted); + free(dir); + return (ret); +} + +/* + * Save everything. + * + * Returns 0 on success and -1 on failure. + */ +static int +save_all(void) +{ + int ret = 0; + + ret |= save_untrusted(); + ret |= save_trusted(); + if (!nobundle) + ret |= save_bundle(); + return (ret); +} + +/* + * List the contents of a certificate tree. + */ +static void +list_certs(struct cert_tree *tree) +{ + struct cert *cert; + char *path, *name; + + RB_FOREACH(cert, cert_tree, tree) { + path = longnames ? NULL : strrchr(cert->path, '/'); + name = longnames ? NULL : strrchr(cert->name, '='); + printf("%s\t%s\n", path ? path + 1 : cert->path, + name ? name + 1 : cert->name); + } +} + +/* + * Load installed trusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_list(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load trusted certificates */ + load_trusted(false, NULL); + /* list them */ + list_certs(&trusted); + free_certs(&trusted); + return (0); +} + +/* + * Load installed untrusted certificates, then list them. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrusted(int argc, char **argv __unused) +{ + if (argc > 1) + usage(); + /* load untrusted certificates */ + load_untrusted(false); + /* list them */ + list_certs(&untrusted); + free_certs(&untrusted); + return (0); +} + +/* + * Load trusted and untrusted certificates from all sources, then + * regenerate both the hashed directories and the bundle. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_rehash(int argc, char **argv __unused) +{ + int ret; + + if (argc > 1) + usage(); + + if (unprivileged && (mlf = fopen(metalog, "a")) == NULL) { + warn("%s", metalog); + return (-1); + } + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + if (mlf != NULL) + fclose(mlf); + return (ret); +} + +/* + * Manually add one or more certificates to the list of trusted certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_trust(int argc, char **argv) +{ + struct cert_tree extra = RB_INITIALIZER(&extra); + struct cert *cert, *other, *tmp; + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* now load the additional trusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &extra, &trusted); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new trusted certificates found"); + free_certs(&untrusted); + free_certs(&trusted); + free_certs(&extra); + return (0); + } + + /* + * For each new trusted cert, move it from the extra list to the + * trusted list, then check if a matching certificate exists on + * the untrusted list. If that is the case, warn the user, then + * remove the matching certificate from the untrusted list. + */ + RB_FOREACH_SAFE(cert, cert_tree, &extra, tmp) { + RB_REMOVE(cert_tree, &extra, cert); + RB_INSERT(cert_tree, &trusted, cert); + if ((other = RB_FIND(cert_tree, &untrusted, cert)) != NULL) { + warnx("%s was previously untrusted", cert->name); + RB_REMOVE(cert_tree, &untrusted, other); + free_cert(other); + } + } + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +/* + * Manually add one or more certificates to the list of untrusted + * certificates. + * + * Returns 0 on success and -1 on failure. + */ +static int +certctl_untrust(int argc, char **argv) +{ + unsigned int n; + int i, ret; + + if (argc < 2) + usage(); + + /* load untrusted certs first */ + load_untrusted(true); + + /* now load the additional untrusted certificates */ + n = 0; + for (i = 1; i < argc; i++) { + ret = read_cert(argv[i], &untrusted, NULL); + if (ret > 0) + n += ret; + } + if (n == 0) { + warnx("no new untrusted certificates found"); + free_certs(&untrusted); + return (0); + } + + /* load trusted certs, excluding any that are already untrusted */ + load_trusted(true, &untrusted); + + /* save everything */ + ret = save_all(); + + /* clean up */ + free_certs(&untrusted); + free_certs(&trusted); + return (ret); +} + +static void +set_defaults(void) +{ + const char *value; + char *str; + size_t len; + + if (localbase == NULL && + (localbase = getenv("LOCALBASE")) == NULL) { + if ((str = malloc((len = PATH_MAX) + 1)) == NULL) + err(1, NULL); + while (sysctlbyname("user.localbase", str, &len, NULL, 0) < 0) { + if (errno != ENOMEM) + err(1, "sysctl(user.localbase)"); + if ((str = realloc(str, len + 1)) == NULL) + err(1, NULL); + } + str[len] = '\0'; + localbase = str; + } + + if (destdir == NULL && + (destdir = getenv("DESTDIR")) == NULL) + destdir = ""; + + if (unprivileged && metalog == NULL && + (metalog = getenv("METALOG")) == NULL) + metalog = xasprintf("%s/METALOG", destdir); + + if (!verbose) { + if ((value = getenv("CERTCTL_VERBOSE")) != NULL) { + if (value[0] != '\0') { + verbose = true; + } + } + } + + if ((value = getenv("TRUSTPATH")) != NULL) + trusted_paths = split_paths(value); + else + trusted_paths = expand_paths(default_trusted_paths); + + if ((value = getenv("UNTRUSTPATH")) != NULL) + untrusted_paths = split_paths(value); + else + untrusted_paths = expand_paths(default_untrusted_paths); + + if ((value = getenv("TRUSTDESTDIR")) != NULL || + (value = getenv("CERTDESTDIR")) != NULL) + trusted_dest = xstrdup(value); + else + trusted_dest = expand_path(TRUSTED_PATH); + + if ((value = getenv("UNTRUSTDESTDIR")) != NULL) + untrusted_dest = xstrdup(value); + else + untrusted_dest = expand_path(UNTRUSTED_PATH); + + if ((value = getenv("BUNDLE")) != NULL) + bundle_dest = xstrdup(value); + else + bundle_dest = expand_path(BUNDLE_PATH); + + info("localbase:\t%s", localbase); + info("destdir:\t%s", destdir); + info("unprivileged:\t%s", unprivileged ? "true" : "false"); + info("verbose:\t%s", verbose ? "true" : "false"); +} + +typedef int (*main_t)(int, char **); + +static struct { + const char *name; + main_t func; +} commands[] = { + { "list", certctl_list }, + { "untrusted", certctl_untrusted }, + { "rehash", certctl_rehash }, + { "untrust", certctl_untrust }, + { "trust", certctl_trust }, + { 0 }, +}; + +static void +usage(void) +{ + fprintf(stderr, "usage: certctl [-lv] [-D destdir] list\n" + " certctl [-lv] [-D destdir] untrusted\n" + " certctl [-BnUv] [-D destdir] [-M metalog] rehash\n" + " certctl [-nv] [-D destdir] untrust <file>\n" + " certctl [-nv] [-D destdir] trust <file>\n"); + exit(1); +} + +int +main(int argc, char *argv[]) +{ + const char *command; + int opt; + + while ((opt = getopt(argc, argv, "BcD:g:lL:M:no:Uv")) != -1) + switch (opt) { + case 'B': + nobundle = true; + break; + case 'c': + /* ignored for compatibility */ + break; + case 'D': + destdir = optarg; + break; + case 'g': + gname = optarg; + break; + case 'l': + longnames = true; + break; + case 'L': + localbase = optarg; + break; + case 'M': + metalog = optarg; + break; + case 'n': + dryrun = true; + break; + case 'o': + uname = optarg; + break; + case 'U': + unprivileged = true; + break; + case 'v': + verbose = true; + break; + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + command = *argv; + + if ((nobundle || unprivileged || metalog != NULL) && + strcmp(command, "rehash") != 0) + usage(); + if (!unprivileged && metalog != NULL) { + warnx("-M may only be used in conjunction with -U"); + usage(); + } + + set_defaults(); + + for (unsigned i = 0; commands[i].name != NULL; i++) + if (strcmp(command, commands[i].name) == 0) + exit(!!commands[i].func(argc, argv)); + usage(); +} diff --git a/usr.sbin/certctl/certctl.sh b/usr.sbin/certctl/certctl.sh deleted file mode 100755 index 2bde651de126..000000000000 --- a/usr.sbin/certctl/certctl.sh +++ /dev/null @@ -1,366 +0,0 @@ -#!/bin/sh -#- -# SPDX-License-Identifier: BSD-2-Clause -# -# Copyright 2018 Allan Jude <allanjude@freebsd.org> -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted providing 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 ``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 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. -# - -set -u - -############################################################ CONFIGURATION - -: ${DESTDIR:=} -: ${DISTBASE:=} - -############################################################ GLOBALS - -SCRIPTNAME="${0##*/}" -LINK=-lrs -ERRORS=0 -NOOP=false -UNPRIV=false -VERBOSE=false - -############################################################ FUNCTIONS - -info() -{ - echo "${0##*/}: $@" >&2 -} - -verbose() -{ - if "${VERBOSE}" ; then - info "$@" - fi -} - -perform() -{ - if ! "${NOOP}" ; then - "$@" - fi -} - -cert_files_in() -{ - find -L "$@" -type f \( \ - -name '*.pem' -or \ - -name '*.crt' -or \ - -name '*.cer' \ - \) 2>/dev/null -} - -eolcvt() -{ - cat "$@" | tr -s '\r' '\n' -} - -do_hash() -{ - local hash - - if hash=$(openssl x509 -noout -subject_hash -in "$1") ; then - echo "$hash" - return 0 - else - info "Error: $1" - ERRORS=$((ERRORS + 1)) - return 1 - fi -} - -get_decimal() -{ - local checkdir hash decimal - - checkdir=$1 - hash=$2 - decimal=0 - - while [ -e "$checkdir/$hash.$decimal" ] ; do - decimal=$((decimal + 1)) - done - - echo ${decimal} - return 0 -} - -create_trusted() -{ - local hash certhash otherfile otherhash - local suffix - - hash=$(do_hash "$1") || return - certhash=$(openssl x509 -sha1 -in "$1" -noout -fingerprint) - for otherfile in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - info "Skipping untrusted certificate $hash ($otherfile)" - return 0 - fi - done - for otherfile in $(find $CERTDESTDIR -name "$hash.*") ; do - otherhash=$(openssl x509 -sha1 -in "$otherfile" -noout -fingerprint) - if [ "$certhash" = "$otherhash" ] ; then - verbose "Skipping duplicate entry for certificate $hash" - return 0 - fi - done - suffix=$(get_decimal "$CERTDESTDIR" "$hash") - verbose "Adding $hash.$suffix to trust store" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$(realpath "$1")" "$CERTDESTDIR/$hash.$suffix" -} - -# Accepts either dot-hash form from `certctl list` or a path to a valid cert. -resolve_certname() -{ - local hash srcfile filename - local suffix - - # If it exists as a file, we'll try that; otherwise, we'll scan - if [ -e "$1" ] ; then - hash=$(do_hash "$1") || return - srcfile=$(realpath "$1") - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - elif [ -e "${CERTDESTDIR}/$1" ] ; then - srcfile=$(realpath "${CERTDESTDIR}/$1") - hash=$(echo "$1" | sed -Ee 's/\.([0-9])+$//') - suffix=$(get_decimal "$UNTRUSTDESTDIR" "$hash") - filename="$hash.$suffix" - echo "$srcfile" "$hash.$suffix" - fi -} - -create_untrusted() -{ - local srcfile filename - - set -- $(resolve_certname "$1") - srcfile=$1 - filename=$2 - - if [ -z "$srcfile" -o -z "$filename" ] ; then - return - fi - - verbose "Adding $filename to untrusted list" - perform install ${INSTALLFLAGS} -m 0444 ${LINK} \ - "$srcfile" "$UNTRUSTDESTDIR/$filename" -} - -do_scan() -{ - local CFUNC CSEARCH CPATH CFILE CERT SPLITDIR - local oldIFS="$IFS" - CFUNC="$1" - CSEARCH="$2" - - IFS=: - set -- $CSEARCH - IFS="$oldIFS" - for CFILE in $(cert_files_in "$@") ; do - verbose "Reading $CFILE" - case $(eolcvt "$CFILE" | egrep -c '^-+BEGIN CERTIFICATE-+$') in - 0) - ;; - 1) - "$CFUNC" "$CFILE" - ;; - *) - verbose "Multiple certificates found, splitting..." - SPLITDIR=$(mktemp -d) - eolcvt "$CFILE" | egrep '^(---|[0-9A-Za-z/+=]+$)' | \ - split -p '^-+BEGIN CERTIFICATE-+$' - "$SPLITDIR/x" - for CERT in $(find "$SPLITDIR" -type f) ; do - "$CFUNC" "$CERT" - done - rm -rf "$SPLITDIR" - ;; - esac - done -} - -do_list() -{ - local CFILE subject - - for CFILE in $(find "$@" \( -type f -or -type l \) -name '*.[0-9]') ; do - if [ ! -s "$CFILE" ] ; then - info "Unable to read $CFILE" - ERRORS=$((ERRORS + 1)) - continue - fi - subject= - if ! "$VERBOSE" ; then - subject=$(openssl x509 -noout -subject -nameopt multiline -in "$CFILE" | sed -n '/commonName/s/.*= //p') - fi - if [ -z "$subject" ] ; then - subject=$(openssl x509 -noout -subject -in "$CFILE") - fi - printf "%s\t%s\n" "${CFILE##*/}" "$subject" - done -} - -cmd_rehash() -{ - - if [ -e "$CERTDESTDIR" ] ; then - perform find "$CERTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$CERTDESTDIR" - fi - if [ -e "$UNTRUSTDESTDIR" ] ; then - perform find "$UNTRUSTDESTDIR" \( -type f -or -type l \) -delete - else - perform install -d -m 0755 "$UNTRUSTDESTDIR" - fi - - do_scan create_untrusted "$UNTRUSTPATH" - do_scan create_trusted "$TRUSTPATH" -} - -cmd_list() -{ - info "Listing Trusted Certificates:" - do_list "$CERTDESTDIR" -} - -cmd_untrust() -{ - local UTFILE - - shift # verb - perform install -d -m 0755 "$UNTRUSTDESTDIR" - for UTFILE in "$@"; do - info "Adding $UTFILE to untrusted list" - create_untrusted "$UTFILE" - done -} - -cmd_trust() -{ - local UTFILE untrustedhash certhash hash - - shift # verb - for UTFILE in "$@"; do - if [ -s "$UTFILE" ] ; then - hash=$(do_hash "$UTFILE") - certhash=$(openssl x509 -sha1 -in "$UTFILE" -noout -fingerprint) - for UNTRUSTEDFILE in $(find $UNTRUSTDESTDIR -name "$hash.*") ; do - untrustedhash=$(openssl x509 -sha1 -in "$UNTRUSTEDFILE" -noout -fingerprint) - if [ "$certhash" = "$untrustedhash" ] ; then - info "Removing $(basename "$UNTRUSTEDFILE") from untrusted list" - perform rm -f $UNTRUSTEDFILE - fi - done - elif [ -e "$UNTRUSTDESTDIR/$UTFILE" ] ; then - info "Removing $UTFILE from untrusted list" - perform rm -f "$UNTRUSTDESTDIR/$UTFILE" - else - info "Cannot find $UTFILE" - ERRORS=$((ERRORS + 1)) - fi - done -} - -cmd_untrusted() -{ - info "Listing Untrusted Certificates:" - do_list "$UNTRUSTDESTDIR" -} - -usage() -{ - exec >&2 - echo "Manage the TLS trusted certificates on the system" - echo " $SCRIPTNAME [-v] list" - echo " List trusted certificates" - echo " $SCRIPTNAME [-v] untrusted" - echo " List untrusted certificates" - echo " $SCRIPTNAME [-cnUv] [-D <destdir>] [-d <distbase>] [-M <metalog>] rehash" - echo " Rehash all trusted and untrusted certificates" - echo " $SCRIPTNAME [-cnv] untrust <file>" - echo " Add <file> to the list of untrusted certificates" - echo " $SCRIPTNAME [-cnv] trust <file>" - echo " Remove <file> from the list of untrusted certificates" - exit 64 -} - -############################################################ MAIN - -while getopts cD:d:M:nUv flag; do - case "$flag" in - c) LINK=-c ;; - D) DESTDIR=${OPTARG} ;; - d) DISTBASE=${OPTARG} ;; - M) METALOG=${OPTARG} ;; - n) NOOP=true ;; - U) UNPRIV=true ;; - v) VERBOSE=true ;; - esac -done -shift $((OPTIND - 1)) - -DESTDIR=${DESTDIR%/} - -if ! [ -z "${CERTCTL_VERBOSE:-}" ] ; then - VERBOSE=true -fi -: ${METALOG:=${DESTDIR}/METALOG} -INSTALLFLAGS= -if "$UNPRIV" ; then - INSTALLFLAGS="-U -M ${METALOG} -D ${DESTDIR:-/} -o root -g wheel" -fi -: ${LOCALBASE:=$(sysctl -n user.localbase)} -: ${TRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/trusted:${DESTDIR}${LOCALBASE}/share/certs:${DESTDIR}${LOCALBASE}/etc/ssl/certs} -: ${UNTRUSTPATH:=${DESTDIR}${DISTBASE}/usr/share/certs/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/untrusted:${DESTDIR}${LOCALBASE}/etc/ssl/blacklisted} -: ${CERTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/certs} -: ${UNTRUSTDESTDIR:=${DESTDIR}${DISTBASE}/etc/ssl/untrusted} - -[ $# -gt 0 ] || usage -case "$1" in -list) cmd_list ;; -rehash) cmd_rehash ;; -blacklist) cmd_untrust "$@" ;; -untrust) cmd_untrust "$@" ;; -trust) cmd_trust "$@" ;; -unblacklist) cmd_trust "$@" ;; -untrusted) cmd_untrusted ;; -blacklisted) cmd_untrusted ;; -*) usage # NOTREACHED -esac - -retval=$? -if [ $ERRORS -gt 0 ] ; then - info "Encountered $ERRORS errors" -fi -exit $retval - -################################################################################ -# END -################################################################################ diff --git a/usr.sbin/certctl/tests/Makefile b/usr.sbin/certctl/tests/Makefile new file mode 100644 index 000000000000..da301c3ded03 --- /dev/null +++ b/usr.sbin/certctl/tests/Makefile @@ -0,0 +1,5 @@ +PACKAGE= tests +ATF_TESTS_SH= certctl_test +${PACKAGE}FILES+= certctl.subr + +.include <bsd.test.mk> diff --git a/usr.sbin/certctl/tests/certctl.subr b/usr.sbin/certctl/tests/certctl.subr new file mode 100644 index 000000000000..841cc1781e69 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl.subr @@ -0,0 +1,44 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +# Generate a random name +rand_name() { + local length=${1:-32} + + jot -r -c -s '' ${length} A Z +} + +# Generate a subject for a given name +subject() { + local crtname=$1 + + echo "/CN=${crtname}/O=FreeBSD/OU=Test/" +} + +# Generate a key +gen_key() { + local keyname=$1 + + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl genrsa -out ${keyname}.key +} + +# Generate a certificate for a given name, key, and serial number +gen_crt() { + local crtname=$1 + local keyname=${2:-${crtname}} + local serial=${3:-1} + + if ! [ -f "${keyname}".key ]; then + gen_key "${keyname}" + fi + env -i PATH="${PATH}" OPENSSL_CONF=/dev/null \ + openssl req -x509 -new \ + -subj="$(subject ${crtname})" \ + -set_serial ${serial} \ + -key ${keyname}.key \ + -out ${crtname}.crt +} diff --git a/usr.sbin/certctl/tests/certctl_test.sh b/usr.sbin/certctl/tests/certctl_test.sh new file mode 100644 index 000000000000..4e236d5bfae8 --- /dev/null +++ b/usr.sbin/certctl/tests/certctl_test.sh @@ -0,0 +1,221 @@ +# +# Copyright (c) 2025 Dag-Erling Smørgrav <des@FreeBSD.org> +# +# SPDX-License-Identifier: BSD-2-Clause +# + +. $(atf_get_srcdir)/certctl.subr + +# Random sets of eight non-colliding names +set1() +{ + cat <<EOF +AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB 0ca83bbe +UYSYXKDNNJTYOQPBGIKQDHRJYZHTDPKK 0d9a6512 +LODHGFXMZYKGOKAYGWTMMYQJYHDATDDM 4e6219f5 +NBBTQHJLHKBFFFWJTHHSNKOQYMGLHLPW 5dd76abc +BJFAQZXZHYQLIDDPCAQFPDMNXICUXBXW ad68573d +IOKNTHVEVVIJMNMYAVILMEMQQWLVRESN b577803d +BHGMAJJGNJPIVMHMFCUTJLGFROJICEKN c98a6338 +HCRFQMGDQJALMLUQNXMPGLXFLLJRODJW f50c6379 +EOF +} + +set2() +{ + cat <<EOF +GOHKZTSKIPDSYNLMGYXGLROPTATELXIU 30789c88 +YOOTYHEGHZIYFXOBLNKENPSJUDGOPJJU 7fadbc13 +ETRINNYBGKIENAVGOKVJYFSSHFZIJZRH 8ed664af +DBFGMFFMRNLPQLQPOLXOEUVLCRXLRSWT 8f34355e +WFOPBQPLQFHDHZOUQFEIDGSYDUOTSNDQ ac0471df +HMNETZMGNIWRGXQCVZXVZGWSGFBRRDQC b32f1472 +SHFYBXDVAUACBFPPAIGDAQIAGYOYGMQE baca75fa +PCBGDNVPYCDGNRQSGRSLXFHYKXLAVLHW ddeeae01 +EOF +} + +set3() +{ + cat <<EOF +NJWIRLPWAIICVJBKXXHFHLCPAERZATRL 000aa2e5 +RJAENDPOCZQEVCPFUWOWDXPCSMYJPVYC 021b95a3 +PQUQDSWHBNVLBTNBGONYRLGZZVEFXVLO 071e8c50 +VZEXRKJUPZSFBDWBOLUZXOGLNTEAPCZM 3af7bb9b +ZXOWOXQTXNZMAMZIWVFDZDJEWOOAGAOH 48d5c7cc +KQSFQYVJMFTMADIHJIWGSQISWKSHRYQO 509f5ba1 +AIECYSLWZOIEPJWWUTWSQXCNCIHHZHYI 8cb0c503 +RFHWDJZEPOFLMPGXAHVEJFHCDODAPVEV 9ae4e049 +EOF +} + +# Random set of three colliding names +collhash=f2888ce3 +coll() +{ + cat <<EOF +EJFTZEOANQLOYPEHWWXBWEWEFVKHMSNA $collhash +LEMRWZAZLKZLPPSFLNLQZVGKKBEOFYWG $collhash +ZWUPHYWKKTVEFBJOLLPDAIKGRDFVXZID $collhash +EOF +} + +certctl_setup() +{ + export DESTDIR="$PWD" + + # Create input directories + mkdir -p usr/share/certs/trusted + mkdir -p usr/share/certs/untrusted + mkdir -p usr/local/share/certs + + # Create output directories + mkdir -p etc/ssl/certs + mkdir -p etc/ssl/untrusted + + # Generate a random key + keyname="testkey" + gen_key ${keyname} + + # Generate certificates + set1 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt usr/share/certs/trusted + done + coll | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt usr/share/certs/trusted + done + set2 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + openssl x509 -in ${crtname}.crt + rm ${crtname}.crt + done >usr/local/share/certs/bundle.crt + set3 | while read crtname hash ; do + gen_crt ${crtname} ${keyname} + mv ${crtname}.crt usr/share/certs/untrusted + done +} + +check_trusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + etc/ssl/certs + atf_check -o match:"found: 0\$" \ + openssl storeutl -noout -subject "${subject}" \ + etc/ssl/untrusted +} + +check_untrusted() { + local crtname=$1 + local subject="$(subject ${crtname})" + local c=${2:-1} + + atf_check -o match:"found: 0\$" \ + openssl storeutl -noout -subject "${subject}" \ + etc/ssl/certs + atf_check -o match:"found: ${c}\$" \ + openssl storeutl -noout -subject "${subject}" \ + etc/ssl/untrusted +} + +check_in_bundle() { + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check grep -q "${line}" etc/ssl/cert.pem +} + +check_not_in_bundle() { + local crtfile=$1 + local line + + line=$(tail +5 "${crtfile}" | head -1) + atf_check -s exit:1 grep -q "${line}" etc/ssl/cert.pem +} + +atf_test_case rehash +rehash_head() +{ + atf_set "descr" "Test the rehash command" +} +rehash_body() +{ + certctl_setup + atf_check certctl rehash + + # Verify non-colliding trusted certificates + (set1 ; set2) > trusted + while read crtname hash ; do + check_trusted "${crtname}" + done <trusted + + # Verify colliding trusted certificates + coll >coll + while read crtname hash ; do + check_trusted "${crtname}" $(wc -l <coll) + done <coll + + # Verify untrusted certificates + set3 >untrusted + while read crtname hash ; do + check_untrusted "${crtname}" + done <untrusted + + # Verify bundle; storeutl is no help here + for f in etc/ssl/certs/*.? ; do + check_in_bundle "${f}" + done + for f in etc/ssl/untrusted/*.? ; do + check_not_in_bundle "${f}" + done +} + +atf_test_case trust +trust_head() +{ + atf_set "descr" "Test the trust command" +} +trust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=NJWIRLPWAIICVJBKXXHFHLCPAERZATRL + crtfile=usr/share/certs/untrusted/${crtname}.crt + check_untrusted ${crtname} + check_not_in_bundle ${crtfile} + atf_check -e match:"was previously untrusted" \ + certctl trust ${crtfile} + check_trusted ${crtname} + check_in_bundle ${crtfile} +} + +atf_test_case untrust +untrust_head() +{ + atf_set "descr" "Test the untrust command" +} +untrust_body() +{ + certctl_setup + atf_check certctl rehash + crtname=AVOYKJHSLFHWPVQMKBHENUAHJTEGMCCB + crtfile=usr/share/certs/trusted/${crtname}.crt + check_trusted "${crtname}" + check_in_bundle ${crtfile} + atf_check certctl untrust "${crtfile}" + check_untrusted "${crtname}" + check_not_in_bundle ${crtfile} +} + +atf_init_test_cases() +{ + atf_add_test_case rehash + atf_add_test_case trust + atf_add_test_case untrust +} diff --git a/usr.sbin/chroot/chroot.c b/usr.sbin/chroot/chroot.c index 7ec5a00b50f0..e1af0a4131d3 100644 --- a/usr.sbin/chroot/chroot.c +++ b/usr.sbin/chroot/chroot.c @@ -147,15 +147,10 @@ main(int argc, char *argv[]) gid = resolve_group(group); if (grouplist != NULL) { - ngroups_max = sysconf(_SC_NGROUPS_MAX) + 1; + ngroups_max = sysconf(_SC_NGROUPS_MAX); if ((gidlist = malloc(sizeof(gid_t) * ngroups_max)) == NULL) err(1, "malloc"); - /* Populate the egid slot in our groups to avoid accidents. */ - if (gid == 0) - gidlist[0] = getegid(); - else - gidlist[0] = gid; - for (gids = 1; (p = strsep(&grouplist, ",")) != NULL && + for (gids = 0; (p = strsep(&grouplist, ",")) != NULL && gids < ngroups_max; ) { if (*p == '\0') continue; diff --git a/usr.sbin/lpr/lpc/lpc.c b/usr.sbin/lpr/lpc/lpc.c index a3da852de46e..b4db5bb2e29f 100644 --- a/usr.sbin/lpr/lpc/lpc.c +++ b/usr.sbin/lpr/lpc/lpc.c @@ -358,6 +358,8 @@ ingroup(const char *grname) err(1, "getgroups"); } gid = gptr->gr_gid; + if (gid == getegid()) + return(1); for (i = 0; i < ngroups; i++) if (gid == groups[i]) return(1); diff --git a/usr.sbin/makefs/zfs.c b/usr.sbin/makefs/zfs.c index 8d50c450541b..e33a182e5c8f 100644 --- a/usr.sbin/makefs/zfs.c +++ b/usr.sbin/makefs/zfs.c @@ -596,7 +596,7 @@ pool_labels_write(zfs_opt_t *zfs) * checksum is calculated in vdev_label_write(). */ for (size_t uoff = 0; uoff < sizeof(label->vl_uberblock); - uoff += (1 << zfs->ashift)) { + uoff += ASHIFT_UBERBLOCK_SIZE(zfs->ashift)) { ub = (uberblock_t *)(&label->vl_uberblock[0] + uoff); ub->ub_magic = UBERBLOCK_MAGIC; ub->ub_version = SPA_VERSION; diff --git a/usr.sbin/makefs/zfs/vdev.c b/usr.sbin/makefs/zfs/vdev.c index afcce402cb13..a2423e180ca3 100644 --- a/usr.sbin/makefs/zfs/vdev.c +++ b/usr.sbin/makefs/zfs/vdev.c @@ -200,7 +200,7 @@ vdev_label_write(zfs_opt_t *zfs, int ind, const vdev_label_t *labelp) * per sector; for example, with an ashift of 12 we end up with * 128KB/4KB=32 copies of the uberblock in the ring. */ - blksz = 1 << zfs->ashift; + blksz = ASHIFT_UBERBLOCK_SIZE(zfs->ashift); assert(sizeof(label->vl_uberblock) % blksz == 0); for (size_t roff = 0; roff < sizeof(label->vl_uberblock); roff += blksz) { diff --git a/usr.sbin/syslogd/syslogd.8 b/usr.sbin/syslogd/syslogd.8 index fa61e78eaf3e..d39d9fdc8f5a 100644 --- a/usr.sbin/syslogd/syslogd.8 +++ b/usr.sbin/syslogd/syslogd.8 @@ -403,7 +403,7 @@ The message can contain a priority code, which should be a preceding decimal number in angle braces, for example, -.Sq Aq 5 . +.Sq <5> . This priority code should map into the priorities defined in the include file .In sys/syslog.h . diff --git a/usr.sbin/watchdogd/watchdogd.c b/usr.sbin/watchdogd/watchdogd.c index 88b467486da1..27123f2143d0 100644 --- a/usr.sbin/watchdogd/watchdogd.c +++ b/usr.sbin/watchdogd/watchdogd.c @@ -63,25 +63,25 @@ static long fetchtimeout(int opt, const char *longopt, const char *myoptarg, int zero_ok); static void parseargs(int, char *[]); -static int seconds_to_pow2ns(int); static void sighandler(int); static void watchdog_loop(void); static int watchdog_init(void); static int watchdog_onoff(int onoff); -static int watchdog_patpat(u_int timeout); +static int watchdog_patpat(sbintime_t); static void usage(void); -static int tstotv(struct timeval *tv, struct timespec *ts); static int tvtohz(struct timeval *tv); static int debugging = 0; static int end_program = 0; static const char *pidfile = _PATH_VARRUN "watchdogd.pid"; -static u_int timeout = WD_TO_128SEC; +static sbintime_t timeout = 128 * SBT_1S; static u_int exit_timeout = WD_TO_NEVER; static u_int pretimeout = 0; static u_int timeout_sec; static u_int nap = 10; +#ifdef notyet static int passive = 0; +#endif static int is_daemon = 0; static int is_dry_run = 0; /* do not arm the watchdog, only report on timing of the watch @@ -174,38 +174,23 @@ main(int argc, char *argv[]) pidfile_remove(pfh); return (EX_OK); } else { - if (passive) - timeout |= WD_PASSIVE; - else - timeout |= WD_ACTIVE; if (watchdog_patpat(timeout) < 0) err(EX_OSERR, "patting the dog"); return (EX_OK); } } -static void -pow2ns_to_ts(int pow2ns, struct timespec *ts) -{ - uint64_t ns; - - ns = 1ULL << pow2ns; - ts->tv_sec = ns / 1000000000ULL; - ts->tv_nsec = ns % 1000000000ULL; -} - /* * Convert a timeout in seconds to N where 2^N nanoseconds is close to * "seconds". * * The kernel expects the timeouts for watchdogs in "2^N nanosecond format". */ -static u_int -parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) +static sbintime_t +parse_timeout_to_sbt(char opt, const char *longopt, const char *myoptarg) { - double a; - u_int rv; - struct timespec ts; + long a; + sbintime_t rv; struct timeval tv; int ticks; char shortopt[] = "- "; @@ -216,19 +201,17 @@ parse_timeout_to_pow2ns(char opt, const char *longopt, const char *myoptarg) a = fetchtimeout(opt, longopt, myoptarg, 1); if (a == 0) - rv = WD_TO_NEVER; + rv = 0; else - rv = seconds_to_pow2ns(a); - pow2ns_to_ts(rv, &ts); - tstotv(&tv, &ts); + rv = a * SBT_1S; + tv = sbttotv(rv); ticks = tvtohz(&tv); if (debugging) { printf("Timeout for %s%s " - "is 2^%d nanoseconds " - "(in: %s sec -> out: %jd sec %ld ns -> %d ticks)\n", + "is " + "(in: %s sec -> out: %jd sec %ld us -> %d ticks)\n", longopt ? "-" : "", longopt ? longopt : shortopt, - rv, - myoptarg, (intmax_t)ts.tv_sec, ts.tv_nsec, ticks); + myoptarg, (intmax_t)tv.tv_sec, tv.tv_usec, ticks); } if (ticks <= 0) { errx(1, "Timeout for %s%s is too small, please choose a higher timeout.", longopt ? "-" : "", longopt ? longopt : shortopt); @@ -364,7 +347,7 @@ watchdog_loop(void) } if (failed == 0) - watchdog_patpat(timeout|WD_ACTIVE); + watchdog_patpat(timeout); waited = watchdog_check_dogfunction_time(&ts_start, &ts_end); if (nap - waited > 0) @@ -387,13 +370,22 @@ try_end: * to keep the watchdog from firing. */ static int -watchdog_patpat(u_int t) +watchdog_patpat(sbintime_t sbt) { if (is_dry_run) return 0; - return ioctl(fd, WDIOCPATPAT, &t); + return ioctl(fd, WDIOC_SETTIMEOUT, &sbt); +} + +static int +watchdog_control(u_int control) +{ + if (is_dry_run) + return (0); + + return ioctl(fd, WDIOC_CONTROL, &control); } /* @@ -420,7 +412,7 @@ watchdog_onoff(int onoff) warn("setting WDIOC_SETSOFT %d", softtimeout_set); return (error); } - error = watchdog_patpat((timeout|WD_ACTIVE)); + error = watchdog_patpat(timeout); if (error) { warn("watchdog_patpat failed"); goto failsafe; @@ -452,12 +444,12 @@ watchdog_onoff(int onoff) } } /* pat one more time for good measure */ - return watchdog_patpat((timeout|WD_ACTIVE)); + return watchdog_patpat(timeout); } else { - return watchdog_patpat(exit_timeout); + return watchdog_control(WD_CTRL_DISABLE); } failsafe: - watchdog_patpat(exit_timeout); + watchdog_control(WD_CTRL_DISABLE); return (error); } @@ -567,15 +559,6 @@ timeout_act_str2int(const char *lopt, const char *acts) return rv; } -int -tstotv(struct timeval *tv, struct timespec *ts) -{ - - tv->tv_sec = ts->tv_sec; - tv->tv_usec = ts->tv_nsec / 1000; - return 0; -} - /* * Convert a timeval to a number of ticks. * Mostly copied from the kernel. @@ -647,30 +630,6 @@ tvtohz(struct timeval *tv) return ((int)ticks); } -static int -seconds_to_pow2ns(int seconds) -{ - uint64_t power; - uint64_t ns; - uint64_t shifted; - - if (seconds <= 0) - errx(1, "seconds %d < 0", seconds); - ns = ((uint64_t)seconds) * 1000000000ULL; - power = flsll(ns); - shifted = 1ULL << power; - if (shifted <= ns) { - power++; - } - if (debugging) { - printf("shifted %lld\n", (long long)shifted); - printf("seconds_to_pow2ns: seconds: %d, ns %lld, power %d\n", - seconds, (long long)ns, (int)power); - } - return (power); -} - - /* * Handle the few command line arguments supported. */ @@ -683,8 +642,7 @@ parseargs(int argc, char *argv[]) const char *lopt; /* Get the default value of timeout_sec from the default timeout. */ - pow2ns_to_ts(timeout, &ts); - timeout_sec = ts.tv_sec; + timeout_sec = sbintime_getsec(timeout); /* * if we end with a 'd' aka 'watchdogd' then we are the daemon program, @@ -727,10 +685,10 @@ parseargs(int argc, char *argv[]) break; case 't': timeout_sec = atoi(optarg); - timeout = parse_timeout_to_pow2ns(c, NULL, optarg); + timeout = parse_timeout_to_sbt(c, NULL, optarg); if (debugging) - printf("Timeout is 2^%d nanoseconds\n", - timeout); + printf("Timeout is %d\n", + (int)(timeout / SBT_1S)); break; case 'T': carp_thresh_seconds = @@ -740,7 +698,7 @@ parseargs(int argc, char *argv[]) do_timedog = 1; break; case 'x': - exit_timeout = parse_timeout_to_pow2ns(c, NULL, optarg); + exit_timeout = parse_timeout_to_sbt(c, NULL, optarg); if (exit_timeout != 0) exit_timeout |= WD_ACTIVE; break; |