]> Projects (at) Tadryanom (dot) Me - AdrOS.git/commitdiff
vfs: add cwd check to umount to prevent filesystem in use
authorTulio A M Mendes <[email protected]>
Tue, 26 May 2026 02:42:57 +0000 (23:42 -0300)
committerTulio A M Mendes <[email protected]>
Wed, 3 Jun 2026 04:02:35 +0000 (01:02 -0300)
Added busy check in vfs_umount_nolock to reject unmount if any process
has its current working directory (cwd) within the mount being unmounted.

Implementation details:
- Checks current_process->cwd first (process executing umount)
- Iterates over ready_queue_head to check all other processes
- Uses path_is_mountpoint_prefix to determine if cwd is within mount
- Returns -EBUSY if any process has cwd in the mount
- Note: root directory check not implemented (would require vfs_getpath
  or storing root as path in process struct)

Also added test I20 (umount cwd) to fulltest.c:
- Mounts tmpfs to /tmp/mnt_cwd
- Changes cwd into the mount
- Verifies umount fails with -EBUSY
- Changes cwd back to /
- Verifies umount succeeds

Updated test harnesses (smoke_test.exp, test_battery.exp) to include
the new test pattern.

Test results:
- Smoke test: 124/124 PASS
- Zero regressions

src/kernel/fs.c
tests/smoke_test.exp
tests/test_battery.exp
user/cmds/fulltest/fulltest.c

index 493d4679ef6eecd366d2cf0dc253cbfb332d0c52..658f94cd57ab20a789fa17cd038d3549c5718f07 100644 (file)
@@ -180,11 +180,20 @@ int vfs_umount_nolock(const char* mountpoint) {
         }
     }
 
-    /* Busy check: reject if any process has cwd or root in this mount */
+    /* Busy check: reject if any process has cwd in this mount */
     extern struct process* ready_queue_head;
+    extern struct process* current_process;
     extern spinlock_t sched_lock;
     
     uintptr_t sched_fl = spin_lock_irqsave(&sched_lock);
+    
+    /* Check current process first */
+    if (current_process && current_process->cwd[0] && path_is_mountpoint_prefix(mp, current_process->cwd)) {
+        spin_unlock_irqrestore(&sched_lock, sched_fl);
+        return -EBUSY;
+    }
+    
+    /* Check all processes in ready queue */
     struct process* p = ready_queue_head;
     while (p) {
         /* Check if cwd is within this mount */
@@ -192,10 +201,6 @@ int vfs_umount_nolock(const char* mountpoint) {
             spin_unlock_irqrestore(&sched_lock, sched_fl);
             return -EBUSY;
         }
-        /* Check if root is within this mount (if different from cwd) */
-        /* Note: root is not stored as a path in current process struct,
-         * so we only check cwd for now. A full implementation would require
-         * storing root as a path or implementing vfs_getpath. */
         p = p->rq_next;
     }
     spin_unlock_irqrestore(&sched_lock, sched_fl);
index 07a7929b9930631715296e508c7a3b3943b725d2..9a65c38c13f279462dc0a0c1b3fbe7395f14c954 100755 (executable)
@@ -161,6 +161,7 @@ set tests {
     {"mount replace rejection" "\\[test\\] mount replace rejection OK"}
     {"ftruncate readonly"   "\\[test\\] ftruncate readonly OK"}
     {"mountpoint validation" "\\[test\\] mountpoint validation OK"}
+    {"umount cwd"           "\\[test\\] umount cwd OK"}
     {"sigqueue"            "\\[test\\] sigqueue OK"}
     {"clone"               "\\[test\\] clone OK"}
     {"inotify_init1"       "\\[test\\] inotify_init1 OK"}
index 272d97064d77009686df07eb6733bb97fa4070d9..86a2700213cc48f3f52be994c39def2e80e30b7f 100644 (file)
@@ -268,6 +268,7 @@ set patterns {
     {"mount replace rejection" "\\[test\\] mount replace rejection OK"}
     {"ftruncate readonly"   "\\[test\\] ftruncate readonly OK"}
     {"mountpoint validation" "\\[test\\] mountpoint validation OK"}
+    {"umount cwd"           "\\[test\\] umount cwd OK"}
     {"sigqueue"            "\\[test\\] sigqueue OK"}
     {"clone"               "\\[test\\] clone OK"}
     {"inotify_init1"       "\\[test\\] inotify_init1 OK"}
index 2cf577234f8a1e781becca74427e5d347f38c346..1a84a615fd94c62cfa2e7e57ec7101aff040794b 100644 (file)
@@ -5262,6 +5262,44 @@ void _start(void) {
                   (uint32_t)(sizeof("[test] mountpoint validation OK\n") - 1));
     }
 
+    // I20: umount with active cwd — should fail with -EBUSY
+    {
+        (void)sys_mkdir("/tmp/mnt_cwd");
+        if (sys_mount("none", "/tmp/mnt_cwd", "tmpfs", 0) < 0) {
+            sys_write(1, "[test] umount cwd: mount failed\n",
+                      (uint32_t)(sizeof("[test] umount cwd: mount failed\n") - 1));
+            sys_exit(1);
+        }
+        /* Change cwd into the mount */
+        if (sys_chdir("/tmp/mnt_cwd") < 0) {
+            sys_write(1, "[test] umount cwd: chdir failed\n",
+                      (uint32_t)(sizeof("[test] umount cwd: chdir failed\n") - 1));
+            sys_exit(1);
+        }
+        /* umount should fail because cwd is in the mount */
+        int rc = sys_umount2("/tmp/mnt_cwd");
+        if (rc >= 0) {
+            sys_write(1, "[test] umount cwd: should fail with -EBUSY\n",
+                      (uint32_t)(sizeof("[test] umount cwd: should fail with -EBUSY\n") - 1));
+            sys_exit(1);
+        }
+        /* Change cwd back to / */
+        if (sys_chdir("/") < 0) {
+            sys_write(1, "[test] umount cwd: chdir back failed\n",
+                      (uint32_t)(sizeof("[test] umount cwd: chdir back failed\n") - 1));
+            sys_exit(1);
+        }
+        /* Now umount should succeed */
+        if (sys_umount2("/tmp/mnt_cwd") < 0) {
+            sys_write(1, "[test] umount cwd: umount failed\n",
+                      (uint32_t)(sizeof("[test] umount cwd: umount failed\n") - 1));
+            sys_exit(1);
+        }
+        sys_write(1, "[test] umount cwd OK\n",
+                  (uint32_t)(sizeof("[test] umount cwd OK\n") - 1));
+    }
+
+
     // I13: clone — create a thread sharing address space
     {
         /* We use a simple clone with CLONE_VM|CLONE_FS|CLONE_FILES|CLONE_SIGHAND