In-Memory File System: Strict mkdir, touch, ls, rm, rmdir and a Command Runner

Quick 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.

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.

|Home/Software Engineering Fundamentals/Perplexity
Perplexity logo
Perplexity
Sep 25, 2026
hardSoftware EngineerOnsiteSoftware Engineering Fundamentals
3
0

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 Guidance

  • 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.

Clarifying Questions for this Part Guidance

  • 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 Guidance

  • 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.

Clarifying Questions for this Part Guidance

  • 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 Guidance

  • 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 Guidance

  • 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 Guidance

  • 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?
Loading comments...