- README.md: Overview, core concept diagram, component summary - architecture.md: System design, initialization flow, memory model - components.md: Deep dive on all classes and functions - data-flow.md: Complete read/write operation flows with diagrams - analysis.md: Performance analysis (latency, memory footprint, I/O) - drawbacks.md: 27 identified issues and limitations catalog - modernization.md: Python 3 migration guide with effort estimates
10 KiB
beetfs Drawbacks & Limitations
Overview
This document catalogs all identified issues, limitations, and missing features in beetfs. Issues are categorized by severity and type.
Critical Issues (🔴)
1. Full File Loading into Memory
Location: Lines 463, 480-481
self.inf = InterpolatedFLAC(self.file_object.read()) # Entire file
# ...
self.music_data = self.file_object.read() # Audio portion again
Impact:
- Memory usage = O(file_size) per open file
- 50MB FLAC = ~50MB RAM
- Library scan of 100 files = 5GB+ RAM
- Out-of-memory crashes on large libraries
Fix Required: Implement lazy loading with seek-based reads.
2. MP3 Support Disabled
Location: Lines 475-477
elif self.format == "mp3":
self.bound = 0 # disable interpolation for now
self.music_offset = 0 # disable interpolation for now
Impact:
- MP3 files return original metadata, not database metadata
- Breaks the core promise of metadata overlay
- MP3 is still one of the most common formats
Fix Required: Implement InterpolatedID3 header generation.
3. Python 2 Only
Location: Throughout
except fuse.FuseError, e: # Python 2 syntax
if isinstance(value, basestring): # Removed in Python 3
return reduce(lambda a, b: (a << 8) + ord(b), string, 0L) # Long literals
Impact:
- Python 2 EOL was January 2020
- Security vulnerabilities unfixed
- No modern library support
- Cannot run on Python 3 without migration
Fix Required: Full Python 3 migration (see modernization.md).
4. Deprecated FUSE Library
Location: Line 25, 51
import fuse
fuse.fuse_python_api = (0, 2)
Impact:
- fuse-python is unmaintained
- Missing modern FUSE features (FUSE 3.x)
- Compatibility issues with recent kernels
- No async support
Fix Required: Migrate to pyfuse3 or llfuse.
5. Single-Threaded Execution
Location: Line 178
server.multithreaded = 0
Impact:
- All operations serialized
- One slow open blocks all other operations
- Cannot utilize multiple CPU cores
- Poor performance under concurrent access
Fix Required: Enable multithreading with proper locking.
Major Issues (🟡)
6. Limited Metadata Fields
Location: Lines 466-469, 540-547
# Only these 4 fields are actually used:
self.inf["title"] = self.item.title
self.inf["album"] = self.item.album
self.inf["artist"] = self.item.artist
self.inf["genre"] = self.item.genre
Defined but not implemented (lines 55-77):
composer,groupingyear,month,daytrack,tracktotaldisc,disctotallyrics,commentsbpm,compalbumartist(not even defined)
Impact:
- Track numbers not from database
- Album artist not supported
- Year/date not interpolated
- Cover art not handled
7. No File Handle Caching/Eviction
Location: Lines 1004-1018
if path in self.files:
self.files[path].open()
else:
self.files[path] = FileHandler(path, self.lib)
Missing:
- No maximum cache size
- No LRU eviction
- No memory pressure handling
- Files stay in memory until explicitly closed
Impact:
- Memory grows unbounded
- No protection against OOM
- Applications that open-then-close still leave data cached
8. Blocking Database Operations
Location: Lines 549-550
self.lib.store(self.item)
self.lib.save()
Impact:
- SQLite operations in FUSE thread
- Write operations block all reads
- No transaction batching
- Potential deadlocks with beets
9. No Library Hot Reload
Issue: Virtual directory tree built once at mount time.
Location: Lines 142-172
for item in lib.items():
# Build tree...
Impact:
- New files added to beets library not visible
- Deleted files still appear (ENOENT on access)
- Metadata changes in beets not reflected until remount
- Must unmount/remount to see changes
10. Static Path Format
Location: Lines 44-45
PATH_FORMAT = ("$artist/$album ($year) [$format_upper]/"
"$track - $artist - $title.$format")
Impact:
- Cannot customize organization
- Hard-coded template
- No configuration option
- Incompatible with different organizational preferences
11. No Extended Attribute Support
Location: Not implemented
Impact:
- Cannot store/retrieve xattrs
- Some applications use xattrs for metadata
- macOS Finder metadata lost
- Linux capabilities not supported
12. No Symlink Support
Location: Lines 758-765
def readlink(self, path):
return -errno.EOPNOTSUPP
Impact:
- Cannot create symlinks in mount
- Some applications expect symlink support
- Cannot link to external files
13. Silent Error Swallowing
Location: Lines 705-707, 1019-1021, 1103-1104
except Exception as e:
logging.error(e)
return -errno.ENOENT # Always returns same error
Impact:
- All errors appear as "file not found"
- Hard to debug issues
- No distinction between permission, I/O, parse errors
- Lost stack traces in many cases
Minor Issues (🟢)
14. Global State
Location: Lines 125-140
global structure_split
global structure_depth
global library
global directory_structure
Impact:
- Cannot mount multiple instances
- Difficult to unit test
- Tight coupling between components
- No dependency injection
15. Hard-coded Log File
Location: Lines 624-625
LOG_FILENAME = "LOG"
logging.basicConfig(filename=LOG_FILENAME, level=logging.INFO,)
Impact:
- Log file created in current directory
- No log rotation
- No configurable log level
- Fills disk on busy systems
16. Reference Count Manual Management
Location: Lines 485-495
def open(self):
self.instance_count = self.instance_count + 1
def release(self):
if self.instance_count > 0:
self.instance_count = self.instance_count - 1
Issues:
- Race conditions possible if multithreaded
- No context manager support
- Manual counting error-prone
- Off-by-one potential
17. Inefficient Directory Building
Location: Lines 153-172
for level in range(0, structure_depth - 1):
if level-1 in level_subbed:
sub_elements.append(level_subbed[level-1])
directory_structure.adddir(sub_elements, level_subbed[level])
Issues:
- Rebuilds path for every item
- O(items × depth) complexity
- String allocations in inner loop
- Could use trie-based insertion
18. No Cover Art Handling
Issue: Cover art embedded in FLAC not addressed.
Impact:
- Cover art from original file used, not database
- Cannot replace/add cover art through overlay
- PICTURE metadata blocks passed through unchanged
19. No Cue Sheet Support
Issue: Cue sheets not handled specially.
Impact:
.cuefiles point to original file paths- Cannot play cue-referenced tracks correctly
- Split-by-cue not supported
20. File Size Mismatch Potential
Issue: Virtual file size differs from physical if header size changes.
Location: Lines 675-688
statinfo = os.stat(item)
st = Stat(st_mode=statinfo.st_mode,
st_size=statinfo.st_size, # Original size, not virtual!
...)
Impact:
stat()returns original file size- If generated header is larger/smaller, size is wrong
- Some applications may fail on size mismatch
- Range requests could break
Missing Features
Essential
| Feature | Status | Notes |
|---|---|---|
| MP3 metadata interpolation | ❌ Disabled | Code exists but disabled |
| OGG/Opus support | ❌ Missing | No implementation |
| AAC/M4A support | ❌ Missing | No implementation |
| Lazy file loading | ❌ Missing | Full file loaded |
| Memory management | ❌ Missing | No limits or eviction |
| Configuration file | ❌ Missing | Hard-coded values |
Nice to Have
| Feature | Status | Notes |
|---|---|---|
| Cover art interpolation | ❌ Missing | Would need PICTURE block handling |
| ReplayGain from database | ❌ Missing | Tags not interpolated |
| Lyrics from database | ❌ Missing | Listed in fields, not implemented |
| Watch mode (hot reload) | ❌ Missing | No inotify integration |
| Multiple mount points | ❌ Missing | Global state prevents |
| Remote database | ❌ Missing | Local beets only |
| Read-only mode | ❌ Missing | Always allows writes |
| Custom path templates | ❌ Missing | Hard-coded PATH_FORMAT |
Security Considerations
1. No Input Validation
Location: Throughout
pathsplit = path[1:].split('/')
item_id = node.files[pathsplit[structure_depth-1]] # No bounds check
Risk: Path traversal, injection attacks unlikely but possible.
2. Database Credentials Exposed
Issue: Uses beets library directly with stored credentials.
Risk: Low - local access only.
3. No Permission Enforcement
Location: Lines 749-756
if flags | os.R_OK:
pass # TODO: actually check the file permissions
if flags | os.W_OK:
pass
Risk: All users can read/write through mount.
Compatibility Issues
| Component | Issue |
|---|---|
| Jellyfin | May scan entire library, causing OOM |
| Plex | Same library scan issue |
| Navidrome | Expects certain tag fields not implemented |
| mpd | Works for playback, database features limited |
| macOS | fuse-python macOS support questionable |
| Docker | FUSE in containers requires privileged mode |
Summary Table
| Category | Critical | Major | Minor |
|---|---|---|---|
| Performance | 2 | 4 | 2 |
| Functionality | 2 | 5 | 4 |
| Code Quality | 2 | 2 | 4 |
| Total | 6 | 11 | 10 |
Prioritized Fix List
- 🔴 Memory: Implement lazy loading (Critical for usability)
- 🔴 Python 3: Migrate to Python 3 (Required for any changes)
- 🔴 FUSE lib: Switch to pyfuse3/llfuse (Required for Python 3)
- 🔴 MP3: Enable MP3 interpolation (Core functionality)
- 🟡 Metadata: Implement all fields (Feature completeness)
- 🟡 Threading: Enable multithreading (Performance)
- 🟡 Config: Add configuration file (Usability)
- 🟡 Hot reload: Watch for library changes (Usability)
- 🟢 Globals: Remove global state (Code quality)
- 🟢 Logging: Configurable logging (Operations)