Skip to content

Instantly share code, notes, and snippets.

@pavelrevak
Created February 8, 2026 13:04
Show Gist options
  • Select an option

  • Save pavelrevak/8074f843cb7b57fa5f14290b2c56879c to your computer and use it in GitHub Desktop.

Select an option

Save pavelrevak/8074f843cb7b57fa5f14290b2c56879c to your computer and use it in GitHub Desktop.
Simple dict based Filesystem for micropython
# boot_ram.py - Dict-based RAM filesystem for MicroPython
# No block device, no FAT - files stored directly in nested dicts
# Copy to device as boot.py or run via exec
import gc
import os
_IFDIR = 0x4000
_IFREG = 0x8000
class DictFile:
def __init__(self, parent, name, mode):
self._parent = parent
self._name = name
if 'w' in mode:
self._data = bytearray()
parent[name] = self._data
elif 'a' in mode:
self._data = parent.get(name, bytearray())
parent[name] = self._data
else:
self._data = parent[name]
self._pos = len(self._data) if 'a' in mode else 0
def read(self, n=-1):
if n < 0 or n > len(self._data) - self._pos:
n = len(self._data) - self._pos
result = self._data[self._pos:self._pos + n]
self._pos += n
return bytes(result)
def readinto(self, buf):
n = min(len(buf), len(self._data) - self._pos)
buf[:n] = self._data[self._pos:self._pos + n]
self._pos += n
return n
def readline(self):
start = self._pos
end = len(self._data)
while self._pos < end:
self._pos += 1
if self._data[self._pos - 1] == 0x0a:
break
return bytes(self._data[start:self._pos])
def write(self, buf):
n = len(buf)
if self._pos == len(self._data):
self._data.extend(buf)
else:
end = self._pos + n
if end > len(self._data):
self._data.extend(bytearray(end - len(self._data)))
self._data[self._pos:self._pos + n] = buf
self._pos += n
return n
def seek(self, offset, whence=0):
if whence == 0:
self._pos = offset
elif whence == 1:
self._pos += offset
elif whence == 2:
self._pos = len(self._data) + offset
if self._pos < 0:
self._pos = 0
return self._pos
def tell(self):
return self._pos
def flush(self):
pass
def close(self):
pass
def __enter__(self):
return self
def __exit__(self, *a):
pass
class DictFS:
def __init__(self):
self._root = {}
self._cwd = '/'
def _split(self, path):
if not path.startswith('/'):
path = self._cwd.rstrip('/') + '/' + path
return [p for p in path.split('/') if p]
def _find(self, path):
node = self._root
for p in self._split(path):
if not isinstance(node, dict) or p not in node:
raise OSError(2)
node = node[p]
return node
def _parent(self, path):
parts = self._split(path)
if not parts:
raise OSError(2)
node = self._root
for p in parts[:-1]:
if not isinstance(node, dict) or p not in node:
raise OSError(2)
node = node[p]
if not isinstance(node, dict):
raise OSError(20)
return node, parts[-1]
def mount(self, readonly, mkfs):
pass
def umount(self):
pass
def open(self, path, mode):
parent, name = self._parent(path)
if 'r' in mode and name not in parent:
raise OSError(2)
if name in parent and isinstance(parent[name], dict):
raise OSError(21)
return DictFile(parent, name, mode)
def stat(self, path):
node = self._find(path)
if isinstance(node, dict):
return (_IFDIR, 0, 0, 0, 0, 0, 0, 0, 0, 0)
return (_IFREG, 0, 0, 0, 0, 0, len(node), 0, 0, 0)
def statvfs(self, path):
gc.collect()
used = self._du(self._root)
free = gc.mem_free()
total = used + free
return (1, 1, total, free, free, 0, 0, 0, 0, 255)
def _du(self, node):
if isinstance(node, dict):
return sum(self._du(v) for v in node.values())
return len(node)
def ilistdir(self, path):
node = self._find(path)
if not isinstance(node, dict):
raise OSError(20)
for name, val in node.items():
if isinstance(val, dict):
yield (name, _IFDIR, 0, 0)
else:
yield (name, _IFREG, 0, len(val))
def mkdir(self, path):
parent, name = self._parent(path)
if name in parent:
raise OSError(17)
parent[name] = {}
def rmdir(self, path):
parent, name = self._parent(path)
if name not in parent or not isinstance(parent[name], dict):
raise OSError(2)
if parent[name]:
raise OSError(39)
del parent[name]
def remove(self, path):
parent, name = self._parent(path)
if name not in parent:
raise OSError(2)
if isinstance(parent[name], dict):
raise OSError(21)
del parent[name]
def rename(self, old, new):
op, on = self._parent(old)
np, nn = self._parent(new)
if on not in op:
raise OSError(2)
np[nn] = op.pop(on)
def chdir(self, path):
parts = self._split(path)
node = self._root
for p in parts:
if not isinstance(node, dict) or p not in node:
raise OSError(2)
node = node[p]
if not isinstance(node, dict):
raise OSError(20)
self._cwd = '/' + '/'.join(parts) if parts else '/'
def getcwd(self):
return self._cwd
os.mount(DictFS(), '/ramfs')
os.chdir('/ramfs')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment