diff options
114 files changed, 2760 insertions, 1624 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index faf7c421af80..a5e41d9ac6d6 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -51,6 +51,15 @@ # xargs -n1 | sort | uniq -d; # done +# 20250710: share: Delete bitrotted make_*_driver.sh scripts +OLD_FILES+=usr/share/examples/drivers/README +OLD_FILES+=usr/share/examples/drivers/make_device_driver.sh +OLD_FILES+=usr/share/examples/drivers/make_pseudo_driver.sh +OLD_DIRS+=usr/share/examples/drivers + +# 20250710: shar(1) removed +OLD_FILES+=usr/bin/shar + # 20250708: For 34 days 15.0-CURRENT installed libkadm5clnt symlink without .so OLD_FILES+=usr/lib/libkadm5clnt @@ -60,10 +69,6 @@ MOVED_LIBS+=usr/lib/libtpool.so.2 # 20250626: replace yaml.lua with lyaml OLD_FILES+=usr/share/flua/yaml.lua -# 20250623: fscandir() renamed to fdscandir() -OLD_FILES+=usr/share/man/man3/fscandir.3.gz -OLD_FILES+=usr/share/man/man3/fscandir_b.3.gz - # 20250615: don't install man page for absent function OLD_FILES+=usr/share/man/man9/vm_map_simplify_entry.9.gz @@ -27,6 +27,11 @@ 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".) +20250710: + The shar(1) utility has been removed from base. The + sysutils/freebsd-shar port was created to maintain this version of + shar(1) past its removal from base. + 20250704: LinuxKPI device.h and acpi changes effecting drivers and drm-kmod. Bump __FreeBSD_version 1500050 to be able to detect these changes. diff --git a/bin/cp/cp.1 b/bin/cp/cp.1 index 2856391a029e..5231fa72621c 100644 --- a/bin/cp/cp.1 +++ b/bin/cp/cp.1 @@ -29,7 +29,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 28, 2024 +.Dd July 9, 2025 .Dt CP 1 .Os .Sh NAME @@ -84,16 +84,16 @@ If the .Fl R option is specified, symbolic links on the command line are followed. (Symbolic links encountered in the tree traversal are not followed.) -.It Fl L +.It Fl L , Fl -dereference If the .Fl R option is specified, all symbolic links are followed. -.It Fl P +.It Fl P , Fl -no-dereference No symbolic links are followed. This is the default if the .Fl R option is specified. -.It Fl R +.It Fl R , Fl -recursive If .Ar source_file designates a directory, @@ -121,11 +121,11 @@ If you need to preserve hard links, consider using or .Xr pax 1 instead. -.It Fl a +.It Fl a , Fl -archive Archive mode. Same as .Fl RpP . -.It Fl f +.It Fl f , Fl -force For each existing destination pathname, remove it and create a new file, without prompting for confirmation regardless of its permissions. @@ -136,10 +136,8 @@ option overrides any previous or .Fl n options.) -.It Fl i -Cause -.Nm -to write a prompt to the standard error output before copying a file +.It Fl i , Fl -interactive +Write a prompt to the standard error output before copying a file that would overwrite an existing file. If the response from the standard input begins with the character .Sq Li y @@ -153,13 +151,13 @@ option overrides any previous or .Fl n options.) -.It Fl l +.It Fl l , Fl -link Create hard links to regular files in a hierarchy instead of copying. .It Fl N When used with .Fl p , suppress copying file flags. -.It Fl n +.It Fl n , Fl -no-clobber Do not overwrite an existing file. (The .Fl n @@ -169,9 +167,7 @@ or .Fl i options.) .It Fl p -Cause -.Nm -to preserve the following attributes of each source +Preserve the following attributes of each source file in the copy: modification time, access time, file flags, file mode, ACL, user ID, and group ID, as allowed by permissions. .Pp @@ -188,14 +184,25 @@ If the source file has both its set-user-ID and set-group-ID bits on, and either the user ID or group ID cannot be preserved, neither the set-user-ID nor set-group-ID bits are preserved in the copy's permissions. -.It Fl s -Create symbolic links to regular files in a hierarchy instead of copying. -.It Fl v -Cause +.It Fl -sort +Visit and traverse sources in (non-localized) lexicographical order. +Normally, .Nm -to be verbose, showing files as they are copied. -.It Fl x -File system mount points are not traversed. +visits the sources in the order they were listed on the command line, +and if recursing, traverses their contents in whichever order they +were returned in by the kernel, which may be the order in which they +were created, lexicographical order, or something else entirely. +With +.Fl -sort , +the sources are both visited and traversed in lexicographical order. +This is mostly useful for testing. +.It Fl s , Fl -symbolic-link +Create symbolic links to regular files in a hierarchy instead of copying. +.It Fl v , Fl -verbose +Be verbose, showing both the source and destination path of each file +as is copied. +.It Fl x , Fl -one-file-system +Do not traverse file system mount points. .El .Pp For each destination file that already exists, its contents are diff --git a/bin/cp/cp.c b/bin/cp/cp.c index 7e97715c3ef4..38fe65399d06 100644 --- a/bin/cp/cp.c +++ b/bin/cp/cp.c @@ -55,6 +55,7 @@ #include <errno.h> #include <fcntl.h> #include <fts.h> +#include <getopt.h> #include <limits.h> #include <signal.h> #include <stdbool.h> @@ -69,8 +70,8 @@ static char dot[] = "."; #define END(buf) (buf + sizeof(buf)) PATH_T to = { .dir = -1, .end = to.path }; -int Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag; -static int Hflag, Lflag, Pflag, Rflag, rflag; +bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag; +static bool Hflag, Lflag, Pflag, Rflag, rflag, Sflag; volatile sig_atomic_t info; enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; @@ -78,6 +79,27 @@ enum op { FILE_TO_FILE, FILE_TO_DIR, DIR_TO_DNE }; static int copy(char *[], enum op, int, struct stat *); static void siginfo(int __unused); +enum { + SORT_OPT = CHAR_MAX, +}; + +static const struct option long_opts[] = +{ + { "archive", no_argument, NULL, 'a' }, + { "force", no_argument, NULL, 'f' }, + { "interactive", no_argument, NULL, 'i' }, + { "dereference", no_argument, NULL, 'L' }, + { "link", no_argument, NULL, 'l' }, + { "no-clobber", no_argument, NULL, 'n' }, + { "no-dereference", no_argument, NULL, 'P' }, + { "recursive", no_argument, NULL, 'R' }, + { "symbolic-link", no_argument, NULL, 's' }, + { "verbose", no_argument, NULL, 'v' }, + { "one-file-system", no_argument, NULL, 'x' }, + { "sort", no_argument, NULL, SORT_OPT }, + { 0 } +}; + int main(int argc, char *argv[]) { @@ -88,63 +110,67 @@ main(int argc, char *argv[]) bool have_trailing_slash = false; fts_options = FTS_NOCHDIR | FTS_PHYSICAL; - while ((ch = getopt(argc, argv, "HLPRafilNnprsvx")) != -1) + while ((ch = getopt_long(argc, argv, "+HLPRafilNnprsvx", long_opts, + NULL)) != -1) switch (ch) { case 'H': - Hflag = 1; - Lflag = Pflag = 0; + Hflag = true; + Lflag = Pflag = false; break; case 'L': - Lflag = 1; - Hflag = Pflag = 0; + Lflag = true; + Hflag = Pflag = false; break; case 'P': - Pflag = 1; - Hflag = Lflag = 0; + Pflag = true; + Hflag = Lflag = false; break; case 'R': - Rflag = 1; + Rflag = true; break; case 'a': - pflag = 1; - Rflag = 1; - Pflag = 1; - Hflag = Lflag = 0; + pflag = true; + Rflag = true; + Pflag = true; + Hflag = Lflag = false; break; case 'f': - fflag = 1; - iflag = nflag = 0; + fflag = true; + iflag = nflag = false; break; case 'i': - iflag = 1; - fflag = nflag = 0; + iflag = true; + fflag = nflag = false; break; case 'l': - lflag = 1; + lflag = true; break; case 'N': - Nflag = 1; + Nflag = true; break; case 'n': - nflag = 1; - fflag = iflag = 0; + nflag = true; + fflag = iflag = false; break; case 'p': - pflag = 1; + pflag = true; break; case 'r': - rflag = Lflag = 1; - Hflag = Pflag = 0; + rflag = Lflag = true; + Hflag = Pflag = false; break; case 's': - sflag = 1; + sflag = true; break; case 'v': - vflag = 1; + vflag = true; break; case 'x': fts_options |= FTS_XDEV; break; + case SORT_OPT: + Sflag = true; + break; default: usage(); } @@ -159,7 +185,7 @@ main(int argc, char *argv[]) if (lflag && sflag) errx(1, "the -l and -s options may not be specified together"); if (rflag) - Rflag = 1; + Rflag = true; if (Rflag) { if (Hflag) fts_options |= FTS_COMFOLLOW; @@ -263,6 +289,12 @@ main(int argc, char *argv[]) } static int +ftscmp(const FTSENT * const *a, const FTSENT * const *b) +{ + return (strcmp((*a)->fts_name, (*b)->fts_name)); +} + +static int copy(char *argv[], enum op type, int fts_options, struct stat *root_stat) { char rootname[NAME_MAX]; @@ -305,7 +337,7 @@ copy(char *argv[], enum op type, int fts_options, struct stat *root_stat) } level = FTS_ROOTLEVEL; - if ((ftsp = fts_open(argv, fts_options, NULL)) == NULL) + if ((ftsp = fts_open(argv, fts_options, Sflag ? ftscmp : NULL)) == NULL) err(1, "fts_open"); for (badcp = rval = 0; (curr = fts_read(ftsp)) != NULL; diff --git a/bin/cp/extern.h b/bin/cp/extern.h index c0c524756980..683e6e5f289f 100644 --- a/bin/cp/extern.h +++ b/bin/cp/extern.h @@ -37,7 +37,7 @@ typedef struct { } PATH_T; extern PATH_T to; -extern int Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag; +extern bool Nflag, fflag, iflag, lflag, nflag, pflag, sflag, vflag; extern volatile sig_atomic_t info; __BEGIN_DECLS diff --git a/bin/cp/tests/cp_test.sh b/bin/cp/tests/cp_test.sh index 1d2cd4292459..999993bfad67 100755 --- a/bin/cp/tests/cp_test.sh +++ b/bin/cp/tests/cp_test.sh @@ -34,6 +34,10 @@ check_size() } atf_test_case basic +basic_head() +{ + atf_set "descr" "Copy a file" +} basic_body() { echo "foo" > bar @@ -43,18 +47,26 @@ basic_body() } atf_test_case basic_symlink +basic_symlink_head() +{ + atf_set "descr" "Copy a symlink to a file" +} basic_symlink_body() { echo "foo" > bar ln -s bar baz atf_check cp baz foo - atf_check test '!' -L foo + atf_check test ! -L foo atf_check cmp foo bar } atf_test_case chrdev +chrdev_head() +{ + atf_set "descr" "Copy a character device" +} chrdev_body() { echo "foo" > bar @@ -69,6 +81,10 @@ chrdev_body() } atf_test_case hardlink +hardlink_head() +{ + atf_set "descr" "Create a hard link to a file" +} hardlink_body() { echo "foo" >foo @@ -78,6 +94,11 @@ hardlink_body() } atf_test_case hardlink_exists +hardlink_exists_head() +{ + atf_set "descr" "Attempt to create a hard link to a file, " \ + "but the destination already exists" +} hardlink_exists_body() { echo "foo" >foo @@ -88,6 +109,11 @@ hardlink_exists_body() } atf_test_case hardlink_exists_force +hardlink_exists_force_head() +{ + atf_set "descr" "Force creation of a hard link to a file " \ + "when the destination already exists" +} hardlink_exists_force_body() { echo "foo" >foo @@ -98,9 +124,12 @@ hardlink_exists_force_body() } atf_test_case matching_srctgt +matching_srctgt_head() +{ + atf_set "descr" "Avoid infinite loop when copying a directory to itself" +} matching_srctgt_body() { - # PR235438: `cp -R foo foo` would previously infinitely recurse and # eventually error out. mkdir foo @@ -110,13 +139,17 @@ matching_srctgt_body() atf_check cp -R foo foo atf_check -o inline:"qux\n" cat foo/foo/bar atf_check -o inline:"qux\n" cat foo/foo/zoo - atf_check -e not-empty -s not-exit:0 stat foo/foo/foo + atf_check test ! -e foo/foo/foo } atf_test_case matching_srctgt_contained +matching_srctgt_contained_head() +{ + atf_set "descr" "Avoid infinite loop when copying a directory " \ + "into an existing subdirectory of itself" +} matching_srctgt_contained_body() { - # Let's do the same thing, except we'll try to recursively copy foo into # one of its subdirectories. mkdir foo @@ -142,9 +175,13 @@ matching_srctgt_contained_body() } atf_test_case matching_srctgt_link +matching_srctgt_link_head() +{ + atf_set "descr" "Avoid infinite loop when recursively copying a " \ + "symlink to a directory into the directory it links to" +} matching_srctgt_link_body() { - mkdir foo echo "qux" > foo/bar cp foo/bar foo/zoo @@ -156,9 +193,13 @@ matching_srctgt_link_body() } atf_test_case matching_srctgt_nonexistent +matching_srctgt_nonexistent_head() +{ + atf_set "descr" "Avoid infinite loop when recursively copying a " \ + "directory into a new subdirectory of itself" +} matching_srctgt_nonexistent_body() { - # We'll copy foo to a nonexistent subdirectory; ideally, we would # skip just the directory and end up with a layout like; # @@ -180,6 +221,10 @@ matching_srctgt_nonexistent_body() } atf_test_case pflag_acls +pflag_acls_head() +{ + atf_set "descr" "Verify that -p preserves access control lists" +} pflag_acls_body() { mkdir dir @@ -216,6 +261,10 @@ pflag_acls_body() } atf_test_case pflag_flags +pflag_flags_head() +{ + atf_set "descr" "Verify that -p preserves file flags" +} pflag_flags_body() { mkdir dir @@ -263,6 +312,11 @@ recursive_link_setup() } atf_test_case recursive_link_dflt +recursive_link_dflt_head() +{ + atf_set "descr" "Copy a directory containing a subdirectory and a " \ + "symlink to that subdirectory" +} recursive_link_dflt_body() { recursive_link_setup @@ -270,9 +324,15 @@ recursive_link_dflt_body() # -P is the default, so this should work and preserve the link. atf_check cp -R foo foo-mirror atf_check test -L foo-mirror/foo/baz + atf_check test -d foo-mirror/foo/baz } atf_test_case recursive_link_Hflag +recursive_link_Hflag_head() +{ + atf_set "descr" "Copy a directory containing a subdirectory and a " \ + "symlink to that subdirectory" +} recursive_link_Hflag_body() { recursive_link_setup @@ -281,22 +341,32 @@ recursive_link_Hflag_body() # link. atf_check cp -RH foo foo-mirror atf_check test -L foo-mirror/foo/baz + atf_check test -d foo-mirror/foo/baz } atf_test_case recursive_link_Lflag +recursive_link_Lflag_head() +{ + atf_set "descr" "Copy a directory containing a subdirectory and a " \ + "symlink to that subdirectory" +} recursive_link_Lflag_body() { recursive_link_setup -L # -L will work, but foo/baz ends up expanded to a directory. - atf_check test -d foo-mirror/foo/baz -a \ - '(' ! -L foo-mirror/foo/baz ')' + atf_check test ! -L foo-mirror/foo/baz + atf_check test -d foo-mirror/foo/baz atf_check cp -RL foo foo-mirror - atf_check test -d foo-mirror/foo/baz -a \ - '(' ! -L foo-mirror/foo/baz ')' + atf_check test ! -L foo-mirror/foo/baz + atf_check test -d foo-mirror/foo/baz } atf_test_case samefile +samefile_head() +{ + atf_set "descr" "Copy a file to itself" +} samefile_body() { echo "foo" >foo @@ -324,6 +394,10 @@ files_are_equal() } atf_test_case sparse_leading_hole +sparse_leading_hole_head() +{ + atf_set "descr" "Copy a sparse file stat starts with a hole" +} sparse_leading_hole_body() { # A 16-megabyte hole followed by one megabyte of data @@ -337,6 +411,10 @@ sparse_leading_hole_body() } atf_test_case sparse_multiple_holes +sparse_multiple_hole_head() +{ + atf_set "descr" "Copy a sparse file with multiple holes" +} sparse_multiple_holes_body() { # Three one-megabyte blocks of data preceded, separated, and @@ -356,6 +434,10 @@ sparse_multiple_holes_body() } atf_test_case sparse_only_hole +sparse_only_hole_head() +{ + atf_set "descr" "Copy a sparse file consisting entirely of a hole" +} sparse_only_hole_body() { # A 16-megabyte hole @@ -368,6 +450,10 @@ sparse_only_hole_body() } atf_test_case sparse_to_dev +sparse_to_dev_head() +{ + atf_set "descr" "Copy a sparse file to a device" +} sparse_to_dev_body() { # Three one-megabyte blocks of data preceded, separated, and @@ -385,6 +471,10 @@ sparse_to_dev_body() } atf_test_case sparse_trailing_hole +sparse_trailing_hole_head() +{ + atf_set "descr" "Copy a sparse file that ends with a hole" +} sparse_trailing_hole_body() { # One megabyte of data followed by a 16-megabyte hole @@ -398,16 +488,24 @@ sparse_trailing_hole_body() } atf_test_case standalone_Pflag +standalone_Pflag_head() +{ + atf_set "descr" "Test -P without -R" +} standalone_Pflag_body() { echo "foo" > bar ln -s bar foo atf_check cp -P foo baz - atf_check -o inline:'Symbolic Link\n' stat -f %SHT baz + atf_check test -L baz } atf_test_case symlink +symlink_head() +{ + atf_set "descr" "Create a symbolic link to a file" +} symlink_body() { echo "foo" >foo @@ -417,6 +515,11 @@ symlink_body() } atf_test_case symlink_exists +symlink_exists_head() +{ + atf_set "descr" "Attempt to create a symbolic link to a file, " \ + "but the destination already exists" +} symlink_exists_body() { echo "foo" >foo @@ -426,6 +529,11 @@ symlink_exists_body() } atf_test_case symlink_exists_force +symlink_exists_force_head() +{ + atf_set "descr" "Force creation of a symbolic link to a file " \ + "when the destination already exists" +} symlink_exists_force_body() { echo "foo" >foo @@ -436,6 +544,10 @@ symlink_exists_force_body() } atf_test_case directory_to_symlink +directory_to_symlink_head() +{ + atf_set "descr" "Attempt to copy a directory to a symlink" +} directory_to_symlink_body() { mkdir -p foo @@ -449,6 +561,10 @@ directory_to_symlink_body() } atf_test_case overwrite_directory +overwrite_directory_head() +{ + atf_set "descr" "Attempt to overwrite a directory with a file" +} overwrite_directory_body() { mkdir -p foo/bar/baz @@ -465,6 +581,10 @@ overwrite_directory_body() } atf_test_case to_dir_dne +to_dir_dne_head() +{ + atf_set "descr" "Copy a directory to a nonexistent directory" +} to_dir_dne_body() { mkdir dir @@ -476,6 +596,10 @@ to_dir_dne_body() } atf_test_case to_nondir +to_dir_dne_head() +{ + atf_set "descr" "Copy one or more files to a non-directory" +} to_nondir_body() { echo "foo" >foo @@ -490,6 +614,10 @@ to_nondir_body() } atf_test_case to_deadlink +to_deadlink_head() +{ + atf_set "descr" "Copy a file to a dead symbolic link" +} to_deadlink_body() { echo "foo" >foo @@ -499,6 +627,10 @@ to_deadlink_body() } atf_test_case to_deadlink_append +to_deadlink_append_head() +{ + atf_set "descr" "Copy multiple files to a dead symbolic link" +} to_deadlink_append_body() { echo "foo" >foo @@ -517,6 +649,10 @@ to_deadlink_append_body() } atf_test_case to_dirlink +to_dirlink_head() +{ + atf_set "descr" "Copy things to a symbolic link to a directory" +} to_dirlink_body() { mkdir src dir @@ -542,6 +678,11 @@ to_dirlink_body() } atf_test_case to_deaddirlink +to_deaddirlink_head() +{ + atf_set "descr" "Copy things to a symbolic link to a nonexistent " \ + "directory" +} to_deaddirlink_body() { mkdir src @@ -572,6 +713,11 @@ to_deaddirlink_body() } atf_test_case to_link_outside +to_link_outside_head() +{ + atf_set "descr" "Recursively copy a directory containing a symbolic " \ + "link that points to somewhere outside the source directory" +} to_link_outside_body() { mkdir dir dst dst/dir @@ -584,6 +730,11 @@ to_link_outside_body() } atf_test_case dstmode +dstmode_head() +{ + atf_set "descr" "Verify that directories are created with the " \ + "correct permissions" +} dstmode_body() { mkdir -m 0755 dir @@ -646,6 +797,7 @@ atf_test_case unrdir unrdir_head() { atf_set "descr" "Test handling of unreadable directories" + atf_set "require.user" "unprivileged" } unrdir_body() { @@ -657,7 +809,7 @@ unrdir_body() atf_check \ -s exit:1 \ -e match:"^cp: src/b: Permission denied" \ - cp -R src dst + cp -R --sort src dst atf_check test -d dst/a atf_check cmp src/a/f dst/a/f atf_check test -d dst/b @@ -670,6 +822,7 @@ atf_test_case unrfile unrfile_head() { atf_set "descr" "Test handling of unreadable files" + atf_set "require.user" "unprivileged" } unrfile_body() { @@ -681,13 +834,28 @@ unrfile_body() atf_check \ -s exit:1 \ -e match:"^cp: src/b: Permission denied" \ - cp -R src dst + cp -R --sort src dst atf_check test -d dst atf_check cmp src/a dst/a atf_check test ! -e dst/b atf_check cmp src/c dst/c } +atf_test_case nopermute +nopermute_head() +{ + atf_set descr "Check that getopt_long does not permute options" +} +nopermute_body() +{ + mkdir src dst + atf_check \ + -s exit:1 \ + -e match:'cp: -p: No such file' \ + cp -R src -p dst + atf_check test -d dst/src +} + atf_init_test_cases() { atf_add_test_case basic @@ -729,4 +897,5 @@ atf_init_test_cases() atf_add_test_case dirloop atf_add_test_case unrdir atf_add_test_case unrfile + atf_add_test_case nopermute } diff --git a/bin/cp/utils.c b/bin/cp/utils.c index cfc0f0f12603..2036056ada68 100644 --- a/bin/cp/utils.c +++ b/bin/cp/utils.c @@ -105,7 +105,7 @@ copy_file(const FTSENT *entp, bool dne, bool beneath) ssize_t wcount; off_t wtotal; int ch, checkch, from_fd, rval, to_fd; - int use_copy_file_range = 1; + bool use_copy_file_range = true; fs = entp->fts_statp; from_fd = to_fd = -1; @@ -210,7 +210,7 @@ copy_file(const FTSENT *entp, bool dne, bool beneath) to_fd, NULL, SSIZE_MAX, 0); if (wcount < 0 && errno == EINVAL) { /* probably a non-seekable descriptor */ - use_copy_file_range = 0; + use_copy_file_range = false; } } if (!use_copy_file_range) { diff --git a/contrib/kyua/utils/fs/operations.cpp b/contrib/kyua/utils/fs/operations.cpp index 7a96d0b2058a..185d164b88d7 100644 --- a/contrib/kyua/utils/fs/operations.cpp +++ b/contrib/kyua/utils/fs/operations.cpp @@ -692,6 +692,7 @@ fs::rm_r(const fs::path& directory) { const fs::directory dir(directory); + ::chmod(directory.c_str(), 0700); for (fs::directory::const_iterator iter = dir.begin(); iter != dir.end(); ++iter) { if (iter->name == "." || iter->name == "..") @@ -701,6 +702,7 @@ fs::rm_r(const fs::path& directory) if (fs::is_directory(entry)) { LD(F("Descending into %s") % entry); + ::chmod(entry.c_str(), 0700); fs::rm_r(entry); } else { LD(F("Removing file %s") % entry); diff --git a/contrib/kyua/utils/fs/operations_test.cpp b/contrib/kyua/utils/fs/operations_test.cpp index f1349351166e..6f0fa52811c9 100644 --- a/contrib/kyua/utils/fs/operations_test.cpp +++ b/contrib/kyua/utils/fs/operations_test.cpp @@ -664,6 +664,19 @@ ATF_TEST_CASE_BODY(rm_r__files_and_directories) } +ATF_TEST_CASE_WITHOUT_HEAD(rm_r__bad_perms); +ATF_TEST_CASE_BODY(rm_r__bad_perms) +{ + fs::mkdir(fs::path("root"), 0755); + fs::mkdir(fs::path("root/dir"), 0755); + atf::utils::create_file("root/dir/file", ""); + ::chmod(fs::path("root/dir").c_str(), 0000); + ATF_REQUIRE(lookup(".", "root", S_IFDIR)); + fs::rm_r(fs::path("root")); + ATF_REQUIRE(!lookup(".", "root", S_IFDIR)); +} + + ATF_TEST_CASE_WITHOUT_HEAD(rmdir__ok) ATF_TEST_CASE_BODY(rmdir__ok) { @@ -811,6 +824,7 @@ ATF_INIT_TEST_CASES(tcs) ATF_ADD_TEST_CASE(tcs, rm_r__empty); ATF_ADD_TEST_CASE(tcs, rm_r__files_and_directories); + ATF_ADD_TEST_CASE(tcs, rm_r__bad_perms); ATF_ADD_TEST_CASE(tcs, rmdir__ok); ATF_ADD_TEST_CASE(tcs, rmdir__fail); diff --git a/lib/libc/gen/Symbol.map b/lib/libc/gen/Symbol.map index 50dbf3425964..26f638568efc 100644 --- a/lib/libc/gen/Symbol.map +++ b/lib/libc/gen/Symbol.map @@ -458,8 +458,6 @@ FBSD_1.8 { aio_read2; aio_write2; execvpe; - fscandir; - fscandir_b; fdscandir; fdscandir_b; fts_open_b; @@ -598,7 +596,6 @@ FBSDprivate_1.0 { __libc_tcdrain; - __pthread_distribute_static_tls; __pthread_map_stacks_exec; __fillcontextx; __fillcontextx2; diff --git a/lib/libc/gen/elf_utils.c b/lib/libc/gen/elf_utils.c index 330aa8f17f7e..3714a0dc42b5 100644 --- a/lib/libc/gen/elf_utils.c +++ b/lib/libc/gen/elf_utils.c @@ -41,7 +41,6 @@ #include "libc_private.h" void __pthread_map_stacks_exec(void); -void __pthread_distribute_static_tls(size_t, void *, size_t, size_t); int __elf_phdr_match_addr(struct dl_phdr_info *phdr_info, void *addr) @@ -105,28 +104,3 @@ __pthread_map_stacks_exec(void) ((void (*)(void))__libc_interposing[INTERPOS_map_stacks_exec])(); } - -void -__libc_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len) -{ - char *tlsbase; - -#ifdef TLS_VARIANT_I - tlsbase = (char *)_tcb_get() + offset; -#else - tlsbase = (char *)_tcb_get() - offset; -#endif - memcpy(tlsbase, src, len); - memset(tlsbase + len, 0, total_len - len); -} - -#pragma weak __pthread_distribute_static_tls -void -__pthread_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len) -{ - - ((void (*)(size_t, void *, size_t, size_t))__libc_interposing[ - INTERPOS_distribute_static_tls])(offset, src, len, total_len); -} diff --git a/lib/libc/gen/fts.3 b/lib/libc/gen/fts.3 index 5860d1be1a1e..ee558b892c8c 100644 --- a/lib/libc/gen/fts.3 +++ b/lib/libc/gen/fts.3 @@ -394,7 +394,7 @@ must be specified. The options are selected by .Em or Ns 'ing the following values: -.Bl -tag -width "FTS_PHYSICAL" +.Bl -tag -width "FTS_COMFOLLOWDIR" .It Dv FTS_COMFOLLOW This option causes any symbolic link specified as a root path to be followed immediately whether or not diff --git a/lib/libc/gen/gen-private.h b/lib/libc/gen/gen-private.h index 3792a61ff942..b6749b3435cd 100644 --- a/lib/libc/gen/gen-private.h +++ b/lib/libc/gen/gen-private.h @@ -43,8 +43,8 @@ struct pthread_mutex; */ struct _dirdesc { int dd_fd; /* file descriptor associated with directory */ - long dd_loc; /* offset in current buffer */ - long dd_size; /* amount of data returned by getdirentries */ + size_t dd_loc; /* offset in current buffer */ + size_t dd_size; /* amount of data returned by getdirentries */ char *dd_buf; /* data buffer */ int dd_len; /* size of data buffer */ off_t dd_seek; /* magic cookie returned by getdirentries */ diff --git a/lib/libc/gen/libc_interposing_table.c b/lib/libc/gen/libc_interposing_table.c index 8eae6c7f5d95..025a67ac3eac 100644 --- a/lib/libc/gen/libc_interposing_table.c +++ b/lib/libc/gen/libc_interposing_table.c @@ -42,7 +42,6 @@ interpos_func_t __libc_interposing[INTERPOS_MAX] = { SLOT(spinlock, __libc_spinlock_stub), SLOT(spinunlock, __libc_spinunlock_stub), SLOT(map_stacks_exec, __libc_map_stacks_exec), - SLOT(distribute_static_tls, __libc_distribute_static_tls), SLOT(uexterr_gettext, __libc_uexterr_gettext), }; #undef SLOT diff --git a/lib/libc/gen/opendir2.c b/lib/libc/gen/opendir2.c index 928145b468c1..c5c2e662efd8 100644 --- a/lib/libc/gen/opendir2.c +++ b/lib/libc/gen/opendir2.c @@ -264,6 +264,7 @@ DIR * __opendir_common(int fd, int flags, bool use_current_pos) { DIR *dirp; + ssize_t ret; int incr; int saved_errno; bool unionstack; @@ -313,13 +314,11 @@ __opendir_common(int fd, int flags, bool use_current_pos) * to prime dd_seek. This also checks if the * fd passed to fdopendir() is a directory. */ - dirp->dd_size = _getdirentries(dirp->dd_fd, + ret = _getdirentries(dirp->dd_fd, dirp->dd_buf, dirp->dd_len, &dirp->dd_seek); - if (dirp->dd_size < 0) { - if (errno == EINVAL) - errno = ENOTDIR; + if (ret < 0) goto fail; - } + dirp->dd_size = (size_t)ret; dirp->dd_flags |= __DTF_SKIPREAD; } else { dirp->dd_size = 0; diff --git a/lib/libc/gen/readdir.c b/lib/libc/gen/readdir.c index 2a2fa999b7ce..32603c0b4677 100644 --- a/lib/libc/gen/readdir.c +++ b/lib/libc/gen/readdir.c @@ -48,8 +48,8 @@ struct dirent * _readdir_unlocked(DIR *dirp, int flags) { struct dirent *dp; - long initial_seek; - long initial_loc = 0; + off_t initial_seek; + size_t initial_loc = 0; for (;;) { if (dirp->dd_loc >= dirp->dd_size) { diff --git a/lib/libc/gen/scandir.c b/lib/libc/gen/scandir.c index 8e62fe980868..fb589f4b36b6 100644 --- a/lib/libc/gen/scandir.c +++ b/lib/libc/gen/scandir.c @@ -252,9 +252,3 @@ scandir_thunk_cmp(const void *p1, const void *p2, void *thunk) return (dc((const struct dirent **)p1, (const struct dirent **)p2)); } #endif - -#ifdef I_AM_SCANDIR_B -__weak_reference(fdscandir_b, fscandir_b); -#else -__weak_reference(fdscandir, fscandir); -#endif diff --git a/lib/libc/gen/telldir.c b/lib/libc/gen/telldir.c index b751fafd975f..1731cc4d7a2c 100644 --- a/lib/libc/gen/telldir.c +++ b/lib/libc/gen/telldir.c @@ -118,7 +118,7 @@ _seekdir(DIR *dirp, long loc) struct dirent *dp; union ddloc_packed ddloc; off_t loc_seek; - long loc_loc; + size_t loc_loc; ddloc.l = loc; @@ -171,7 +171,7 @@ _seekdir(DIR *dirp, long loc) * fetching a new block to fix any such telldir locations. */ void -_fixtelldir(DIR *dirp, long oldseek, long oldloc) +_fixtelldir(DIR *dirp, off_t oldseek, size_t oldloc) { struct ddloc_mem *lp; diff --git a/lib/libc/gen/telldir.h b/lib/libc/gen/telldir.h index 6d113491e819..02fd52af9060 100644 --- a/lib/libc/gen/telldir.h +++ b/lib/libc/gen/telldir.h @@ -46,9 +46,9 @@ */ struct ddloc_mem { LIST_ENTRY(ddloc_mem) loc_lqe; /* entry in list */ - long loc_index; /* key associated with structure */ + size_t loc_index; /* key associated with structure */ off_t loc_seek; /* magic cookie returned by getdirentries */ - long loc_loc; /* offset of entry in buffer */ + size_t loc_loc; /* offset of entry in buffer */ }; #ifdef __LP64__ @@ -102,7 +102,7 @@ bool _filldir(DIR *, bool); struct dirent *_readdir_unlocked(DIR *, int); void _reclaim_telldir(DIR *); void _seekdir(DIR *, long); -void _fixtelldir(DIR *dirp, long oldseek, long oldloc); +void _fixtelldir(DIR *dirp, off_t oldseek, size_t oldloc); DIR *__opendir_common(int, int, bool); #define RDU_SKIP 0x0001 diff --git a/lib/libc/include/libc_private.h b/lib/libc/include/libc_private.h index 1bc22f3931a5..db4cbc32be35 100644 --- a/lib/libc/include/libc_private.h +++ b/lib/libc/include/libc_private.h @@ -249,7 +249,7 @@ enum { INTERPOS_map_stacks_exec, INTERPOS_fdatasync, INTERPOS_clock_nanosleep, - INTERPOS_distribute_static_tls, + INTERPOS__reserved0, /* was distribute_static_tls */ INTERPOS_pdfork, INTERPOS_uexterr_gettext, INTERPOS_MAX @@ -361,8 +361,6 @@ struct dl_phdr_info; int __elf_phdr_match_addr(struct dl_phdr_info *, void *); void __init_elf_aux_vector(void); void __libc_map_stacks_exec(void); -void __libc_distribute_static_tls(__size_t, void *, __size_t, __size_t); -__uintptr_t __libc_static_tls_base(__size_t); void _pthread_cancel_enter(int); void _pthread_cancel_leave(int); diff --git a/lib/libnvmf/libnvmf.h b/lib/libnvmf/libnvmf.h index 9840e190a24f..7cdd7e433455 100644 --- a/lib/libnvmf/libnvmf.h +++ b/lib/libnvmf/libnvmf.h @@ -342,7 +342,8 @@ int nvmf_host_request_queues(struct nvmf_qpair *qp, u_int requested, */ int nvmf_handoff_host(const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, - struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata); + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata, + uint32_t reconnect_delay, uint32_t controller_loss_timeout); /* * Disconnect an active host association previously handed off to the @@ -370,7 +371,8 @@ int nvmf_reconnect_params(int fd, nvlist_t **nvlp); */ int nvmf_reconnect_host(int fd, const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, - struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata); + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata, + uint32_t reconnect_delay, uint32_t controller_loss_timeout); /* * Fetch connection status from an existing kernel host. diff --git a/lib/libnvmf/nvmf_host.c b/lib/libnvmf/nvmf_host.c index 89cdd5c6bb70..3266f8898296 100644 --- a/lib/libnvmf/nvmf_host.c +++ b/lib/libnvmf/nvmf_host.c @@ -792,7 +792,8 @@ static int prepare_queues_for_handoff(struct nvmf_ioc_nv *nv, const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, - struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata, + uint32_t reconnect_delay, uint32_t controller_loss_timeout) { const struct nvmf_association *na = admin_qp->nq_association; nvlist_t *nvl, *nvl_qp, *nvl_rparams; @@ -820,6 +821,9 @@ prepare_queues_for_handoff(struct nvmf_ioc_nv *nv, nvlist_add_string(nvl_rparams, "hostnqn", hostnqn); nvlist_add_number(nvl_rparams, "num_io_queues", num_queues); nvlist_add_number(nvl_rparams, "kato", admin_qp->nq_kato); + nvlist_add_number(nvl_rparams, "reconnect_delay", reconnect_delay); + nvlist_add_number(nvl_rparams, "controller_loss_timeout", + controller_loss_timeout); nvlist_add_number(nvl_rparams, "io_qsize", io_queues[0]->nq_qsize); nvlist_add_bool(nvl_rparams, "sq_flow_control", na->na_params.sq_flow_control); @@ -842,6 +846,9 @@ prepare_queues_for_handoff(struct nvmf_ioc_nv *nv, nvl = nvlist_create(0); nvlist_add_number(nvl, "trtype", na->na_trtype); nvlist_add_number(nvl, "kato", admin_qp->nq_kato); + nvlist_add_number(nvl, "reconnect_delay", reconnect_delay); + nvlist_add_number(nvl, "controller_loss_timeout", + controller_loss_timeout); nvlist_move_nvlist(nvl, "rparams", nvl_rparams); /* First, the admin queue. */ @@ -872,7 +879,8 @@ prepare_queues_for_handoff(struct nvmf_ioc_nv *nv, int nvmf_handoff_host(const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, - struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata, + uint32_t reconnect_delay, uint32_t controller_loss_timeout) { struct nvmf_ioc_nv nv; u_int i; @@ -885,7 +893,8 @@ nvmf_handoff_host(const struct nvme_discovery_log_entry *dle, } error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp, - num_queues, io_queues, cdata); + num_queues, io_queues, cdata, reconnect_delay, + controller_loss_timeout); if (error != 0) goto out; @@ -981,14 +990,16 @@ nvmf_reconnect_params(int fd, nvlist_t **nvlp) int nvmf_reconnect_host(int fd, const struct nvme_discovery_log_entry *dle, const char *hostnqn, struct nvmf_qpair *admin_qp, u_int num_queues, - struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata) + struct nvmf_qpair **io_queues, const struct nvme_controller_data *cdata, + uint32_t reconnect_delay, uint32_t controller_loss_timeout) { struct nvmf_ioc_nv nv; u_int i; int error; error = prepare_queues_for_handoff(&nv, dle, hostnqn, admin_qp, - num_queues, io_queues, cdata); + num_queues, io_queues, cdata, reconnect_delay, + controller_loss_timeout); if (error != 0) goto out; diff --git a/lib/libsys/getdirentries.2 b/lib/libsys/getdirentries.2 index 0e5840ce25cd..202ae133f548 100644 --- a/lib/libsys/getdirentries.2 +++ b/lib/libsys/getdirentries.2 @@ -25,7 +25,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd September 5, 2023 +.Dd July 8, 2025 .Dt GETDIRENTRIES 2 .Os .Sh NAME @@ -178,9 +178,7 @@ or non-NULL .Fa basep point outside the allocated address space. .It Bq Er EINVAL -The file referenced by -.Fa fd -is not a directory, or +The value of .Fa nbytes is too small for returning a directory entry or block of entries, or the current position pointer is invalid. @@ -192,6 +190,10 @@ error occurred while reading from or writing to the file system. Corrupted data was detected while reading from the file system. .It Bq Er ENOENT Directory unlinked but still open. +.It Bq Er ENOTDIR +The file referenced by +.Fa fd +is not a directory. .El .Sh SEE ALSO .Xr lseek 2 , diff --git a/lib/libthr/pthread.map b/lib/libthr/pthread.map index 1c8dde03367b..3a5353a32dc3 100644 --- a/lib/libthr/pthread.map +++ b/lib/libthr/pthread.map @@ -136,7 +136,6 @@ FBSDprivate_1.0 { __pthread_mutex_lock; __pthread_mutex_timedlock; __pthread_mutex_trylock; - __pthread_distribute_static_tls; _pthread_atfork; _pthread_barrier_destroy; _pthread_barrier_init; diff --git a/lib/libthr/thread/thr_list.c b/lib/libthr/thread/thr_list.c index 820766f6f5e0..cbf16179f619 100644 --- a/lib/libthr/thread/thr_list.c +++ b/lib/libthr/thread/thr_list.c @@ -363,41 +363,3 @@ _thr_find_thread(struct pthread *curthread, struct pthread *thread, THREAD_LIST_UNLOCK(curthread); return (ret); } - -static void -thr_distribute_static_tls(char *tlsbase, void *src, size_t len, - size_t total_len) -{ - - memcpy(tlsbase, src, len); - memset(tlsbase + len, 0, total_len - len); -} - -void -__pthread_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len) -{ - struct pthread *curthread, *thrd; - char *tlsbase; - - if (!_thr_is_inited()) { -#ifdef TLS_VARIANT_I - tlsbase = (char *)_tcb_get() + offset; -#else - tlsbase = (char *)_tcb_get() - offset; -#endif - thr_distribute_static_tls(tlsbase, src, len, total_len); - return; - } - curthread = _get_curthread(); - THREAD_LIST_RDLOCK(curthread); - TAILQ_FOREACH(thrd, &_thread_list, tle) { -#ifdef TLS_VARIANT_I - tlsbase = (char *)thrd->tcb + offset; -#else - tlsbase = (char *)thrd->tcb - offset; -#endif - thr_distribute_static_tls(tlsbase, src, len, total_len); - } - THREAD_LIST_UNLOCK(curthread); -} diff --git a/lib/libthr/thread/thr_private.h b/lib/libthr/thread/thr_private.h index bca890829057..d7b889930365 100644 --- a/lib/libthr/thread/thr_private.h +++ b/lib/libthr/thread/thr_private.h @@ -986,8 +986,6 @@ void __pthread_cxa_finalize(struct dl_phdr_info *phdr_info); void _thr_tsd_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_sigact_unload(struct dl_phdr_info *phdr_info) __hidden; void _thr_stack_fix_protection(struct pthread *thrd); -void __pthread_distribute_static_tls(size_t offset, void *src, size_t len, - size_t total_len); int *__error_threaded(void) __hidden; void __thr_interpose_libc(void) __hidden; diff --git a/libexec/flua/modules/lfbsd.c b/libexec/flua/modules/lfbsd.c index a361fa25601b..ef660ba9fd77 100644 --- a/libexec/flua/modules/lfbsd.c +++ b/libexec/flua/modules/lfbsd.c @@ -31,6 +31,7 @@ #include <errno.h> #include <fcntl.h> #include <spawn.h> +#include <stdbool.h> #include <string.h> #include <stdio.h> #include <unistd.h> @@ -44,6 +45,7 @@ struct fbsd_process { int pid; int stdin_fileno; + int stdout_fileno; }; extern char **environ; @@ -70,6 +72,16 @@ luaL_checkarraystrings(lua_State *L, int arg) return ret; } +static void +close_pipes(int pipes[2]) +{ + + if (pipes[0] != -1) + close(pipes[0]); + if (pipes[1] != -1) + close(pipes[1]); +} + static int lua_exec(lua_State *L) { @@ -77,31 +89,57 @@ lua_exec(lua_State *L) int r; posix_spawn_file_actions_t action; int stdin_pipe[2] = {-1, -1}; + int stdout_pipe[2] = {-1, -1}; pid_t pid; const char **argv; int n = lua_gettop(L); - luaL_argcheck(L, n == 1, n > 1 ? 2 : n, - "fbsd.exec takes exactly one argument"); + bool capture_stdout; + luaL_argcheck(L, n > 0 && n <= 2, n >= 2 ? 2 : n, + "fbsd.exec takes exactly one or two arguments"); + capture_stdout = lua_toboolean(L, 2); if (pipe(stdin_pipe) < 0) { lua_pushnil(L); lua_pushstring(L, strerror(errno)); lua_pushinteger(L, errno); return (3); } + if (capture_stdout && pipe(stdout_pipe) < 0) { + close_pipes(stdin_pipe); + lua_pushnil(L); + lua_pushstring(L, strerror(errno)); + lua_pushinteger(L, errno); + return (3); + } proc = lua_newuserdata(L, sizeof(*proc)); proc->stdin_fileno = stdin_pipe[1]; - + proc->stdout_fileno = stdout_pipe[1]; posix_spawn_file_actions_init(&action); posix_spawn_file_actions_adddup2(&action, stdin_pipe[0], STDIN_FILENO); posix_spawn_file_actions_addclose(&action, stdin_pipe[1]); + if (stdin_pipe[0] != STDIN_FILENO) + posix_spawn_file_actions_addclose(&action, stdin_pipe[0]); + + /* + * Setup stdout to be captured if requested. Otherwise, we just let it + * go to our own stdout. + */ + if (stdout_pipe[0] != -1) { + posix_spawn_file_actions_adddup2(&action, stdout_pipe[0], + STDOUT_FILENO); + posix_spawn_file_actions_addclose(&action, stdout_pipe[1]); + if (stdout_pipe[0] != STDOUT_FILENO) { + posix_spawn_file_actions_addclose(&action, + stdout_pipe[0]); + } + } argv = luaL_checkarraystrings(L, 1); if (0 != (r = posix_spawnp(&pid, argv[0], &action, NULL, (char*const*)argv, environ))) { - close(stdin_pipe[0]); - close(stdin_pipe[1]); + close_pipes(stdin_pipe); + close_pipes(stdout_pipe); posix_spawn_file_actions_destroy(&action); lua_pop(L, 2); /* Pop off the process handle and args. */ @@ -114,12 +152,14 @@ lua_exec(lua_State *L) lua_pop(L, 1); close(stdin_pipe[0]); + if (stdout_pipe[0] != -1) + close(stdout_pipe[0]); posix_spawn_file_actions_destroy(&action); proc->pid = pid; luaL_setmetatable(L, FBSD_PROCESSHANDLE); - return 1; + return (1); } static int @@ -144,24 +184,34 @@ lua_process_close(lua_State *L) return (2); } - if (proc->stdin_fileno >= 0) + if (proc->stdin_fileno >= 0) { close(proc->stdin_fileno); - proc->stdin_fileno = -1; + proc->stdin_fileno = -1; + } + + if (proc->stdout_fileno >= 0) { + close(proc->stdout_fileno); + proc->stdout_fileno = -1; + } lua_pushboolean(L, 1); - return 1; + return (1); } static int -lua_process_stdin(lua_State *L) +lua_process_makestdio(lua_State *L, int fd, const char *mode) { - struct fbsd_process *proc; luaL_Stream *p; FILE *fp; int r; - proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE); - fp = fdopen(proc->stdin_fileno, "w"); + if (fd == -1) { + lua_pushnil(L); + lua_pushstring(L, "Stream not captured"); + return (2); + } + + fp = fdopen(fd, mode); if (fp == NULL) { r = errno; @@ -178,10 +228,29 @@ lua_process_stdin(lua_State *L) return (1); } +static int +lua_process_stdin(lua_State *L) +{ + struct fbsd_process *proc; + + proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE); + return (lua_process_makestdio(L, proc->stdin_fileno, "w")); +} + +static int +lua_process_stdout(lua_State *L) +{ + struct fbsd_process *proc; + + proc = luaL_checkudata(L, 1, FBSD_PROCESSHANDLE); + return (lua_process_makestdio(L, proc->stdout_fileno, "r")); +} + #define PROCESS_SIMPLE(n) { #n, lua_process_ ## n } static const struct luaL_Reg fbsd_process[] = { PROCESS_SIMPLE(close), PROCESS_SIMPLE(stdin), + PROCESS_SIMPLE(stdout), { NULL, NULL }, }; diff --git a/libexec/rtld-elf/rtld.c b/libexec/rtld-elf/rtld.c index 1459b38f3720..17196f55c271 100644 --- a/libexec/rtld-elf/rtld.c +++ b/libexec/rtld-elf/rtld.c @@ -82,9 +82,15 @@ struct dlerror_save { char *msg; }; +struct tcb_list_entry { + TAILQ_ENTRY(tcb_list_entry) next; +}; + /* * Function declarations. */ +static bool allocate_tls_offset_common(size_t *offp, size_t tlssize, + size_t tlsalign, size_t tlspoffset); static const char *basename(const char *); static void digest_dynamic1(Obj_Entry *, int, const Elf_Dyn **, const Elf_Dyn **, const Elf_Dyn **); @@ -92,7 +98,7 @@ static bool digest_dynamic2(Obj_Entry *, const Elf_Dyn *, const Elf_Dyn *, const Elf_Dyn *); static bool digest_dynamic(Obj_Entry *, int); static Obj_Entry *digest_phdr(const Elf_Phdr *, int, caddr_t, const char *); -static void distribute_static_tls(Objlist *, RtldLockState *); +static void distribute_static_tls(Objlist *); static Obj_Entry *dlcheck(void *); static int dlclose_locked(void *, RtldLockState *); static Obj_Entry *dlopen_object(const char *name, int fd, Obj_Entry *refobj, @@ -303,6 +309,10 @@ static size_t tls_static_max_align; Elf_Addr tls_dtv_generation = 1; /* Used to detect when dtv size changes */ int tls_max_index = 1; /* Largest module index allocated */ +static TAILQ_HEAD(, tcb_list_entry) tcb_list = + TAILQ_HEAD_INITIALIZER(tcb_list); +static size_t tcb_list_entry_offset; + static bool ld_library_path_rpath = false; bool ld_fast_sigblock = false; @@ -929,6 +939,19 @@ _rtld(Elf_Addr *sp, func_ptr_type *exit_proc, Obj_Entry **objp) allocate_tls_offset(entry->obj); } + if (!allocate_tls_offset_common(&tcb_list_entry_offset, + sizeof(struct tcb_list_entry), _Alignof(struct tcb_list_entry), + 0)) { + /* + * This should be impossible as the static block size is not + * yet fixed, but catch and diagnose it failing if that ever + * changes or somehow turns out to be false. + */ + _rtld_error("Could not allocate offset for tcb_list_entry"); + rtld_die(); + } + dbg("tcb_list_entry_offset %zu", tcb_list_entry_offset); + if (relocate_objects(obj_main, ld_bind_now != NULL && *ld_bind_now != '\0', &obj_rtld, SYMLOOK_EARLY, NULL) == -1) @@ -3973,7 +3996,7 @@ dlopen_object(const char *name, int fd, Obj_Entry *refobj, int lo_flags, if ((lo_flags & RTLD_LO_EARLY) == 0) { map_stacks_exec(lockstate); if (obj != NULL) - distribute_static_tls(&initlist, lockstate); + distribute_static_tls(&initlist); } if (initlist_objects_ifunc(&initlist, (mode & RTLD_MODEMASK) == @@ -5400,6 +5423,44 @@ tls_get_addr_common(struct tcb *tcb, int index, size_t offset) return (tls_get_addr_slow(tcb, index, offset, false)); } +static struct tcb * +tcb_from_tcb_list_entry(struct tcb_list_entry *tcbelm) +{ +#ifdef TLS_VARIANT_I + return ((struct tcb *)((char *)tcbelm - tcb_list_entry_offset)); +#else + return ((struct tcb *)((char *)tcbelm + tcb_list_entry_offset)); +#endif +} + +static struct tcb_list_entry * +tcb_list_entry_from_tcb(struct tcb *tcb) +{ +#ifdef TLS_VARIANT_I + return ((struct tcb_list_entry *)((char *)tcb + tcb_list_entry_offset)); +#else + return ((struct tcb_list_entry *)((char *)tcb - tcb_list_entry_offset)); +#endif +} + +static void +tcb_list_insert(struct tcb *tcb) +{ + struct tcb_list_entry *tcbelm; + + tcbelm = tcb_list_entry_from_tcb(tcb); + TAILQ_INSERT_TAIL(&tcb_list, tcbelm, next); +} + +static void +tcb_list_remove(struct tcb *tcb) +{ + struct tcb_list_entry *tcbelm; + + tcbelm = tcb_list_entry_from_tcb(tcb); + TAILQ_REMOVE(&tcb_list, tcbelm, next); +} + #ifdef TLS_VARIANT_I /* @@ -5513,6 +5574,7 @@ allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign) } } + tcb_list_insert(tcb); return (tcb); } @@ -5524,6 +5586,8 @@ free_tls(void *tcb, size_t tcbsize, size_t tcbalign __unused) size_t post_size; size_t i, tls_init_align __unused; + tcb_list_remove(tcb); + assert(tcbsize >= TLS_TCB_SIZE); tls_init_align = MAX(obj_main->tlsalign, 1); @@ -5624,6 +5688,7 @@ allocate_tls(Obj_Entry *objs, void *oldtcb, size_t tcbsize, size_t tcbalign) } } + tcb_list_insert(tcb); return (tcb); } @@ -5635,6 +5700,8 @@ free_tls(void *tcb, size_t tcbsize __unused, size_t tcbalign) size_t i; uintptr_t tlsstart, tlsend; + tcb_list_remove(tcb); + /* * Figure out the size of the initial TLS block so that we can * find stuff which ___tls_get_addr() allocated dynamically. @@ -5698,32 +5765,22 @@ allocate_module_tls(struct tcb *tcb, int index) return (p); } -bool -allocate_tls_offset(Obj_Entry *obj) +static bool +allocate_tls_offset_common(size_t *offp, size_t tlssize, size_t tlsalign, + size_t tlspoffset __unused) { size_t off; - if (obj->tls_dynamic) - return (false); - - if (obj->tls_static) - return (true); - - if (obj->tlssize == 0) { - obj->tls_static = true; - return (true); - } - if (tls_last_offset == 0) - off = calculate_first_tls_offset(obj->tlssize, obj->tlsalign, - obj->tlspoffset); + off = calculate_first_tls_offset(tlssize, tlsalign, + tlspoffset); else off = calculate_tls_offset(tls_last_offset, tls_last_size, - obj->tlssize, obj->tlsalign, obj->tlspoffset); + tlssize, tlsalign, tlspoffset); - obj->tlsoffset = off; + *offp = off; #ifdef TLS_VARIANT_I - off += obj->tlssize; + off += tlssize; #endif /* @@ -5735,12 +5792,34 @@ allocate_tls_offset(Obj_Entry *obj) if (tls_static_space != 0) { if (off > tls_static_space) return (false); - } else if (obj->tlsalign > tls_static_max_align) { - tls_static_max_align = obj->tlsalign; + } else if (tlsalign > tls_static_max_align) { + tls_static_max_align = tlsalign; } tls_last_offset = off; - tls_last_size = obj->tlssize; + tls_last_size = tlssize; + + return (true); +} + +bool +allocate_tls_offset(Obj_Entry *obj) +{ + if (obj->tls_dynamic) + return (false); + + if (obj->tls_static) + return (true); + + if (obj->tlssize == 0) { + obj->tls_static = true; + return (true); + } + + if (!allocate_tls_offset_common(&obj->tlsoffset, obj->tlssize, + obj->tlsalign, obj->tlspoffset)) + return (false); + obj->tls_static = true; return (true); @@ -6124,25 +6203,29 @@ map_stacks_exec(RtldLockState *lockstate) } static void -distribute_static_tls(Objlist *list, RtldLockState *lockstate) +distribute_static_tls(Objlist *list) { - Objlist_Entry *elm; + struct tcb_list_entry *tcbelm; + Objlist_Entry *objelm; + struct tcb *tcb; Obj_Entry *obj; - void (*distrib)(size_t, void *, size_t, size_t); + char *tlsbase; - distrib = (void (*)(size_t, void *, size_t, size_t))( - uintptr_t)get_program_var_addr("__pthread_distribute_static_tls", - lockstate); - if (distrib == NULL) - return; - STAILQ_FOREACH(elm, list, link) { - obj = elm->obj; + STAILQ_FOREACH(objelm, list, link) { + obj = objelm->obj; if (obj->marker || !obj->tls_static || obj->static_tls_copied) continue; - lock_release(rtld_bind_lock, lockstate); - distrib(obj->tlsoffset, obj->tlsinit, obj->tlsinitsize, - obj->tlssize); - wlock_acquire(rtld_bind_lock, lockstate); + TAILQ_FOREACH(tcbelm, &tcb_list, next) { + tcb = tcb_from_tcb_list_entry(tcbelm); +#ifdef TLS_VARIANT_I + tlsbase = (char *)tcb + obj->tlsoffset; +#else + tlsbase = (char *)tcb - obj->tlsoffset; +#endif + memcpy(tlsbase, obj->tlsinit, obj->tlsinitsize); + memset(tlsbase + obj->tlsinitsize, 0, + obj->tlssize - obj->tlsinitsize); + } obj->static_tls_copied = true; } } diff --git a/sbin/bectl/bectl.8 b/sbin/bectl/bectl.8 index cc88c7019d13..0e08b3383e9a 100644 --- a/sbin/bectl/bectl.8 +++ b/sbin/bectl/bectl.8 @@ -3,12 +3,12 @@ .\" .\" SPDX-License-Identifier: BSD-2-Clause .\" -.Dd April 9, 2024 +.Dd June 13, 2025 .Dt BECTL 8 .Os .Sh NAME .Nm bectl -.Nd Utility to manage boot environments on ZFS +.Nd manage ZFS boot environments .Sh SYNOPSIS .Nm .Op Fl h @@ -80,34 +80,31 @@ .Sh DESCRIPTION The .Nm -command is used to setup and interact with ZFS boot environments, which are -bootable clones of datasets. -.Pp -A boot environment allows the system to be upgraded, while preserving the -pre-upgrade system environment. -.Pp -.Nm -itself accepts an -.Fl r -flag specified before the command to indicate the -.Ar beroot -that should be used as the boot environment root, or the dataset whose children -are all boot environments. -Normally this information is derived from the bootfs property of the pool that -is mounted at -.Pa / , -but it is useful when the system has not been booted into a ZFS root or a -different pool should be operated on. -For instance, booting into the recovery media and manually importing a pool from -one of the system's resident disks will require the -.Fl r -flag to work. +utility manages bootable ZFS clones called boot environments. +Boot envionments allow system changes to be tested safely, +as they are selectable directly from the boot +.Xr loader 8 . +This utility can +.Cm create , +.Cm list , +.Cm mount , +or +.Cm jail +boot environments. +Once the changes have been tested, the boot environment can be +.Cm unmount Ns ed , +.Cm activate Ns d , +.Cm rename Ns d , +and +.Cm destroy Ns ed . .Ss Supported Subcommands and Flags -.Bl -tag -width activate -.It Xo -.Fl h -.Xc +.Bl -tag -width indent +.It Fl h Print usage information and exit. +.It Fl r Ar beroot Sy Ar subcommand +Specify a parent dataset for the boot environment to use for +.Ar subcommand +for operation on manually imported pools or unusual layouts. .It Xo .Cm activate .Op Fl t | Fl T @@ -122,19 +119,19 @@ flag is given, this takes effect only for the next boot. Flag .Fl T removes temporary boot once configuration. -Without temporary configuration, the next boot will use zfs dataset specified -in boot pool +Without temporary configuration, +the next boot will use zfs dataset specified in boot pool .Ar bootfs property. .It Xo .Cm check .Xc -Performs a silent sanity check on the current system. +Perform a check to see if the current system can use boot environments. If boot environments are supported and used, .Nm will exit with a status code of 0. -Any other status code is not currently defined and may, in the future, grow -special meaning for different degrees of sanity check failures. +Any other status code is not currently defined and may, in the future, +grow special meaning for different degrees of sanity check failures. .It Xo .Cm create .Op Fl r @@ -162,8 +159,8 @@ environment. .Pp If .Nm -is creating from another boot environment, a snapshot of that boot environment -will be created to clone from. +is creating from another boot environment, +a snapshot of that boot environment will be created to clone from. .It Xo .Cm create .Op Fl r @@ -174,8 +171,10 @@ Create a snapshot of the boot environment named .Pp If the .Fl r -flag is given, a recursive snapshot of the boot environment will be created. -A snapshot is created for each descendant dataset of the boot environment. +flag is given, +a recursive snapshot of the boot environment will be created. +A snapshot is created for each descendant dataset +of the boot environment. See .Sx Boot Environment Structures for a discussion on different layouts. @@ -241,8 +240,8 @@ If .Ar utility is specified, it will be executed instead of .Pa /bin/sh . -The jail will be destroyed and the boot environment unmounted when the command -finishes executing, unless the +The jail will be destroyed and the boot environment unmounted +when the command finishes executing, unless the .Fl U argument is specified. .Pp @@ -269,11 +268,11 @@ The following default parameters are provided: .It Va allow.mount Ta Cm true .It Va allow.mount.devfs Ta Cm true .It Va enforce_statfs Ta Cm 1 -.It Va name Ta Set to jail ID. +.It Va name Ta set to jail ID .It Va host.hostname Ta Va bootenv -.It Va path Ta Set to a path in Pa /tmp +.It Va path Ta set to a path in Pa /tmp generated by -.Xr libbe 3 . +.Xr libbe 3 .El .Pp All default parameters may be overwritten. @@ -298,8 +297,8 @@ or combination of .It Fl a Display all datasets. .It Fl D -Display the full space usage for each boot environment, assuming all -other boot environments were destroyed. +Display the full space usage for each boot environment, +assuming all other boot environments were destroyed. .It Fl H Used for scripting. Do not print headers and separate fields by a single tab instead of @@ -351,8 +350,8 @@ will make a directory such as .Pa be_mount.c6Sf in .Pa /tmp . -Randomness in the last four characters of the directory name will prevent -mount point conflicts. +Randomness in the last four characters of the directory name +will prevent mount point conflicts. Unmount of an environment, followed by mount of the same environment without giving a .Ar mountpoint , @@ -362,7 +361,7 @@ Rename the given .Ar origBeName to the given .Ar newBeName . -The boot environment will not be unmounted in order for this rename to occur. +The boot environment will not be unmounted for this rename to occur. .It Cm ujail Brq Ar jailId | jailName | beName .It Cm unjail Brq Ar jailId | jailName | beName Destroy the jail created from the given boot environment. @@ -390,8 +389,8 @@ boot environment layout, as created by the Auto ZFS option to .Xr bsdinstall 8 , is a .Dq shallow -boot environment structure, where boot environment datasets do not have any -directly subordinate datasets. +boot environment structure, where boot environment datasets +do not have any directly subordinate datasets. Instead, they're organized off in .Pa zroot/ROOT , and they rely on datasets elsewhere in the pool having @@ -419,7 +418,8 @@ set to .Dv off , thus files in .Pa /usr -typically fall into the boot environment because this dataset is not mounted. +typically fall into the boot environment +because this dataset is not mounted. .Pa zroot/usr/src is mounted, thus files in .Pa /usr/src @@ -445,8 +445,8 @@ Note that the subordinate datasets now have .Dv canmount set to .Dv noauto . -These are more obviously a part of the boot environment, as indicated by their -positioning in the layout. +These are more obviously a part of the boot environment, +as indicated by their positioning in the layout. These subordinate datasets will be mounted by the .Dv zfsbe .Xr rc 8 @@ -468,16 +468,25 @@ A future version of may default to handling both styles and deprecate the various .Fl r flags. -.\" .Sh EXAMPLES -.\" .Bl -bullet -.\" .It +.Sh EXAMPLES +Create a boot environment, named with today's date, +containing snapshots of the root dataset and of all child datasets: +.Pp +.Dl bectl create -r `date +%Y%m%d` +.Pp +Mount a previous boot environment, +.Ar yesterdaysbe , +to +.Pa /mnt : +.Pp +.Dl bectl mount yesterdaysbe /mnt .\" To fill in with jail upgrade example when behavior is firm. -.\" .El .Sh SEE ALSO .Xr libbe 3 , .Xr zfsprops 7 , .Xr beinstall.sh 8 , .Xr jail 8 , +.Xr loader 8 , .Xr zfs 8 , .Xr zpool 8 .Sh HISTORY diff --git a/sbin/devd/Makefile b/sbin/devd/Makefile index 4ff0187a5a22..5d5721d16884 100644 --- a/sbin/devd/Makefile +++ b/sbin/devd/Makefile @@ -46,6 +46,11 @@ HYPERV+= hyperv.conf HYPERVPACKAGE= hyperv-tools .endif +CONFGROUPS+= NVME +NVMEDIR= ${DEVDDIR} +NVME+= nvmf.conf +NVMEPACKAGE= nvme-tools + .if ${MK_USB} != "no" DEVD+= uath.conf ulpt.conf .endif diff --git a/sbin/devd/devd.conf.5 b/sbin/devd/devd.conf.5 index 4dbd7338edb1..baf4b9d3a183 100644 --- a/sbin/devd/devd.conf.5 +++ b/sbin/devd/devd.conf.5 @@ -38,7 +38,7 @@ .\" ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS .\" SOFTWARE. .\" -.Dd July 8, 2025 +.Dd July 9, 2025 .Dt DEVD.CONF 5 .Os .Sh NAME @@ -517,6 +517,8 @@ and representing the start of a controller reset, the successful completion of a controller reset, or a timeout while waiting for the controller to reset, respectively. +.It Li nvme Ta Li controller Ta Li RECONNECT Ta +An NVMe over Fabrics host has disconnected and is requesting a reconnect. .El .Pp .Bl -column "SYSTEM" "SUBSYSTEM" "SHUTDOWN-THRESHOLD" -compact diff --git a/sbin/devd/nvmf.conf b/sbin/devd/nvmf.conf new file mode 100644 index 000000000000..eaf3ebe86cec --- /dev/null +++ b/sbin/devd/nvmf.conf @@ -0,0 +1,7 @@ +# Attempt to reconnect NVMeoF host devices when requested +notify 100 { + match "system" "nvme"; + match "subsystem" "controller"; + match "type" "RECONNECT"; + action "nvmecontrol reconnect $name"; +}; diff --git a/sbin/nvmecontrol/connect.c b/sbin/nvmecontrol/connect.c index c1d5d2cbaf5a..3d6d12bf2c48 100644 --- a/sbin/nvmecontrol/connect.c +++ b/sbin/nvmecontrol/connect.c @@ -31,6 +31,8 @@ static struct options { const char *subnqn; const char *hostnqn; uint32_t kato; + uint32_t reconnect_delay; + uint32_t controller_loss_timeout; uint16_t num_io_queues; uint16_t queue_size; bool data_digests; @@ -43,6 +45,8 @@ static struct options { .subnqn = NULL, .hostnqn = NULL, .kato = NVMF_KATO_DEFAULT / 1000, + .reconnect_delay = NVMF_DEFAULT_RECONNECT_DELAY, + .controller_loss_timeout = NVMF_DEFAULT_CONTROLLER_LOSS, .num_io_queues = 1, .queue_size = 0, .data_digests = false, @@ -107,7 +111,7 @@ connect_nvm_controller(enum nvmf_trtype trtype, int adrfam, const char *address, } error = nvmf_handoff_host(dle, hostnqn, admin, opt.num_io_queues, io, - &cdata); + &cdata, opt.reconnect_delay, opt.controller_loss_timeout); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -259,6 +263,11 @@ static const struct opts connect_opts[] = { "Number of entries in each I/O queue"), OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, "Keep Alive timeout (in seconds)"), + OPT("reconnect-delay", 'r', arg_uint32, opt, reconnect_delay, + "Delay between reconnect attempts after connection loss " + "(in seconds)"), + OPT("ctrl-loss-tmo", 'l', arg_uint32, opt, controller_loss_timeout, + "Controller loss timeout after connection loss (in seconds)"), OPT("hostnqn", 'q', arg_string, opt, hostnqn, "Host NQN"), OPT("flow_control", 'F', arg_none, opt, flow_control, diff --git a/sbin/nvmecontrol/nvmecontrol.8 b/sbin/nvmecontrol/nvmecontrol.8 index d886b60a2545..624a0c93719b 100644 --- a/sbin/nvmecontrol/nvmecontrol.8 +++ b/sbin/nvmecontrol/nvmecontrol.8 @@ -33,7 +33,7 @@ .\" .\" Author: Jim Harris <jimharris@FreeBSD.org> .\" -.Dd April 29, 2025 +.Dd July 9, 2025 .Dt NVMECONTROL 8 .Os .Sh NAME @@ -216,6 +216,8 @@ .Op Fl c Ar cntl-id .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -226,6 +228,8 @@ .Op Fl FGg .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -241,6 +245,8 @@ .Op Fl FGg .Op Fl i Ar queues .Op Fl k Ar seconds +.Op Fl l Ar seconds +.Op Fl r Ar seconds .Op Fl t Ar transport .Op Fl q Ar HostNQN .Op Fl Q Ar entries @@ -786,6 +792,29 @@ The default is 1. .It Fl k Ar seconds Keep Alive timer duration in seconds. The default is 120. +.It Fl l Ar seconds +Controller Loss timer duration in seconds. +The default is 600. +.Pp +This timer starts when an association is lost with a remote I/O controller +and is cancelled when a new association is established. +If the timer expires, the controller device is deleted. +A setting of zero disables this timer. +.It Fl r Ar seconds +Reconnect timer duration in seconds. +The default is 10. +.Pp +When an association is lost with a remote I/O controller, +the controller device will request reconnection via periodic +.Xr devctl 4 +notifications until either a new association is established or the controller +device is deleted. +This timer sets the interval between each +.Xr devctl 4 +notification. +Note that the first notification is triggered immediately after an association +is lost. +A setting of zero disables this timer. .It Fl t Ar transport Transport to use. The default is diff --git a/sbin/nvmecontrol/reconnect.c b/sbin/nvmecontrol/reconnect.c index adf1edac662b..06af40624177 100644 --- a/sbin/nvmecontrol/reconnect.c +++ b/sbin/nvmecontrol/reconnect.c @@ -27,6 +27,8 @@ static struct options { const char *transport; const char *hostnqn; uint32_t kato; + uint32_t reconnect_delay; + uint32_t controller_loss_timeout; uint16_t num_io_queues; uint16_t queue_size; bool data_digests; @@ -37,6 +39,8 @@ static struct options { .transport = "tcp", .hostnqn = NULL, .kato = NVMF_KATO_DEFAULT / 1000, + .reconnect_delay = NVMF_DEFAULT_RECONNECT_DELAY, + .controller_loss_timeout = NVMF_DEFAULT_CONTROLLER_LOSS, .num_io_queues = 1, .queue_size = 0, .data_digests = false, @@ -59,6 +63,7 @@ static int reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, enum nvmf_trtype trtype, int adrfam, const char *address, const char *port, uint16_t cntlid, const char *subnqn, const char *hostnqn, uint32_t kato, + uint32_t reconnect_delay, uint32_t controller_loss_timeout, u_int num_io_queues, u_int queue_size, const struct nvme_discovery_log_entry *dle) { @@ -88,7 +93,7 @@ reconnect_nvm_controller(int fd, const struct nvmf_association_params *aparams, } error = nvmf_reconnect_host(fd, dle, hostnqn, admin, num_io_queues, io, - &cdata); + &cdata, reconnect_delay, controller_loss_timeout); if (error != 0) { warnc(error, "Failed to handoff queues to kernel"); free(io); @@ -137,7 +142,8 @@ reconnect_by_address(int fd, const nvlist_t *rparams, const char *addr) error = reconnect_nvm_controller(fd, &aparams, trtype, AF_UNSPEC, address, port, le16toh(dle->cntlid), subnqn, hostnqn, - opt.kato * 1000, opt.num_io_queues, opt.queue_size, NULL); + opt.kato * 1000, opt.reconnect_delay, opt.controller_loss_timeout, + opt.num_io_queues, opt.queue_size, NULL); free(subnqn); free(tofree); return (error); @@ -196,6 +202,8 @@ reconnect_by_params(int fd, const nvlist_t *rparams) address, port, le16toh(dle->cntlid), dle->subnqn, nvlist_get_string(rparams, "hostnqn"), dnvlist_get_number(rparams, "kato", 0), + dnvlist_get_number(rparams, "reconnect_delay", 0), + dnvlist_get_number(rparams, "controller_loss_timeout", 0), nvlist_get_number(rparams, "num_io_queues"), nvlist_get_number(rparams, "io_qsize"), dle); free(subnqn); @@ -291,6 +299,11 @@ static const struct opts reconnect_opts[] = { "Number of entries in each I/O queue"), OPT("keep-alive-tmo", 'k', arg_uint32, opt, kato, "Keep Alive timeout (in seconds)"), + OPT("reconnect-delay", 'r', arg_uint32, opt, reconnect_delay, + "Delay between reconnect attempts after connection loss " + "(in seconds)"), + OPT("ctrl-loss-tmo", 'l', arg_uint32, opt, controller_loss_timeout, + "Controller loss timeout after connection loss (in seconds)"), OPT("hostnqn", 'q', arg_string, opt, hostnqn, "Host NQN"), OPT("flow_control", 'F', arg_none, opt, flow_control, diff --git a/sbin/pfctl/parse.y b/sbin/pfctl/parse.y index af1fb95398f8..28f461bf715d 100644 --- a/sbin/pfctl/parse.y +++ b/sbin/pfctl/parse.y @@ -95,7 +95,7 @@ static struct file { int eof_reached; int lineno; int errors; -} *file; +} *file, *topfile; struct file *pushfile(const char *, int); int popfile(void); int check_file_secrecy(int, const char *); @@ -3905,7 +3905,7 @@ uid_item : uid { $$->tail = $$; } | unaryop uid { - if ($2 == UID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) { yyerror("user unknown requires operator = or " "!="); YYERROR; @@ -3920,7 +3920,7 @@ uid_item : uid { $$->tail = $$; } | uid PORTBINARY uid { - if ($1 == UID_MAX || $3 == UID_MAX) { + if ($1 == -1 || $3 == -1) { yyerror("user unknown requires operator = or " "!="); YYERROR; @@ -3938,7 +3938,7 @@ uid_item : uid { uid : STRING { if (!strcmp($1, "unknown")) - $$ = UID_MAX; + $$ = -1; else { uid_t uid; @@ -3983,7 +3983,7 @@ gid_item : gid { $$->tail = $$; } | unaryop gid { - if ($2 == GID_MAX && $1 != PF_OP_EQ && $1 != PF_OP_NE) { + if ($2 == -1 && $1 != PF_OP_EQ && $1 != PF_OP_NE) { yyerror("group unknown requires operator = or " "!="); YYERROR; @@ -3998,7 +3998,7 @@ gid_item : gid { $$->tail = $$; } | gid PORTBINARY gid { - if ($1 == GID_MAX || $3 == GID_MAX) { + if ($1 == -1 || $3 == -1) { yyerror("group unknown requires operator = or " "!="); YYERROR; @@ -4016,7 +4016,7 @@ gid_item : gid { gid : STRING { if (!strcmp($1, "unknown")) - $$ = GID_MAX; + $$ = -1; else { gid_t gid; @@ -6743,7 +6743,7 @@ lgetc(int quotec) if (quotec) { if ((c = igetc()) == EOF) { yyerror("reached end of file while parsing quoted string"); - if (popfile() == EOF) + if (file == topfile || popfile() == EOF) return (EOF); return (quotec); } @@ -6771,7 +6771,7 @@ lgetc(int quotec) return ('\n'); } while (c == EOF) { - if (popfile() == EOF) + if (file == topfile || popfile() == EOF) return (EOF); c = igetc(); } @@ -7069,17 +7069,17 @@ popfile(void) { struct file *prev; - if ((prev = TAILQ_PREV(file, files, entry)) != NULL) { + if ((prev = TAILQ_PREV(file, files, entry)) != NULL) prev->errors += file->errors; - TAILQ_REMOVE(&files, file, entry); - fclose(file->stream); - free(file->name); - free(file->ungetbuf); - free(file); - file = prev; - return (0); - } - return (EOF); + + TAILQ_REMOVE(&files, file, entry); + fclose(file->stream); + free(file->name); + free(file->ungetbuf); + free(file); + file = prev; + + return (file ? 0 : EOF); } int @@ -7102,6 +7102,7 @@ parse_config(char *filename, struct pfctl *xpf) warn("cannot open the main config file!"); return (-1); } + topfile = file; yyparse(); errors = file->errors; diff --git a/sbin/pfctl/pfctl.8 b/sbin/pfctl/pfctl.8 index 28efff896956..508dcc6ea8d4 100644 --- a/sbin/pfctl/pfctl.8 +++ b/sbin/pfctl/pfctl.8 @@ -24,7 +24,7 @@ .\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF .\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. .\" -.Dd July 2, 2025 +.Dd July 7, 2025 .Dt PFCTL 8 .Os .Sh NAME @@ -186,6 +186,13 @@ as the anchor name: .Bd -literal -offset indent # pfctl -a '*' -sr .Ed +.Pp +To flush all rulesets and tables recursively, specify only +.Sq * +as the anchor name: +.Bd -literal -offset indent +# pfctl -a '*' -Fa +.Ed .It Fl D Ar macro Ns = Ns Ar value Define .Ar macro @@ -231,6 +238,19 @@ for details. .It Fl F Cm all Flush all of the above. .El +.Pp +If +.Fl a +is specified as well and +.Ar anchor +is terminated with a +.Sq * +character, +.Cm rules , +.Cm Tables +and +.Cm all +flush the given anchor recursively. .It Fl f Ar file Load the rules contained in .Ar file . diff --git a/sbin/pfctl/pfctl.c b/sbin/pfctl/pfctl.c index defba3b56c44..8d2b556d7085 100644 --- a/sbin/pfctl/pfctl.c +++ b/sbin/pfctl/pfctl.c @@ -59,6 +59,8 @@ #include <stdlib.h> #include <string.h> #include <unistd.h> +#include <stdarg.h> +#include <libgen.h> #include "pfctl_parser.h" #include "pfctl.h" @@ -72,7 +74,7 @@ void pfctl_check_skip_ifaces(char *); void pfctl_adjust_skip_ifaces(struct pfctl *); void pfctl_clear_interface_flags(int, int); void pfctl_flush_eth_rules(int, int, char *); -void pfctl_flush_rules(int, int, char *); +int pfctl_flush_rules(int, int, char *); void pfctl_flush_nat(int, int, char *); int pfctl_clear_altq(int, int); void pfctl_clear_src_nodes(int, int); @@ -124,6 +126,17 @@ int pfctl_load_ruleset(struct pfctl *, char *, int pfctl_load_rule(struct pfctl *, char *, struct pfctl_rule *, int); const char *pfctl_lookup_option(char *, const char * const *); void pfctl_reset(int, int); +int pfctl_walk_show(int, struct pfioc_ruleset *, void *); +int pfctl_walk_get(int, struct pfioc_ruleset *, void *); +int pfctl_walk_anchors(int, int, const char *, + int(*)(int, struct pfioc_ruleset *, void *), void *); +struct pfr_anchors * + pfctl_get_anchors(int, char *, int); +int pfctl_recurse(int, int, char *, + int(*)(int, int, struct pfr_anchoritem *)); +int pfctl_call_clearrules(int, int, struct pfr_anchoritem *); +int pfctl_call_cleartables(int, int, struct pfr_anchoritem *); +int pfctl_call_clearanchors(int, int, struct pfr_anchoritem *); static struct pfctl_anchor_global pf_anchors; struct pfctl_anchor pf_main_anchor; @@ -151,6 +164,7 @@ int dev = -1; struct pfctl_handle *pfh = NULL; static int first_title = 1; static int labels = 0; +static int exit_val = 0; #define INDENT(d, o) do { \ if (o) { \ @@ -269,6 +283,40 @@ usage(void) exit(1); } +void +pfctl_err(int opts, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if ((opts & PF_OPT_IGNFAIL) == 0) + verr(eval, fmt, ap); + else + vwarn(fmt, ap); + + va_end(ap); + + exit_val = eval; +} + +void +pfctl_errx(int opts, int eval, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + + if ((opts & PF_OPT_IGNFAIL) == 0) + verrx(eval, fmt, ap); + else + vwarnx(fmt, ap); + + va_end(ap); + + exit_val = eval; +} + /* * Cache protocol number to name translations. * @@ -361,7 +409,7 @@ pfctl_clear_stats(struct pfctl_handle *h, int opts) { int ret; if ((ret = pfctl_clear_status(h)) != 0) - errc(1, ret, "DIOCCLRSTATUS"); + pfctl_err(opts, 1, "DIOCCLRSTATUS"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "pf: statistics cleared\n"); } @@ -469,16 +517,19 @@ pfctl_flush_eth_rules(int dev, int opts, char *anchorname) fprintf(stderr, "Ethernet rules cleared\n"); } -void +int pfctl_flush_rules(int dev, int opts, char *anchorname) { int ret; ret = pfctl_clear_rules(dev, anchorname); - if (ret != 0) - err(1, "pfctl_clear_rules"); - if ((opts & PF_OPT_QUIET) == 0) + if (ret != 0) { + pfctl_err(opts, 1, "%s", __func__); + return (1); + } else if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "rules cleared\n"); + + return (0); } void @@ -515,7 +566,7 @@ void pfctl_clear_src_nodes(int dev, int opts) { if (ioctl(dev, DIOCCLRSRCNODES)) - err(1, "DIOCCLRSRCNODES"); + pfctl_err(opts, 1, "DIOCCLRSRCNODES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "source tracking entries cleared\n"); } @@ -530,13 +581,13 @@ pfctl_clear_iface_states(int dev, const char *iface, int opts) memset(&kill, 0, sizeof(kill)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; if ((ret = pfctl_clear_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCCLRSTATES"); + pfctl_err(opts, 1, "DIOCCLRSTATUS"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "%d states cleared\n", killed); } @@ -687,7 +738,7 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) memset(&last_dst, 0xff, sizeof(last_dst)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (state_killers == 2 && (strcmp(state_kill[0], "nat") == 0)) { kill.nat = true; @@ -740,13 +791,13 @@ pfctl_net_kill_states(int dev, const char *iface, int opts) resp[1]->ai_addr); if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } freeaddrinfo(res[1]); } else { if ((ret = pfctl_kill_states_h(pfh, &kill, &newkilled)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } } @@ -778,7 +829,7 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) memset(&last_src, 0xff, sizeof(last_src)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; @@ -799,7 +850,7 @@ pfctl_gateway_kill_states(int dev, const char *iface, int opts) copy_satopfaddr(&kill.rt_addr.addr.v.a.addr, resp->ai_addr); if (pfctl_kill_states_h(pfh, &kill, &newkilled)) - err(1, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); killed += newkilled; } @@ -823,7 +874,7 @@ pfctl_label_kill_states(int dev, const char *iface, int opts) memset(&kill, 0, sizeof(kill)); if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); if (opts & PF_OPT_KILLMATCH) kill.kill_match = true; @@ -833,7 +884,7 @@ pfctl_label_kill_states(int dev, const char *iface, int opts) errx(1, "label too long: %s", state_kill[1]); if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); @@ -871,7 +922,7 @@ pfctl_id_kill_states(int dev, const char *iface, int opts) } if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); @@ -895,7 +946,7 @@ pfctl_key_kill_states(int dev, const char *iface, int opts) if (iface != NULL && strlcpy(kill.ifname, iface, sizeof(kill.ifname)) >= sizeof(kill.ifname)) - errx(1, "invalid interface: %s", iface); + pfctl_errx(opts, 1, "invalid interface: %s", iface); s = strdup(state_kill[1]); if (!s) @@ -931,7 +982,7 @@ pfctl_key_kill_states(int dev, const char *iface, int opts) errx(1, "invalid host: %s", tokens[didx]); if ((ret = pfctl_kill_states_h(pfh, &kill, &killed)) != 0) - errc(1, ret, "DIOCKILLSTATES"); + pfctl_errx(opts, 1, "DIOCKILLSTATES"); if ((opts & PF_OPT_QUIET) == 0) fprintf(stderr, "killed %d states\n", killed); @@ -2038,8 +2089,8 @@ pfctl_load_ruleset(struct pfctl *pf, char *path, struct pfctl_ruleset *rs, if ((pf->opts & PF_OPT_NOACTION) == 0 && (error = pfctl_ruleset_trans(pf, path, rs->anchor, false))) { - printf("pfctl_load_rulesets: " - "pfctl_ruleset_trans %d\n", error); + printf("%s: " + "pfctl_ruleset_trans %d\n", __func__, error); goto error; } } else if (pf->opts & PF_OPT_VERBOSE) @@ -2805,7 +2856,7 @@ pfctl_set_interface_flags(struct pfctl *pf, char *ifname, int flags, int how) if ((pf->opts & PF_OPT_NOACTION) == 0) { if (how == 0) { if (ioctl(pf->dev, DIOCCLRIFFLAG, &pi)) - err(1, "DIOCCLRIFFLAG"); + pfctl_err(pf->opts, 1, "DIOCCLRIFFLAG"); } else { if (ioctl(pf->dev, DIOCSETIFFLAG, &pi)) err(1, "DIOCSETIFFLAG"); @@ -2864,17 +2915,56 @@ pfctl_test_altqsupport(int dev, int opts) } int -pfctl_show_anchors(int dev, int opts, char *anchorname) +pfctl_walk_show(int opts, struct pfioc_ruleset *pr, void *warg) +{ + if (pr->path[0]) { + if (pr->path[0] != '_' || (opts & PF_OPT_VERBOSE)) + printf(" %s/%s\n", pr->path, pr->name); + } else if (pr->name[0] != '_' || (opts & PF_OPT_VERBOSE)) + printf(" %s\n", pr->name); + + return (0); +} + +int +pfctl_walk_get(int opts, struct pfioc_ruleset *pr, void *warg) +{ + struct pfr_anchoritem *pfra; + struct pfr_anchors *anchors; + int e; + + anchors = (struct pfr_anchors *)warg; + + pfra = malloc(sizeof(*pfra)); + if (pfra == NULL) + err(1, "%s", __func__); + + if (pr->path[0]) + e = asprintf(&pfra->pfra_anchorname, "%s/%s", pr->path, + pr->name); + else + e = asprintf(&pfra->pfra_anchorname, "%s", pr->name); + + if (e == -1) + err(1, "%s", __func__); + + SLIST_INSERT_HEAD(anchors, pfra, pfra_sle); + + return (0); +} + +int +pfctl_walk_anchors(int dev, int opts, const char *anchor, + int(walkf)(int, struct pfioc_ruleset *, void *), void *warg) { struct pfioc_ruleset pr; u_int32_t mnr, nr; int ret; memset(&pr, 0, sizeof(pr)); - if ((ret = pfctl_get_rulesets(pfh, anchorname, &mnr)) != 0) { + if ((ret = pfctl_get_rulesets(pfh, anchor, &mnr)) != 0) { if (ret == EINVAL) - fprintf(stderr, "Anchor '%s' not found.\n", - anchorname); + fprintf(stderr, "Anchor '%s' not found.\n", anchor); else errc(1, ret, "DIOCGETRULESETS"); return (-1); @@ -2882,25 +2972,123 @@ pfctl_show_anchors(int dev, int opts, char *anchorname) for (nr = 0; nr < mnr; ++nr) { char sub[MAXPATHLEN]; - if ((ret = pfctl_get_ruleset(pfh, anchorname, nr, &pr)) != 0) + if ((ret = pfctl_get_ruleset(pfh, anchor, nr, &pr)) != 0) errc(1, ret, "DIOCGETRULESET"); if (!strcmp(pr.name, PF_RESERVED_ANCHOR)) continue; sub[0] = '\0'; - if (pr.path[0]) { - strlcat(sub, pr.path, sizeof(sub)); - strlcat(sub, "/", sizeof(sub)); - } - strlcat(sub, pr.name, sizeof(sub)); - if (sub[0] != '_' || (opts & PF_OPT_VERBOSE)) - printf(" %s\n", sub); - if ((opts & PF_OPT_VERBOSE) && pfctl_show_anchors(dev, opts, sub)) + if (walkf(opts, &pr, warg)) + return (-1); + + if (pr.path[0]) + snprintf(sub, sizeof(sub), "%s/%s", pr.path, pr.name); + else + snprintf(sub, sizeof(sub), "%s", pr.name); + if (pfctl_walk_anchors(dev, opts, sub, walkf, warg)) return (-1); } return (0); } int +pfctl_show_anchors(int dev, int opts, char *anchor) +{ + return ( + pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_show, NULL)); +} + +struct pfr_anchors * +pfctl_get_anchors(int dev, char *anchor, int opts) +{ + struct pfioc_ruleset pr; + static struct pfr_anchors anchors; + char *n; + + SLIST_INIT(&anchors); + + memset(&pr, 0, sizeof(pr)); + if (*anchor != '\0') { + n = dirname(anchor); + if (n[0] != '.' && n[1] != '\0') + strlcpy(pr.path, n, sizeof(pr.path)); + n = basename(anchor); + if (n != NULL) + strlcpy(pr.name, n, sizeof(pr.name)); + } + + /* insert a root anchor first. */ + pfctl_walk_get(opts, &pr, &anchors); + + if (pfctl_walk_anchors(dev, opts, anchor, pfctl_walk_get, &anchors)) + errx(1, "%s failed to retrieve list of anchors, can't continue", + __func__); + + return (&anchors); +} + +int +pfctl_call_cleartables(int dev, int opts, struct pfr_anchoritem *pfra) +{ + /* + * PF_OPT_QUIET makes pfctl_clear_tables() to stop printing number of + * tables cleared for given anchor. + */ + opts |= PF_OPT_QUIET; + return ((pfctl_do_clear_tables(pfra->pfra_anchorname, opts) == -1) ? + 1 : 0); +} + +int +pfctl_call_clearrules(int dev, int opts, struct pfr_anchoritem *pfra) +{ + /* + * PF_OPT_QUIET makes pfctl_clear_rules() to stop printing a 'rules + * cleared' message for every anchor it deletes. + */ + opts |= PF_OPT_QUIET; + return (pfctl_flush_rules(dev, opts, pfra->pfra_anchorname)); +} + +int +pfctl_call_clearanchors(int dev, int opts, struct pfr_anchoritem *pfra) +{ + int rv = 0; + + rv |= pfctl_call_cleartables(dev, opts, pfra); + rv |= pfctl_call_clearrules(dev, opts, pfra); + + return (rv); +} + +int +pfctl_recurse(int dev, int opts, char *anchorname, + int(*walkf)(int, int, struct pfr_anchoritem *)) +{ + int rv = 0; + struct pfr_anchors *anchors; + struct pfr_anchoritem *pfra, *pfra_save; + + anchors = pfctl_get_anchors(dev, anchorname, opts); + /* + * While traversing the list, pfctl_clear_*() must always return + * so that failures on one anchor do not prevent clearing others. + */ + opts |= PF_OPT_IGNFAIL; + printf("Removing:\n"); + SLIST_FOREACH_SAFE(pfra, anchors, pfra_sle, pfra_save) { + printf(" %s\n", + (*pfra->pfra_anchorname == '\0') ? "/" : + pfra->pfra_anchorname); + rv |= walkf(dev, opts, pfra); + SLIST_REMOVE(anchors, pfra, pfr_anchoritem, pfra_sle); + free(pfra->pfra_anchorname); + free(pfra); + } + + return (rv); +} + +int pfctl_show_eth_anchors(int dev, int opts, char *anchorname) { struct pfctl_eth_rulesets_info ri; @@ -2990,7 +3178,6 @@ pfctl_reset(int dev, int opts) int main(int argc, char *argv[]) { - int error = 0; int ch; int mode = O_RDONLY; int opts = 0; @@ -3218,7 +3405,7 @@ main(int argc, char *argv[]) if (opts & PF_OPT_DISABLE) if (pfctl_disable(dev, opts)) - error = 1; + exit_val = 1; if ((path = calloc(1, MAXPATHLEN)) == NULL) errx(1, "%s: calloc", __func__); @@ -3259,7 +3446,7 @@ main(int argc, char *argv[]) pfctl_show_status(dev, opts); break; case 'R': - error = pfctl_show_running(dev); + exit_val = pfctl_show_running(dev); break; case 't': pfctl_show_timeouts(dev, opts); @@ -3321,7 +3508,11 @@ main(int argc, char *argv[]) pfctl_flush_eth_rules(dev, opts, anchorname); break; case 'r': - pfctl_flush_rules(dev, opts, anchorname); + if (opts & PF_OPT_RECURSE) + pfctl_recurse(dev, opts, anchorname, + pfctl_call_clearrules); + else + pfctl_flush_rules(dev, opts, anchorname); break; case 'n': pfctl_flush_nat(dev, opts, anchorname); @@ -3347,7 +3538,13 @@ main(int argc, char *argv[]) pfctl_flush_eth_rules(dev, opts, anchorname); pfctl_flush_rules(dev, opts, anchorname); pfctl_flush_nat(dev, opts, anchorname); - pfctl_do_clear_tables(anchorname, opts); + if (opts & PF_OPT_RECURSE) + pfctl_recurse(dev, opts, anchorname, + pfctl_call_clearanchors); + else { + pfctl_do_clear_tables(anchorname, opts); + pfctl_flush_rules(dev, opts, anchorname); + } if (!*anchorname) { pfctl_clear_altq(dev, opts); pfctl_clear_iface_states(dev, ifaceopt, opts); @@ -3361,7 +3558,11 @@ main(int argc, char *argv[]) pfctl_clear_fingerprints(dev, opts); break; case 'T': - pfctl_do_clear_tables(anchorname, opts); + if ((opts & PF_OPT_RECURSE) == 0) + pfctl_do_clear_tables(anchorname, opts); + else + pfctl_recurse(dev, opts, anchorname, + pfctl_call_cleartables); break; case 'R': pfctl_reset(dev, opts); @@ -3385,7 +3586,7 @@ main(int argc, char *argv[]) pfctl_kill_src_nodes(dev, opts); if (tblcmdopt != NULL) { - error = pfctl_table(argc, argv, tableopt, + exit_val = pfctl_table(argc, argv, tableopt, tblcmdopt, rulesopt, anchorname, opts); rulesopt = NULL; } @@ -3411,17 +3612,17 @@ main(int argc, char *argv[]) if (rulesopt != NULL && !(opts & PF_OPT_MERGE) && !anchorname[0] && (loadopt & PFCTL_FLAG_OPTION)) if (pfctl_file_fingerprints(dev, opts, PF_OSFP_FILE)) - error = 1; + exit_val = 1; if (rulesopt != NULL) { if (pfctl_rules(dev, rulesopt, opts, optimize, anchorname, NULL)) - error = 1; + exit_val = 1; } if (opts & PF_OPT_ENABLE) if (pfctl_enable(dev, opts)) - error = 1; + exit_val = 1; if (debugopt != NULL) { switch (*debugopt) { @@ -3440,5 +3641,5 @@ main(int argc, char *argv[]) } } - exit(error); + exit(exit_val); } diff --git a/sbin/pfctl/pfctl.h b/sbin/pfctl/pfctl.h index d8196c129187..5b5b3d3e5fff 100644 --- a/sbin/pfctl/pfctl.h +++ b/sbin/pfctl/pfctl.h @@ -55,6 +55,13 @@ struct pfr_buffer { (var) != NULL; \ (var) = pfr_buf_next((buf), (var))) +struct pfr_anchoritem { + SLIST_ENTRY(pfr_anchoritem) pfra_sle; + char *pfra_anchorname; +}; + +SLIST_HEAD(pfr_anchors, pfr_anchoritem); + int pfr_get_fd(void); int pfr_add_table(struct pfr_table *, int *, int); int pfr_del_table(struct pfr_table *, int *, int); @@ -81,7 +88,7 @@ int pfi_get_ifaces(const char *, struct pfi_kif *, int *); int pfi_clr_istats(const char *, int *, int); void pfctl_print_title(char *); -void pfctl_do_clear_tables(const char *, int); +int pfctl_do_clear_tables(const char *, int); void pfctl_show_tables(const char *, int); int pfctl_table(int, char *[], char *, const char *, char *, const char *, int); @@ -150,4 +157,7 @@ void expand_label(char *, size_t, struct pfctl_rule *); const char *pfctl_proto2name(int); +void pfctl_err(int, int, const char *, ...); +void pfctl_errx(int, int, const char *, ...); + #endif /* _PFCTL_H_ */ diff --git a/sbin/pfctl/pfctl_optimize.c b/sbin/pfctl/pfctl_optimize.c index b58bace326c2..e727324bbf40 100644 --- a/sbin/pfctl/pfctl_optimize.c +++ b/sbin/pfctl/pfctl_optimize.c @@ -273,7 +273,10 @@ pfctl_optimize_ruleset(struct pfctl *pf, struct pfctl_ruleset *rs) struct pfctl_rule *r; struct pfctl_rulequeue *old_rules; - DEBUG("optimizing ruleset"); + if (TAILQ_EMPTY(rs->rules[PF_RULESET_FILTER].active.ptr)) + return (0); + + DEBUG("optimizing ruleset \"%s\"", rs->anchor->path); memset(&table_buffer, 0, sizeof(table_buffer)); skip_init(); TAILQ_INIT(&opt_queue); diff --git a/sbin/pfctl/pfctl_osfp.c b/sbin/pfctl/pfctl_osfp.c index 3a94c2e8c81b..5770c8343a46 100644 --- a/sbin/pfctl/pfctl_osfp.c +++ b/sbin/pfctl/pfctl_osfp.c @@ -264,7 +264,7 @@ void pfctl_clear_fingerprints(int dev, int opts) { if (ioctl(dev, DIOCOSFPFLUSH)) - err(1, "DIOCOSFPFLUSH"); + pfctl_err(opts, 1, "DIOCOSFPFLUSH"); } /* flush pfctl's view of the fingerprints */ diff --git a/sbin/pfctl/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 26a213c3ffd9..bd2c10c8080f 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -68,7 +68,7 @@ void print_op (u_int8_t, const char *, const char *); void print_port (u_int8_t, u_int16_t, u_int16_t, const char *, int); -void print_ugid (u_int8_t, unsigned, unsigned, const char *, unsigned); +void print_ugid (u_int8_t, id_t, id_t, const char *); void print_flags (uint16_t); void print_fromto(struct pf_rule_addr *, pf_osfp_t, struct pf_rule_addr *, sa_family_t, u_int8_t, int, int); @@ -364,14 +364,14 @@ print_port(u_int8_t op, u_int16_t p1, u_int16_t p2, const char *proto, int numer } void -print_ugid(u_int8_t op, unsigned u1, unsigned u2, const char *t, unsigned umax) +print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t) { char a1[11], a2[11]; - snprintf(a1, sizeof(a1), "%u", u1); - snprintf(a2, sizeof(a2), "%u", u2); + snprintf(a1, sizeof(a1), "%ju", (uintmax_t)i1); + snprintf(a2, sizeof(a2), "%ju", (uintmax_t)i2); printf(" %s", t); - if (u1 == umax && (op == PF_OP_EQ || op == PF_OP_NE)) + if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE)) print_op(op, "unknown", a2); else print_op(op, a1, a2); @@ -977,11 +977,9 @@ print_rule(struct pfctl_rule *r, const char *anchor_call, int verbose, int numer printf(" %sreceived-on %s", r->rcvifnot ? "!" : "", r->rcv_ifname); if (r->uid.op) - print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user", - UID_MAX); + print_ugid(r->uid.op, r->uid.uid[0], r->uid.uid[1], "user"); if (r->gid.op) - print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group", - GID_MAX); + print_ugid(r->gid.op, r->gid.gid[0], r->gid.gid[1], "group"); if (r->flags || r->flagset) { printf(" flags "); print_flags(r->flags); diff --git a/sbin/pfctl/pfctl_parser.h b/sbin/pfctl/pfctl_parser.h index b91d37c791ae..778105c2b96d 100644 --- a/sbin/pfctl/pfctl_parser.h +++ b/sbin/pfctl/pfctl_parser.h @@ -55,6 +55,7 @@ #define PF_OPT_RECURSE 0x04000 #define PF_OPT_KILLMATCH 0x08000 #define PF_OPT_NODNS 0x10000 +#define PF_OPT_IGNFAIL 0x20000 #define PF_NAT_PROXY_PORT_LOW 50001 #define PF_NAT_PROXY_PORT_HIGH 65535 diff --git a/sbin/pfctl/pfctl_table.c b/sbin/pfctl/pfctl_table.c index 0842b042df41..0b52f88eafbb 100644 --- a/sbin/pfctl/pfctl_table.c +++ b/sbin/pfctl/pfctl_table.c @@ -79,7 +79,8 @@ static const char *istats_text[2][2][2] = { if ((!(opts & PF_OPT_NOACTION) || \ (opts & PF_OPT_DUMMYACTION)) && \ (fct)) { \ - radix_perror(); \ + if ((opts & PF_OPT_RECURSE) == 0) \ + radix_perror(); \ goto _error; \ } \ } while (0) @@ -103,11 +104,17 @@ static const char *istats_text[2][2][2] = { table.pfrt_flags &= ~PFR_TFLAG_PERSIST; \ } while(0) -void +int pfctl_do_clear_tables(const char *anchor, int opts) { - if (pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) - exit(1); + int rv; + + if ((rv = pfctl_table(0, NULL, NULL, "-F", NULL, anchor, opts)) == -1) { + if ((opts & PF_OPT_IGNFAIL) == 0) + exit(1); + } + + return (rv); } void diff --git a/sbin/pfctl/tests/macro.sh b/sbin/pfctl/tests/macro.sh index 9c48dbbc69f0..071c6cb4f426 100755 --- a/sbin/pfctl/tests/macro.sh +++ b/sbin/pfctl/tests/macro.sh @@ -3,6 +3,7 @@ atf_test_case "space" cleanup space_head() { atf_set descr "Test macros with spaces" + atf_set require.kmods "pf" } space_body() diff --git a/sbin/pfctl/tests/pfctl_test.c b/sbin/pfctl/tests/pfctl_test.c index dbdcaa4900ea..5f0aa7826bb4 100644 --- a/sbin/pfctl/tests/pfctl_test.c +++ b/sbin/pfctl/tests/pfctl_test.c @@ -65,24 +65,6 @@ * Copied from OpenBSD. */ -static bool -check_pf_module_available(void) -{ - int modid; - struct module_stat stat; - - if ((modid = modfind("pf")) < 0) { - warn("pf module not found"); - return false; - } - stat.version = sizeof(struct module_stat); - if (modstat(modid, &stat) < 0) { - warn("can't stat pf module id %d", modid); - return false; - } - return (true); -} - extern char **environ; static struct sbuf * @@ -185,9 +167,6 @@ run_pfctl_test(const char *input_path, const char *output_path, struct sbuf *expected_output; struct sbuf *real_output; - if (!check_pf_module_available()) - atf_tc_skip("pf(4) is not loaded"); - /* The test inputs need to be able to use relative includes. */ snprintf(input_files_path, sizeof(input_files_path), "%s/files", atf_tc_get_config_var(tc, "srcdir")); @@ -292,6 +271,7 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) ATF_TC_HEAD(pf##number, tc) \ { \ atf_tc_set_md_var(tc, "descr", descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(pf##number, tc) \ { \ @@ -301,6 +281,7 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) ATF_TC_HEAD(selfpf##number, tc) \ { \ atf_tc_set_md_var(tc, "descr", "Self " descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(selfpf##number, tc) \ { \ @@ -312,6 +293,7 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) ATF_TC_HEAD(pf##number, tc) \ { \ atf_tc_set_md_var(tc, "descr", descr); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(pf##number, tc) \ { \ @@ -325,6 +307,7 @@ do_selfpf_test(const char *number, const atf_tc_t *tc) atf_tc_set_md_var(tc, "descr", descr); \ atf_tc_set_md_var(tc, "execenv", "jail"); \ atf_tc_set_md_var(tc, "execenv.jail.params", "vnet"); \ + atf_tc_set_md_var(tc, "require.kmods", "pf"); \ } \ ATF_TC_BODY(pf##number, tc) \ { \ diff --git a/share/examples/Makefile b/share/examples/Makefile index f0c050a36306..0a65b8c40d39 100644 --- a/share/examples/Makefile +++ b/share/examples/Makefile @@ -10,7 +10,6 @@ LDIRS= BSD_daemon \ FreeBSD_version \ bootforth \ csh \ - drivers \ etc \ find_interface \ flua \ @@ -74,12 +73,6 @@ SE_DIRS+= csh SE_CSHPACKAGE= csh SE_CSH= dot.cshrc -SE_DIRS+= drivers -SE_DRIVERS= \ - README \ - make_device_driver.sh \ - make_pseudo_driver.sh - SE_DIRS+= etc SE_ETC= \ README.examples \ diff --git a/share/examples/drivers/README b/share/examples/drivers/README deleted file mode 100644 index 8628029a62f8..000000000000 --- a/share/examples/drivers/README +++ /dev/null @@ -1,42 +0,0 @@ - -Author: Julian Elischer - -The files in this directory are shell scripts. - -They will, when run, create an example skeleton driver -for you. You can use this driver as a starting point for -writing drivers for your own devices. They have all the hooks needed -for initialization, probing, attaching, as well as DEVFS -node creation. They also create sample ioctl commands and a sample -ioctl definition .h file in /sys/sys. In other words they are fully -functional in a 'skeleton' sort of a way. They support multiple devices -so that you may have several of your 'foobar' devices probed and attached -at once. - -I expect that these scripts will improve with time. - -At present these scripts also link the newly created driver into -the kernel sources in /sys. Possibly a better way would be -to make them interactive. (and ask what kernel tree to use as well as -a name for the driver.). - -There are presently two scripts. -One for making a real device driver for ISA devices, and -one for making a device driver for pseudo devices (e.g. /dev/null). -Hopefully they will be joined by similar scripts for creating -skeletons for PCI devices as well. - -Give them a single argument: the name of the driver. -They will use this given name in many places within the driver, -both in lower and upper case form. (conforming to normal usage). - -The skeleton driver should already link with the kernel -and in fact the shell script will compile a kernel with the new -drive linked in.. The new kernel should still be -runnable and the new driver should be -fully callable (once you get your device to probe). -You should simply edit the driver and continue to use -'make' (as done in the script) until your driver does what you want. - -The driver will end up in /sys/i386/isa for the device driver script, -and in /sys/dev for the pseudo driver script. diff --git a/share/examples/drivers/make_pseudo_driver.sh b/share/examples/drivers/make_pseudo_driver.sh deleted file mode 100644 index 5d6d09aa9648..000000000000 --- a/share/examples/drivers/make_pseudo_driver.sh +++ /dev/null @@ -1,435 +0,0 @@ -#!/bin/sh -# This writes a skeleton driver and puts it into the kernel tree for you -# -# arg1 is lowercase "foo" -# arg2 path to the kernel sources, "/sys" if omitted -# -# Trust me, RUN THIS SCRIPT :) -# -# -#-------cut here------------------ - -if [ "${1}X" = "X" ] -then - echo "Hey , how about some help here.. give me a device name!" - exit 1 -fi -if [ "X${2}" = "X" ]; then - TOP=`cd /sys; pwd -P` - echo "Using ${TOP} as the path to the kernel sources!" -else - TOP=${2} -fi - -for i in "" "conf" "i386" "i386/conf" "dev" "sys" "modules" -do - if [ -d ${TOP}/${i} ] - then - continue - fi - echo "${TOP}/${i}: no such directory." - echo "Please, correct the error and try again." - exit 1 -done - -UPPER=`echo ${1} |tr "[:lower:]" "[:upper:]"` - -if [ -d ${TOP}/modules/${1} ]; then - echo "There appears to already be a module called ${1}" - echo -n "Should it be overwritten? [Y]" - read VAL - if [ "-z" "$VAL" ]; then - VAL=YES - fi - case ${VAL} in - [yY]*) - echo "Cleaning up from prior runs" - rm -rf ${TOP}/dev/${1} - rm -rf ${TOP}/modules/${1} - rm ${TOP}/conf/files.${UPPER} - rm ${TOP}/i386/conf/${UPPER} - rm ${TOP}/sys/${1}io.h - ;; - *) - exit 1 - ;; - esac -fi - -echo "The following files will be created:" -echo ${TOP}/modules/${1} -echo ${TOP}/conf/files.${UPPER} -echo ${TOP}/i386/conf/${UPPER} -echo ${TOP}/dev/${1} -echo ${TOP}/dev/${1}/${1}.c -echo ${TOP}/sys/${1}io.h -echo ${TOP}/modules/${1} -echo ${TOP}/modules/${1}/Makefile - -mkdir ${TOP}/modules/${1} - -cat >${TOP}/conf/files.${UPPER} <<DONE -dev/${1}/${1}.c optional ${1} -DONE - -cat >${TOP}/i386/conf/${UPPER} <<DONE -# Configuration file for kernel type: ${UPPER} - -files "${TOP}/conf/files.${UPPER}" - -include GENERIC - -ident ${UPPER} - -# trust me, you'll need this -options KDB -options DDB -device ${1} -DONE - -if [ ! -d ${TOP}/dev/${1} ]; then - mkdir -p ${TOP}/dev/${1} -fi - -cat >${TOP}/dev/${1}/${1}.c <<DONE -/* - * Copyright (c) [year] [your name] - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * ${1} driver - */ - -#include <sys/param.h> -#include <sys/systm.h> -#include <sys/kernel.h> /* SYSINIT stuff */ -#include <sys/uio.h> /* SYSINIT stuff */ -#include <sys/conf.h> /* cdevsw stuff */ -#include <sys/malloc.h> /* malloc region definitions */ -#include <sys/proc.h> -#include <sys/${1}io.h> /* ${1} IOCTL definitions */ - -#include <machine/clock.h> /* DELAY() */ - -#define N${UPPER} 3 /* defines number of instances */ - -/* XXX These should be defined in terms of bus-space ops. */ -#define ${UPPER}_INB(port) inb(port) -#define ${UPPER}_OUTB(port, val) (port, (val)) - -/* Function prototypes (these should all be static) */ -static d_open_t ${1}open; -static d_close_t ${1}close; -static d_read_t ${1}read; -static d_write_t ${1}write; -static d_ioctl_t ${1}ioctl; -static d_mmap_t ${1}mmap; -static d_poll_t ${1}poll; - -#define CDEV_MAJOR 20 -static struct cdevsw ${1}_cdevsw = { - .d_version = D_VERSION, - .d_open = ${1}open, - .d_close = ${1}close, - .d_read = ${1}read, - .d_write = ${1}write, - .d_ioctl = ${1}ioctl, - .d_poll = ${1}poll, - .d_mmap = ${1}mmap, - .d_name = "${1}", -}; - -/* - * device specific Misc defines - */ -#define BUFFERSIZE 1024 -#define UNIT(dev) dev2unit(dev) /* assume one minor number per unit */ - -/* - * One of these per allocated device - */ -struct ${1}_softc { - u_long iobase; - char buffer[BUFFERSIZE]; - struct cdev *dev; -}; - -typedef struct ${1}_softc *sc_p; - -static sc_p sca[N${UPPER}]; - -/* - * Macro to check that the unit number is valid - * Often this isn't needed as once the open() is performed, - * the unit number is pretty much safe.. The exception would be if we - * implemented devices that could "go away". in which case all these routines - * would be wise to check the number, DIAGNOSTIC or not. - */ -#define CHECKUNIT(RETVAL) \ -do { /* the do-while is a safe way to do this grouping */ \ - if (unit > N${UPPER}) { \ - printf("%s: bad unit %d\n", __func__, unit); \ - return (RETVAL); \ - } \ - if (scp == NULL) { \ - printf("%s: unit %d not attached\n", __func__, unit); \ - return (RETVAL); \ - } \ -} while (0) - -#ifdef DIAGNOSTIC -#define CHECKUNIT_DIAG(RETVAL) CHECKUNIT(RETVAL) -#else /* DIAGNOSTIC */ -#define CHECKUNIT_DIAG(RETVAL) -#endif /* DIAGNOSTIC */ - -static int -${1}ioctl (struct cdev *dev, u_long cmd, caddr_t data, int flag, struct thread *td) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - - CHECKUNIT_DIAG(ENXIO); - - switch (cmd) { - case DHIOCRESET: - /* whatever resets it */ - (void)scp; /* Delete this line after using scp. */ -#if 0 - ${UPPER}_OUTB(scp->iobase, 0xff); -#endif - break; - default: - return ENXIO; - } - return (0); -} - -/* - * You also need read, write, open, close routines. - * This should get you started - */ -static int -${1}open(struct cdev *dev, int oflags, int devtype, struct thread *td) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - - CHECKUNIT(ENXIO); - - (void)scp; /* Delete this line after using scp. */ - /* - * Do processing - */ - return (0); -} - -static int -${1}close(struct cdev *dev, int fflag, int devtype, struct thread *td) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - - CHECKUNIT_DIAG(ENXIO); - - (void)scp; /* Delete this line after using scp. */ - /* - * Do processing - */ - return (0); -} - -static int -${1}read(struct cdev *dev, struct uio *uio, int ioflag) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - int toread; - - - CHECKUNIT_DIAG(ENXIO); - - /* - * Do processing - * read from buffer - */ - toread = (min(uio->uio_resid, sizeof(scp->buffer))); - return(uiomove(scp->buffer, toread, uio)); -} - -static int -${1}write(struct cdev *dev, struct uio *uio, int ioflag) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - int towrite; - - CHECKUNIT_DIAG(ENXIO); - - /* - * Do processing - * write to buffer - */ - towrite = (min(uio->uio_resid, sizeof(scp->buffer))); - return(uiomove(scp->buffer, towrite, uio)); -} - -static int -${1}mmap(struct cdev *dev, vm_offset_t offset, vm_paddr_t *paddr, int nprot) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - - CHECKUNIT_DIAG(-1); - - (void)scp; /* Delete this line after using scp. */ - /* - * Do processing - */ -#if 0 /* if we had a frame buffer or whatever.. do this */ - if (offset > FRAMEBUFFERSIZE - PAGE_SIZE) { - return (-1); - } - return i386_btop((FRAMEBASE + offset)); -#else - return (-1); -#endif -} - -static int -${1}poll(struct cdev *dev, int which, struct thread *td) -{ - int unit = UNIT(dev); - sc_p scp = sca[unit]; - - CHECKUNIT_DIAG(ENXIO); - - (void)scp; /* Delete this line after using scp. */ - /* - * Do processing - */ - return (0); /* this is the wrong value I'm sure */ -} - -/* - * Now for some driver initialisation. - * Occurs ONCE during boot (very early). - */ -static void -${1}_drvinit(void *unused) -{ - int unit; - sc_p scp; - - for (unit = 0; unit < N${UPPER}; unit++) { - /* - * Allocate storage for this instance . - */ - scp = malloc(sizeof(*scp), M_DEVBUF, M_NOWAIT | M_ZERO); - if( scp == NULL) { - printf("${1}%d failed to allocate strorage\n", unit); - return; - } - sca[unit] = scp; - scp->dev = make_dev(&${1}_cdevsw, unit, - UID_ROOT, GID_KMEM, 0640, "${1}%d", unit); - } -} - -SYSINIT(${1}dev, SI_SUB_DRIVERS, SI_ORDER_MIDDLE+CDEV_MAJOR, - ${1}_drvinit, NULL); -DONE - -cat >${TOP}/sys/${1}io.h <<DONE -/* - * Definitions needed to access the ${1} device (ioctls etc) - * see mtio.h , ioctl.h as examples - */ -#ifndef SYS_DHIO_H -#define SYS_DHIO_H - -#ifndef KERNEL -#include <sys/types.h> -#endif -#include <sys/ioccom.h> - -/* - * define an ioctl here - */ -#define DHIOCRESET _IO('D', 0) /* reset the ${1} device */ -#endif -DONE - -if [ ! -d ${TOP}/modules/${1} ]; then - mkdir -p ${TOP}/modules/${1} -fi - -cat >${TOP}/modules/${1}/Makefile <<DONE -# ${UPPER} Loadable Kernel Module - -.PATH: \${.CURDIR}/../../dev/${1} -KMOD = ${1} -SRCS = ${1}.c - -.include <bsd.kmod.mk> -DONE - -echo -n "Do you want to build the '${1}' module? [Y]" -read VAL -if [ "-z" "$VAL" ]; then - VAL=YES -fi -case ${VAL} in -[yY]*) - (cd ${TOP}/modules/${1}; make depend; make ) - ;; -*) -# exit - ;; -esac - -echo "" -echo -n "Do you want to build the '${UPPER}' kernel? [Y]" -read VAL -if [ "-z" "$VAL" ]; then - VAL=YES -fi -case ${VAL} in -[yY]*) - ( - cd ${TOP}/i386/conf; \ - config ${UPPER}; \ - cd ${TOP}/i386/compile/${UPPER}; \ - make depend; \ - make; \ - ) - ;; -*) -# exit - ;; -esac - -#--------------end of script--------------- -# -#edit to your taste.. -# -# diff --git a/share/man/man1/Makefile b/share/man/man1/Makefile index e5ab6597ead2..5b1d3ac1091d 100644 --- a/share/man/man1/Makefile +++ b/share/man/man1/Makefile @@ -55,6 +55,7 @@ MLINKS= builtin.1 alias.1 \ builtin.1 if.1 \ builtin.1 jobid.1 \ builtin.1 jobs.1 \ + builtin.1 keybinds.1 \ builtin.1 limit.1 \ builtin.1 log.1 \ builtin.1 logout.1 \ diff --git a/share/man/man1/builtin.1 b/share/man/man1/builtin.1 index d546548ab4e5..ee89006caea5 100644 --- a/share/man/man1/builtin.1 +++ b/share/man/man1/builtin.1 @@ -1,4 +1,6 @@ .\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" .\" Copyright (c) 1999 Sheldon Hearn .\" .\" All rights reserved. @@ -24,175 +26,33 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd December 21, 2010 +.Dd March 29, 2025 .Dt BUILTIN 1 .Os .Sh NAME .Nm builtin , -.Nm \&! , -.Nm % , -.Nm \&. , -.Nm \&: , -.Nm @ , -.Nm \&[ , -.Nm { , -.Nm } , -.Nm alias , -.Nm alloc , -.Nm bg , -.Nm bind , -.Nm bindkey , -.Nm break , -.Nm breaksw , -.Nm builtins , -.Nm case , -.Nm cd , -.Nm chdir , -.Nm command , -.Nm complete , -.Nm continue , -.Nm default , -.Nm dirs , -.Nm do , -.Nm done , -.Nm echo , -.Nm echotc , -.Nm elif , -.Nm else , -.Nm end , -.Nm endif , -.Nm endsw , -.Nm esac , -.Nm eval , -.Nm exec , -.Nm exit , -.Nm export , -.Nm false , -.Nm fc , -.Nm fg , -.Nm filetest , -.Nm fi , -.Nm for , -.Nm foreach , -.Nm getopts , -.Nm glob , -.Nm goto , -.Nm hash , -.Nm hashstat , -.Nm history , -.Nm hup , -.Nm if , -.Nm jobid , -.Nm jobs , -.Nm kill , -.Nm limit , -.Nm local , -.Nm log , -.Nm login , -.Nm logout , -.Nm ls-F , -.Nm nice , -.Nm nohup , -.Nm notify , -.Nm onintr , -.Nm popd , -.Nm printenv , -.Nm printf , -.Nm pushd , -.Nm pwd , -.Nm read , -.Nm readonly , -.Nm rehash , -.Nm repeat , -.Nm return , -.Nm sched , -.Nm set , -.Nm setenv , -.Nm settc , -.Nm setty , -.Nm setvar , -.Nm shift , -.Nm source , -.Nm stop , -.Nm suspend , -.Nm switch , -.Nm telltc , -.Nm test , -.Nm then , -.Nm time , -.Nm times , -.Nm trap , -.Nm true , -.Nm type , -.Nm ulimit , -.Nm umask , -.Nm unalias , -.Nm uncomplete , -.Nm unhash , -.Nm unlimit , -.Nm unset , -.Nm unsetenv , -.Nm until , -.Nm wait , -.Nm where , -.Nm which , -.Nm while -.Nd shell built-in commands +.Nm keybinds +.Nd index of FreeBSD shell built-in commands .Sh SYNOPSIS -See the built-in command description in the appropriate shell manual page. +See the manual for your shell for operation details. .Sh DESCRIPTION -Shell builtin commands are commands that can be executed within the -running shell's process. -Note that, in the case of -.Xr csh 1 -builtin commands, the command is executed in a subshell if it occurs as -any component of a pipeline except the last. -.Pp -If a command specified to the shell contains a slash -.Ql / , -the shell will not execute a builtin command, even if the last component -of the specified command matches the name of a builtin command. -Thus, while specifying -.Dq Li echo -causes a builtin command to be executed under shells that support the -.Nm echo -builtin command, -specifying -.Dq Li /bin/echo -or -.Dq Li ./echo -does not. -.Pp -While some builtin commands may exist in more than one shell, their -operation may be different under each shell which supports them. -Below is a table which lists shell builtin commands, the standard shells -that support them and whether they exist as standalone utilities. -.Pp -Only builtin commands for the +This page provides an index of +.Nm +commands, keywords, and keyboard bindings provided by .Xr csh 1 and -.Xr sh 1 -shells are listed here. -Consult a shell's manual page for -details on the operation its builtin commands. -Beware that the -.Xr sh 1 -manual page, at least, calls some of these commands -.Dq built-in commands -and some of them -.Dq reserved words . -Users of other shells may need to consult an -.Xr info 1 -page or other sources of documentation. -.Pp -Commands marked -.Dq Li No** -under -.Em External -do exist externally, -but are implemented as scripts using a builtin command of the same name. -.Bl -column ".Ic uncomplete" ".Em External" ".Xr csh 1" ".Xr sh 1" -offset indent -.It Em Command Ta Em External Ta Xr csh 1 Ta Xr sh 1 +.Xr sh 1 , +the command line interpreters which comprise the +.Bx +user environment. +.Ss Commands +Below is a table which lists +.Nm +commands and keywords, +whether they exist as standalone utilities, +and the standard shells that provide them. +.Bl -column "uncomplete" "Standalone" "csh(1)" "sh(1)" -offset indent +.It Em Command Ta Em Standalone Ta Xr csh 1 Ta Xr sh 1 .It Ic \&! Ta \&No Ta \&No Ta Yes .It Ic % Ta \&No Ta Yes Ta \&No .It Ic \&. Ta \&No Ta \&No Ta Yes @@ -201,9 +61,9 @@ but are implemented as scripts using a builtin command of the same name. .It Ic \&[ Ta Yes Ta \&No Ta Yes .It Ic { Ta \&No Ta \&No Ta Yes .It Ic } Ta \&No Ta \&No Ta Yes -.It Ic alias Ta No** Ta Yes Ta Yes +.It Ic alias Ta No* Ta Yes Ta Yes .It Ic alloc Ta \&No Ta Yes Ta \&No -.It Ic bg Ta No** Ta Yes Ta Yes +.It Ic bg Ta No* Ta Yes Ta Yes .It Ic bind Ta \&No Ta \&No Ta Yes .It Ic bindkey Ta \&No Ta Yes Ta \&No .It Ic break Ta \&No Ta Yes Ta Yes @@ -211,9 +71,9 @@ but are implemented as scripts using a builtin command of the same name. .It Ic builtin Ta \&No Ta \&No Ta Yes .It Ic builtins Ta \&No Ta Yes Ta \&No .It Ic case Ta \&No Ta Yes Ta Yes -.It Ic cd Ta No** Ta Yes Ta Yes +.It Ic cd Ta No* Ta Yes Ta Yes .It Ic chdir Ta \&No Ta Yes Ta Yes -.It Ic command Ta No** Ta \&No Ta Yes +.It Ic command Ta No* Ta \&No Ta Yes .It Ic complete Ta \&No Ta Yes Ta \&No .It Ic continue Ta \&No Ta Yes Ta Yes .It Ic default Ta \&No Ta Yes Ta \&No @@ -233,22 +93,22 @@ but are implemented as scripts using a builtin command of the same name. .It Ic exit Ta \&No Ta Yes Ta Yes .It Ic export Ta \&No Ta \&No Ta Yes .It Ic false Ta Yes Ta \&No Ta Yes -.It Ic fc Ta No** Ta \&No Ta Yes -.It Ic fg Ta No** Ta Yes Ta Yes +.It Ic fc Ta No* Ta \&No Ta Yes +.It Ic fg Ta No* Ta Yes Ta Yes .It Ic filetest Ta \&No Ta Yes Ta \&No .It Ic fi Ta \&No Ta \&No Ta Yes .It Ic for Ta \&No Ta \&No Ta Yes .It Ic foreach Ta \&No Ta Yes Ta \&No -.It Ic getopts Ta No** Ta \&No Ta Yes +.It Ic getopts Ta No* Ta \&No Ta Yes .It Ic glob Ta \&No Ta Yes Ta \&No .It Ic goto Ta \&No Ta Yes Ta \&No -.It Ic hash Ta No** Ta \&No Ta Yes +.It Ic hash Ta No* Ta \&No Ta Yes .It Ic hashstat Ta \&No Ta Yes Ta \&No .It Ic history Ta \&No Ta Yes Ta \&No .It Ic hup Ta \&No Ta Yes Ta \&No .It Ic if Ta \&No Ta Yes Ta Yes .It Ic jobid Ta \&No Ta \&No Ta Yes -.It Ic jobs Ta No** Ta Yes Ta Yes +.It Ic jobs Ta No* Ta Yes Ta Yes .It Ic kill Ta Yes Ta Yes Ta Yes .It Ic limit Ta \&No Ta Yes Ta \&No .It Ic local Ta \&No Ta \&No Ta Yes @@ -265,7 +125,7 @@ but are implemented as scripts using a builtin command of the same name. .It Ic printf Ta Yes Ta \&No Ta Yes .It Ic pushd Ta \&No Ta Yes Ta \&No .It Ic pwd Ta Yes Ta \&No Ta Yes -.It Ic read Ta No** Ta \&No Ta Yes +.It Ic read Ta No* Ta \&No Ta Yes .It Ic readonly Ta \&No Ta \&No Ta Yes .It Ic rehash Ta \&No Ta Yes Ta \&No .It Ic repeat Ta \&No Ta Yes Ta \&No @@ -288,26 +148,68 @@ but are implemented as scripts using a builtin command of the same name. .It Ic times Ta \&No Ta \&No Ta Yes .It Ic trap Ta \&No Ta \&No Ta Yes .It Ic true Ta Yes Ta \&No Ta Yes -.It Ic type Ta No** Ta \&No Ta Yes -.It Ic ulimit Ta No** Ta \&No Ta Yes -.It Ic umask Ta No** Ta Yes Ta Yes -.It Ic unalias Ta No** Ta Yes Ta Yes +.It Ic type Ta No* Ta \&No Ta Yes +.It Ic ulimit Ta No* Ta \&No Ta Yes +.It Ic umask Ta No* Ta Yes Ta Yes +.It Ic unalias Ta No* Ta Yes Ta Yes .It Ic uncomplete Ta \&No Ta Yes Ta \&No .It Ic unhash Ta \&No Ta Yes Ta \&No .It Ic unlimit Ta \&No Ta Yes Ta \&No .It Ic unset Ta \&No Ta Yes Ta Yes .It Ic unsetenv Ta \&No Ta Yes Ta \&No .It Ic until Ta \&No Ta \&No Ta Yes -.It Ic wait Ta No** Ta Yes Ta Yes +.It Ic wait Ta No* Ta Yes Ta Yes .It Ic where Ta \&No Ta Yes Ta \&No .It Ic which Ta Yes Ta Yes Ta \&No .It Ic while Ta \&No Ta Yes Ta Yes .El +.Pp +\&No*: Commands marked +.Ql No* +exist externally, but are implemented as scripts using a +.Nm +command of the same name. +.Ss Keybinds +The command line environment also provides the following +default keyboard bindings: +.Bl -column "Process Info (SIGINFO)" "^M | ^J" "^M | ^J" -offset indent +.It Em Signal Ta Xr csh 1 Ta Xr sh 1 +.It Ic Backspace Ta ^H Ta ^H +.It Ic Carriage Return Ta ^M | ^J Ta ^M | ^J +.It Ic Tab Ta ^I Ta ^I +.It Ic Beginning of Line Ta ^A Ta ^A +.It Ic End of Line Ta ^E Ta ^E +.It Ic Cursor Forward Ta ^F Ta ^F +.It Ic Cursor Backward Ta ^B Ta ^B +.It Ic Clear Screen Ta ^L Ta ^L +.It Ic Cut Line Ta ^U Ta ^U +.It Ic Cut Word Backwards Ta ^W Ta ^W +.It Ic Cut Rest of Line Ta ^K Ta ^K +.It Ic Paste Last Cut Ta ^Y Ta ^Y +.It Ic Typo Ta ^T Ta ^T +.It End of File Po Ic EOF Pc Ta ^D Ta ^D +.It Interupt Po Ic SIGINT Pc Ta ^C Ta ^C +.It Process info Po Ic SIGINFO Pc Ta ^T Ta ^T +.It Ic Search History Ta \&No Ta ^R +.It Ic Exit Search History Ta \&No Ta ^G +.It Ic Previous Command Ta ^P Ta ^P +.It Ic Next Command Ta ^N Ta ^N +.It Ic Print Next Character Ta ^V Ta ^V +.It Ic Pause Job Ta ^S Ta ^S +.It Ic Resume Job Ta ^Q Ta ^Q +.It Suspend Job Ic (SIGTSTP) Ta ^Z Ta ^Z +.It Ic Scrollback Mode Ta ScrLk* Ta ScrLk* +.El +.Pp +\&*: Bindings marked +.Ql * +are provided by +.Xr vt 4 , +the console driver. .Sh SEE ALSO .Xr csh 1 , .Xr echo 1 , .Xr false 1 , -.Xr info 1 , .Xr kill 1 , .Xr login 1 , .Xr nice 1 , @@ -326,5 +228,18 @@ The manual page first appeared in .Fx 3.4 . .Sh AUTHORS +.An -nosplit This manual page was written by +.An Alexander Ziaee Aq Mt ziaee@FreeBSD.org +from an earlier version by .An Sheldon Hearn Aq Mt sheldonh@FreeBSD.org . +.Sh CAVEATS +While +.Nm +commands may exist in more than one shell or standalone, +each may be implemented differently. +.Pp +Standalone utilities and their manuals must be called by their path +from a shell with a +.Nm +command of the same name. diff --git a/share/man/man4/Makefile b/share/man/man4/Makefile index 4f12e70f2ae4..7c8a8f3afc45 100644 --- a/share/man/man4/Makefile +++ b/share/man/man4/Makefile @@ -213,6 +213,7 @@ MAN= aac.4 \ ${_hv_vmbus.4} \ ${_hv_vss.4} \ hwpmc.4 \ + ${_hwt.4} \ ${_hwpstate_intel.4} \ i2ctinyusb.4 \ iavf.4 \ @@ -926,6 +927,17 @@ _vmm.4= vmm.4 .endif .endif +.if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "aarch64" +_hwt.4= hwt.4 +.if ${MACHINE_CPUARCH} == "amd64" +MLINKS+=hwt.4 intel_pt.4 +.endif +.if ${MACHINE_CPUARCH} == "aarch64" +MLINKS+=hwt.4 coresight.4 +MLINKS+=hwt.4 spe.4 +.endif +.endif + .if ${MACHINE_CPUARCH} == "amd64" || ${MACHINE_CPUARCH} == "i386" || \ ${MACHINE_CPUARCH} == "aarch64" _gve.4= gve.4 diff --git a/share/man/man4/hwt.4 b/share/man/man4/hwt.4 new file mode 100644 index 000000000000..7bc8ed4b396d --- /dev/null +++ b/share/man/man4/hwt.4 @@ -0,0 +1,142 @@ +.\" +.\" Copyright (c) 2025 Ruslan Bukin <br@bsdpad.com> +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd July 7, 2025 +.Dt HWT 4 +.Os +.Sh NAME +.Nm hwt +.Nd Hardware Trace Framework +.Sh SYNOPSIS +.Cd "options HWT_HOOKS" +.Cd "device hwt" +.Pp +At least one of: +.Cd "device intel_pt" +.Pq amd64 +.Cd "device coresight" +.Pq arm64 +.Cd "device spe" +.Pq arm64 +.Pp +In +.Xr rc.conf 5 : +.Cd kld_list="hwt" +.Sh DESCRIPTION +The +.Nm +framework provides infrastructure for hardware-assisted tracing. +It collects detailed information about software execution and stores it as +events in highly compressed format into DRAM. +The events cover information about control flow changes of a program, whether +branches taken or not, exceptions taken, timing information, cycles elapsed and +more. +The information collected allows to reconstruct entire program flow of a given +application without noticeable performance impact. +.Sh HARDWARE +The framework supports several tracing technologies found on +.Cd arm64 +and +.Cd amd64 +systems: +.Pp +.Bl -bullet -compact +.It +ARM Coresight +.It +ARM Statistical Profiling Extension (SPE) +.It +Intel Processor Trace (PT) +.El +.Pp +The +.Nm +framework supports two modes of operation: +.Bl -tag -width "Thread mode" +.It Em CPU mode +Capture CPU activity in kernel mode. +.It Em Thread mode +Capture activity of each of a process's threads in user mode. +.El +.Sh MANAGEMENT +When loaded into kernel, the +.Nm +framework provides +.Pa /dev/hwt +character device. +The only +.Xr ioctl 2 +request it accepts is +.Dv HWT_IOC_ALLOC . +This request allocates kernel tracing context (CTX) based on requested mode of +operation, set of CPUs and/or pid. +.Pp +Upon successfull CTX allocation, the ioctl returns a CTX identification +number (ident). +.Pp +Each CTX is then managed using its own dedicated character device found at +.Pa "/dev/hwt_${ident}_${d}", +where ident is a unique identification number of tracing context, d is either +cpu_id (in HWT CPU mode) or process pid (in HWT Thread mode). +.Sh HOOKS +During tracing of a target process, HWT records runtime events such as threads +creation, exec and mmap system calls. +These events are logged as "records" within a particular CTX associated with +traced process. +.Pp +Additionally, HWT can suspend the target thread upon exec or mmap system calls +if requested by the user. +This pause allows user-space tools to retrieve the records and adjust tracing +settings before execution continues. +This feature is especially useful when address range filtering is enabled, +allowing tracing of specific functions within the target executable or a +dynamic library. +.Sh KERNEL OPTIONS +The following options in the kernel configuration file are mandatory and +related to +.Nm +operation: +.Pp +.Bl -tag -width ".Dv HWT_HOOKS" -compact +.It Dv HWT_HOOKS +Enable kernel hooks. +.El +.Sh IOCTL INTERFACE +Once a CTX is allocated, it's management character device accepts several IOC +requests: +.Bl -tag -width "HWT_IOC_RECORD_GET" +.It Dv HWT_IOC_START +Start tracing. +In HWT CPU mode the tracing does actually start with this +.Xr ioctl 2 +request. +In the Thread mode, the tracing "running" flag set, but tracing begins after +scheduler switches the target thread onto CPU and return to user mode. +.It Dv HWT_IOC_STOP +Stop tracing of the particular CTX. +.It Dv HWT_IOC_RECORD_GET +Copy all or part of records collected during hook invocation and associated +with this CTX to userspace. +.It Dv HWT_IOC_BUFPTR_GET +Get current pointer in buffer that is filled by tracing units in real-time. +.It Dv HWT_IOC_SET_CONFIG +Set achitecture-specific config (optional). +.It Dv HWT_IOC_WAKEUP +Wake up a thread that has been put to sleep by HWT framework hooks. +.It Dv HWT_IOC_SVC_BUF +For SPE-only, the kernel is waiting for userspace to notify that it's copied +out a buffer to avoid data loss/overwriting buffers. +.El +.Sh SEE ALSO +.Xr hwt 8 +.Sh HISTORY +The +.Nm +framework first appeared in +.Fx 15.0 . +.Sh AUTHORS +.An Ruslan Bukin Aq Mt br@FreeBSD.org +.An Bojan Novković Aq Mt bnovkov@freebsd.org +.An Zachary Leaf Aq Mt zachary.leaf@arm.com diff --git a/share/man/man7/Makefile b/share/man/man7/Makefile index 021bf9251bda..7daa0ffed8ea 100644 --- a/share/man/man7/Makefile +++ b/share/man/man7/Makefile @@ -17,6 +17,7 @@ MAN= arch.7 \ intro.7 \ maclabel.7 \ mitigations.7 \ + named_attribute.7 \ operator.7 \ orders.7 \ ports.7 \ diff --git a/share/man/man7/named_attribute.7 b/share/man/man7/named_attribute.7 new file mode 100644 index 000000000000..7cd778620357 --- /dev/null +++ b/share/man/man7/named_attribute.7 @@ -0,0 +1,275 @@ +.\" +.\" Copyright (c) 2025 Rick Macklem +.\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" +.Dd July 3, 2025 +.Dt NAMED_ATTRIBUTE 7 +.Os +.Sh NAME +.Nm named_attribute +.Nd Solaris-like extended attribute system interface +.Sh DESCRIPTION +Description of the system interface for named attributes +(the NFS Version 4 terminology). +.Ss Introduction +This document describes an alternate system interface for extended +attributes as compared to +.Xr extattr 2 . +It is based on the interface provided by Solaris and NFS Version 4. +.Pp +This interface associates a directory, known as a named attribute directory, +to a file system object. +This directory is read in the same manner as a normal directory via the +.Xr getdents 2 +or +.Xr getdirentries 2 +system calls. +The +.Pa .\& +and +.Pa ..\& +entries refer to the directory itself and to the associated file object, +respectively. +The other entries in this directory +are the names of the extended attributes for the associated file object +and are referred to as named attributes. +These named attributes are regular files used to store the attribute's +value. +.Pp +A named attribute directory does not live in the file system's name space. +It is accessed via an +.Xr open 2 +or +.Xr openat 2 +system call done on a file to query the named attributes for the file, +with the +.Dv O_NAMEDATTR +flag specified and a +.Fa path +argument of +.Pa .\& . +This file descriptor can be used as the +.Fa fd +argument for a variety of system calls, such as: +.Xr fchdir 2 , +.Xr unlinkat 2 +and +.Xr renameat 2 . +.Xr renameat 2 +is only permitted to rename a named attribute within the same named +attribute directory. +.Pp +When a file descriptor for a file object in the file system's namespace +is used as the +.Fa fd +argument of an +.Xr openat 2 +along with the +.Fa flag +.Dv O_NAMEDATTR +and a +.Fa path +argument that is the name of a named attribute (not +.Pa .\& +or +.Pa ..\& +), a file descriptor for the named attribute is returned. +If the +.Fa flag +.Dv O_CREAT +is specified, the named attribute will be created if it does not exist. +The +.Fa path +argument must be a single component name, with no embedded +.Dq / +in it. +I/O on these named attribute file descriptors may be performed by +standard I/O system calls +such as: +.Xr read 2 , +.Xr write 2 , +.Xr lseek 2 +and +.Xr ftruncate 2 . +.Pp +The +.Dv _PC_NAMEDATTR_ENABLED +.Fa name +argument to +.Xr pathconf 2 +will return 1 if the file system supports named attributes. +The +.Dv _PC_HAS_NAMEDATTR +.Fa name +argument to +.Xr pathconf 2 +will return 1 if there are one or more named attributes for the file. +If an application does a +.Xr openat 2 +of +.Dq .\& +to open a named attribute directory when no named attribute directory exists, +an empty named attribute directory will be created. +Testing +.Dv _PC_HAS_NAMEDATTR +can be done to avoid creating these named attribute directories unnecessarily. +.Pp +The named attribute interface is a different mechanism/system call interface for +manipulating extended attributes compared with +.Xr extattr 2 . +Although the named attribute machanism might require different internal +implementation +of extended attributes within a file system, both ZFS and NFSv4 provide +both mechanisms, which can be used interchangeably to manipulate +extended attributes, but with a couple of limitations. +.Bl -bullet +.It +The +.Xr extattr 2 +interface requires that an extended attribute's value be set or acquired +via a single system call using a single buffer. +This limits the size of the attribute's value. +.It +The named attribute interface does not support system namespace +extended attributes and, +as such, system namespace extended attributes must be manipulated via +.Xr extattr 2 . +.El +.Pp +The named attribute mechanism/system call interface provides certain +advantages over +.Xr extattr 2 . +Since the attribute's value is updated via +.Xr read 2 +and +.Xr write 2 +system calls, the attribute's data may be as large as any regular file +and may be partially updated. +(Note that this interface does not provide the atomicity guarantee that +.Xr extattr 2 +does.) +The permission to access a named attribute directory is determined from +the access control information for the associated file object. +However, access control information can be set on each individual attribute +in a manner similar to a regular file. +This provides +.Dq per attribute +granular control over attribute permissions via +.Xr fchown 2 . +.Pp +At this time, the only local file system which supports this interface +is ZFS and only if the +.Dv xattr +property is set to +.Dq dir . +(Note that, even when +.Dq zfs get xattr <file-system> +shows +.Dq on +the command +.Dq zfs set xattr=dir <file-system> +must be done, followed by a remount to make the setting take effect.) +A NFSv4 mount will also support this interface, but only if the NFSv4 +server file system supports named attributes (the openattr operation). +The +.Fx +NFSv4 server supports named attributes only +for ZFS exported file systems where the +.Dq xattr +property is set to +.Dq dir +for the file system. +.Sh EXAMPLES +.Bd -literal +#include <stdio.h> +#include <dirent.h> +#include <fcntl.h> +#include <unistd.h> + +\&... + +/* For a file called "myfile". Failure checks removed for brevity. */ +int file_fd, nameddir_fd, namedattr_fd; +ssize_t siz; +char buf[DIRBLKSIZ], *cp; +struct dirent *dp; +long named_enabled, has_named_attrs; + +\&... +/* Check to see if named attributes are supported. */ +named_enabled = pathconf("myfile", _PC_NAMEDATTR_ENABLED); +if (named_enabled <= 0) + err(1, "Named attributes not enabled"); +/* Test to see if named attribute(s) exist for the file. */ +has_named_attrs = pathconf("myfile", _PC_HAS_NAMEDATTR); +if (has_named_attrs == 1) + printf("myfile has named attribute(s)\\n"); +else + printf("myfile does not have any named attributes\\n"); +/* Open a named attribute directory. */ +file_fd = open("myfile", O_RDONLY, 0); +nameddir_fd = openat(file_fd, ".", O_NAMEDATTR, 0); +\&... +/* and read it, assuming it all fits in DIRBLKSIZ for simplicity. */ +siz = getdents(fd, buf, sizeof(buf)); +cp = buf; +while (cp < &buf[siz]) { + dp = (struct dirent *)cp; + printf("name=%s\\n", dp->d_name); + cp += dp->d_reclen; +} +\&... +/* Open/create a named attribute called "foo". */ +namedattr_fd = openat(file_fd, "foo", O_CREAT | O_RDWR | + O_TRUNC | O_NAMEDATTR, 0600); +\&... +/* Write foo's attribute value. */ +write(namedattr_fd, "xxxyyy", 6); +\&... +/* Read foo's attribute value. */ +lseek(namedattr_fd, 0, SEEK_SET); +siz = read(namedattr_fd, buf, sizeof(buf)); +\&... +/* And close "foo". */ +close(namedattr_fd); +\&... +/* Rename "foo" to "oldfoo". */ +renameat(nameddir_fd, "foo", nameddir_fd, "oldfoo"); +/* and delete "oldfoo". */ +unlinkat(nameddir_fd, "oldfoo", AT_RESOLVE_BENEATH); +.Ed +.Pp +The +.Xr runat 1 +command may be used to perform shell commands on named attributes. +For example: +.Bd -literal +$ runat myfile cp /etc/hosts attrhosts # creates attrhosts +$ runat myfile cat attrhosts # displays contents of attrhosts +$ runat myfile ls -l # lists the attributes for myfile +.Ed +.Pp +If using the +.Xr bash 1 +shell, the command +.Dq cd -@ foo +enters the named attribute directory for the file object +.Dq foo . +.Sh SEE ALSO +.Xr bash 1 , +.Xr runat 1 , +.Xr chdir 2 , +.Xr extattr 2 , +.Xr lseek 2 , +.Xr open 2 , +.Xr pathconf 2 , +.Xr read 2 , +.Xr rename 2 , +.Xr truncate 2 , +.Xr unlinkat 2 , +.Xr write 2 , +.Xr zfsprops 7 +.Sh HISTORY +This interface first appeared in +.Fx 15.0 . diff --git a/stand/efi/libefi/efinet.c b/stand/efi/libefi/efinet.c index 186d816cd323..e872110ef08f 100644 --- a/stand/efi/libefi/efinet.c +++ b/stand/efi/libefi/efinet.c @@ -256,6 +256,7 @@ efi_env_net_params(struct iodesc *desc) rootip.s_addr = rootaddr; #ifdef EFINET_DEBUG + printf("%s: proto=%d\n", __func__, netproto); printf("%s: ip=%s\n", __func__, inet_ntoa(myip)); printf("%s: mask=%s\n", __func__, intoa(netmask)); printf("%s: gateway=%s\n", __func__, inet_ntoa(gateip)); @@ -427,6 +428,7 @@ efinet_dev_init(void) dif->dif_private = handles2[i]; } + efinet_dev.dv_cleanup = netdev.dv_cleanup; efinet_dev.dv_open = netdev.dv_open; efinet_dev.dv_close = netdev.dv_close; efinet_dev.dv_strategy = netdev.dv_strategy; diff --git a/stand/efi/loader/arch/amd64/elf64_freebsd.c b/stand/efi/loader/arch/amd64/elf64_freebsd.c index c4265aca035e..35bd4d6c1419 100644 --- a/stand/efi/loader/arch/amd64/elf64_freebsd.c +++ b/stand/efi/loader/arch/amd64/elf64_freebsd.c @@ -209,6 +209,12 @@ elf64_exec(struct preloaded_file *fp) trampoline, PT4); printf("Start @ 0x%lx ...\n", ehdr->e_entry); + /* + * we have to cleanup here because net_cleanup() doesn't work after + * we call ExitBootServices + */ + dev_cleanup(); + efi_time_fini(); err = bi_load(fp->f_args, &modulep, &kernend, true); if (err != 0) { @@ -218,8 +224,6 @@ elf64_exec(struct preloaded_file *fp) return (err); } - dev_cleanup(); - trampoline(trampstack, copy_staging == COPY_STAGING_ENABLE ? efi_copy_finish : efi_copy_finish_nop, kernend, modulep, PT4, ehdr->e_entry); diff --git a/stand/efi/loader/arch/arm/exec.c b/stand/efi/loader/arch/arm/exec.c index c2a79523c02a..3963b6c0104b 100644 --- a/stand/efi/loader/arch/arm/exec.c +++ b/stand/efi/loader/arch/arm/exec.c @@ -74,16 +74,17 @@ __elfN(arm_exec)(struct preloaded_file *fp) printf("Kernel entry at %p...\n", entry); printf("Kernel args: %s\n", fp->f_args); + /* + * we have to cleanup here because net_cleanup() doesn't work after + * we call ExitBootServices + */ + dev_cleanup(); + if ((error = bi_load(fp->f_args, &modulep, &kernend, true)) != 0) { efi_time_init(); return (error); } - /* At this point we've called ExitBootServices, so we can't call - * printf or any other function that uses Boot Services */ - - dev_cleanup(); - (*entry)((void *)modulep); panic("exec returned"); } diff --git a/stand/efi/loader/arch/arm64/exec.c b/stand/efi/loader/arch/arm64/exec.c index 91a0503a976f..89e2ad7521a8 100644 --- a/stand/efi/loader/arch/arm64/exec.c +++ b/stand/efi/loader/arch/arm64/exec.c @@ -69,6 +69,12 @@ elf64_exec(struct preloaded_file *fp) ehdr = (Elf_Ehdr *)&(md->md_data); entry = efi_translate(ehdr->e_entry); + /* + * we have to cleanup here because net_cleanup() doesn't work after + * we call ExitBootServices + */ + dev_cleanup(); + efi_time_fini(); err = bi_load(fp->f_args, &modulep, &kernendp, true); if (err != 0) { @@ -76,8 +82,6 @@ elf64_exec(struct preloaded_file *fp) return (err); } - dev_cleanup(); - /* Clean D-cache under kernel area and invalidate whole I-cache */ clean_addr = (vm_offset_t)efi_translate(fp->f_addr); clean_size = (vm_offset_t)efi_translate(kernendp) - clean_addr; diff --git a/stand/efi/loader/arch/i386/elf64_freebsd.c b/stand/efi/loader/arch/i386/elf64_freebsd.c index b02cda2269bc..22cdd685ea9b 100644 --- a/stand/efi/loader/arch/i386/elf64_freebsd.c +++ b/stand/efi/loader/arch/i386/elf64_freebsd.c @@ -252,6 +252,13 @@ elf64_exec(struct preloaded_file *fp) ehdr->e_entry ); + + /* + * we have to cleanup here because net_cleanup() doesn't work after + * we call ExitBootServices + */ + dev_cleanup(); + efi_time_fini(); err = bi_load(fp->f_args, &modulep, &kernend, true); if (err != 0) { @@ -259,8 +266,6 @@ elf64_exec(struct preloaded_file *fp) return (err); } - dev_cleanup(); - trampoline(trampstack, type == AllocateMaxAddress ? efi_copy_finish : efi_copy_finish_nop, kernend, modulep, PT4, gdtr, ehdr->e_entry); diff --git a/stand/efi/loader/arch/riscv/exec.c b/stand/efi/loader/arch/riscv/exec.c index 9da61229ef68..a53fbd9442b0 100644 --- a/stand/efi/loader/arch/riscv/exec.c +++ b/stand/efi/loader/arch/riscv/exec.c @@ -86,17 +86,17 @@ __elfN(exec)(struct preloaded_file *fp) printf("Kernel entry at %p...\n", entry); printf("Kernel args: %s\n", fp->f_args); + /* + * we have to cleanup here because net_cleanup() doesn't work after + * we call ExitBootServices + */ + dev_cleanup(); + if ((error = bi_load(fp->f_args, &modulep, &kernend, true)) != 0) { efi_time_init(); return (error); } - /* - * At this point we've called ExitBootServices, so we can't call - * printf or any other function that uses Boot Services - */ - dev_cleanup(); - (*entry)((void *)modulep); panic("exec returned"); } diff --git a/sys/cam/ata/ata_da.c b/sys/cam/ata/ata_da.c index ae7cf14c8f8e..1facab47473c 100644 --- a/sys/cam/ata/ata_da.c +++ b/sys/cam/ata/ata_da.c @@ -1359,10 +1359,7 @@ adaasync(void *callback_arg, uint32_t code, case AC_GETDEV_CHANGED: { softc = (struct ada_softc *)periph->softc; - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, periph->path); /* * Update our information based on the new Identify data. diff --git a/sys/cam/cam_periph.c b/sys/cam/cam_periph.c index 833df6cfb99b..730656684e2a 100644 --- a/sys/cam/cam_periph.c +++ b/sys/cam/cam_periph.c @@ -767,27 +767,28 @@ camperiphfree(struct cam_periph *periph) CAM_DEBUG(periph->path, CAM_DEBUG_INFO, ("Periph destroyed\n")); if (periph->flags & CAM_PERIPH_NEW_DEV_FOUND) { - union ccb ccb; - void *arg; - - memset(&ccb, 0, sizeof(ccb)); switch (periph->deferred_ac) { - case AC_FOUND_DEVICE: - ccb.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_setup_ccb(&ccb.ccb_h, periph->path, CAM_PRIORITY_NORMAL); - xpt_action(&ccb); - arg = &ccb; + case AC_FOUND_DEVICE: { + struct ccb_getdev cgd; + + xpt_gdev_type(&cgd, periph->path); + periph->deferred_callback(NULL, periph->deferred_ac, + periph->path, &cgd); break; - case AC_PATH_REGISTERED: - xpt_path_inq(&ccb.cpi, periph->path); - arg = &ccb; + } + case AC_PATH_REGISTERED: { + struct ccb_pathinq cpi; + + xpt_path_inq(&cpi, periph->path); + periph->deferred_callback(NULL, periph->deferred_ac, + periph->path, &cpi); break; + } default: - arg = NULL; + periph->deferred_callback(NULL, periph->deferred_ac, + periph->path, NULL); break; } - periph->deferred_callback(NULL, periph->deferred_ac, - periph->path, arg); } xpt_free_path(periph->path); free(periph, M_CAMPERIPH); @@ -1682,10 +1683,7 @@ camperiphscsisenseerror(union ccb *ccb, union ccb **orig, /* * Grab the inquiry data for this device. */ - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, ccb->ccb_h.path, CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, ccb->ccb_h.path); err_action = scsi_error_action(&ccb->csio, &cgd.inq_data, sense_flags); @@ -2133,11 +2131,7 @@ cam_periph_devctl_notify(union ccb *ccb) sbuf_cat(&sb, "serial=\""); if ((cgd = (struct ccb_getdev *)xpt_alloc_ccb_nowait()) != NULL) { - xpt_setup_ccb(&cgd->ccb_h, ccb->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd->ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)cgd); - + xpt_gdev_type(cgd, ccb->ccb_h.path); if (cgd->ccb_h.status == CAM_REQ_CMP) sbuf_bcat(&sb, cgd->serial_num, cgd->serial_num_len); xpt_free_ccb((union ccb *)cgd); diff --git a/sys/cam/cam_xpt.c b/sys/cam/cam_xpt.c index 38bc82c69aad..2ec736e7f4ac 100644 --- a/sys/cam/cam_xpt.c +++ b/sys/cam/cam_xpt.c @@ -2471,15 +2471,12 @@ xptsetasyncfunc(struct cam_ed *device, void *arg) if ((device->flags & CAM_DEV_UNCONFIGURED) != 0) return (1); - memset(&cgd, 0, sizeof(cgd)); xpt_compile_path(&path, NULL, device->target->bus->path_id, device->target->target_id, device->lun_id); - xpt_setup_ccb(&cgd.ccb_h, &path, CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, &path); csa->callback(csa->callback_arg, AC_FOUND_DEVICE, &path, &cgd); diff --git a/sys/cam/cam_xpt.h b/sys/cam/cam_xpt.h index 57225a0ae92d..3815f42cba40 100644 --- a/sys/cam/cam_xpt.h +++ b/sys/cam/cam_xpt.h @@ -145,8 +145,8 @@ uint32_t xpt_poll_setup(union ccb *start_ccb); void xpt_sim_poll(struct cam_sim *sim); /* - * Perform a path inquiry at the request priority. bzero may be redundant for - * allocated CCBs, but for the on-stack CCBs it's required. + * Perform a path inquiry. bzero may be redundant for allocated CCBs, but for + * the on-stack CCBs it's required. */ static inline void xpt_path_inq(struct ccb_pathinq *cpi, struct cam_path *path) @@ -158,6 +158,20 @@ xpt_path_inq(struct ccb_pathinq *cpi, struct cam_path *path) xpt_action((union ccb *)cpi); } +/* + * Perform get device type. bzero may be redundant for allocated CCBs, but for + * the on-stack CCBs it's required. + */ +static inline void +xpt_gdev_type(struct ccb_getdev *cgd, struct cam_path *path) +{ + + bzero(cgd, sizeof(*cgd)); + xpt_setup_ccb(&cgd->ccb_h, path, CAM_PRIORITY_NONE); + cgd->ccb_h.func_code = XPT_GDEV_TYPE; + xpt_action((union ccb *)cgd); +} + #endif /* _KERNEL */ #endif /* _CAM_CAM_XPT_H */ diff --git a/sys/cam/mmc/mmc_da.c b/sys/cam/mmc/mmc_da.c index f7c478076144..7f8bf3516804 100644 --- a/sys/cam/mmc/mmc_da.c +++ b/sys/cam/mmc/mmc_da.c @@ -692,10 +692,7 @@ sddaasync(void *callback_arg, uint32_t code, case AC_GETDEV_CHANGED: { CAM_DEBUG(path, CAM_DEBUG_TRACE, ("=> AC_GETDEV_CHANGED\n")); - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, periph->path, CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, periph->path); cam_periph_async(periph, code, path, arg); break; } diff --git a/sys/cam/scsi/scsi_all.c b/sys/cam/scsi/scsi_all.c index 13a376ebb6e3..b518f84454ad 100644 --- a/sys/cam/scsi/scsi_all.c +++ b/sys/cam/scsi/scsi_all.c @@ -3708,11 +3708,7 @@ scsi_command_string(struct cam_device *device, struct ccb_scsiio *csio, /* * Get the device information. */ - xpt_setup_ccb(&cgd->ccb_h, - csio->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd->ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)cgd); + xpt_gdev_type(cgd, csio->ccb_h.path); /* * If the device is unconfigured, just pretend that it is a hard @@ -5144,11 +5140,7 @@ scsi_sense_sbuf(struct cam_device *device, struct ccb_scsiio *csio, /* * Get the device information. */ - xpt_setup_ccb(&cgd->ccb_h, - csio->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd->ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)cgd); + xpt_gdev_type(cgd, csio->ccb_h.path); /* * If the device is unconfigured, just pretend that it is a hard diff --git a/sys/cam/scsi/scsi_cd.c b/sys/cam/scsi/scsi_cd.c index 00a417f65052..e622a96ec77e 100644 --- a/sys/cam/scsi/scsi_cd.c +++ b/sys/cam/scsi/scsi_cd.c @@ -1240,13 +1240,7 @@ cddone(struct cam_periph *periph, union ccb *done_ccb) /*getcount_only*/0); status = done_ccb->ccb_h.status; - - bzero(&cgd, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, - done_ccb->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, done_ccb->ccb_h.path); if (scsi_extract_sense_ccb(done_ccb, &error_code, &sense_key, &asc, &ascq)) diff --git a/sys/cam/scsi/scsi_ch.c b/sys/cam/scsi/scsi_ch.c index 89a817c1b488..3da22ba61392 100644 --- a/sys/cam/scsi/scsi_ch.c +++ b/sys/cam/scsi/scsi_ch.c @@ -1705,11 +1705,7 @@ chscsiversion(struct cam_periph *periph) /* * Get the device information. */ - xpt_setup_ccb(&cgd->ccb_h, - periph->path, - CAM_PRIORITY_NORMAL); - cgd->ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)cgd); + xpt_gdev_type(cgd, periph->path); if (cgd->ccb_h.status != CAM_REQ_CMP) { xpt_free_ccb((union ccb *)cgd); diff --git a/sys/cam/scsi/scsi_da.c b/sys/cam/scsi/scsi_da.c index 0a2389cd9b5d..d02750aaacaf 100644 --- a/sys/cam/scsi/scsi_da.c +++ b/sys/cam/scsi/scsi_da.c @@ -5035,11 +5035,7 @@ dadone_proberc(struct cam_periph *periph, union ccb *done_ccb) /*timeout*/0, /*getcount_only*/0); - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, done_ccb->ccb_h.path, - CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, done_ccb->ccb_h.path); if (scsi_extract_sense_ccb(done_ccb, &error_code, &sense_key, &asc, &ascq)) @@ -5077,6 +5073,18 @@ dadone_proberc(struct cam_periph *periph, union ccb *done_ccb) * behind a SATL translation that's fallen into a * terminally fatal state. * + * 4/2 happens on some HGST drives that are quite + * ill. We've already sent the start unit command (for + * which we ignore a 44/0 asc/ascq, which I'm hesitant + * to change since it's so basic and there's other error + * conditions to the START UNIT we should ignore). So to + * require initialization at this point when it should + * be fine implies to me, at least, that we should + * invalidate. Since we do read capacity in geom tasting + * a lot, and since this timeout is long, this leads to + * up to a 10 minute delay in booting. + * + * 4/2: LOGICAL UNIT NOT READY, INITIALIZING COMMAND REQUIRED * 25/0: LOGICAL UNIT NOT SUPPORTED * 44/0: INTERNAL TARGET FAILURE * 44/1: PERSISTENT RESERVATION INFORMATION LOST @@ -5084,6 +5092,7 @@ dadone_proberc(struct cam_periph *periph, union ccb *done_ccb) */ if ((have_sense) && (asc != 0x25) && (asc != 0x44) + && (asc != 0x04 && ascq != 0x02) && (error_code == SSD_CURRENT_ERROR || error_code == SSD_DESC_CURRENT_ERROR)) { const char *sense_key_desc; diff --git a/sys/cam/scsi/scsi_enc_ses.c b/sys/cam/scsi/scsi_enc_ses.c index c429e820a1fd..435874a9874a 100644 --- a/sys/cam/scsi/scsi_enc_ses.c +++ b/sys/cam/scsi/scsi_enc_ses.c @@ -979,10 +979,7 @@ ses_paths_iter(enc_softc_t *enc, enc_element_t *elm, != CAM_REQ_CMP) return; - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, path, CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, path); if (cam_ccb_success((union ccb *)&cgd)) callback(enc, elm, path, callback_arg); diff --git a/sys/cam/scsi/scsi_sa.c b/sys/cam/scsi/scsi_sa.c index cfd48c98f30e..88147393192f 100644 --- a/sys/cam/scsi/scsi_sa.c +++ b/sys/cam/scsi/scsi_sa.c @@ -4731,12 +4731,7 @@ saextget(struct cdev *dev, struct cam_periph *periph, struct sbuf *sb, SASBADDVARSTR(sb, indent, periph->periph_name, %s, periph_name, strlen(periph->periph_name) + 1); SASBADDUINT(sb, indent, periph->unit_number, %u, unit_number); - memset(&cgd, 0, sizeof(cgd)); - xpt_setup_ccb(&cgd.ccb_h, - periph->path, - CAM_PRIORITY_NORMAL); - cgd.ccb_h.func_code = XPT_GDEV_TYPE; - xpt_action((union ccb *)&cgd); + xpt_gdev_type(&cgd, periph->path); if ((cgd.ccb_h.status & CAM_STATUS_MASK) != CAM_REQ_CMP) { g->status = MT_EXT_GET_ERROR; snprintf(g->error_str, sizeof(g->error_str), diff --git a/sys/dev/hyperv/vmbus/vmbus_chan.c b/sys/dev/hyperv/vmbus/vmbus_chan.c index 189a3e66a039..7ea60a499c72 100644 --- a/sys/dev/hyperv/vmbus/vmbus_chan.c +++ b/sys/dev/hyperv/vmbus/vmbus_chan.c @@ -1555,7 +1555,7 @@ vmbus_event_flags_proc(struct vmbus_softc *sc, volatile u_long *event_flags, continue; flags = atomic_swap_long(&event_flags[f], 0); - chid_base = f << VMBUS_EVTFLAG_SHIFT; + chid_base = f * VMBUS_EVTFLAG_LEN; while ((chid_ofs = ffsl(flags)) != 0) { struct vmbus_channel *chan; @@ -1599,7 +1599,7 @@ vmbus_event_proc_compat(struct vmbus_softc *sc, int cpu) eventf = VMBUS_PCPU_GET(sc, event_flags, cpu) + VMBUS_SINT_MESSAGE; if (atomic_testandclear_long(&eventf->evt_flags[0], 0)) { vmbus_event_flags_proc(sc, sc->vmbus_rx_evtflags, - VMBUS_CHAN_MAX_COMPAT >> VMBUS_EVTFLAG_SHIFT); + VMBUS_CHAN_MAX_COMPAT / VMBUS_EVTFLAG_LEN); } } @@ -1903,7 +1903,7 @@ vmbus_chan_msgproc_choffer(struct vmbus_softc *sc, * Setup event flag. */ chan->ch_evtflag = - &sc->vmbus_tx_evtflags[chan->ch_id >> VMBUS_EVTFLAG_SHIFT]; + &sc->vmbus_tx_evtflags[chan->ch_id / VMBUS_EVTFLAG_LEN]; chan->ch_evtflag_mask = 1UL << (chan->ch_id & VMBUS_EVTFLAG_MASK); /* diff --git a/sys/dev/hyperv/vmbus/vmbus_reg.h b/sys/dev/hyperv/vmbus/vmbus_reg.h index 4aa729475b5d..76cdca0ebeb2 100644 --- a/sys/dev/hyperv/vmbus/vmbus_reg.h +++ b/sys/dev/hyperv/vmbus/vmbus_reg.h @@ -60,16 +60,10 @@ CTASSERT(sizeof(struct vmbus_message) == VMBUS_MSG_SIZE); * Hyper-V SynIC event flags */ -#ifdef __LP64__ -#define VMBUS_EVTFLAGS_MAX 32 -#define VMBUS_EVTFLAG_SHIFT 6 -#else -#define VMBUS_EVTFLAGS_MAX 64 -#define VMBUS_EVTFLAG_SHIFT 5 -#endif -#define VMBUS_EVTFLAG_LEN (1 << VMBUS_EVTFLAG_SHIFT) +#define VMBUS_EVTFLAG_LEN (sizeof(u_long) * 8) #define VMBUS_EVTFLAG_MASK (VMBUS_EVTFLAG_LEN - 1) #define VMBUS_EVTFLAGS_SIZE 256 +#define VMBUS_EVTFLAGS_MAX (VMBUS_EVTFLAGS_SIZE / sizeof(u_long)) struct vmbus_evtflags { u_long evt_flags[VMBUS_EVTFLAGS_MAX]; diff --git a/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls_rx.c b/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls_rx.c index 8b8f2e570245..4de451f1b039 100644 --- a/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls_rx.c +++ b/sys/dev/mlx5/mlx5_en/mlx5_en_hw_tls_rx.c @@ -42,13 +42,30 @@ static if_snd_tag_free_t mlx5e_tls_rx_snd_tag_free; static if_snd_tag_modify_t mlx5e_tls_rx_snd_tag_modify; +static if_snd_tag_status_str_t mlx5e_tls_rx_snd_tag_status_str; static const struct if_snd_tag_sw mlx5e_tls_rx_snd_tag_sw = { .snd_tag_modify = mlx5e_tls_rx_snd_tag_modify, .snd_tag_free = mlx5e_tls_rx_snd_tag_free, + .snd_tag_status_str = mlx5e_tls_rx_snd_tag_status_str, .type = IF_SND_TAG_TYPE_TLS_RX }; +static const char *mlx5e_tls_rx_progress_params_auth_state_str[] = { + [MLX5E_TLS_RX_PROGRESS_PARAMS_AUTH_STATE_NO_OFFLOAD] = "no_offload", + [MLX5E_TLS_RX_PROGRESS_PARAMS_AUTH_STATE_OFFLOAD] = "offload", + [MLX5E_TLS_RX_PROGRESS_PARAMS_AUTH_STATE_AUTHENTICATION] = + "authentication", +}; + +static const char *mlx5e_tls_rx_progress_params_record_tracker_state_str[] = { + [MLX5E_TLS_RX_PROGRESS_PARAMS_RECORD_TRACKER_STATE_START] = "start", + [MLX5E_TLS_RX_PROGRESS_PARAMS_RECORD_TRACKER_STATE_TRACKING] = + "tracking", + [MLX5E_TLS_RX_PROGRESS_PARAMS_RECORD_TRACKER_STATE_SEARCHING] = + "searching", +}; + MALLOC_DEFINE(M_MLX5E_TLS_RX, "MLX5E_TLS_RX", "MLX5 ethernet HW TLS RX"); /* software TLS RX context */ @@ -250,7 +267,8 @@ mlx5e_tls_rx_send_progress_parameters_sync(struct mlx5e_iq *iq, mtx_unlock(&iq->lock); while (1) { - if (wait_for_completion_timeout(&ptag->progress_complete, hz) != 0) + if (wait_for_completion_timeout(&ptag->progress_complete, + msecs_to_jiffies(1000)) != 0) break; priv = container_of(iq, struct mlx5e_channel, iq)->priv; if (priv->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR || @@ -331,7 +349,8 @@ done: * Zero is returned upon success, else some error happened. */ static int -mlx5e_tls_rx_receive_progress_parameters(struct mlx5e_iq *iq, struct mlx5e_tls_rx_tag *ptag) +mlx5e_tls_rx_receive_progress_parameters(struct mlx5e_iq *iq, + struct mlx5e_tls_rx_tag *ptag, mlx5e_iq_callback_t *cb) { struct mlx5e_get_tls_progress_params_wqe *wqe; const u32 ds_cnt = DIV_ROUND_UP(sizeof(*wqe), MLX5_SEND_WQE_DS); @@ -367,7 +386,7 @@ mlx5e_tls_rx_receive_progress_parameters(struct mlx5e_iq *iq, struct mlx5e_tls_r memcpy(iq->doorbell.d32, &wqe->ctrl, sizeof(iq->doorbell.d32)); iq->data[pi].num_wqebbs = DIV_ROUND_UP(ds_cnt, MLX5_SEND_WQEBB_NUM_DS); - iq->data[pi].callback = &mlx5e_tls_rx_receive_progress_parameters_cb; + iq->data[pi].callback = cb; iq->data[pi].arg = ptag; m_snd_tag_ref(&ptag->tag); @@ -819,6 +838,7 @@ mlx5e_tls_rx_snd_tag_alloc(if_t ifp, } ptag->flow_rule = flow_rule; + init_completion(&ptag->progress_complete); return (0); @@ -968,7 +988,8 @@ mlx5e_tls_rx_snd_tag_modify(struct m_snd_tag *pmt, union if_snd_tag_modify_param params->tls_rx.tls_rec_length, params->tls_rx.tls_seq_number) && ptag->tcp_resync_pending == 0) { - err = mlx5e_tls_rx_receive_progress_parameters(iq, ptag); + err = mlx5e_tls_rx_receive_progress_parameters(iq, ptag, + &mlx5e_tls_rx_receive_progress_parameters_cb); if (err != 0) { MLX5E_TLS_RX_STAT_INC(ptag, rx_resync_err, 1); } else { @@ -1001,6 +1022,74 @@ mlx5e_tls_rx_snd_tag_free(struct m_snd_tag *pmt) queue_work(priv->tls_rx.wq, &ptag->work); } +static void +mlx5e_tls_rx_str_status_cb(void *arg) +{ + struct mlx5e_tls_rx_tag *ptag; + + ptag = (struct mlx5e_tls_rx_tag *)arg; + complete_all(&ptag->progress_complete); + m_snd_tag_rele(&ptag->tag); +} + +static int +mlx5e_tls_rx_snd_tag_status_str(struct m_snd_tag *pmt, char *buf, size_t *sz) +{ + int err, out_size; + struct mlx5e_iq *iq; + void *buffer; + uint32_t tracker_state_val; + uint32_t auth_state_val; + struct mlx5e_priv *priv; + struct mlx5e_tls_rx_tag *ptag = + container_of(pmt, struct mlx5e_tls_rx_tag, tag); + + if (buf == NULL) + return (0); + + MLX5E_TLS_RX_TAG_LOCK(ptag); + priv = container_of(ptag->tls_rx, struct mlx5e_priv, tls_rx); + iq = mlx5e_tls_rx_get_iq(priv, ptag->flowid, ptag->flowtype); + reinit_completion(&ptag->progress_complete); + err = mlx5e_tls_rx_receive_progress_parameters(iq, ptag, + &mlx5e_tls_rx_str_status_cb); + MLX5E_TLS_RX_TAG_UNLOCK(ptag); + if (err != 0) + return (err); + + for (;;) { + if (wait_for_completion_timeout(&ptag->progress_complete, + msecs_to_jiffies(1000)) != 0) + break; + if (priv->mdev->state == MLX5_DEVICE_STATE_INTERNAL_ERROR || + pci_channel_offline(priv->mdev->pdev) != 0) + return (ENXIO); + } + buffer = mlx5e_tls_rx_get_progress_buffer(ptag); + tracker_state_val = MLX5_GET(tls_progress_params, buffer, + record_tracker_state); + auth_state_val = MLX5_GET(tls_progress_params, buffer, auth_state); + + /* Validate tracker state value is in range */ + if (tracker_state_val > + MLX5E_TLS_RX_PROGRESS_PARAMS_RECORD_TRACKER_STATE_SEARCHING) + return (EINVAL); + + /* Validate auth state value is in range */ + if (auth_state_val > + MLX5E_TLS_RX_PROGRESS_PARAMS_AUTH_STATE_AUTHENTICATION) + return (EINVAL); + + out_size = snprintf(buf, *sz, "tracker_state: %s, auth_state: %s", + mlx5e_tls_rx_progress_params_record_tracker_state_str[ + tracker_state_val], + mlx5e_tls_rx_progress_params_auth_state_str[auth_state_val]); + + if (out_size <= *sz) + *sz = out_size; + return (0); +} + #else int diff --git a/sys/dev/nvmf/host/nvmf.c b/sys/dev/nvmf/host/nvmf.c index dbdd4568bdf1..1ac0d142443b 100644 --- a/sys/dev/nvmf/host/nvmf.c +++ b/sys/dev/nvmf/host/nvmf.c @@ -27,6 +27,7 @@ #include <dev/nvmf/host/nvmf_var.h> static struct cdevsw nvmf_cdevsw; +static struct taskqueue *nvmf_tq; bool nvmf_fail_disconnect = false; SYSCTL_BOOL(_kern_nvmf, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN, @@ -34,7 +35,10 @@ SYSCTL_BOOL(_kern_nvmf, OID_AUTO, fail_on_disconnection, CTLFLAG_RWTUN, MALLOC_DEFINE(M_NVMF, "nvmf", "NVMe over Fabrics host"); +static void nvmf_controller_loss_task(void *arg, int pending); static void nvmf_disconnect_task(void *arg, int pending); +static void nvmf_request_reconnect(struct nvmf_softc *sc); +static void nvmf_request_reconnect_task(void *arg, int pending); static void nvmf_shutdown_pre_sync(void *arg, int howto); static void nvmf_shutdown_post_sync(void *arg, int howto); @@ -294,6 +298,9 @@ nvmf_establish_connection(struct nvmf_softc *sc, nvlist_t *nvl) admin = nvlist_get_nvlist(nvl, "admin"); io = nvlist_get_nvlist_array(nvl, "io", &num_io_queues); kato = dnvlist_get_number(nvl, "kato", 0); + sc->reconnect_delay = dnvlist_get_number(nvl, "reconnect_delay", 0); + sc->controller_loss_timeout = dnvlist_get_number(nvl, + "controller_loss_timeout", 0); /* Setup the admin queue. */ sc->admin = nvmf_init_qp(sc, trtype, admin, "admin queue", 0); @@ -504,6 +511,10 @@ nvmf_attach(device_t dev) callout_init(&sc->ka_tx_timer, 1); sx_init(&sc->connection_lock, "nvmf connection"); TASK_INIT(&sc->disconnect_task, 0, nvmf_disconnect_task, sc); + TIMEOUT_TASK_INIT(nvmf_tq, &sc->controller_loss_task, 0, + nvmf_controller_loss_task, sc); + TIMEOUT_TASK_INIT(nvmf_tq, &sc->request_reconnect_task, 0, + nvmf_request_reconnect_task, sc); oid = SYSCTL_ADD_NODE(device_get_sysctl_ctx(dev), SYSCTL_CHILDREN(device_get_sysctl_tree(dev)), OID_AUTO, "ioq", @@ -603,7 +614,9 @@ out: nvmf_destroy_aer(sc); - taskqueue_drain(taskqueue_thread, &sc->disconnect_task); + taskqueue_drain_timeout(nvmf_tq, &sc->request_reconnect_task); + taskqueue_drain_timeout(nvmf_tq, &sc->controller_loss_task); + taskqueue_drain(nvmf_tq, &sc->disconnect_task); sx_destroy(&sc->connection_lock); nvlist_destroy(sc->rparams); free(sc->cdata, M_NVMF); @@ -613,7 +626,7 @@ out: void nvmf_disconnect(struct nvmf_softc *sc) { - taskqueue_enqueue(taskqueue_thread, &sc->disconnect_task); + taskqueue_enqueue(nvmf_tq, &sc->disconnect_task); } static void @@ -676,6 +689,74 @@ nvmf_disconnect_task(void *arg, int pending __unused) nvmf_destroy_qp(sc->admin); sc->admin = NULL; + if (sc->reconnect_delay != 0) + nvmf_request_reconnect(sc); + if (sc->controller_loss_timeout != 0) + taskqueue_enqueue_timeout(nvmf_tq, + &sc->controller_loss_task, sc->controller_loss_timeout * + hz); + + sx_xunlock(&sc->connection_lock); +} + +static void +nvmf_controller_loss_task(void *arg, int pending) +{ + struct nvmf_softc *sc = arg; + device_t dev; + int error; + + bus_topo_lock(); + sx_xlock(&sc->connection_lock); + if (sc->admin != NULL || sc->detaching) { + /* Reconnected or already detaching. */ + sx_xunlock(&sc->connection_lock); + bus_topo_unlock(); + return; + } + + sc->controller_timedout = true; + sx_xunlock(&sc->connection_lock); + + /* + * XXX: Doing this from here is a bit ugly. We don't have an + * extra reference on `dev` but bus_topo_lock should block any + * concurrent device_delete_child invocations. + */ + dev = sc->dev; + error = device_delete_child(root_bus, dev); + if (error != 0) + device_printf(dev, + "failed to detach after controller loss: %d\n", error); + bus_topo_unlock(); +} + +static void +nvmf_request_reconnect(struct nvmf_softc *sc) +{ + char buf[64]; + + sx_assert(&sc->connection_lock, SX_LOCKED); + + snprintf(buf, sizeof(buf), "name=\"%s\"", device_get_nameunit(sc->dev)); + devctl_notify("nvme", "controller", "RECONNECT", buf); + taskqueue_enqueue_timeout(nvmf_tq, &sc->request_reconnect_task, + sc->reconnect_delay * hz); +} + +static void +nvmf_request_reconnect_task(void *arg, int pending) +{ + struct nvmf_softc *sc = arg; + + sx_xlock(&sc->connection_lock); + if (sc->admin != NULL || sc->detaching || sc->controller_timedout) { + /* Reconnected or already detaching. */ + sx_xunlock(&sc->connection_lock); + return; + } + + nvmf_request_reconnect(sc); sx_xunlock(&sc->connection_lock); } @@ -699,7 +780,7 @@ nvmf_reconnect_host(struct nvmf_softc *sc, struct nvmf_ioc_nv *nv) } sx_xlock(&sc->connection_lock); - if (sc->admin != NULL || sc->detaching) { + if (sc->admin != NULL || sc->detaching || sc->controller_timedout) { error = EBUSY; goto out; } @@ -745,6 +826,9 @@ nvmf_reconnect_host(struct nvmf_softc *sc, struct nvmf_ioc_nv *nv) nvmf_reconnect_sim(sc); nvmf_rescan_all_ns(sc); + + taskqueue_cancel_timeout(nvmf_tq, &sc->request_reconnect_task, NULL); + taskqueue_cancel_timeout(nvmf_tq, &sc->controller_loss_task, NULL); out: sx_xunlock(&sc->connection_lock); nvlist_destroy(nvl); @@ -852,7 +936,21 @@ nvmf_detach(device_t dev) } free(sc->io, M_NVMF); - taskqueue_drain(taskqueue_thread, &sc->disconnect_task); + taskqueue_drain(nvmf_tq, &sc->disconnect_task); + if (taskqueue_cancel_timeout(nvmf_tq, &sc->request_reconnect_task, + NULL) != 0) + taskqueue_drain_timeout(nvmf_tq, &sc->request_reconnect_task); + + /* + * Don't cancel/drain the controller loss task if that task + * has fired and is triggering the detach. + */ + if (!sc->controller_timedout) { + if (taskqueue_cancel_timeout(nvmf_tq, &sc->controller_loss_task, + NULL) != 0) + taskqueue_drain_timeout(nvmf_tq, + &sc->controller_loss_task); + } if (sc->admin != NULL) nvmf_destroy_qp(sc->admin); @@ -1154,14 +1252,25 @@ static struct cdevsw nvmf_cdevsw = { static int nvmf_modevent(module_t mod, int what, void *arg) { + int error; + switch (what) { case MOD_LOAD: - return (nvmf_ctl_load()); + error = nvmf_ctl_load(); + if (error != 0) + return (error); + + nvmf_tq = taskqueue_create("nvmf", M_WAITOK | M_ZERO, + taskqueue_thread_enqueue, &nvmf_tq); + taskqueue_start_threads(&nvmf_tq, 1, PWAIT, "nvmf taskq"); + return (0); case MOD_QUIESCE: return (0); case MOD_UNLOAD: nvmf_ctl_unload(); destroy_dev_drain(&nvmf_cdevsw); + if (nvmf_tq != NULL) + taskqueue_free(nvmf_tq); return (0); default: return (EOPNOTSUPP); diff --git a/sys/dev/nvmf/host/nvmf_var.h b/sys/dev/nvmf/host/nvmf_var.h index e45a31f413a4..606245b3969c 100644 --- a/sys/dev/nvmf/host/nvmf_var.h +++ b/sys/dev/nvmf/host/nvmf_var.h @@ -75,9 +75,15 @@ struct nvmf_softc { struct callout ka_rx_timer; sbintime_t ka_rx_sbt; + struct timeout_task request_reconnect_task; + struct timeout_task controller_loss_task; + uint32_t reconnect_delay; + uint32_t controller_loss_timeout; + struct sx connection_lock; struct task disconnect_task; bool detaching; + bool controller_timedout; u_int num_aer; struct nvmf_aer *aer; diff --git a/sys/dev/nvmf/nvmf.h b/sys/dev/nvmf/nvmf.h index d4e7b1511e9d..9b2b4c1dea40 100644 --- a/sys/dev/nvmf/nvmf.h +++ b/sys/dev/nvmf/nvmf.h @@ -27,6 +27,13 @@ #define NVMF_NN (1024) /* + * Default timeouts for Fabrics hosts. These match values used by + * Linux. + */ +#define NVMF_DEFAULT_RECONNECT_DELAY 10 +#define NVMF_DEFAULT_CONTROLLER_LOSS 600 + +/* * (data, size) is the userspace buffer for a packed nvlist. * * For requests that copyout an nvlist, len is the amount of data @@ -68,6 +75,8 @@ struct nvmf_ioc_nv { * * number trtype * number kato (optional) + * number reconnect_delay (optional) + * number controller_loss_timeout (optional) * qpair handoff nvlist admin * qpair handoff nvlist array io * binary cdata struct nvme_controller_data @@ -81,6 +90,8 @@ struct nvmf_ioc_nv { * string hostnqn * number num_io_queues * number kato (optional) + * number reconnect_delay (optional) + * number controller_loss_timeout (optional) * number io_qsize * bool sq_flow_control * diff --git a/sys/fs/nfsclient/nfs_clvnops.c b/sys/fs/nfsclient/nfs_clvnops.c index fbfcdafaa06b..fa451887e73e 100644 --- a/sys/fs/nfsclient/nfs_clvnops.c +++ b/sys/fs/nfsclient/nfs_clvnops.c @@ -1096,12 +1096,11 @@ nfs_setattr(struct vop_setattr_args *ap) /* * Disallow write attempts if the filesystem is mounted read-only. */ - if ((vap->va_flags != VNOVAL || vap->va_uid != (uid_t)VNOVAL || + if ((vap->va_flags != (u_long)VNOVAL || vap->va_uid != (uid_t)VNOVAL || vap->va_gid != (gid_t)VNOVAL || vap->va_atime.tv_sec != VNOVAL || vap->va_mtime.tv_sec != VNOVAL || vap->va_birthtime.tv_sec != VNOVAL || - vap->va_mode != (mode_t)VNOVAL || - vap->va_flags != (u_long)VNOVAL) && + vap->va_mode != (mode_t)VNOVAL) && (vp->v_mount->mnt_flag & MNT_RDONLY)) return (EROFS); if (vap->va_size != VNOVAL) { diff --git a/sys/fs/nfsserver/nfs_nfsdport.c b/sys/fs/nfsserver/nfs_nfsdport.c index a81f1492ef95..43ee0383669f 100644 --- a/sys/fs/nfsserver/nfs_nfsdport.c +++ b/sys/fs/nfsserver/nfs_nfsdport.c @@ -1652,10 +1652,11 @@ nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp, } if (fvp == tvp) { /* - * If source and destination are the same, there is nothing to - * do. Set error to -1 to indicate this. + * If source and destination are the same, there is + * nothing to do. Set error to EJUSTRETURN to indicate + * this. */ - error = -1; + error = EJUSTRETURN; goto out; } if (nd->nd_flag & ND_NFSV4) { @@ -1697,10 +1698,26 @@ nfsvno_rename(struct nameidata *fromndp, struct nameidata *tondp, " dsdvp=%p\n", dsdvp[0]); } out: - if (!error) { + mp = NULL; + if (error == 0) { + error = VOP_GETWRITEMOUNT(tondp->ni_dvp, &mp); + if (error == 0) { + if (mp == NULL) { + error = ENOENT; + } else { + error = lockmgr(&mp->mnt_renamelock, + LK_EXCLUSIVE | LK_NOWAIT, NULL); + if (error != 0) + error = ERELOOKUP; + } + } + } + if (error == 0) { error = VOP_RENAME(fromndp->ni_dvp, fromndp->ni_vp, &fromndp->ni_cnd, tondp->ni_dvp, tondp->ni_vp, &tondp->ni_cnd); + lockmgr(&mp->mnt_renamelock, LK_RELEASE, 0); + vfs_rel(mp); } else { if (tdvp == tvp) vrele(tdvp); @@ -1710,8 +1727,13 @@ out: vput(tvp); vrele(fromndp->ni_dvp); vrele(fvp); - if (error == -1) + if (error == EJUSTRETURN) { error = 0; + } else if (error == ERELOOKUP && mp != NULL) { + lockmgr(&mp->mnt_renamelock, LK_EXCLUSIVE, 0); + lockmgr(&mp->mnt_renamelock, LK_RELEASE, 0); + vfs_rel(mp); + } } /* diff --git a/sys/kern/subr_pctrie.c b/sys/kern/subr_pctrie.c index 3a3548bad52b..bb86c779b936 100644 --- a/sys/kern/subr_pctrie.c +++ b/sys/kern/subr_pctrie.c @@ -691,21 +691,23 @@ _pctrie_lookup_ge(struct pctrie *ptree, struct pctrie_node *node, */ if (node == PCTRIE_NULL || *pctrie_toval(node) < index) { /* Climb the path to find a node with a descendant > index. */ - for (node = parent; node != NULL; node = pctrie_parent(node)) { - slot = pctrie_slot(node, index) + 1; - if ((node->pn_popmap >> slot) != 0) + node = NULL; + while (parent != NULL) { + slot = pctrie_slot(parent, index) + 1; + if ((parent->pn_popmap >> slot) != 0) break; + node = parent; + parent = pctrie_parent(node); } - if (node == NULL) { + if (parent == NULL) { if (parent_out != NULL) - *parent_out = NULL; + *parent_out = node; return (NULL); } /* Step to the least child with a descendant > index. */ - slot += ffs(node->pn_popmap >> slot) - 1; - parent = node; - node = pctrie_node_load(&node->pn_child[slot], NULL, + slot += ffs(parent->pn_popmap >> slot) - 1; + node = pctrie_node_load(&parent->pn_child[slot], NULL, PCTRIE_LOCKED); } /* Descend to the least leaf of the subtrie. */ @@ -785,21 +787,23 @@ _pctrie_lookup_le(struct pctrie *ptree, struct pctrie_node *node, */ if (node == PCTRIE_NULL || *pctrie_toval(node) > index) { /* Climb the path to find a node with a descendant < index. */ - for (node = parent; node != NULL; node = pctrie_parent(node)) { - slot = pctrie_slot(node, index); - if ((node->pn_popmap & ((1 << slot) - 1)) != 0) + node = NULL; + while (parent != NULL) { + slot = pctrie_slot(parent, index); + if ((parent->pn_popmap & ((1 << slot) - 1)) != 0) break; + node = parent; + parent = pctrie_parent(node); } - if (node == NULL) { + if (parent == NULL) { if (parent_out != NULL) - *parent_out = NULL; + *parent_out = node; return (NULL); } /* Step to the greatest child with a descendant < index. */ - slot = ilog2(node->pn_popmap & ((1 << slot) - 1)); - parent = node; - node = pctrie_node_load(&node->pn_child[slot], NULL, + slot = ilog2(parent->pn_popmap & ((1 << slot) - 1)); + node = pctrie_node_load(&parent->pn_child[slot], NULL, PCTRIE_LOCKED); } /* Descend to the greatest leaf of the subtrie. */ diff --git a/sys/kern/vfs_inotify.c b/sys/kern/vfs_inotify.c index 41e73bb41a49..2b42228465a4 100644 --- a/sys/kern/vfs_inotify.c +++ b/sys/kern/vfs_inotify.c @@ -760,9 +760,11 @@ vn_inotify_add_watch(struct vnode *vp, struct inotify_softc *sc, uint32_t mask, * directory if it's specified as a vnode. */ vrefact(vp); + VOP_UNLOCK(vp); NDINIT_ATVP(&nd, LOOKUP, NOFOLLOW, UIO_SYSSPACE, dp->d_name, vp); error = namei(&nd); + vn_lock(vp, LK_SHARED | LK_RETRY); if (error != 0) break; vn_irflag_set_cond(nd.ni_vp, VIRF_INOTIFY_PARENT); diff --git a/sys/kern/vfs_syscalls.c b/sys/kern/vfs_syscalls.c index d880733cbfe7..c71e0d9ee569 100644 --- a/sys/kern/vfs_syscalls.c +++ b/sys/kern/vfs_syscalls.c @@ -4314,10 +4314,6 @@ kern_getdirentries(struct thread *td, int fd, char *buf, size_t count, vp = fp->f_vnode; foffset = foffset_lock(fp, 0); unionread: - if (vp->v_type != VDIR) { - error = EINVAL; - goto fail; - } if (__predict_false((vp->v_vflag & VV_UNLINKED) != 0)) { error = ENOENT; goto fail; @@ -4330,6 +4326,19 @@ unionread: auio.uio_segflg = bufseg; auio.uio_td = td; vn_lock(vp, LK_SHARED | LK_RETRY); + /* + * We want to return ENOTDIR for anything that is not VDIR, but + * not for VBAD, and we can't check for VBAD while the vnode is + * unlocked. + */ + if (vp->v_type != VDIR) { + if (vp->v_type == VBAD) + error = EBADF; + else + error = ENOTDIR; + VOP_UNLOCK(vp); + goto fail; + } AUDIT_ARG_VNODE1(vp); loff = auio.uio_offset = foffset; #ifdef MAC diff --git a/sys/net/pfvar.h b/sys/net/pfvar.h index 1416f0c2cdbe..36fab1a03ee6 100644 --- a/sys/net/pfvar.h +++ b/sys/net/pfvar.h @@ -551,6 +551,9 @@ extern struct sx pf_end_lock; #endif /* PF_INET_INET6 */ #ifdef _KERNEL + +void unhandled_af(int) __dead2; + static void inline pf_addrcpy(struct pf_addr *dst, const struct pf_addr *src, sa_family_t af) { @@ -565,6 +568,8 @@ pf_addrcpy(struct pf_addr *dst, const struct pf_addr *src, sa_family_t af) memcpy(&dst->v6, &src->v6, sizeof(dst->v6)); break; #endif /* INET6 */ + default: + unhandled_af(af); } } #endif @@ -2300,7 +2305,6 @@ VNET_DECLARE(struct pf_krule *, pf_rulemarker); #define V_pf_rulemarker VNET(pf_rulemarker) #endif -void unhandled_af(int) __dead2; int pf_start(void); int pf_stop(void); void pf_initialize(void); @@ -2670,6 +2674,7 @@ int pf_ioctl_get_addrs(struct pf_nl_pooladdr *); int pf_ioctl_get_addr(struct pf_nl_pooladdr *); int pf_ioctl_get_rulesets(struct pfioc_ruleset *); int pf_ioctl_get_ruleset(struct pfioc_ruleset *); +int pf_ioctl_natlook(struct pfioc_natlook *); void pf_krule_free(struct pf_krule *); void pf_krule_clear_counters(struct pf_krule *); diff --git a/sys/netinet/in_pcb.c b/sys/netinet/in_pcb.c index bccd4b84561a..dbe48242381d 100644 --- a/sys/netinet/in_pcb.c +++ b/sys/netinet/in_pcb.c @@ -1745,6 +1745,23 @@ in_pcbrele(struct inpcb *inp, const inp_lookup_t lock) } /* + * Dereference and rlock inp, for which the caller must own the + * reference. Returns true if inp no longer usable, false otherwise. + */ +bool +in_pcbrele_rlock(struct inpcb *inp) +{ + INP_RLOCK(inp); + if (in_pcbrele_rlocked(inp)) + return (true); + if ((inp->inp_flags & INP_FREED) != 0) { + INP_RUNLOCK(inp); + return (true); + } + return (false); +} + +/* * Unconditionally schedule an inpcb to be freed by decrementing its * reference count, which should occur only after the inpcb has been detached * from its socket. If another thread holds a temporary reference (acquired diff --git a/sys/netinet/in_pcb.h b/sys/netinet/in_pcb.h index 57cf15ca37fc..9e0618e87601 100644 --- a/sys/netinet/in_pcb.h +++ b/sys/netinet/in_pcb.h @@ -681,6 +681,7 @@ void in_pcbref(struct inpcb *); bool in_pcbrele(struct inpcb *, inp_lookup_t); bool in_pcbrele_rlocked(struct inpcb *); bool in_pcbrele_wlocked(struct inpcb *); +bool in_pcbrele_rlock(struct inpcb *inp); typedef bool inp_match_t(const struct inpcb *, void *); struct inpcb_iterator { diff --git a/sys/netinet/tcp_subr.c b/sys/netinet/tcp_subr.c index cd42a67294a6..db415f6bdf03 100644 --- a/sys/netinet/tcp_subr.c +++ b/sys/netinet/tcp_subr.c @@ -2720,9 +2720,15 @@ tcp_ktlslist_locked(SYSCTL_HANDLER_ARGS, bool export_keys) ksr->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; - ksr->snd_tag->sw->snd_tag_status_str( + in_pcbref(inp); + INP_RUNLOCK(inp); + error = ksr->snd_tag->sw-> + snd_tag_status_str( ksr->snd_tag, NULL, &sz); - len += sz; + if (in_pcbrele_rlock(inp)) + return (EDEADLK); + if (error == 0) + len += sz; } } kss = so->so_snd.sb_tls_info; @@ -2739,9 +2745,15 @@ tcp_ktlslist_locked(SYSCTL_HANDLER_ARGS, bool export_keys) kss->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; - kss->snd_tag->sw->snd_tag_status_str( + in_pcbref(inp); + INP_RUNLOCK(inp); + error = kss->snd_tag->sw-> + snd_tag_status_str( kss->snd_tag, NULL, &sz); - len += sz; + if (in_pcbrele_rlock(inp)) + return (EDEADLK); + if (error == 0) + len += sz; } } if (p) { @@ -2811,9 +2823,16 @@ tcp_ktlslist_locked(SYSCTL_HANDLER_ARGS, bool export_keys) if (ksr->snd_tag != NULL && ksr->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; - ksr->snd_tag->sw->snd_tag_status_str( + in_pcbref(inp); + INP_RUNLOCK(inp); + error = ksr->snd_tag->sw->snd_tag_status_str( ksr->snd_tag, buf + len, &sz); - len += sz; + if (in_pcbrele_rlock(inp)) + return (EDEADLK); + if (error == 0) { + xktls->rcv.drv_st_len = sz; + len += sz; + } } } if (kss != NULL && kss->gen == xig.xig_gen) { @@ -2828,9 +2847,16 @@ tcp_ktlslist_locked(SYSCTL_HANDLER_ARGS, bool export_keys) if (kss->snd_tag != NULL && kss->snd_tag->sw->snd_tag_status_str != NULL) { sz = SND_TAG_STATUS_MAXLEN; - kss->snd_tag->sw->snd_tag_status_str( + in_pcbref(inp); + INP_RUNLOCK(inp); + error = kss->snd_tag->sw->snd_tag_status_str( kss->snd_tag, buf + len, &sz); - len += sz; + if (in_pcbrele_rlock(inp)) + return (EDEADLK); + if (error == 0) { + xktls->snd.drv_st_len = sz; + len += sz; + } } } len = roundup2(len, __alignof(*xktls)); @@ -2858,12 +2884,23 @@ tcp_ktlslist_locked(SYSCTL_HANDLER_ARGS, bool export_keys) static int tcp_ktlslist1(SYSCTL_HANDLER_ARGS, bool export_keys) { - int res; - - sx_xlock(&ktlslist_lock); - res = tcp_ktlslist_locked(oidp, arg1, arg2, req, export_keys); - sx_xunlock(&ktlslist_lock); - return (res); + int repeats, error; + + for (repeats = 0; repeats < 100; repeats++) { + if (sx_xlock_sig(&ktlslist_lock)) + return (EINTR); + error = tcp_ktlslist_locked(oidp, arg1, arg2, req, + export_keys); + sx_xunlock(&ktlslist_lock); + if (error != EDEADLK) + break; + if (sig_intr() != 0) { + error = EINTR; + break; + } + req->oldidx = 0; + } + return (error); } static int diff --git a/sys/netpfil/pf/if_pflog.c b/sys/netpfil/pf/if_pflog.c index 6a87ea2471cb..0a84f9d680ac 100644 --- a/sys/netpfil/pf/if_pflog.c +++ b/sys/netpfil/pf/if_pflog.c @@ -289,7 +289,7 @@ pflog_packet(uint8_t action, u_int8_t reason, if (pd->lookup.done > 0) hdr.uid = pd->lookup.uid; else - hdr.uid = UID_MAX; + hdr.uid = -1; hdr.pid = NO_PID; hdr.rule_uid = rm->cuid; hdr.rule_pid = rm->cpid; diff --git a/sys/netpfil/pf/if_pfsync.c b/sys/netpfil/pf/if_pfsync.c index fdedb9424117..2391edaf1a5a 100644 --- a/sys/netpfil/pf/if_pfsync.c +++ b/sys/netpfil/pf/if_pfsync.c @@ -763,6 +763,10 @@ pfsync_state_import(union pfsync_state_union *sp, int flags, int msg_version) __func__, msg_version); } + if (! (st->act.rtableid == -1 || + (st->act.rtableid >= 0 && st->act.rtableid < rt_numfibs))) + goto cleanup; + st->id = sp->pfs_1301.id; st->creatorid = sp->pfs_1301.creatorid; pf_state_peer_ntoh(&sp->pfs_1301.src, &st->src); @@ -1083,7 +1087,7 @@ pfsync_in_ins(struct mbuf *m, int offset, int count, int flags, int action) msg_version = PFSYNC_MSG_VERSION_1400; break; default: - V_pfsyncstats.pfsyncs_badact++; + V_pfsyncstats.pfsyncs_badver++; return (-1); } @@ -1110,9 +1114,8 @@ pfsync_in_ins(struct mbuf *m, int offset, int count, int flags, int action) continue; } - if (pfsync_state_import(sp, flags, msg_version) == ENOMEM) - /* Drop out, but process the rest of the actions. */ - break; + if (pfsync_state_import(sp, flags, msg_version) != 0) + V_pfsyncstats.pfsyncs_badact++; } return (total_len); diff --git a/sys/netpfil/pf/pf.c b/sys/netpfil/pf/pf.c index 127b29320acb..d5f01e5c4956 100644 --- a/sys/netpfil/pf/pf.c +++ b/sys/netpfil/pf/pf.c @@ -4579,7 +4579,7 @@ pf_match_port(u_int8_t op, u_int16_t a1, u_int16_t a2, u_int16_t p) static int pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) { - if (u == UID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + if (u == -1 && op != PF_OP_EQ && op != PF_OP_NE) return (0); return (pf_match(op, a1, a2, u)); } @@ -4587,7 +4587,7 @@ pf_match_uid(u_int8_t op, uid_t a1, uid_t a2, uid_t u) static int pf_match_gid(u_int8_t op, gid_t a1, gid_t a2, gid_t g) { - if (g == GID_MAX && op != PF_OP_EQ && op != PF_OP_NE) + if (g == -1 && op != PF_OP_EQ && op != PF_OP_NE) return (0); return (pf_match(op, a1, a2, g)); } @@ -4914,8 +4914,8 @@ pf_socket_lookup(struct pf_pdesc *pd) struct inpcbinfo *pi; struct inpcb *inp; - pd->lookup.uid = UID_MAX; - pd->lookup.gid = GID_MAX; + pd->lookup.uid = -1; + pd->lookup.gid = -1; switch (pd->proto) { case IPPROTO_TCP: diff --git a/sys/netpfil/pf/pf_ioctl.c b/sys/netpfil/pf/pf_ioctl.c index 357b2be194a5..c96741023db9 100644 --- a/sys/netpfil/pf/pf_ioctl.c +++ b/sys/netpfil/pf/pf_ioctl.c @@ -2155,51 +2155,51 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, rule->rcv_kif = NULL; if (rule->rtableid > 0 && rule->rtableid >= rt_numfibs) - error = EBUSY; + ERROUT(EBUSY); #ifdef ALTQ /* set queue IDs */ if (rule->qname[0] != 0) { if ((rule->qid = pf_qname2qid(rule->qname)) == 0) - error = EBUSY; + ERROUT(EBUSY); else if (rule->pqname[0] != 0) { if ((rule->pqid = pf_qname2qid(rule->pqname)) == 0) - error = EBUSY; + ERROUT(EBUSY); } else rule->pqid = rule->qid; } #endif if (rule->tagname[0]) if ((rule->tag = pf_tagname2tag(rule->tagname)) == 0) - error = EBUSY; + ERROUT(EBUSY); if (rule->match_tagname[0]) if ((rule->match_tag = pf_tagname2tag(rule->match_tagname)) == 0) - error = EBUSY; + ERROUT(EBUSY); if (rule->rt && !rule->direction) - error = EINVAL; + ERROUT(EINVAL); if (!rule->log) rule->logif = 0; if (! pf_init_threshold(&rule->pktrate, rule->pktrate.limit, rule->pktrate.seconds)) - error = ENOMEM; + ERROUT(ENOMEM); if (pf_addr_setup(ruleset, &rule->src.addr, rule->af)) - error = ENOMEM; + ERROUT(ENOMEM); if (pf_addr_setup(ruleset, &rule->dst.addr, rule->af)) - error = ENOMEM; + ERROUT(ENOMEM); if (pf_kanchor_setup(rule, ruleset, anchor_call)) - error = EINVAL; + ERROUT(EINVAL); if (rule->scrub_flags & PFSTATE_SETPRIO && (rule->set_prio[0] > PF_PRIO_MAX || rule->set_prio[1] > PF_PRIO_MAX)) - error = EINVAL; + ERROUT(EINVAL); for (int i = 0; i < 3; i++) { TAILQ_FOREACH(pa, &V_pf_pabuf[i], entries) if (pa->addr.type == PF_ADDR_TABLE) { pa->addr.p.tbl = pfr_attach_table(ruleset, pa->addr.v.tblname); if (pa->addr.p.tbl == NULL) - error = ENOMEM; + ERROUT(ENOMEM); } } @@ -2207,7 +2207,7 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, if (rule->overload_tblname[0]) { if ((rule->overload_tbl = pfr_attach_table(ruleset, rule->overload_tblname)) == NULL) - error = EINVAL; + ERROUT(EINVAL); else rule->overload_tbl->pfrkt_flags |= PFR_TFLAG_ACTIVE; @@ -2230,23 +2230,19 @@ pf_ioctl_addrule(struct pf_krule *rule, uint32_t ticket, if (((rule->action == PF_NAT) || (rule->action == PF_RDR) || (rule->action == PF_BINAT)) && rule->anchor == NULL && TAILQ_FIRST(&rule->rdr.list) == NULL) { - error = EINVAL; + ERROUT(EINVAL); } if (rule->rt > PF_NOPFROUTE && (TAILQ_FIRST(&rule->route.list) == NULL)) { - error = EINVAL; + ERROUT(EINVAL); } if (rule->action == PF_PASS && (rule->rdr.opts & PF_POOL_STICKYADDR || rule->nat.opts & PF_POOL_STICKYADDR) && !rule->keep_state) { - error = EINVAL; + ERROUT(EINVAL); } - if (error) { - pf_free_rule(rule); - rule = NULL; - ERROUT(error); - } + MPASS(error == 0); rule->nat.cur = TAILQ_FIRST(&rule->nat.list); rule->rdr.cur = TAILQ_FIRST(&rule->rdr.list); @@ -2699,7 +2695,7 @@ pf_ioctl_get_addr(struct pf_nl_pooladdr *pp) PF_RULES_RLOCK_TRACKER; - pp->anchor[sizeof(pp->anchor) - 1] = 0; + pp->anchor[sizeof(pp->anchor) - 1] = '\0'; PF_RULES_RLOCK(); pool = pf_get_kpool(pp->anchor, pp->ticket, pp->r_action, @@ -2732,7 +2728,7 @@ pf_ioctl_get_rulesets(struct pfioc_ruleset *pr) PF_RULES_RLOCK_TRACKER; - pr->path[sizeof(pr->path) - 1] = 0; + pr->path[sizeof(pr->path) - 1] = '\0'; PF_RULES_RLOCK(); if ((ruleset = pf_find_kruleset(pr->path)) == NULL) { @@ -2771,7 +2767,7 @@ pf_ioctl_get_ruleset(struct pfioc_ruleset *pr) return (ENOENT); } - pr->name[0] = 0; + pr->name[0] = '\0'; if (ruleset == &pf_main_ruleset) { /* XXX kludge for pf_main_ruleset */ RB_FOREACH(anchor, pf_kanchor_global, &V_pf_anchors) @@ -2796,6 +2792,78 @@ pf_ioctl_get_ruleset(struct pfioc_ruleset *pr) return (error); } +int +pf_ioctl_natlook(struct pfioc_natlook *pnl) +{ + struct pf_state_key *sk; + struct pf_kstate *state; + struct pf_state_key_cmp key; + int m = 0, direction = pnl->direction; + int sidx, didx; + + /* NATLOOK src and dst are reversed, so reverse sidx/didx */ + sidx = (direction == PF_IN) ? 1 : 0; + didx = (direction == PF_IN) ? 0 : 1; + + if (!pnl->proto || + PF_AZERO(&pnl->saddr, pnl->af) || + PF_AZERO(&pnl->daddr, pnl->af) || + ((pnl->proto == IPPROTO_TCP || + pnl->proto == IPPROTO_UDP) && + (!pnl->dport || !pnl->sport))) + return (EINVAL); + + switch (pnl->direction) { + case PF_IN: + case PF_OUT: + case PF_INOUT: + break; + default: + return (EINVAL); + } + + switch (pnl->af) { +#ifdef INET + case AF_INET: + break; +#endif /* INET */ +#ifdef INET6 + case AF_INET6: + break; +#endif /* INET6 */ + default: + return (EAFNOSUPPORT); + } + + bzero(&key, sizeof(key)); + key.af = pnl->af; + key.proto = pnl->proto; + pf_addrcpy(&key.addr[sidx], &pnl->saddr, pnl->af); + key.port[sidx] = pnl->sport; + pf_addrcpy(&key.addr[didx], &pnl->daddr, pnl->af); + key.port[didx] = pnl->dport; + + state = pf_find_state_all(&key, direction, &m); + if (state == NULL) + return (ENOENT); + + if (m > 1) { + PF_STATE_UNLOCK(state); + return (E2BIG); /* more than one state */ + } + + sk = state->key[sidx]; + pf_addrcpy(&pnl->rsaddr, + &sk->addr[sidx], sk->af); + pnl->rsport = sk->port[sidx]; + pf_addrcpy(&pnl->rdaddr, + &sk->addr[didx], sk->af); + pnl->rdport = sk->port[didx]; + PF_STATE_UNLOCK(state); + + return (0); +} + static int pfioctl(struct cdev *dev, u_long cmd, caddr_t addr, int flags, struct thread *td) { @@ -3502,7 +3570,7 @@ DIOCADDRULENV_error: break; } - pr->anchor[sizeof(pr->anchor) - 1] = 0; + pr->anchor[sizeof(pr->anchor) - 1] = '\0'; /* Frees rule on error */ error = pf_ioctl_addrule(rule, pr->ticket, pr->pool_ticket, @@ -3514,7 +3582,7 @@ DIOCADDRULENV_error: case DIOCGETRULES: { struct pfioc_rule *pr = (struct pfioc_rule *)addr; - pr->anchor[sizeof(pr->anchor) - 1] = 0; + pr->anchor[sizeof(pr->anchor) - 1] = '\0'; error = pf_ioctl_getrules(pr); @@ -3653,7 +3721,7 @@ DIOCGETRULENV_error: u_int32_t nr = 0; int rs_num; - pcr->anchor[sizeof(pcr->anchor) - 1] = 0; + pcr->anchor[sizeof(pcr->anchor) - 1] = '\0'; if (pcr->action < PF_CHANGE_ADD_HEAD || pcr->action > PF_CHANGE_GET_TICKET) { @@ -4133,51 +4201,8 @@ DIOCGETSTATESV2_full: case DIOCNATLOOK: { struct pfioc_natlook *pnl = (struct pfioc_natlook *)addr; - struct pf_state_key *sk; - struct pf_kstate *state; - struct pf_state_key_cmp key; - int m = 0, direction = pnl->direction; - int sidx, didx; - - /* NATLOOK src and dst are reversed, so reverse sidx/didx */ - sidx = (direction == PF_IN) ? 1 : 0; - didx = (direction == PF_IN) ? 0 : 1; - - if (!pnl->proto || - PF_AZERO(&pnl->saddr, pnl->af) || - PF_AZERO(&pnl->daddr, pnl->af) || - ((pnl->proto == IPPROTO_TCP || - pnl->proto == IPPROTO_UDP) && - (!pnl->dport || !pnl->sport))) - error = EINVAL; - else { - bzero(&key, sizeof(key)); - key.af = pnl->af; - key.proto = pnl->proto; - pf_addrcpy(&key.addr[sidx], &pnl->saddr, pnl->af); - key.port[sidx] = pnl->sport; - pf_addrcpy(&key.addr[didx], &pnl->daddr, pnl->af); - key.port[didx] = pnl->dport; - - state = pf_find_state_all(&key, direction, &m); - if (state == NULL) { - error = ENOENT; - } else { - if (m > 1) { - PF_STATE_UNLOCK(state); - error = E2BIG; /* more than one state */ - } else { - sk = state->key[sidx]; - pf_addrcpy(&pnl->rsaddr, - &sk->addr[sidx], sk->af); - pnl->rsport = sk->port[sidx]; - pf_addrcpy(&pnl->rdaddr, - &sk->addr[didx], sk->af); - pnl->rdport = sk->port[didx]; - PF_STATE_UNLOCK(state); - } - } - } + + error = pf_ioctl_natlook(pnl); break; } @@ -4498,7 +4523,7 @@ DIOCGETSTATESV2_full: struct pf_kruleset *ruleset; struct pfi_kkif *kif = NULL; - pca->anchor[sizeof(pca->anchor) - 1] = 0; + pca->anchor[sizeof(pca->anchor) - 1] = '\0'; if (pca->action < PF_CHANGE_ADD_HEAD || pca->action > PF_CHANGE_REMOVE) { @@ -4629,7 +4654,7 @@ DIOCCHANGEADDR_error: case DIOCGETRULESETS: { struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; - pr->path[sizeof(pr->path) - 1] = 0; + pr->path[sizeof(pr->path) - 1] = '\0'; error = pf_ioctl_get_rulesets(pr); break; @@ -4638,7 +4663,7 @@ DIOCCHANGEADDR_error: case DIOCGETRULESET: { struct pfioc_ruleset *pr = (struct pfioc_ruleset *)addr; - pr->path[sizeof(pr->path) - 1] = 0; + pr->path[sizeof(pr->path) - 1] = '\0'; error = pf_ioctl_get_ruleset(pr); break; @@ -5362,7 +5387,7 @@ DIOCCHANGEADDR_error: PF_RULES_WLOCK(); /* First makes sure everything will succeed. */ for (i = 0, ioe = ioes; i < io->size; i++, ioe++) { - ioe->anchor[sizeof(ioe->anchor) - 1] = 0; + ioe->anchor[sizeof(ioe->anchor) - 1] = '\0'; switch (ioe->rs_num) { case PF_RULESET_ETH: ers = pf_find_keth_ruleset(ioe->anchor); diff --git a/sys/netpfil/pf/pf_nl.c b/sys/netpfil/pf/pf_nl.c index d5d6dc70255e..73933c022ca2 100644 --- a/sys/netpfil/pf/pf_nl.c +++ b/sys/netpfil/pf/pf_nl.c @@ -1256,23 +1256,13 @@ pf_handle_clear_status(struct nlmsghdr *hdr, struct nl_pstate *npt) return (0); } -struct pf_nl_natlook { - sa_family_t af; - uint8_t direction; - uint8_t proto; - struct pf_addr src; - struct pf_addr dst; - uint16_t sport; - uint16_t dport; -}; - -#define _OUT(_field) offsetof(struct pf_nl_natlook, _field) +#define _OUT(_field) offsetof(struct pfioc_natlook, _field) static const struct nlattr_parser nla_p_natlook[] = { { .type = PF_NL_AF, .off = _OUT(af), .cb = nlattr_get_uint8 }, { .type = PF_NL_DIRECTION, .off = _OUT(direction), .cb = nlattr_get_uint8 }, { .type = PF_NL_PROTO, .off = _OUT(proto), .cb = nlattr_get_uint8 }, - { .type = PF_NL_SRC_ADDR, .off = _OUT(src), .cb = nlattr_get_in6_addr }, - { .type = PF_NL_DST_ADDR, .off = _OUT(dst), .cb = nlattr_get_in6_addr }, + { .type = PF_NL_SRC_ADDR, .off = _OUT(saddr), .cb = nlattr_get_in6_addr }, + { .type = PF_NL_DST_ADDR, .off = _OUT(daddr), .cb = nlattr_get_in6_addr }, { .type = PF_NL_SRC_PORT, .off = _OUT(sport), .cb = nlattr_get_uint16 }, { .type = PF_NL_DST_PORT, .off = _OUT(dport), .cb = nlattr_get_uint16 }, }; @@ -1282,63 +1272,31 @@ NL_DECLARE_PARSER(natlook_parser, struct genlmsghdr, nlf_p_empty, nla_p_natlook) static int pf_handle_natlook(struct nlmsghdr *hdr, struct nl_pstate *npt) { - struct pf_nl_natlook attrs = {}; - struct pf_state_key_cmp key = {}; + struct pfioc_natlook attrs = {}; struct nl_writer *nw = npt->nw; - struct pf_state_key *sk; - struct pf_kstate *state; struct genlmsghdr *ghdr_new; - int error, m = 0; - int sidx, didx; + int error; error = nl_parse_nlmsg(hdr, &natlook_parser, npt, &attrs); if (error != 0) return (error); - if (attrs.proto == 0 || - PF_AZERO(&attrs.src, attrs.af) || - PF_AZERO(&attrs.dst, attrs.af) || - ((attrs.proto == IPPROTO_TCP || attrs.proto == IPPROTO_UDP) && - (attrs.sport == 0 || attrs.dport == 0))) - return (EINVAL); - - /* NATLOOK src and dst are reversed, so reverse sidx/didx */ - sidx = (attrs.direction == PF_IN) ? 1 : 0; - didx = (attrs.direction == PF_IN) ? 0 : 1; - - key.af = attrs.af; - key.proto = attrs.proto; - pf_addrcpy(&key.addr[sidx], &attrs.src, attrs.af); - key.port[sidx] = attrs.sport; - pf_addrcpy(&key.addr[didx], &attrs.dst, attrs.af); - key.port[didx] = attrs.dport; - - state = pf_find_state_all(&key, attrs.direction, &m); - if (state == NULL) - return (ENOENT); - if (m > 1) { - PF_STATE_UNLOCK(state); - return (E2BIG); - } + error = pf_ioctl_natlook(&attrs); + if (error != 0) + return (error); - if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) { - PF_STATE_UNLOCK(state); + if (!nlmsg_reply(nw, hdr, sizeof(struct genlmsghdr))) return (ENOMEM); - } ghdr_new = nlmsg_reserve_object(nw, struct genlmsghdr); ghdr_new->cmd = PFNL_CMD_NATLOOK; ghdr_new->version = 0; ghdr_new->reserved = 0; - sk = state->key[sidx]; - - nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &sk->addr[sidx].v6); - nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &sk->addr[didx].v6); - nlattr_add_u16(nw, PF_NL_SRC_PORT, sk->port[sidx]); - nlattr_add_u16(nw, PF_NL_DST_PORT, sk->port[didx]); - - PF_STATE_UNLOCK(state); + nlattr_add_in6_addr(nw, PF_NL_SRC_ADDR, &attrs.rsaddr.v6); + nlattr_add_in6_addr(nw, PF_NL_DST_ADDR, &attrs.rdaddr.v6); + nlattr_add_u16(nw, PF_NL_SRC_PORT, attrs.rsport); + nlattr_add_u16(nw, PF_NL_DST_PORT, attrs.rdport); if (!nlmsg_end(nw)) { nlmsg_abort(nw); diff --git a/tests/atf_python/sys/net/vnet.py b/tests/atf_python/sys/net/vnet.py index 68c8ce4e0cba..7afb5c721bf3 100644 --- a/tests/atf_python/sys/net/vnet.py +++ b/tests/atf_python/sys/net/vnet.py @@ -61,6 +61,7 @@ class VnetInterface(object): self.iftype = self.IFT_LOOP else: self.iftype = self.IFT_ETHER + self.ether = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % iface_name).rstrip() @property def ifindex(self): @@ -99,9 +100,12 @@ class VnetInterface(object): name = run_cmd("/sbin/ifconfig {} create".format(iface_name)).rstrip() if not name: raise Exception("Unable to create iface {}".format(iface_name)) - ret = [cls(alias_name, name)] + if1 = cls(alias_name, name) + ret = [if1] if name.startswith("epair"): - ret.append(cls(alias_name, name[:-1] + "b")) + if2 = cls(alias_name, name[:-1] + "b") + if1.epairb = if2 + ret.append(if2); return ret def setup_addr(self, _addr: str): diff --git a/tests/sys/kern/Makefile b/tests/sys/kern/Makefile index 26c0013696c7..f2c24ad9dec9 100644 --- a/tests/sys/kern/Makefile +++ b/tests/sys/kern/Makefile @@ -18,6 +18,7 @@ ATF_TESTS_C+= kern_descrip_test # One test modifies the maxfiles limit, which can cause spurious test failures. TEST_METADATA.kern_descrip_test+= is_exclusive="true" ATF_TESTS_C+= fdgrowtable_test +ATF_TESTS_C+= getdirentries_test ATF_TESTS_C+= jail_lookup_root ATF_TESTS_C+= inotify_test ATF_TESTS_C+= kill_zombie diff --git a/tests/sys/kern/getdirentries_test.c b/tests/sys/kern/getdirentries_test.c new file mode 100644 index 000000000000..e66872ffe5b6 --- /dev/null +++ b/tests/sys/kern/getdirentries_test.c @@ -0,0 +1,172 @@ +/*- + * Copyright (c) 2025 Klara, Inc. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#include <sys/stat.h> +#include <sys/mount.h> + +#include <dirent.h> +#include <fcntl.h> +#include <errno.h> +#include <stdint.h> + +#include <atf-c.h> + +ATF_TC(getdirentries_ok); +ATF_TC_HEAD(getdirentries_ok, tc) +{ + atf_tc_set_md_var(tc, "descr", "Successfully read a directory."); +} +ATF_TC_BODY(getdirentries_ok, tc) +{ + char dbuf[4096]; + struct dirent *d; + off_t base; + ssize_t ret; + int dd, n; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); + ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_REQUIRE_EQ(base, lseek(dd, 0, SEEK_CUR)); + ATF_CHECK_EQ(0, close(dd)); + for (n = 0, d = (struct dirent *)dbuf; + d < (struct dirent *)(dbuf + ret); + d = (struct dirent *)((char *)d + d->d_reclen), n++) + /* nothing */ ; + ATF_CHECK_EQ((struct dirent *)(dbuf + ret), d); + ATF_CHECK_EQ(2, n); +} + +ATF_TC(getdirentries_ebadf); +ATF_TC_HEAD(getdirentries_ebadf, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "from an invalid descriptor."); +} +ATF_TC_BODY(getdirentries_ebadf, tc) +{ + char dbuf[4096]; + off_t base; + int fd; + + ATF_REQUIRE((fd = open("file", O_CREAT | O_WRONLY, 0644)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EBADF, errno); + ATF_REQUIRE_EQ(0, close(fd)); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EBADF, errno); +} + +ATF_TC(getdirentries_efault); +ATF_TC_HEAD(getdirentries_efault, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "to an invalid buffer."); +} +ATF_TC_BODY(getdirentries_efault, tc) +{ + char dbuf[4096]; + off_t base, *basep; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(dd, NULL, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EFAULT, errno); + basep = NULL; + basep++; + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), basep)); + ATF_CHECK_EQ(EFAULT, errno); + ATF_CHECK_EQ(0, close(dd)); +} + +ATF_TC(getdirentries_einval); +ATF_TC_HEAD(getdirentries_einval, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "with various invalid parameters."); +} +ATF_TC_BODY(getdirentries_einval, tc) +{ + struct statfs fsb; + char dbuf[4096]; + off_t base; + ssize_t ret; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(0, fstatfs(dd, &fsb)); + /* nbytes too small */ + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, 8, &base)); + ATF_CHECK_EQ(EINVAL, errno); + /* nbytes too big */ + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, SIZE_MAX, &base)); + ATF_CHECK_EQ(EINVAL, errno); + /* invalid position */ + ATF_REQUIRE((ret = getdirentries(dd, dbuf, sizeof(dbuf), &base)) > 0); + ATF_REQUIRE_EQ(0, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_REQUIRE(base > 0); + ATF_REQUIRE_EQ(base + 3, lseek(dd, 3, SEEK_CUR)); + /* known to fail on ufs (FFS2) and zfs, and work on tmpfs */ + if (strcmp(fsb.f_fstypename, "ufs") == 0 || + strcmp(fsb.f_fstypename, "zfs") == 0) { + atf_tc_expect_fail("incorrectly returns 0 instead of EINVAL " + "on %s", fsb.f_fstypename); + } + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(EINVAL, errno); + ATF_CHECK_EQ(0, close(dd)); +} + +ATF_TC(getdirentries_enoent); +ATF_TC_HEAD(getdirentries_enoent, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "after it is deleted."); +} +ATF_TC_BODY(getdirentries_enoent, tc) +{ + char dbuf[4096]; + off_t base; + int dd; + + ATF_REQUIRE_EQ(0, mkdir("dir", 0755)); + ATF_REQUIRE((dd = open("dir", O_DIRECTORY | O_RDONLY)) >= 0); + ATF_REQUIRE_EQ(0, rmdir("dir")); + ATF_REQUIRE_EQ(-1, getdirentries(dd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(ENOENT, errno); +} + +ATF_TC(getdirentries_enotdir); +ATF_TC_HEAD(getdirentries_enotdir, tc) +{ + atf_tc_set_md_var(tc, "descr", "Attempt to read a directory " + "from a descriptor not associated with a directory."); +} +ATF_TC_BODY(getdirentries_enotdir, tc) +{ + char dbuf[4096]; + off_t base; + int fd; + + ATF_REQUIRE((fd = open("file", O_CREAT | O_RDWR, 0644)) >= 0); + ATF_REQUIRE_EQ(-1, getdirentries(fd, dbuf, sizeof(dbuf), &base)); + ATF_CHECK_EQ(ENOTDIR, errno); + ATF_CHECK_EQ(0, close(fd)); +} + +ATF_TP_ADD_TCS(tp) +{ + ATF_TP_ADD_TC(tp, getdirentries_ok); + ATF_TP_ADD_TC(tp, getdirentries_ebadf); + ATF_TP_ADD_TC(tp, getdirentries_efault); + ATF_TP_ADD_TC(tp, getdirentries_einval); + ATF_TP_ADD_TC(tp, getdirentries_enoent); + ATF_TP_ADD_TC(tp, getdirentries_enotdir); + return (atf_no_error()); +} diff --git a/tests/sys/netpfil/pf/anchor.sh b/tests/sys/netpfil/pf/anchor.sh index b4b52d7a24d6..4692c06142df 100644 --- a/tests/sys/netpfil/pf/anchor.sh +++ b/tests/sys/netpfil/pf/anchor.sh @@ -437,6 +437,62 @@ quick_cleanup() pft_cleanup } +atf_test_case "recursive_flush" "cleanup" +recursive_flush_head() +{ + atf_set descr 'Test recursive flushing of rules' + atf_set require.user root +} + +recursive_flush_body() +{ + pft_init + + epair=$(vnet_mkepair) + vnet_mkjail alcatraz ${epair}a + + ifconfig ${epair}b 192.0.2.2/24 up + jexec alcatraz ifconfig ${epair}a 192.0.2.1/24 up + + # Sanity check + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + jexec alcatraz pfctl -e + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\" {\n\ + pass\n\ + }" + + # We can ping thanks to the pass rule in foo + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + # Only reset the main rules. I.e. not a recursive flush + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\"" + + # "foo" still has the pass rule, so this works + jexec alcatraz pfctl -a "*" -sr + atf_check -s exit:0 -o ignore ping -c 1 192.0.2.1 + + # Now do a recursive flush + atf_check -s exit:0 -e ignore -o ignore \ + jexec alcatraz pfctl -a "*" -Fr + pft_set_rules alcatraz \ + "block" \ + "anchor \"foo\"" + + # So this fails + jexec alcatraz pfctl -a "*" -sr + atf_check -s exit:2 -o ignore ping -c 1 192.0.2.1 +} + +recursive_flush_cleanup() +{ + pft_cleanup +} + atf_init_test_cases() { atf_add_test_case "pr183198" @@ -450,4 +506,5 @@ atf_init_test_cases() atf_add_test_case "nat" atf_add_test_case "include" atf_add_test_case "quick" + atf_add_test_case "recursive_flush" } diff --git a/tests/sys/netpfil/pf/header.py b/tests/sys/netpfil/pf/header.py index 6832cfe6d42b..a5e36bc85d14 100644 --- a/tests/sys/netpfil/pf/header.py +++ b/tests/sys/netpfil/pf/header.py @@ -53,10 +53,9 @@ class TestHeader(VnetTestTemplate): def test_too_many(self): "Verify that we drop packets with silly numbers of headers." - sendif = self.vnet.iface_alias_map["if1"].name + sendif = self.vnet.iface_alias_map["if1"] recvif = self.vnet.iface_alias_map["if2"].name - gw_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - gw_mac = re.sub("0a$", "0b", gw_mac) + gw_mac = sendif.epairb.ether ToolsHelper.print_output("/sbin/route add default 192.0.2.1") @@ -67,7 +66,7 @@ class TestHeader(VnetTestTemplate): pkt = sp.Ether(dst=gw_mac) \ / sp.IP(dst="198.51.100.3") \ / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -89,7 +88,7 @@ class TestHeader(VnetTestTemplate): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) found = False @@ -109,7 +108,7 @@ class TestHeader(VnetTestTemplate): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=1, payloadlen=1) / sp.ICMP(type='echo-request') - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -148,10 +147,10 @@ class TestHeader6(VnetTestTemplate): "Verify that we drop packets with silly numbers of headers." ToolsHelper.print_output("/sbin/ifconfig") - sendif = self.vnet.iface_alias_map["if1"].name + sendif = self.vnet.iface_alias_map["if1"] recvif = self.vnet.iface_alias_map["if2"].name - our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - gw_mac = re.sub("0a$", "0b", our_mac) + our_mac = sendif.ether + gw_mac = sendif.epairb.ether ToolsHelper.print_output("/sbin/route -6 add default 2001:db8::1") @@ -162,7 +161,7 @@ class TestHeader6(VnetTestTemplate): pkt = sp.Ether(src=our_mac, dst=gw_mac) \ / sp.IPv6(src="2001:db8::2", dst="2001:db8:1::3") \ / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -182,7 +181,7 @@ class TestHeader6(VnetTestTemplate): for i in range(0, 18): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) @@ -202,7 +201,7 @@ class TestHeader6(VnetTestTemplate): for i in range(0, 19): pkt = pkt / sp.AH(nh=51, payloadlen=1) pkt = pkt / sp.AH(nh=58, payloadlen=1) / sp.ICMPv6EchoRequest() - s = DelayedSend(pkt, sendif) + s = DelayedSend(pkt, sendif.name) reply = sp.sniff(iface=recvif, timeout=3) print(reply) diff --git a/tests/sys/netpfil/pf/icmp.py b/tests/sys/netpfil/pf/icmp.py index 83096886691e..59f2e8190b30 100644 --- a/tests/sys/netpfil/pf/icmp.py +++ b/tests/sys/netpfil/pf/icmp.py @@ -91,10 +91,10 @@ class TestICMP(VnetTestTemplate): def test_inner_match(self): vnet = self.vnet_map["vnet1"] dst_vnet = self.vnet_map["vnet3"] - sendif = vnet.iface_alias_map["if1"].name + sendif = vnet.iface_alias_map["if1"] - our_mac = ToolsHelper.get_output("/sbin/ifconfig %s ether | awk '/ether/ { print $2; }'" % sendif) - dst_mac = re.sub("0a$", "0b", our_mac) + our_mac = sendif.ether + dst_mac = sendif.epairb.ether # Import in the correct vnet, so at to not confuse Scapy import scapy.all as sp @@ -111,7 +111,7 @@ class TestICMP(VnetTestTemplate): / sp.IP(src="192.0.2.2", dst="198.51.100.2") \ / sp.ICMP(type='echo-request') \ / "PAYLOAD" - sp.sendp(pkt, sendif, verbose=False) + sp.sendp(pkt, sendif.name, verbose=False) # Now try to pass an ICMP error message piggy-backing on that state, but # use a different source address @@ -120,7 +120,7 @@ class TestICMP(VnetTestTemplate): / sp.ICMP(type='dest-unreach') \ / sp.IP(src="198.51.100.2", dst="192.0.2.2") \ / sp.ICMP(type='echo-reply') - sp.sendp(pkt, sendif, verbose=False) + sp.sendp(pkt, sendif.name, verbose=False) try: rcvd = self.wait_object(dst_vnet.pipe, timeout=1) diff --git a/tests/sys/netpfil/pf/ioctl/validation.c b/tests/sys/netpfil/pf/ioctl/validation.c index 1ce8999dcb91..18fafe11c6ab 100644 --- a/tests/sys/netpfil/pf/ioctl/validation.c +++ b/tests/sys/netpfil/pf/ioctl/validation.c @@ -32,6 +32,7 @@ #include <net/if.h> #include <net/pfvar.h> +#include <errno.h> #include <fcntl.h> #include <stdio.h> @@ -893,6 +894,39 @@ ATF_TC_CLEANUP(rpool_mtx2, tc) COMMON_CLEANUP(); } +ATF_TC_WITH_CLEANUP(natlook); +ATF_TC_HEAD(natlook, tc) +{ + atf_tc_set_md_var(tc, "require.user", "root"); +} + +ATF_TC_BODY(natlook, tc) +{ + struct pfioc_natlook nl = { 0 }; + + COMMON_HEAD(); + + nl.af = AF_INET; + nl.proto = IPPROTO_ICMP; + nl.saddr.v4.s_addr = 0x01020304; + nl.daddr.v4.s_addr = 0x05060708; + + /* Invalid direction */ + nl.direction = 42; + + ATF_CHECK_ERRNO(EINVAL, ioctl(dev, DIOCNATLOOK, &nl) == -1); + + /* Invalid af */ + nl.direction = PF_IN; + nl.af = 99; + + ATF_CHECK_ERRNO(EAFNOSUPPORT, ioctl(dev, DIOCNATLOOK, &nl) == -1); +} + +ATF_TC_CLEANUP(natlook, tc) +{ + COMMON_CLEANUP(); +} ATF_TP_ADD_TCS(tp) { @@ -918,6 +952,7 @@ ATF_TP_ADD_TCS(tp) ATF_TP_ADD_TC(tp, tag); ATF_TP_ADD_TC(tp, rpool_mtx); ATF_TP_ADD_TC(tp, rpool_mtx2); + ATF_TP_ADD_TC(tp, natlook); return (atf_no_error()); } diff --git a/tests/sys/netpfil/pf/pfsync.sh b/tests/sys/netpfil/pf/pfsync.sh index 7f545b43a066..3be4a3024393 100644 --- a/tests/sys/netpfil/pf/pfsync.sh +++ b/tests/sys/netpfil/pf/pfsync.sh @@ -835,6 +835,90 @@ basic_ipv6_cleanup() pfsynct_cleanup } +atf_test_case "rtable" "cleanup" +rtable_head() +{ + atf_set descr 'Test handling of invalid rtableid' + atf_set require.user root +} + +rtable_body() +{ + pfsynct_init + + epair_sync=$(vnet_mkepair) + epair_one=$(vnet_mkepair) + epair_two=$(vnet_mkepair) + + vnet_mkjail one ${epair_one}a ${epair_sync}a + vnet_mkjail two ${epair_two}a ${epair_sync}b + + # pfsync interface + jexec one ifconfig ${epair_sync}a 192.0.2.1/24 up + jexec one ifconfig ${epair_one}a 198.51.100.1/24 up + jexec one ifconfig pfsync0 \ + syncdev ${epair_sync}a \ + maxupd 1 \ + up + jexec two ifconfig ${epair_two}a 198.51.100.1/24 up + jexec two ifconfig ${epair_sync}b 192.0.2.2/24 up + jexec two ifconfig pfsync0 \ + syncdev ${epair_sync}b \ + maxupd 1 \ + up + + # Make life easy, give ${epair_two}a the same mac addrss as ${epair_one}a + mac=$(jexec one ifconfig ${epair_one}a | awk '/ether/ { print($2); }') + jexec two ifconfig ${epair_two}a ether ${mac} + + # Enable pf! + jexec one /sbin/sysctl net.fibs=8 + jexec one pfctl -e + pft_set_rules one \ + "set skip on ${epair_sync}a" \ + "pass rtable 3 keep state" + # No extra fibs in two + jexec two pfctl -e + pft_set_rules two \ + "set skip on ${epair_sync}b" \ + "pass keep state" + + ifconfig ${epair_one}b 198.51.100.254/24 up + ifconfig ${epair_two}b 198.51.100.253/24 up + + # Create a new state + env PYTHONPATH=${common_dir} \ + ${common_dir}/pft_ping.py \ + --sendif ${epair_one}b \ + --fromaddr 198.51.100.254 \ + --to 198.51.100.1 \ + --recvif ${epair_one}b + + # Now + jexec one pfctl -ss -vv + sleep 2 + + # Now try to use that state on jail two + env PYTHONPATH=${common_dir} \ + ${common_dir}/pft_ping.py \ + --sendif ${epair_two}b \ + --fromaddr 198.51.100.254 \ + --to 198.51.100.1 \ + --recvif ${epair_two}b + + echo one + jexec one pfctl -ss -vv + jexec one pfctl -sr -vv + echo two + jexec two pfctl -ss -vv + jexec two pfctl -sr -vv +} + +rtable_cleanup() +{ + pfsynct_cleanup +} + route_to_common_head() { pfsync_version=$1 @@ -1134,6 +1218,7 @@ atf_init_test_cases() atf_add_test_case "timeout" atf_add_test_case "basic_ipv6_unicast" atf_add_test_case "basic_ipv6" + atf_add_test_case "rtable" atf_add_test_case "route_to_1301" atf_add_test_case "route_to_1301_bad_ruleset" atf_add_test_case "route_to_1301_bad_rpool" diff --git a/tools/build/cross-build/include/common/exterr.h b/tools/build/cross-build/include/common/exterr.h new file mode 100644 index 000000000000..62482841c7b2 --- /dev/null +++ b/tools/build/cross-build/include/common/exterr.h @@ -0,0 +1,14 @@ +#ifndef _EXTERR_H_ +#define _EXTERR_H_ + +#include <sys/types.h> + +static inline int +uexterr_gettext(char *buf, size_t bufsz) +{ + if (bufsz > 0) + buf[0] = '\0'; + return (0); +} + +#endif diff --git a/tools/build/cross-build/include/common/sys/exterrvar.h b/tools/build/cross-build/include/common/sys/exterrvar.h new file mode 100644 index 000000000000..0ba821aadef2 --- /dev/null +++ b/tools/build/cross-build/include/common/sys/exterrvar.h @@ -0,0 +1,6 @@ +#ifndef _SYS_EXTERRVAR_H_ +#define _SYS_EXTERRVAR_H_ + +#define UEXTERROR_MAXLEN 256 + +#endif diff --git a/tools/build/mk/OptionalObsoleteFiles.inc b/tools/build/mk/OptionalObsoleteFiles.inc index e0689b2d811f..1e63e4616909 100644 --- a/tools/build/mk/OptionalObsoleteFiles.inc +++ b/tools/build/mk/OptionalObsoleteFiles.inc @@ -1683,9 +1683,6 @@ OLD_FILES+=usr/share/examples/diskless/README.BOOTP OLD_FILES+=usr/share/examples/diskless/README.TEMPLATING OLD_FILES+=usr/share/examples/diskless/clone_root OLD_FILES+=usr/share/examples/dma/mailer.conf -OLD_FILES+=usr/share/examples/drivers/README -OLD_FILES+=usr/share/examples/drivers/make_device_driver.sh -OLD_FILES+=usr/share/examples/drivers/make_pseudo_driver.sh OLD_FILES+=usr/share/examples/dwatch/profile_template OLD_FILES+=usr/share/examples/etc/README.examples OLD_FILES+=usr/share/examples/etc/bsd-style-copyright @@ -1912,7 +1909,6 @@ OLD_DIRS+=usr/share/examples/bsdconfig OLD_DIRS+=usr/share/examples/csh OLD_DIRS+=usr/share/examples/diskless OLD_DIRS+=usr/share/examples/dma -OLD_DIRS+=usr/share/examples/drivers OLD_DIRS+=usr/share/examples/dwatch OLD_DIRS+=usr/share/examples/etc OLD_DIRS+=usr/share/examples/etc/defaults diff --git a/usr.bin/Makefile b/usr.bin/Makefile index b69d25480479..512f75b5d093 100644 --- a/usr.bin/Makefile +++ b/usr.bin/Makefile @@ -133,7 +133,6 @@ SUBDIR= alias \ sdiff \ sed \ seq \ - shar \ showmount \ sockstat \ soelim \ diff --git a/usr.bin/kyua/Makefile b/usr.bin/kyua/Makefile index d3a7b9b61f64..a4f95f1106d9 100644 --- a/usr.bin/kyua/Makefile +++ b/usr.bin/kyua/Makefile @@ -32,7 +32,7 @@ MAN= kyua-about.1 \ CFLAGS+= -I${KYUA_SRCDIR} -I${.CURDIR} CFLAGS+= -I${SRCTOP}/contrib/lutok/include -CFLAGS+= -I${SRCTOP}/contrib/sqlite3 +CFLAGS+= -I${SYSROOT:U${DESTDIR}}/${INCLUDEDIR}/private/sqlite3 CFLAGS+= -DHAVE_CONFIG_H # We compile the kyua libraries as part of the main executable as this saves diff --git a/usr.bin/lockf/lockf.1 b/usr.bin/lockf/lockf.1 index d73033101632..40b4497bc80c 100644 --- a/usr.bin/lockf/lockf.1 +++ b/usr.bin/lockf/lockf.1 @@ -22,7 +22,7 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd November 25, 2023 +.Dd June 24, 2025 .Dt LOCKF 1 .Os .Sh NAME @@ -30,7 +30,7 @@ .Nd execute a command while holding a file lock .Sh SYNOPSIS .Nm -.Op Fl knsw +.Op Fl knpsTw .Op Fl t Ar seconds .Ar file .Ar command @@ -126,6 +126,32 @@ is not specified, will create .Ar file if necessary. +.It Fl p +Write the pid of the +.Ar command +to +.Ar file . +This option will cause +.Nm +to open +.Ar file +for writing rather than reading. +.It Fl T +Upon receipt of a +.Dv SIGTERM , +forward a +.Dv SIGTERM +along to the +.Ar command +before cleaning up the +.Ar file +and exiting. +By default, +.Nm +effectively orphans the +.Ar command +after cleaning up the +.Ar file . .It Fl t Ar seconds Specifies a timeout for waiting for the lock. By default, diff --git a/usr.bin/lockf/lockf.c b/usr.bin/lockf/lockf.c index 7f88753d1743..16bae36a21e0 100644 --- a/usr.bin/lockf/lockf.c +++ b/usr.bin/lockf/lockf.c @@ -34,6 +34,8 @@ #include <fcntl.h> #include <limits.h> #include <signal.h> +#include <stdatomic.h> +#include <stdbool.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -50,39 +52,45 @@ union lock_subject { static int acquire_lock(union lock_subject *subj, int flags, int silent); static void cleanup(void); static void killed(int sig); +static void sigchld(int sig); static void timeout(int sig); static void usage(void) __dead2; static void wait_for_lock(const char *name); static const char *lockname; +_Static_assert(sizeof(sig_atomic_t) >= sizeof(pid_t), + "PIDs cannot be managed safely from a signal handler on this platform."); +static sig_atomic_t child = -1; static int lockfd = -1; -static int keep; -static int fdlock; -static volatile sig_atomic_t timed_out; +static bool keep; +static bool fdlock; +static int status; +static bool termchild; +static sig_atomic_t timed_out; /* * Check if fdlock is implied by the given `lockname`. We'll write the fd that * is represented by it out to ofd, and the caller is expected to do any * necessary validation on it. */ -static int +static bool fdlock_implied(const char *name, long *ofd) { char *endp; long fd; if (strncmp(name, FDLOCK_PREFIX, sizeof(FDLOCK_PREFIX) - 1) != 0) - return (0); + return (false); /* Skip past the prefix. */ name += sizeof(FDLOCK_PREFIX) - 1; errno = 0; fd = strtol(name, &endp, 10); if (errno != 0 || *endp != '\0') - return (0); + return (false); *ofd = fd; - return (1); + return (true); } /* @@ -91,35 +99,44 @@ fdlock_implied(const char *name, long *ofd) int main(int argc, char **argv) { - int ch, flags, silent, status; + struct sigaction sa_chld = { + .sa_handler = sigchld, + .sa_flags = SA_NOCLDSTOP, + }, sa_prev; + sigset_t mask, omask; long long waitsec; - pid_t child; + const char *errstr; union lock_subject subj; + int ch, flags; + bool silent, writepid; - silent = keep = 0; + silent = writepid = false; flags = O_CREAT | O_RDONLY; waitsec = -1; /* Infinite. */ - while ((ch = getopt(argc, argv, "knst:w")) != -1) { + while ((ch = getopt(argc, argv, "knpsTt:w")) != -1) { switch (ch) { case 'k': - keep = 1; + keep = true; break; case 'n': flags &= ~O_CREAT; break; case 's': - silent = 1; + silent = true; + break; + case 'T': + termchild = true; break; case 't': - { - const char *errstr; - waitsec = strtonum(optarg, 0, UINT_MAX, &errstr); if (errstr != NULL) errx(EX_USAGE, "invalid timeout \"%s\"", optarg); - } break; + case 'p': + writepid = true; + flags |= O_TRUNC; + /* FALLTHROUGH */ case 'w': flags = (flags & ~O_RDONLY) | O_WRONLY; break; @@ -143,7 +160,7 @@ main(int argc, char **argv) * If there aren't any arguments left, then we must be in fdlock mode. */ if (argc == 0 && *lockname != '/') { - fdlock = 1; + fdlock = true; subj.subj_fd = -1; } else { fdlock = fdlock_implied(lockname, &subj.subj_fd); @@ -208,13 +225,16 @@ main(int argc, char **argv) */ lockfd = acquire_lock(&subj, flags | O_NONBLOCK, silent); while (lockfd == -1 && !timed_out && waitsec != 0) { - if (keep || fdlock) + if (keep || fdlock) { lockfd = acquire_lock(&subj, flags, silent); - else { + } else { wait_for_lock(lockname); lockfd = acquire_lock(&subj, flags | O_NONBLOCK, silent); } + + /* timed_out */ + atomic_signal_fence(memory_order_acquire); } if (waitsec > 0) alarm(0); @@ -234,9 +254,30 @@ main(int argc, char **argv) if (atexit(cleanup) == -1) err(EX_OSERR, "atexit failed"); + + /* + * Block SIGTERM while SIGCHLD is being processed, so that we can safely + * waitpid(2) for the child without a concurrent termination observing + * an invalid pid (i.e., waited-on). If our setup between here and the + * sigsuspend loop gets any more complicated, we should rewrite it to + * just use a pipe to signal the child onto execvp(). + * + * We're blocking SIGCHLD and SIGTERM here so that we don't do any + * cleanup before we're ready to (after the pid is written out). + */ + sigemptyset(&mask); + sigaddset(&mask, SIGCHLD); + sigaddset(&mask, SIGTERM); + (void)sigprocmask(SIG_BLOCK, &mask, &omask); + + memcpy(&sa_chld.sa_mask, &omask, sizeof(omask)); + sigaddset(&sa_chld.sa_mask, SIGTERM); + (void)sigaction(SIGCHLD, &sa_chld, &sa_prev); + if ((child = fork()) == -1) err(EX_OSERR, "cannot fork"); if (child == 0) { /* The child process. */ + (void)sigprocmask(SIG_SETMASK, &omask, NULL); close(lockfd); execvp(argv[0], argv); warn("%s", argv[0]); @@ -246,11 +287,24 @@ main(int argc, char **argv) signal(SIGINT, SIG_IGN); signal(SIGQUIT, SIG_IGN); signal(SIGTERM, killed); + fclose(stdin); fclose(stdout); fclose(stderr); - if (waitpid(child, &status, 0) == -1) - exit(EX_OSERR); + + /* Write out the pid before we sleep on it. */ + if (writepid) + (void)dprintf(lockfd, "%d\n", (int)child); + + /* Just in case they were blocked on entry. */ + sigdelset(&omask, SIGCHLD); + sigdelset(&omask, SIGTERM); + while (child >= 0) { + (void)sigsuspend(&omask); + /* child */ + atomic_signal_fence(memory_order_acquire); + } + return (WIFEXITED(status) ? WEXITSTATUS(status) : EX_SOFTWARE); } @@ -308,13 +362,35 @@ static void killed(int sig) { + if (termchild && child >= 0) + kill(child, sig); cleanup(); signal(sig, SIG_DFL); - if (kill(getpid(), sig) == -1) + if (raise(sig) == -1) _Exit(EX_OSERR); } /* + * Signal handler for SIGCHLD. Simply waits for the child and ensures that we + * don't end up in a sticky situation if we receive a SIGTERM around the same + * time. + */ +static void +sigchld(int sig __unused) +{ + int ostatus; + + while (waitpid(child, &ostatus, 0) != child) { + if (errno != EINTR) + _exit(EX_OSERR); + } + + status = ostatus; + child = -1; + atomic_signal_fence(memory_order_release); +} + +/* * Signal handler for SIGALRM. */ static void @@ -322,6 +398,7 @@ timeout(int sig __unused) { timed_out = 1; + atomic_signal_fence(memory_order_release); } static void diff --git a/usr.bin/lockf/tests/lockf_test.sh b/usr.bin/lockf/tests/lockf_test.sh index d73c7590653d..823b5673a176 100644 --- a/usr.bin/lockf/tests/lockf_test.sh +++ b/usr.bin/lockf/tests/lockf_test.sh @@ -31,6 +31,24 @@ : ${EX_CANTCREAT:=73} : ${EX_TEMPFAIL:=75} +waitlock() +{ + local cur lockfile tmo + + lockfile="$1" + + cur=0 + tmo=20 + + while [ "$cur" -lt "$tmo" -a ! -f "$lockfile" ]; do + sleep 0.1 + cur=$((cur + 1)) + done + + atf_check_not_equal "$cur" "$tmo" +} + + atf_test_case badargs badargs_body() { @@ -62,6 +80,13 @@ basic_body() atf_check test ! -e "testlock" } +atf_test_case bubble_error +bubble_error_body() +{ + # Ensure that lockf bubbles up the error as expected. + atf_check -s exit:9 lockf testlock sh -c 'exit 9' +} + atf_test_case fdlock fdlock_body() { @@ -189,6 +214,52 @@ needfile_body() atf_check test "$tpass" -lt 10 } +atf_test_case termchild +termchild_body() +{ + lockf -kp testlock sleep 30 & + lpid=$! + + waitlock testlock + + atf_check -o file:testlock pgrep -F testlock + + start=$(date +"%s") + atf_check kill -TERM "$lpid" + wait "$lpid" + end=$(date +"%s") + elapsed=$((end - start)) + + if [ "$elapsed" -gt 5 ]; then + atf_fail "lockf seems to have dodged the SIGTERM ($elapsed passed)" + fi + + # We didn't start lockf with -T this time, so the process should not + # have been terminated. + atf_check -o file:testlock pgrep -F testlock + + lockf -kpT testlock sleep 30 & + lpid=$! + + waitlock testlock + + atf_check -o file:testlock pgrep -F testlock + + start=$(date +"%s") + atf_check kill -TERM "$lpid" + wait "$lpid" + end=$(date +"%s") + elapsed=$((end - start)) + + if [ "$elapsed" -gt 5 ]; then + atf_fail "lockf -T seems to have dodged the SIGTERM ($elapsed passed)" + fi + + # This time, it should have terminated (notably much earlier than our + # 30 second timeout). + atf_check -o empty -e not-empty -s not-exit:0 pgrep -F testlock +} + atf_test_case timeout timeout_body() { @@ -212,6 +283,34 @@ timeout_body() wait "$lpid" || true } +atf_test_case writepid +writepid_body() +{ + lockf -p "testlock" sleep 10 & + lpid=$! + + waitlock "testlock" + + atf_check test -s testlock + atf_check -o file:testlock pgrep -F testlock + atf_check -o file:testlock pgrep -F testlock -fx "sleep 10" + atf_check pkill -TERM -F testlock + + wait + + atf_check test ! -f testlock +} + +atf_test_case writepid_keep +writepid_keep_body() +{ + # Check that we'll clobber any existing contents (a pid, usually) + # once we acquire the lock. + jot -b A -s "" 64 > testlock + atf_check lockf -kp testlock sleep 0 + atf_check -o not-match:"A" cat testlock +} + atf_test_case wrlock wrlock_head() { @@ -233,9 +332,13 @@ atf_init_test_cases() { atf_add_test_case badargs atf_add_test_case basic + atf_add_test_case bubble_error atf_add_test_case fdlock atf_add_test_case keep atf_add_test_case needfile + atf_add_test_case termchild atf_add_test_case timeout + atf_add_test_case writepid + atf_add_test_case writepid_keep atf_add_test_case wrlock } diff --git a/usr.bin/shar/Makefile b/usr.bin/shar/Makefile deleted file mode 100644 index fc940c06d463..000000000000 --- a/usr.bin/shar/Makefile +++ /dev/null @@ -1,4 +0,0 @@ -SCRIPTS=shar.sh -MAN= shar.1 - -.include <bsd.prog.mk> diff --git a/usr.bin/shar/Makefile.depend b/usr.bin/shar/Makefile.depend deleted file mode 100644 index 11aba52f82cf..000000000000 --- a/usr.bin/shar/Makefile.depend +++ /dev/null @@ -1,10 +0,0 @@ -# Autogenerated - do NOT edit! - -DIRDEPS = \ - - -.include <dirdeps.mk> - -.if ${DEP_RELDIR} == ${_DEP_RELDIR} -# local dependencies - needed for -jN in clean tree -.endif diff --git a/usr.bin/shar/shar.1 b/usr.bin/shar/shar.1 deleted file mode 100644 index 6beb1e84ceab..000000000000 --- a/usr.bin/shar/shar.1 +++ /dev/null @@ -1,121 +0,0 @@ -.\" Copyright (c) 1990, 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. -.\" -.Dd January 1, 2025 -.Dt SHAR 1 -.Os -.Sh NAME -.Nm shar -.Nd create a shell archive of files -.Sh DEPRECATION NOTICE -.Nm -is obsolete and may not be present in -.Fx 15 -and later. -Because shell archives are simultaneously data and code and are typically -interpreted by -.Xr sh 1 , -they can easily be trojan-horsed and pose a significant security risk to users. -The -.Xr tar 1 -utility can still produce shar encodings of files if needed. -The -.Pa sysutils/freebsd-shar -port has been created to maintain this version of -.Nm -past its deprecation in base. -.Sh SYNOPSIS -.Nm -.Ar -.Sh DESCRIPTION -The -.Nm -command writes a -.Xr sh 1 -shell script to the standard output which will recreate the file -hierarchy specified by the command line operands. -Directories will be recreated and must be specified before the -files they contain (the -.Xr find 1 -utility does this correctly). -.Pp -The -.Nm -command is normally used for distributing files by -.Xr ftp 1 -or -.Xr mail 1 . -.Sh EXAMPLES -To create a shell archive of the program -.Xr ls 1 -and mail it to Rick: -.Bd -literal -offset indent -cd ls -shar `find . -print` \&| mail -s "ls source" rick -.Ed -.Pp -To recreate the program directory: -.Bd -literal -offset indent -mkdir ls -cd ls -\&... -<delete header lines and examine mailed archive> -\&... -sh archive -.Ed -.Sh SEE ALSO -.Xr compress 1 , -.Xr mail 1 , -.Xr tar 1 , -.Xr uuencode 1 -.Sh HISTORY -The -.Nm -command appeared in -.Bx 4.4 . -.Sh BUGS -The -.Nm -command makes no provisions for special types of files or files containing -magic characters. -The -.Nm -command cannot handle files without a newline ('\\n') -as the last character. -.Pp -It is easy to insert trojan horses into -.Nm -files. -It is strongly recommended that all shell archive files be examined -before running them through -.Xr sh 1 . -Archives produced using this implementation of -.Nm -may be easily examined with the command: -.Bd -literal -offset indent -egrep -av '^[X#]' shar.file -.Ed diff --git a/usr.bin/shar/shar.sh b/usr.bin/shar/shar.sh deleted file mode 100644 index 52c31b419fc4..000000000000 --- a/usr.bin/shar/shar.sh +++ /dev/null @@ -1,78 +0,0 @@ -#!/bin/sh - -# -# SPDX-License-Identifier: BSD-3-Clause -# -# Copyright (c) 1990, 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. - -if [ $# -eq 0 ]; then - echo 'usage: shar file ...' 1>&2 - exit 64 # EX_USAGE -fi - -for i -do - if [ ! \( -d $i -o -r $i \) ]; then - echo "$i inaccessible or not exist" 1>&2 - exit 66 # EX_NOINPUT - fi -done - -cat << EOF -# This is a shell archive. Save it in a file, remove anything before -# this line, and then unpack it by entering "sh file". Note, it may -# create directories; files and directories will be owned by you and -# have default permissions. -# -# This archive contains: -# -EOF - -for i -do - echo "# $i" -done - -echo "#" - -for i -do - if [ -d "$i" ]; then - echo "echo c - '$i'" - echo "mkdir -p '$i' > /dev/null 2>&1" - else - md5sum=`echo -n "$i" | md5` - echo "echo x - '$i'" - echo "sed 's/^X//' >'$i' << '$md5sum'" - sed 's/^/X/' "$i" || exit 1 - echo "$md5sum" - fi -done -echo exit -echo "" - -exit 0 diff --git a/usr.bin/truncate/truncate.1 b/usr.bin/truncate/truncate.1 index f6b8b0cc37c9..aa70943b889f 100644 --- a/usr.bin/truncate/truncate.1 +++ b/usr.bin/truncate/truncate.1 @@ -1,4 +1,6 @@ .\" +.\" SPDX-License-Identifier: BSD-2-Clause +.\" .\" Copyright (c) 2000 Sheldon Hearn <sheldonh@FreeBSD.org>. .\" All rights reserved. .\" Copyright (c) 2021 The FreeBSD Foundation @@ -27,12 +29,12 @@ .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF .\" SUCH DAMAGE. .\" -.Dd March 6, 2025 +.Dd July 9, 2025 .Dt TRUNCATE 1 .Os .Sh NAME .Nm truncate -.Nd truncate, extend the length of files, or perform space management in files +.Nd resize files or manage file space .Sh SYNOPSIS .Nm .Op Fl c @@ -132,7 +134,8 @@ file system space deallocation may be performed in the operation region. The space management operation is performed at the given .Ar offset bytes in the file. -If this option is not specified, the operation is performed at the beginning of the file. +If this option is not specified, +the operation is performed at the beginning of the file. .It Fl l Ar length The length of the operation range in bytes. This option must always be specified if option @@ -195,9 +198,9 @@ truncate -c -s 10M test_file .Pp Same as above but create the file if it does not exist: .Bd -literal -offset indent -truncate -s 10M test_file -ls -l test_file --rw-r--r-- 1 root wheel 10485760 Jul 22 18:48 test_file +truncate -s +10M test_file +ls -lh test_file +-rw-r--r-- 1 root wheel 10M Jul 22 18:48 test_file .Ed .Pp Adjust the size of @@ -207,10 +210,10 @@ to the size of the kernel and create another file with the same size: .Bd -literal -offset indent truncate -r /boot/kernel/kernel test_file test_file2 -ls -l /boot/kernel/kernel test_file* --r-xr-xr-x 1 root wheel 31352552 May 15 14:18 /boot/kernel/kernel --rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file --rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file2 +ls -lh /boot/kernel/kernel test_file* +-r--r--r-- 1 root wheel 30M May 15 14:18 /boot/kernel/kernel +-rw-r--r-- 1 root wheel 30M Jul 22 19:15 test_file +-rw-r--r-- 1 root wheel 30M Jul 22 19:15 test_file2 .Ed .Pp Increase the size of the file @@ -228,9 +231,9 @@ Reduce the size of the file by 5 megabytes: .Bd -literal -offset indent truncate -s -5M test_file -ls -l test_file* --rw-r--r-- 1 root wheel 31352552 Jul 22 19:19 test_file --rw-r--r-- 1 root wheel 31352552 Jul 22 19:15 test_file2 +ls -lh test_file* +-rw-r--r-- 1 root wheel 25M Jul 22 19:17 test_file +-rw-r--r-- 1 root wheel 30M Jul 22 19:15 test_file2 .Ed .Sh SEE ALSO .Xr dd 1 , @@ -247,6 +250,7 @@ The utility first appeared in .Fx 4.2 . .Sh AUTHORS +.An -nosplit The .Nm utility was written by diff --git a/usr.sbin/crunch/examples/really-big.conf b/usr.sbin/crunch/examples/really-big.conf index 12c08b73936f..9c227a7b8d29 100644 --- a/usr.sbin/crunch/examples/really-big.conf +++ b/usr.sbin/crunch/examples/really-big.conf @@ -52,7 +52,7 @@ progs tty ul uname unexpand unifdef uniq units unvis users uudecode uuencode progs vacation vgrind vi vis vmstat w wall wc what whatis whereis who progs whois window write xargs xinstall xstr yacc yes ypcat ypmatch ypwhich -# shell scripts: lorder mkdep shar which +# shell scripts: lorder mkdep which # problems: rdist uses libcompat.a(regex.o), which conflicts with # libedit(readline.o) over regerror(). |