File System
Introduction
Section titled “Introduction”In this comprehensive case study, we’ll design a File System from scratch using the systematic 8-step approach. This is a classic LLD interview problem that demonstrates hierarchical data structures, path navigation, and the powerful Composite Pattern.
Problem Statement
Section titled “Problem Statement”Design a File System that can:
- Support creating directories at any valid path
- Support creating files with content at any valid path
- Read file content by providing the file path
- Delete files and directories
- List all entries (files and directories) in a given directory
- Navigate through directory hierarchy using absolute paths (Unix-style)
- Prevent duplicate entries (cannot create a file if a directory exists at that path, and vice versa)
- Handle concurrent file operations from multiple users
Step 1: Clarify Requirements
Section titled “Step 1: Clarify Requirements”Before designing, let’s ask the right questions!
Clarifying Questions
Section titled “Clarifying Questions”Functional Requirements:
- Path Format: How should paths be represented? → Absolute paths starting with ’/’, using forward slashes as separators (e.g.,
/home/user/file.txt) - Directory Creation: Should parent directories be created automatically? → Yes, when creating files; optional when creating directories
- Duplicate Prevention: What happens if we try to create a file where a directory exists? → Should return false/error, cannot create
- File Updates: What happens if we create a file that already exists? → Update the content
- Directory Updates: What happens if we create a directory that already exists? → Return true (no error, already exists)
- Deletion: Can we delete directories with contents? → Yes, delete recursively or return error (clarify: for simplicity, assume empty directories only)
Non-Functional Requirements:
- Concurrency: Multiple users can access/update simultaneously → Yes, thread safety needed
- Scalability: How many files/directories? → Should handle thousands efficiently
- Path Navigation: How fast should path resolution be? → O(depth) where depth is path length
Edge Cases to Consider:
- What if path doesn’t start with ’/’?
- What if path is empty or just ’/’?
- What if we try to navigate through a file (e.g.,
/home/file.txt/dir)? - What if parent directory doesn’t exist?
- What if we try to delete root directory?
- What if multiple threads create the same path simultaneously?
Requirements Summary
Section titled “Requirements Summary”Step 2: Identify Actors
Section titled “Step 2: Identify Actors”Actors are external entities that interact with the system.
Who Uses This System?
Section titled “Who Uses This System?”Primary Actors:
- User - Creates directories and files, reads file content, deletes entries, lists directory contents
- System Administrator - (Future) Manages file system configuration, permissions, and quotas
- Application - (Future) Automated processes that interact with the file system
Secondary Actors:
- Backup System - (Future) External system for data backup
- Search Service - (Future) Indexes files for search operations
Actor Interactions
Section titled “Actor Interactions”Step 3: Identify Entities
Section titled “Step 3: Identify Entities”Entities are core objects in your system that have data and behavior.
The Noun Hunt
Section titled “The Noun Hunt”Looking at the requirements, we identify these core entities:
- FileSystem - Main orchestrator, manages root directory
- FileSystemEntry - Abstract base class for files and directories
- File - Represents a file with content
- Directory - Represents a directory containing entries
- Path - (Implicit) String representation of location
Entity Relationships
Section titled “Entity Relationships”Each class should have a single, well-defined responsibility (SRP).
Responsibility Assignment
Section titled “Responsibility Assignment”FileSystem:
- Manage root directory (Singleton pattern)
- Parse absolute paths into components
- Navigate through directory tree
- Coordinate file/directory operations (create, read, delete, list)
- Ensure thread safety for concurrent operations
FileSystemEntry:
- Define common interface for files and directories
- Store common properties (name, size)
- Provide type identification methods (isFile, isDirectory)
- Enable uniform treatment via Composite Pattern
File:
- Store file content
- Provide read/write operations
- Calculate file size based on content
- Thread-safe content access
Directory:
- Manage collection of FileSystemEntry objects
- Add/remove entries
- List all entries
- Update directory size
- Thread-safe entry management
Responsibility Visualization
Section titled “Responsibility Visualization”Step 5: Design Class Diagrams
Section titled “Step 5: Design Class Diagrams”Class diagrams show structure, relationships, and design patterns.
classDiagram
class FileSystemEntry {
<<abstract>>
-String name
-int size
-Object lock
+getName() String
+getSize() int
+setName(String) void
+isFile() boolean*
+isDirectory() boolean*
}
class File {
-String content
+read() String
+write(String) void
+append(String) void
+isFile() boolean
+isDirectory() boolean
}
class Directory {
-Map~String,FileSystemEntry~ entries
+addEntry(FileSystemEntry) void
+removeEntry(String) void
+getEntry(String) FileSystemEntry
+listEntries() List~FileSystemEntry~
+containsEntry(String) boolean
-updateSize() void
+isFile() boolean
+isDirectory() boolean
}
class FileSystem {
-Directory root
-Object lock
-static FileSystem instance
+getInstance() FileSystem
-parsePath(String) String[]
-navigateToDirectory(String[], boolean) Directory
+createDirectory(String) boolean
+createFile(String, String) boolean
+readFile(String) String
+deleteEntry(String) boolean
+listDirectory(String) List~String~
+getRoot() Directory
}
FileSystemEntry <|-- File
FileSystemEntry <|-- Directory
FileSystem "1" *-- "1" Directory : root
Directory "1" o-- "*" FileSystemEntry : entries
1. Composite Pattern - FileSystemEntry, File, Directory
- Treats files and directories uniformly
- Enables recursive operations on directory tree
- Natural hierarchical structure
2. Singleton Pattern - FileSystem
- Ensures single file system instance
- Prevents data inconsistency
- Global access point
3. Inheritance - FileSystemEntry hierarchy
- Code reuse for common properties
- Polymorphism for different entry types
Step 6: Define Contracts & APIs
Section titled “Step 6: Define Contracts & APIs”Contracts define how classes interact - method signatures, interfaces, and behavior.
Core Class Contracts
Section titled “Core Class Contracts”FileSystemEntry:
1class FileSystemEntry(ABC):2 """3 Abstract base class for file system entries.4 Enables Composite Pattern for uniform treatment.5 """6
7 @abstractmethod8 def is_file(self) -> bool:9 """10 Check if this entry is a file.11
12 Returns:13 bool: True if file, False if directory14 """15 pass16
17 @abstractmethod18 def is_directory(self) -> bool:19 """20 Check if this entry is a directory.21
22 Returns:23 bool: True if directory, False if file24 """25 pass26
27 def get_name(self) -> str:28 """29 Get the name of this entry.30
31 Returns:32 str: Entry name33 """34 pass35
36 def get_size(self) -> int:37 """38 Get the size of this entry.39
40 Returns:41 int: Entry size42 """43 passFileSystem:
1class FileSystem:2 @classmethod3 def get_instance(cls) -> 'FileSystem':4 """5 Get singleton instance of file system.6
7 Returns:8 FileSystem: The single instance9 """10 pass11
12 def create_directory(self, path: str) -> bool:13 """14 Create a directory at the specified path.15
16 Args:17 path: Absolute path (e.g., '/home/user/documents')18
19 Returns:20 bool: True if created successfully, False if path invalid or conflict exists21
22 Raises:23 ValueError: If path doesn't start with '/'24 """25 pass26
27 def create_file(self, path: str, content: str) -> bool:28 """29 Create a file with content at the specified path.30 Creates parent directories if they don't exist.31
32 Args:33 path: Absolute path (e.g., '/home/user/file.txt')34 content: File content35
36 Returns:37 bool: True if created successfully, False if path invalid or conflict exists38 """39 pass40
41 def read_file(self, path: str) -> Optional[str]:42 """43 Read content of a file.44
45 Args:46 path: Absolute path to file47
48 Returns:49 Optional[str]: File content if exists, None otherwise50 """51 pass52
53 def delete_entry(self, path: str) -> bool:54 """55 Delete a file or directory.56
57 Args:58 path: Absolute path to entry59
60 Returns:61 bool: True if deleted successfully, False if path invalid or doesn't exist62 """63 pass64
65 def list_directory(self, path: str) -> List[str]:66 """67 List all entries in a directory.68
69 Args:70 path: Absolute path to directory71
72 Returns:73 List[str]: List of entry names, empty list if directory doesn't exist74 """75 passFileSystemEntry:
1abstract class FileSystemEntry {2 /**3 * Check if this entry is a file.4 * @return True if file, false if directory5 */6 public abstract boolean isFile();7
8 /**9 * Check if this entry is a directory.10 * @return True if directory, false if file11 */12 public abstract boolean isDirectory();13
14 /**15 * Get the name of this entry.16 * @return Entry name17 */18 public String getName();19
20 /**21 * Get the size of this entry.22 * @return Entry size23 */24 public int getSize();25}FileSystem:
1class FileSystem {2 /**3 * Get singleton instance of file system.4 * @return The single instance5 */6 public static FileSystem getInstance();7
8 /**9 * Create a directory at the specified path.10 * @param path Absolute path (e.g., "/home/user/documents")11 * @return True if created successfully, false if path invalid or conflict exists12 * @throws IllegalArgumentException If path doesn't start with '/'13 */14 public boolean createDirectory(String path);15
16 /**17 * Create a file with content at the specified path.18 * Creates parent directories if they don't exist.19 * @param path Absolute path (e.g., "/home/user/file.txt")20 * @param content File content21 * @return True if created successfully, false if path invalid or conflict exists22 */23 public boolean createFile(String path, String content);24
25 /**26 * Read content of a file.27 * @param path Absolute path to file28 * @return File content if exists, null otherwise29 */30 public String readFile(String path);31
32 /**33 * Delete a file or directory.34 * @param path Absolute path to entry35 * @return True if deleted successfully, false if path invalid or doesn't exist36 */37 public boolean deleteEntry(String path);38
39 /**40 * List all entries in a directory.41 * @param path Absolute path to directory42 * @return List of entry names, empty list if directory doesn't exist43 */44 public List<String> listDirectory(String path);45}Contract Visualization
Section titled “Contract Visualization”Step 7: Handle Edge Cases
Section titled “Step 7: Handle Edge Cases”Edge cases are scenarios that might not be obvious but are important to handle.
Critical Edge Cases
Section titled “Critical Edge Cases”1. Invalid Path Format
- Path doesn’t start with ’/’ → Return false/null
- Empty path or just ’/’ → Return false/null
- Path with multiple consecutive slashes → Normalize to single slashes
- Trailing slashes → Handle gracefully
2. Path Navigation Through Files
- Attempting to navigate through a file (e.g.,
/home/file.txt/dir) → Return null - Files are leaf nodes, cannot contain other entries
3. Duplicate Entry Prevention
- Creating file where directory exists → Return false
- Creating directory where file exists → Return false
- Creating file that already exists → Update content, return true
- Creating directory that already exists → Return true (no error)
4. Non-existent Paths
- Reading file that doesn’t exist → Return null
- Deleting entry that doesn’t exist → Return false
- Listing non-existent directory → Return empty list
5. Root Directory Protection
- Cannot create/delete root directory → Return false
- Root is special, must always exist
6. Concurrent Access
- Multiple threads creating same path → Thread-safe operations prevent race conditions
- One thread reading while another writing → Synchronized access
Edge Case Handling Flow
Section titled “Edge Case Handling Flow”Thread Safety Considerations
Section titled “Thread Safety Considerations”1# Simplified thread-safe file system2class FileSystem:3 def __init__(self):4 self._lock = threading.Lock()5 self.root = Directory("root")6
7 def create_file(self, path: str, content: str) -> bool:8 with self._lock: # Critical section9 path_parts = self._parse_path(path)10 if not path_parts:11 return False12
13 parent_parts = path_parts[:-1]14 file_name = path_parts[-1]15
16 parent = self._navigate_to_directory(parent_parts, True)17 if parent is None:18 return False19
20 # Check for conflicts21 if parent.contains_entry(file_name):22 existing = parent.get_entry(file_name)23 if existing.is_directory():24 return False # Directory exists25
26 # Create file27 new_file = File(file_name)28 new_file.write(content)29 parent.add_entry(new_file)30 return True1// Simplified thread-safe file system2class FileSystem {3 private final ReentrantLock lock = new ReentrantLock();4 private Directory root = new Directory("root");5
6 public boolean createFile(String path, String content) {7 lock.lock(); // Critical section8 try {9 String[] pathParts = parsePath(path);10 if (pathParts.length == 0) {11 return false;12 }13
14 String[] parentParts = Arrays.copyOf(pathParts, pathParts.length - 1);15 String fileName = pathParts[pathParts.length - 1];16
17 Directory parent = navigateToDirectory(parentParts, true);18 if (parent == null) {19 return false;20 }21
22 // Check for conflicts23 if (parent.containsEntry(fileName)) {24 FileSystemEntry existing = parent.getEntry(fileName);25 if (existing.isDirectory()) {26 return false; // Directory exists27 }28 }29
30 // Create file31 File newFile = new File(fileName);32 newFile.write(content);33 parent.addEntry(newFile);34 return true;35 } finally {36 lock.unlock();37 }38 }39}Step 8: Code Implementation
Section titled “Step 8: Code Implementation”Finally, implement your design following SOLID principles and design patterns.
Complete Implementation
Section titled “Complete Implementation”1from abc import ABC, abstractmethod2from threading import Lock3from typing import Optional, List, Dict4
5# =====================6# CORE ENTITIES7# =====================8
9class FileSystemEntry(ABC):10 """Abstract base class for file system entries (Composite Pattern)."""11
12 def __init__(self, name: str):13 self.name = name14 self.size = 015 self.lock = Lock()16
17 def get_name(self) -> str:18 with self.lock:19 return self.name20
21 def get_size(self) -> int:22 with self.lock:23 return self.size24
25 def set_name(self, name: str):26 with self.lock:27 self.name = name28
29 @abstractmethod30 def is_file(self) -> bool:31 pass32
33 @abstractmethod34 def is_directory(self) -> bool:35 pass36
37class File(FileSystemEntry):38 """Represents a file in the file system (Leaf node)."""39
40 def __init__(self, name: str):41 super().__init__(name)42 self.content = ""43
44 def read(self) -> str:45 with self.lock:46 return self.content47
48 def write(self, data: str):49 with self.lock:50 self.content = data51 self.size = len(self.content)52
53 def append(self, data: str):54 with self.lock:55 self.content += data56 self.size = len(self.content)57
58 def is_file(self) -> bool:59 return True60
61 def is_directory(self) -> bool:62 return False63
64class Directory(FileSystemEntry):65 """Represents a directory in the file system (Composite node)."""66
67 def __init__(self, name: str):68 super().__init__(name)69 self.entries: Dict[str, FileSystemEntry] = {}70
71 def add_entry(self, entry: FileSystemEntry):72 with self.lock:73 self.entries[entry.get_name()] = entry74 self._update_size()75
76 def remove_entry(self, name: str):77 with self.lock:78 if name in self.entries:79 del self.entries[name]80 self._update_size()81
82 def get_entry(self, name: str) -> Optional[FileSystemEntry]:83 with self.lock:84 return self.entries.get(name)85
86 def list_entries(self) -> List[FileSystemEntry]:87 with self.lock:88 return list(self.entries.values())89
90 def contains_entry(self, name: str) -> bool:91 with self.lock:92 return name in self.entries93
94 def _update_size(self):95 self.size = len(self.entries)96
97 def is_file(self) -> bool:98 return False99
100 def is_directory(self) -> bool:101 return True102
103# =====================104# SINGLETON PATTERN - MAIN SYSTEM105# =====================106
107class FileSystem:108 """Main file system (Singleton)."""109
110 _instance = None111 _lock = Lock()112
113 def __new__(cls):114 if cls._instance is None:115 with cls._lock:116 if cls._instance is None:117 cls._instance = super().__new__(cls)118 return cls._instance119
120 def __init__(self):121 if hasattr(self, '_initialized'):122 return123 self.root = Directory("root")124 self.lock = Lock()125 self._initialized = True126
127 @classmethod128 def get_instance(cls):129 return cls()130
131 def _parse_path(self, path: str) -> List[str]:132 """Parse absolute path into components."""133 if not path or not path.startswith("/"):134 return []135 parts = path.split("/")136 return [part for part in parts if part]137
138 def _navigate_to_directory(self, path_parts: List[str], create_if_not_exists: bool) -> Optional[Directory]:139 """Navigate to directory, creating intermediate directories if needed."""140 with self.lock:141 current = self.root142
143 for part in path_parts:144 entry = current.get_entry(part)145
146 if entry is None:147 if create_if_not_exists:148 new_dir = Directory(part)149 current.add_entry(new_dir)150 current = new_dir151 else:152 return None153 elif entry.is_directory():154 current = entry155 else:156 return None # Path contains a file, cannot navigate further157
158 return current159
160 def create_directory(self, path: str) -> bool:161 """Create a directory at the specified path."""162 with self.lock:163 path_parts = self._parse_path(path)164 if not path_parts:165 return False # Cannot create root166
167 parent_parts = path_parts[:-1]168 dir_name = path_parts[-1]169
170 parent = self._navigate_to_directory(parent_parts, True)171 if parent is None:172 return False173
174 if parent.contains_entry(dir_name):175 existing = parent.get_entry(dir_name)176 if existing.is_file():177 return False # File exists at this path178 return True # Directory already exists179
180 new_dir = Directory(dir_name)181 parent.add_entry(new_dir)182 return True183
184 def create_file(self, path: str, content: str) -> bool:185 """Create a file with content at the specified path."""186 with self.lock:187 path_parts = self._parse_path(path)188 if not path_parts:189 return False # Cannot create file at root190
191 parent_parts = path_parts[:-1]192 file_name = path_parts[-1]193
194 parent = self._navigate_to_directory(parent_parts, True)195 if parent is None:196 return False197
198 if parent.contains_entry(file_name):199 existing = parent.get_entry(file_name)200 if existing.is_directory():201 return False # Directory exists at this path202 # File exists, update content203 file = existing204 file.write(content)205 return True206
207 new_file = File(file_name)208 new_file.write(content)209 parent.add_entry(new_file)210 return True211
212 def read_file(self, path: str) -> Optional[str]:213 """Read content of a file."""214 with self.lock:215 path_parts = self._parse_path(path)216 if not path_parts:217 return None218
219 parent_parts = path_parts[:-1]220 file_name = path_parts[-1]221
222 parent = self._navigate_to_directory(parent_parts, False)223 if parent is None:224 return None225
226 entry = parent.get_entry(file_name)227 if entry is None or not entry.is_file():228 return None229
230 return entry.read()231
232 def delete_entry(self, path: str) -> bool:233 """Delete a file or directory."""234 with self.lock:235 path_parts = self._parse_path(path)236 if not path_parts:237 return False # Cannot delete root238
239 parent_parts = path_parts[:-1]240 entry_name = path_parts[-1]241
242 parent = self._navigate_to_directory(parent_parts, False)243 if parent is None:244 return False245
246 if not parent.contains_entry(entry_name):247 return False248
249 parent.remove_entry(entry_name)250 return True251
252 def list_directory(self, path: str) -> List[str]:253 """List all entries in a directory."""254 with self.lock:255 path_parts = self._parse_path(path)256 dir_obj = self._navigate_to_directory(path_parts, False)257
258 if dir_obj is None:259 return []260
261 return [entry.get_name() for entry in dir_obj.list_entries()]262
263 def get_root(self) -> Directory:264 return self.root265
266# =====================267# DEMO268# =====================269
270if __name__ == "__main__":271 fs = FileSystem.get_instance()272
273 print("=== File System Demo ===\n")274
275 # Create directories276 print("Creating directories...")277 print(f"Created /home: {fs.create_directory('/home')}")278 print(f"Created /home/user: {fs.create_directory('/home/user')}")279 print(f"Created /home/user/documents: {fs.create_directory('/home/user/documents')}")280
281 # Create files282 print("\nCreating files...")283 print(f"Created /home/user/file1.txt: {fs.create_file('/home/user/file1.txt', 'Hello, World!')}")284 print(f"Created /home/user/documents/readme.txt: {fs.create_file('/home/user/documents/readme.txt', 'This is a readme file.')}")285
286 # Read files287 print("\nReading files...")288 print(f"/home/user/file1.txt: {fs.read_file('/home/user/file1.txt')}")289 print(f"/home/user/documents/readme.txt: {fs.read_file('/home/user/documents/readme.txt')}")290
291 # List directory292 print("\nListing /home/user:")293 entries = fs.list_directory("/home/user")294 for entry in entries:295 print(f" - {entry}")296
297 # Try to create duplicate298 print("\nTrying to create directory where file exists:")299 print(f"Created /home/user/file1.txt (as dir): {fs.create_directory('/home/user/file1.txt')}")300
301 # Delete entry302 print("\nDeleting /home/user/file1.txt:")303 print(f"Deleted: {fs.delete_entry('/home/user/file1.txt')}")304 print(f"Read after delete: {fs.read_file('/home/user/file1.txt')}")305
306 print("\n=== Demo Complete ===")1import java.util.*;2import java.util.concurrent.*;3
4// =====================5// CORE ENTITIES6// =====================7
8abstract class FileSystemEntry {9 protected String name;10 protected int size;11 protected final Object lock = new Object();12
13 public FileSystemEntry(String name) {14 this.name = name;15 this.size = 0;16 }17
18 public String getName() {19 synchronized (lock) {20 return name;21 }22 }23
24 public int getSize() {25 synchronized (lock) {26 return size;27 }28 }29
30 public void setName(String name) {31 synchronized (lock) {32 this.name = name;33 }34 }35
36 public abstract boolean isFile();37 public abstract boolean isDirectory();38}39
40class File extends FileSystemEntry {41 private StringBuilder content;42
43 public File(String name) {44 super(name);45 this.content = new StringBuilder();46 }47
48 public String read() {49 synchronized (lock) {50 return content.toString();51 }52 }53
54 public void write(String data) {55 synchronized (lock) {56 content = new StringBuilder(data);57 size = content.length();58 }59 }60
61 public void append(String data) {62 synchronized (lock) {63 content.append(data);64 size = content.length();65 }66 }67
68 @Override69 public boolean isFile() {70 return true;71 }72
73 @Override74 public boolean isDirectory() {75 return false;76 }77}78
79class Directory extends FileSystemEntry {80 private Map<String, FileSystemEntry> entries;81
82 public Directory(String name) {83 super(name);84 this.entries = new ConcurrentHashMap<>();85 }86
87 public void addEntry(FileSystemEntry entry) {88 synchronized (lock) {89 entries.put(entry.getName(), entry);90 updateSize();91 }92 }93
94 public void removeEntry(String name) {95 synchronized (lock) {96 entries.remove(name);97 updateSize();98 }99 }100
101 public FileSystemEntry getEntry(String name) {102 synchronized (lock) {103 return entries.get(name);104 }105 }106
107 public List<FileSystemEntry> listEntries() {108 synchronized (lock) {109 return new ArrayList<>(entries.values());110 }111 }112
113 public boolean containsEntry(String name) {114 synchronized (lock) {115 return entries.containsKey(name);116 }117 }118
119 private void updateSize() {120 size = entries.size();121 }122
123 @Override124 public boolean isFile() {125 return false;126 }127
128 @Override129 public boolean isDirectory() {130 return true;131 }132}133
134// =====================135// SINGLETON PATTERN - MAIN SYSTEM136// =====================137
138class FileSystem {139 private static FileSystem instance;140 private Directory root;141 private final Object lock = new Object();142
143 private FileSystem() {144 this.root = new Directory("root");145 }146
147 public static FileSystem getInstance() {148 if (instance == null) {149 synchronized (FileSystem.class) {150 if (instance == null) {151 instance = new FileSystem();152 }153 }154 }155 return instance;156 }157
158 private String[] parsePath(String path) {159 if (path == null || path.isEmpty() || !path.startsWith("/")) {160 return new String[0];161 }162 String[] parts = path.split("/");163 List<String> result = new ArrayList<>();164 for (String part : parts) {165 if (!part.isEmpty()) {166 result.add(part);167 }168 }169 return result.toArray(new String[0]);170 }171
172 private Directory navigateToDirectory(String[] pathParts, boolean createIfNotExists) {173 synchronized (lock) {174 Directory current = root;175
176 for (int i = 0; i < pathParts.length; i++) {177 String part = pathParts[i];178 FileSystemEntry entry = current.getEntry(part);179
180 if (entry == null) {181 if (createIfNotExists) {182 Directory newDir = new Directory(part);183 current.addEntry(newDir);184 current = newDir;185 } else {186 return null;187 }188 } else if (entry.isDirectory()) {189 current = (Directory) entry;190 } else {191 return null; // Path contains a file, cannot navigate further192 }193 }194
195 return current;196 }197 }198
199 public boolean createDirectory(String path) {200 synchronized (lock) {201 String[] pathParts = parsePath(path);202 if (pathParts.length == 0) {203 return false; // Cannot create root204 }205
206 String[] parentParts = Arrays.copyOf(pathParts, pathParts.length - 1);207 String dirName = pathParts[pathParts.length - 1];208
209 Directory parent = navigateToDirectory(parentParts, true);210 if (parent == null) {211 return false;212 }213
214 if (parent.containsEntry(dirName)) {215 FileSystemEntry existing = parent.getEntry(dirName);216 if (existing.isFile()) {217 return false; // File exists at this path218 }219 return true; // Directory already exists220 }221
222 Directory newDir = new Directory(dirName);223 parent.addEntry(newDir);224 return true;225 }226 }227
228 public boolean createFile(String path, String content) {229 synchronized (lock) {230 String[] pathParts = parsePath(path);231 if (pathParts.length == 0) {232 return false; // Cannot create file at root233 }234
235 String[] parentParts = Arrays.copyOf(pathParts, pathParts.length - 1);236 String fileName = pathParts[pathParts.length - 1];237
238 Directory parent = navigateToDirectory(parentParts, true);239 if (parent == null) {240 return false;241 }242
243 if (parent.containsEntry(fileName)) {244 FileSystemEntry existing = parent.getEntry(fileName);245 if (existing.isDirectory()) {246 return false; // Directory exists at this path247 }248 // File exists, update content249 File file = (File) existing;250 file.write(content);251 return true;252 }253
254 File newFile = new File(fileName);255 newFile.write(content);256 parent.addEntry(newFile);257 return true;258 }259 }260
261 public String readFile(String path) {262 synchronized (lock) {263 String[] pathParts = parsePath(path);264 if (pathParts.length == 0) {265 return null;266 }267
268 String[] parentParts = Arrays.copyOf(pathParts, pathParts.length - 1);269 String fileName = pathParts[pathParts.length - 1];270
271 Directory parent = navigateToDirectory(parentParts, false);272 if (parent == null) {273 return null;274 }275
276 FileSystemEntry entry = parent.getEntry(fileName);277 if (entry == null || !entry.isFile()) {278 return null;279 }280
281 return ((File) entry).read();282 }283 }284
285 public boolean deleteEntry(String path) {286 synchronized (lock) {287 String[] pathParts = parsePath(path);288 if (pathParts.length == 0) {289 return false; // Cannot delete root290 }291
292 String[] parentParts = Arrays.copyOf(pathParts, pathParts.length - 1);293 String entryName = pathParts[pathParts.length - 1];294
295 Directory parent = navigateToDirectory(parentParts, false);296 if (parent == null) {297 return false;298 }299
300 if (!parent.containsEntry(entryName)) {301 return false;302 }303
304 parent.removeEntry(entryName);305 return true;306 }307 }308
309 public List<String> listDirectory(String path) {310 synchronized (lock) {311 String[] pathParts = parsePath(path);312 Directory dir = navigateToDirectory(pathParts, false);313
314 if (dir == null) {315 return new ArrayList<>();316 }317
318 List<String> result = new ArrayList<>();319 for (FileSystemEntry entry : dir.listEntries()) {320 result.add(entry.getName());321 }322 return result;323 }324 }325
326 public Directory getRoot() {327 return root;328 }329}330
331// =====================332// DEMO333// =====================334
335public class FileSystemDemo {336 public static void main(String[] args) {337 FileSystem fs = FileSystem.getInstance();338
339 System.out.println("=== File System Demo ===\n");340
341 // Create directories342 System.out.println("Creating directories...");343 System.out.println("Created /home: " + fs.createDirectory("/home"));344 System.out.println("Created /home/user: " + fs.createDirectory("/home/user"));345 System.out.println("Created /home/user/documents: " + fs.createDirectory("/home/user/documents"));346
347 // Create files348 System.out.println("\nCreating files...");349 System.out.println("Created /home/user/file1.txt: " +350 fs.createFile("/home/user/file1.txt", "Hello, World!"));351 System.out.println("Created /home/user/documents/readme.txt: " +352 fs.createFile("/home/user/documents/readme.txt", "This is a readme file."));353
354 // Read files355 System.out.println("\nReading files...");356 System.out.println("/home/user/file1.txt: " + fs.readFile("/home/user/file1.txt"));357 System.out.println("/home/user/documents/readme.txt: " +358 fs.readFile("/home/user/documents/readme.txt"));359
360 // List directory361 System.out.println("\nListing /home/user:");362 List<String> entries = fs.listDirectory("/home/user");363 for (String entry : entries) {364 System.out.println(" - " + entry);365 }366
367 // Try to create duplicate368 System.out.println("\nTrying to create directory where file exists:");369 System.out.println("Created /home/user/file1.txt (as dir): " +370 fs.createDirectory("/home/user/file1.txt"));371
372 // Delete entry373 System.out.println("\nDeleting /home/user/file1.txt:");374 System.out.println("Deleted: " + fs.deleteEntry("/home/user/file1.txt"));375 System.out.println("Read after delete: " + fs.readFile("/home/user/file1.txt"));376
377 System.out.println("\n=== Demo Complete ===");378 }379}Key Implementation Highlights
Section titled “Key Implementation Highlights”FileSystemEntryas common interfaceFileas leaf nodeDirectoryas composite node containingFileSystemEntryobjects
- Single
FileSysteminstance - Thread-safe double-checked locking
3. Path Navigation:
- Parse paths into components
- Navigate directory tree efficiently
- Create intermediate directories automatically
4. Thread Safety:
- Synchronized blocks for critical sections
- Concurrent collections for directory entries
- Fine-grained locking at each level
Problem: Files and directories need to be treated uniformly, but directories can contain other entries.
Without Composite:
1# Separate handling for files and directories2def list_directory(path):3 if is_file(path):4 return [] # Files don't have contents5 elif is_directory(path):6 entries = []7 for entry in get_directory_entries(path):8 if is_file(entry):9 entries.append(entry.name)10 elif is_directory(entry):11 entries.append(entry.name + "/")12 return entries13# Type checking everywhere ❌1// Separate handling for files and directories2public List<String> listDirectory(String path) {3 if (isFile(path)) {4 return Collections.emptyList(); // Files don't have contents5 } else if (isDirectory(path)) {6 List<String> entries = new ArrayList<>();7 for (Entry entry : getDirectoryEntries(path)) {8 if (entry instanceof File) {9 entries.add(entry.getName());10 } else if (entry instanceof Directory) {11 entries.add(entry.getName() + "/");12 }13 }14 return entries;15 }16 // Type checking everywhere ❌17}With Composite:
1# Uniform treatment2def list_directory(path):3 dir_obj = navigate_to_directory(path)4 if dir_obj is None:5 return []6
7 return [entry.get_name() for entry in dir_obj.list_entries()]8# Works uniformly for all FileSystemEntry objects ✅1// Uniform treatment2public List<String> listDirectory(String path) {3 Directory dir = navigateToDirectory(path);4 if (dir == null) {5 return Collections.emptyList();6 }7
8 return dir.listEntries().stream()9 .map(FileSystemEntry::getName)10 .collect(Collectors.toList());11 // Works uniformly for all FileSystemEntry objects ✅12}Benefits:
- Uniform Treatment - Code works with
FileSystemEntrywithout type checking - Recursive Operations - Easy to implement recursive operations (e.g., total size)
- Extensibility - Add new entry types (e.g., SymbolicLink) without changing existing code
- Natural Structure - Mirrors real file system hierarchy
Problem: Multiple instances would cause data inconsistency.
Without Singleton:
1# Process A2fs_a = FileSystem() # Creates /home/user/file.txt3fs_a.create_file("/home/user/file.txt", "content")4
5# Process B (different instance!)6fs_b = FileSystem() # Doesn't see file.txt7fs_b.read_file("/home/user/file.txt") # Returns None - INCONSISTENCY!1// Process A2FileSystem fsA = new FileSystem(); // Creates /home/user/file.txt3fsA.createFile("/home/user/file.txt", "content");4
5// Process B (different instance!)6FileSystem fsB = new FileSystem(); // Doesn't see file.txt7fsB.readFile("/home/user/file.txt"); // Returns null - INCONSISTENCY!With Singleton:
1# Process A2fs_a = FileSystem.get_instance() # Same instance3fs_a.create_file("/home/user/file.txt", "content")4
5# Process B6fs_b = FileSystem.get_instance() # Same instance!7fs_b.read_file("/home/user/file.txt") # Returns "content" - CONSISTENT!1// Process A2FileSystem fsA = FileSystem.getInstance(); // Same instance3fsA.createFile("/home/user/file.txt", "content");4
5// Process B6FileSystem fsB = FileSystem.getInstance(); // Same instance!7fsB.readFile("/home/user/file.txt"); // Returns "content" - CONSISTENT!System Flow Diagrams
Section titled “System Flow Diagrams”Create File Flow
Section titled “Create File Flow”Read File Flow
Section titled “Read File Flow”Extensibility & Future Enhancements
Section titled “Extensibility & Future Enhancements”Easy to Extend
Section titled “Easy to Extend”1. Add New Entry Type (Symbolic Link):
1class SymbolicLink(FileSystemEntry):2 """Represents a symbolic link."""3
4 def __init__(self, name: str, target_path: str):5 super().__init__(name)6 self.target_path = target_path7
8 def get_target(self) -> str:9 return self.target_path10
11 def is_file(self) -> bool:12 return False # Symbolic links are neither files nor directories13
14 def is_directory(self) -> bool:15 return False1class SymbolicLink extends FileSystemEntry {2 private String targetPath;3
4 public SymbolicLink(String name, String targetPath) {5 super(name);6 this.targetPath = targetPath;7 }8
9 public String getTarget() {10 return targetPath;11 }12
13 @Override14 public boolean isFile() {15 return false; // Symbolic links are neither files nor directories16 }17
18 @Override19 public boolean isDirectory() {20 return false;21 }22}2. Add File Permissions:
1class FileSystemEntry(ABC):2 def __init__(self, name: str):3 self.name = name4 self.size = 05 self.permissions = Permissions.READ_WRITE # New field6 self.owner = "root" # New field1abstract class FileSystemEntry {2 protected String name;3 protected int size;4 protected Permissions permissions = Permissions.READ_WRITE; // New field5 protected String owner = "root"; // New field6}3. Add Metadata:
1class FileSystemEntry(ABC):2 def __init__(self, name: str):3 self.name = name4 self.size = 05 self.created_date = datetime.now() # New field6 self.modified_date = datetime.now() # New field1abstract class FileSystemEntry {2 protected String name;3 protected int size;4 protected Date createdDate = new Date(); // New field5 protected Date modifiedDate = new Date(); // New field6}Future Enhancements
Section titled “Future Enhancements”1. Recursive Operations:
- Add
getTotalSize()method that recursively calculates directory size - Add
searchFiles(String pattern)method for file search
2. Copy/Move Operations:
- Add
copyEntry(String source, String dest)method - Add
moveEntry(String source, String dest)method
3. Persistence:
- Add
save()andload()methods to serialize to disk - Support loading file system state from storage
4. Advanced Features:
- File locking for concurrent writes
- Directory quotas
- File versioning
- Access control lists (ACL)
Summary
Section titled “Summary”Key Takeaways
Section titled “Key Takeaways”- Follow Systematic Approach - Don’t jump to code
- Identify Actors First - Who uses the system?
- Identify Entities - What are the core objects?
- Assign Responsibilities - Single Responsibility Principle
- Design Class Diagrams - Visualize structure and relationships
- Define Contracts - Clear interfaces and APIs
- Handle Edge Cases - Think about errors and concurrency
- Use Design Patterns - Composite, Singleton
- Make it Extensible - Easy to add new features
- Composite Pattern - Uniform treatment of files and directories
- Singleton Pattern - Single file system instance
- Inheritance - FileSystemEntry hierarchy
Best Practices Demonstrated
Section titled “Best Practices Demonstrated”- SOLID principles (SRP, OCP)
- Thread safety considerations
- Clear separation of concerns
- Extensible design
- Error handling
- Clean code structure
- Path navigation efficiency
Next Steps
Section titled “Next Steps”Now that you’ve mastered the File System:
Practice Similar Problems:
- Parking Lot System
- Elevator System
- Library Management System
- Directory Tree Traversal
Explore More Patterns:
- Observer Pattern (for file system events)
- Command Pattern (for undo/redo operations)
- Visitor Pattern (for recursive operations)
Deepen Your Understanding:
- Study real file systems (ext4, NTFS)
- Learn about inodes and directory structures
- Explore distributed file systems
- Study file system caching strategies