Repositories / jai.git

jai.git

Clone (read-only): git clone http://git.guha-anderson.com/git/jai.git

Branch

don't allow exporting subdirectories of .jai or storage. also don't expect .jai and storage to match ino/dev in casual mode.

don't allow exporting subdirectories of .jai or storage.
also don't expect .jai and storage to match ino/dev in casual mode.
Author
David Mazieres <dm@uun.org>
Date
2026-03-24 02:21:32 -0700
Commit
865f82f7ec77dd9a4acc5f50c140c5882264ae0e
fs.h
index 5defe61..2d4c24a 100644
--- a/fs.h
+++ b/fs.h
@@ -31,6 +31,18 @@ cat(path left, const path &right)
   return left += right;
 }
 
+inline size_t
+components(const path &p)
+{
+  return std::ranges::distance(p.begin(), p.end());
+}
+
+inline bool
+contains(const path &dir, const path &subpath)
+{
+  return std::ranges::mismatch(dir, subpath).in1 == dir.end();
+}
+
 // True is target matches pattern (with * expanded)
 bool glob(std::string_view pattern, std::string_view target);
 
jai.cc
index 34a5f81..a79f26c 100644
--- a/jai.cc
+++ b/jai.cc
@@ -487,6 +487,12 @@ Config::make_mnt_ns()
   for (auto d : grant_directories_) {
     if (d.is_relative())
       d = "/" / d;
+    if (contains(homejaipath_, d))
+      err("{}: cannot export a directory within {}", d.string(),
+          homejaipath_.string());
+    if (contains(storagedir_, d))
+      err("{}: cannot export a directory within {}", d.string(),
+          storagedir_.string());
     xsetns(*oldns, CLONE_NEWNS);
     auto restore_root = asuser();
     Fd src = xopenat(-1, d, O_DIRECTORY | O_PATH | O_CLOEXEC);
@@ -520,12 +526,15 @@ Config::make_mnt_ns()
       return;
     restore_root.reset();
 
-    struct stat sbold, sbnew = xfstat(*target);;
-    xsetns(*oldns, CLONE_NEWNS);
-    int staterr = stat(p.c_str(), &sbold);
-    xsetns(*newns, CLONE_NEWNS);
-    if (staterr || sbold.st_ino != sbnew.st_ino || sbold.st_dev != sbnew.st_dev)
-      return;
+    if (mode_ != kCasual) {
+      struct stat sbold, sbnew = xfstat(*target);
+      xsetns(*oldns, CLONE_NEWNS);
+      int staterr = stat(p.c_str(), &sbold);
+      xsetns(*newns, CLONE_NEWNS);
+      if (staterr || sbold.st_ino != sbnew.st_ino ||
+          sbold.st_dev != sbnew.st_dev)
+        return;
+    }
 
     check_user(*target, p, true);
     Fd empty = xopenat(-1, kRunRoot, O_RDONLY);