In-Memory File System: Strict mkdir, touch, ls, rm, rmdir and a Command Runner
Company: Perplexity
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: hard
Interview Round: Onsite
Implement an in-memory file system with directories and files under a single root, `/`. The task is delivered as a multi-file project with provided test cases, so reading the tests to pin down expected behavior, running them, and debugging failures are part of the work. It comes in parts; the first two are below.
### Clarifying Questions
- Are all paths absolute? How should a relative path, an empty path, a trailing slash, repeated slashes, or components such as `.` and `..` be treated?
- Which characters may a file or directory name contain?
- How should an operation report failure: by raising an exception, returning a boolean, or returning an error string?
- Do files have contents, or only names?
### Part 1 — mkdir, touch and ls
Implement:
- `mkdir(path)`: create a directory.
- `touch(path)`: create an empty file.
- `ls(path)`: list a directory.
Every operation must check that the path is valid. Neither `mkdir` nor `touch` creates missing directories along the path: every component except the last must already exist as a directory, and only the final component is created. For example, `mkdir("/a/b")` fails if `/a` does not exist.
```hint One path walker
Every operation starts by turning a path into the node it names, or into its parent directory plus a final name. Getting that step right once removes most of the bugs.
```
#### Clarifying Questions for this Part
- What should `mkdir` do when the final component already exists as a directory, or as a file?
- What should `touch` do on an existing file (a no-op, as in Unix, or an error), and on an existing directory?
- What does `ls` return for a directory: names only, and in what order? What does it return when the path names a file?
- Should `ls("/")` work, and can `mkdir("/")` ever succeed?
#### What This Part Should Cover
- A tree representation that distinguishes files from directories.
- Path parsing and validation, including failure when an intermediate component is missing or is a file.
- Deterministic `ls` output and clear error behavior.
### Part 2 — rm, rmdir and the execution function
Implement `rm(path)` to remove a file and `rmdir(path)` to remove a directory. Then write the execution function: it takes the commands the tests supply, dispatches each one to the matching operation, and returns the results.
```hint Keep parsing out of the file system
Treat the execution function as a thin layer: parse one command, call one method, format one result. A failing test then tells you which layer is wrong.
```
#### Clarifying Questions for this Part
- Does `rmdir` remove only empty directories, or everything beneath it? Can it remove the root?
- Does `rm` refuse directories, or also remove them recursively?
- What is the command format (one string per command, such as `mkdir /a`, or a structured list), and what does the execution function return: one output per command, or only the outputs of `ls`?
- When one command fails, does execution continue with the next one?
#### What This Part Should Cover
- Removal semantics that keep the tree consistent, with explicit rules for non-empty directories and the root.
- A dispatcher that handles unknown commands and wrong argument counts.
- Consistent reporting of each command's result or error.
### What a Strong Answer Covers
- Clean separation between path handling, the tree, and command execution.
- Every error path handled: missing parents, the wrong node type, duplicates, non-empty directories and invalid paths.
- The complexity of each operation in terms of path depth and directory size.
- Working effectively against the provided tests: reading them to settle ambiguous behavior and debugging failures systematically.
### Follow-up Questions
- How would you add `mkdir -p` style creation of missing parents, and a recursive remove, without duplicating the path walk?
- How would you add file contents with `write` and `cat`, and an `mv` that renames or moves a whole subtree?
- If several threads use the file system at once, what locking would you use, and how does `mv` complicate it?
- How would `ls` stay fast for a directory with a very large number of entries that must be listed in sorted order?
Overview: A two-part coding exercise to build an in-memory file system: first mkdir, touch and ls with strict path validation and no implicit creation of parent directories, then rm, rmdir and a function that executes command sequences. It tests tree modeling, error handling and working against provided tests.