diff options
53 files changed, 1591 insertions, 396 deletions
diff --git a/ObsoleteFiles.inc b/ObsoleteFiles.inc index faf7c421af80..d1095b57c342 100644 --- a/ObsoleteFiles.inc +++ b/ObsoleteFiles.inc @@ -60,10 +60,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 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..c2020ac3d8d2 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; 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/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/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/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/pfctl_parser.c b/sbin/pfctl/pfctl_parser.c index 29d51214e2e5..bd2c10c8080f 100644 --- a/sbin/pfctl/pfctl_parser.c +++ b/sbin/pfctl/pfctl_parser.c @@ -368,8 +368,8 @@ print_ugid(u_int8_t op, id_t i1, id_t i2, const char *t) { char a1[11], a2[11]; - snprintf(a1, sizeof(a1), "%lu", i1); - snprintf(a2, sizeof(a2), "%lu", i2); + snprintf(a1, sizeof(a1), "%ju", (uintmax_t)i1); + snprintf(a2, sizeof(a2), "%ju", (uintmax_t)i2); printf(" %s", t); if (i1 == -1 && (op == PF_OP_EQ || op == PF_OP_NE)) print_op(op, "unknown", a2); 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/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/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/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_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/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/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/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/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 |