]> git.hungrycats.org Git - bees/commitdiff
bees: explain what --show-config ROOT_PATH requires when it is unusable
authorZygo Blaxell <bees@furryterror.org>
Sun, 6 Sep 2026 01:49:42 +0000 (21:49 -0400)
committerZygo Blaxell <bees@furryterror.org>
Sun, 6 Sep 2026 01:49:42 +0000 (21:49 -0400)
--show-config takes the filesystem root, not a configuration file, and
a wrong argument used to fail with whatever errno surfaced first from
context setup (realpath, or an O_NOATIME open refused with EPERM), with
no hint about what the option actually wanted.

Check the cheap requirements up front -- the path exists, is a
directory, is readable and searchable by this user, and is on btrfs --
and name the failed one.  Wrap the remaining checks in set_root_path
(subvol id 5, FS_INFO) the same way.  Every failure ends with the same
statement of why a filesystem is needed at all: the merged configuration
depends on it through the ${ROOT}/${UUID}/${LABEL}/${FS_BYTES}
interpolations, the local configuration filename, and limits validated
against the filesystem block size.

Assisted-by: Claude-Code:claude-fable-5-1
src/bees.cc

index 25f03ec7173bf21f830fe760864c96bc9e0046bb..d55a1ee01c7cf237ebd4b0891e5ac70debf057df 100644 (file)
@@ -148,6 +148,51 @@ wait_for_signals()
        }
 }
 
+/// Why --show-config needs a real filesystem, for the diagnostics below.
+static const char *const show_config_root_reason =
+       "--show-config ROOT_PATH needs the filesystem bees would run on, "
+       "not a configuration file: the merged configuration depends on it "
+       "(${ROOT}, ${UUID}, ${LABEL} and ${FS_BYTES} interpolation, the local "
+       "configuration filename, and limits checked against the filesystem "
+       "block size).  ROOT_PATH must be an existing directory that is the "
+       "root of a mounted btrfs filesystem, readable and searchable by this "
+       "user.";
+
+/// Check the cheap, explainable requirements on a --show-config
+/// ROOT_PATH before opening it, so a wrong argument gets a message
+/// naming the requirement instead of a bare errno from deep inside
+/// context setup.  Returns false after logging if the path cannot
+/// qualify.
+static
+bool
+check_show_config_root(const string &path)
+{
+       struct stat st;
+       if (stat(path.c_str(), &st)) {
+               BEESLOGERR("--show-config: cannot access '" << path << "': " << strerror(errno));
+               BEESLOGERR(show_config_root_reason);
+               return false;
+       }
+       if (!S_ISDIR(st.st_mode)) {
+               BEESLOGERR("--show-config: '" << path << "' is not a directory");
+               BEESLOGERR(show_config_root_reason);
+               return false;
+       }
+       if (access(path.c_str(), R_OK | X_OK)) {
+               BEESLOGERR("--show-config: cannot read directory '" << path << "': " << strerror(errno)
+                       << " (uid " << getuid() << " needs read and search permission on the filesystem root)");
+               BEESLOGERR(show_config_root_reason);
+               return false;
+       }
+       struct statfs sfs;
+       if (statfs(path.c_str(), &sfs) == 0 && sfs.f_type != BTRFS_SUPER_MAGIC) {
+               BEESLOGERR("--show-config: '" << path << "' is not on a btrfs filesystem");
+               BEESLOGERR(show_config_root_reason);
+               return false;
+       }
+       return true;
+}
+
 static
 int
 bees_main(int argc, char *argv[])
@@ -363,6 +408,9 @@ bees_main(int argc, char *argv[])
                        return EXIT_FAILURE;
                }
                root_path = show_config_root_path;
+               if (!check_show_config_root(root_path)) {
+                       return EXIT_FAILURE;
+               }
        } else if (optind + 1 == argc) {
                root_path = argv[optind++];
        } else {
@@ -416,7 +464,20 @@ bees_main(int argc, char *argv[])
        BEESTRACE("context constructed");
 
        // Set root path and open root FD
-       bc->set_root_path(root_path);
+       if (show_merged_config) {
+               // The remaining checks (btrfs, subvol id 5, FS_INFO) happen
+               // inside set_root_path; translate a failure into the same
+               // requirement statement as the pre-checks above.
+               try {
+                       bc->set_root_path(root_path);
+               } catch (const exception &e) {
+                       BEESLOGERR("--show-config: '" << root_path << "' is not usable as ROOT_PATH: " << e.what());
+                       BEESLOGERR(show_config_root_reason);
+                       return EXIT_FAILURE;
+               }
+       } else {
+               bc->set_root_path(root_path);
+       }
 
        // Set configuration
        bc->set_config(bconfig);