Files
MusicFS/docs/drawbacks.md
T
Alexander f0a83df190 Add reverse-engineered documentation
- 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
2026-05-12 11:52:48 +02:00

10 KiB
Raw Blame History

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, grouping
  • year, month, day
  • track, tracktotal
  • disc, disctotal
  • lyrics, comments
  • bpm, comp
  • albumartist (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

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:

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

  1. 🔴 Memory: Implement lazy loading (Critical for usability)
  2. 🔴 Python 3: Migrate to Python 3 (Required for any changes)
  3. 🔴 FUSE lib: Switch to pyfuse3/llfuse (Required for Python 3)
  4. 🔴 MP3: Enable MP3 interpolation (Core functionality)
  5. 🟡 Metadata: Implement all fields (Feature completeness)
  6. 🟡 Threading: Enable multithreading (Performance)
  7. 🟡 Config: Add configuration file (Usability)
  8. 🟡 Hot reload: Watch for library changes (Usability)
  9. 🟢 Globals: Remove global state (Code quality)
  10. 🟢 Logging: Configurable logging (Operations)