Skip to content

feat(shell): add unmount command with proper filesystem teardown #607

Description

@LSantha

Summary

Add an umount shell command that properly unmounts a filesystem: flushes dirty data, closes the filesystem, and removes the mount point. Also add an unmount() method to FileSystemService to complement the existing mount().

Motivation

There is currently no way to unmount a filesystem from the shell. FileSystemService has mount() but no unmount(). FileSystemAPIImpl has a TODO at line 463: // TODO handle removal (+ add unmount. Without unmount, users cannot safely remove removable media, and the flush-on-close path in AbstractFileSystem.close() is never triggered from the shell.

Implementation

1. Add unmount() to FileSystemService

// FileSystemService.java — add method:
/**
 * Unmount the filesystem at the given path.
 * Flushes the filesystem, closes it, and removes the mount point.
 *
 * @param fullPath the mount point path
 * @throws IOException if flush or close fails
 * @throws IllegalArgumentException if path is not a mount point
 */
public void unmount(String fullPath) throws IOException;

2. Implement in FileSystemAPIImpl

public void unmount(String fullPath) throws IOException {
    FileSystem<?> fs = mountPoints.remove(fullPath);
    if (fs == null) {
        throw new IllegalArgumentException("Not a mount point: " + fullPath);
    }
    try {
        if (!fs.isReadOnly()) {
            fs.flush();
        }
        fs.close();
    } catch (IOException ex) {
        // Re-add on failure so state stays consistent
        mountPoints.put(fullPath, fs);
        throw ex;
    }
}

3. Shell command

package org.jnode.fs.command;

public class UnmountCommand extends AbstractCommand {

    private final FileArgument argDir =
        new FileArgument("directory", Argument.MANDATORY,
            "the mount point to unmount");

    public UnmountCommand() {
        super("Unmount a filesystem");
        registerArguments(argDir);
    }

    public static void main(String[] args) throws Exception {
        new UnmountCommand().execute(args);
    }

    @Override
    public void execute() throws Exception {
        FileSystemService fss = InitialNaming.lookup(FileSystemService.NAME);
        PrintWriter out = getOutput().getPrintWriter();
        PrintWriter err = getError().getPrintWriter();
        String path = argDir.getValue().getCanonicalPath();

        if (!fss.isMount(path)) {
            err.println("Not a mount point: " + path);
            err.println("Use 'mount' to list mounted filesystems.");
            exit(1);
        }

        try {
            fss.unmount(path);
            out.println("Unmounted " + path);
        } catch (IOException ex) {
            err.println("Failed to unmount " + path + ": " + ex.getMessage());
            exit(1);
        }
    }
}

4. Plugin XML

Add to fs/descriptors/org.jnode.fs.command.xml:

<extension point="org.jnode.shell.aliases">
    <alias name="umount" class="org.jnode.fs.command.UnmountCommand"/>
</extension>

<extension point="org.jnode.shell.syntaxes">
    <syntax alias="umount">
        <sequence description="Unmount the filesystem at the given path">
            <argument argLabel="directory"/>
        </sequence>
    </syntax>
</extension>

Files to create/modify

  • Modify: fs/src/fs/org/jnode/fs/service/FileSystemService.java — add unmount() method
  • Modify: fs/src/fs/org/jnode/fs/service/def/FileSystemAPIImpl.java — implement unmount(), resolve TODO
  • Create: fs/src/commands/org/jnode/fs/command/UnmountCommand.java
  • Modify: fs/descriptors/org.jnode.fs.command.xml — add alias and syntax

Testing / Validation

Test 1: Basic unmount

  1. Mount: mount /dev/ide0-auto /mnt
  2. Write: echo test > /mnt/test.txt
  3. Unmount: umount /mnt
  4. Expected: Unmounted /mnt
  5. Verify flush + close in serial log
  6. Remount, verify file persists

Test 2: Unmount non-existent path

  1. Run umount /nonexistent
  2. Expected: Not a mount point: /nonexistent, exit code 1

Test 3: Unmount already unmounted

  1. Mount, unmount
  2. Run umount /mnt again
  3. Expected: error, exit code 1

Test 4: Unmount read-only filesystem

  1. Mount ISO9660: mount /dev/cdrom0 /cdrom
  2. Run umount /cdrom
  3. Expected: Unmounted /cdrom (flush skipped for read-only)

Test 5: Flush failure during unmount

  1. Write data to mounted FAT filesystem
  2. Unmount — should flush before close
  3. Verify in serial log: FLUSH CACHE command issued before close

Test 6: Unmount with open file handles

  1. Open a file on the mounted filesystem (via another process or background)
  2. Try to unmount
  3. Expected behavior: should either fail or close handles — document the chosen behavior

Test 7: Integration with sync

  1. Mount, write data
  2. sync /mnt
  3. umount /mnt
  4. Verify clean unmount, no data loss on reboot

Acceptance Criteria

  • umount /path flushes, closes, and removes the mount point
  • Non-mount-point path prints error and exits 1
  • Flush errors during unmount are reported but don't prevent close
  • FileSystemService.unmount() is implemented and documented
  • FileSystemAPIImpl TODO for mount removal is resolved
  • Read-only filesystems unmount without flush errors
  • mount output after unmount no longer lists the removed mount

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent/doneThe agent finished successfully; PR opened or comment posted.enhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions