In-Memory File System with Non-Recursive mkdir and Missing-Path Handling
Company: Perplexity
Role: Software Engineer
Category: Software Engineering Fundamentals
Difficulty: medium
Interview Round: Technical Screen
Implement an in-memory file system as a class. It keeps directories and files in memory and answers operations on absolute, Unix-style paths such as `/a/b/c`. The report names `mkdir` and `ls` and likens the task to a well-known problem whose interface also appends content to a file and reads a file back, so the interface below follows that standard form.
| Method | Behavior |
| --- | --- |
| `ls(path) -> list[str]` | If `path` names a directory, return the names of the entries directly inside it, in lexicographic order. `path` may also name a file (see below). |
| `mkdir(path) -> None` | Create a new, empty directory at `path`. |
| `add_content_to_file(path, content) -> None` | If no file exists at `path`, create one containing `content`; otherwise append `content` to it. |
| `read_content_from_file(path) -> str` | Return the full content of the file at `path`. |
The version in this round is stricter than the standard form. The report lists the cases below as the ones that failed when the statement was read too quickly:
- **Paths that do not exist.** Any operation can receive a path in which a directory along the way, or the target file or directory itself, does not exist. Detect it and respond as the specification requires; never crash.
- **`mkdir` is not `mkdir -p`.** Only the last component of the path may be missing. Every parent directory must already exist, and `mkdir` never creates missing parents.
- **`ls` on a file.** The path passed to `ls` may name a file rather than a directory.
Settle the expected result of each case before writing the methods. An illustrative session, with failures shown as `error` because their exact form is one of the clarifying questions:
```text
mkdir("/a") -> ok
mkdir("/a/b/c") -> error: /a/b does not exist
mkdir("/a/b") -> ok
add_content_to_file("/a/b/f", "hi") -> ok, creates the file
add_content_to_file("/a/b/f", "!") -> ok, appends
read_content_from_file("/a/b/f") -> "hi!"
ls("/a") -> ["b"]
ls("/a/x") -> error: /a/x does not exist
```
```hint One way to walk a path
Every method starts by walking from the root along the path. Decide what that shared walk returns, and how it reports the two different ways it can fail: a component that is missing, and a component that exists but is a file.
```
```hint List the failures per method
For each method, write down every state the path can be in (missing parent, missing target, target is a file, target is a directory, the root itself) and the agreed result for each, before writing any method body.
```
### Constraints and Clarifications
- Paths are absolute and start with `/`. The path `/` alone is the root directory, which always exists.
- Names are non-empty and do not contain `/`. A file and a directory can never have the same path.
- Lexicographic order means ordinary string comparison of the names.
- How a failed operation is reported was not given; settle it before coding.
### Clarifying Questions
- How should a failed operation be reported: an exception, a return value such as `False` or an error string, or a specific message? Must each kind of failure be distinguishable?
- What should `ls` return for a path that names a file: a list containing only the file's name, or an error?
- What should `mkdir` do when the path already exists, as a directory or as a file?
- Does `add_content_to_file` require the parent directory to exist, given that `mkdir` does not create parents? What if the path names an existing directory?
- Can paths contain a trailing slash, repeated slashes, `.` or `..`?
### What a Strong Answer Covers
- Settles the error contract and the result of every reported edge case before coding
- A tree of directory nodes with a clear distinction between files and directories
- One path-resolution routine shared by all methods that tells a missing component apart from a file used as a directory, and handles the root
- Correct non-recursive `mkdir` and file-creation rules, with no partial changes left behind by a failed call
- Sorted listing, the complexity of each operation, and tests for each reported edge case
### Follow-up Questions
- Add a `-p` mode to `mkdir` that creates missing parents. If a component halfway along the path is a file, what state can a naive implementation leave behind, and how do you prevent it?
- `ls` is called far more often than `mkdir` on directories with many entries. How would you avoid sorting on every call?
- Add `rm` and `mv`. What checks does moving a directory need so that it cannot be moved into its own subtree?
- How would you make the file system safe for concurrent callers?
Overview: Implement an in-memory file system class that lists directories, creates directories without creating missing parents, and appends to and reads files on absolute paths. It tests careful reading of a specification and robust path resolution, including missing directories or files, paths that name a file, and consistent error reporting.