Skip to content

_cap_filename's hardcoded limit=200 assumes NAME_MAX=255; breaks on eCryptfs (real limit can be ~143 bytes) #2109

Description

@therealronin23

Summary

graphify export obsidian still crashes with OSError: [Errno 36] File name too long on Linux systems where the export target lives on an eCryptfs-encrypted mount, even after the _cap_filename fix from #1094. That fix's limit=200 assumes the standard 255-byte NAME_MAX, but eCryptfs's ciphertext filename encoding reduces the effective limit well below 255 — 143 bytes on my system.

Root cause

_cap_filename(s, limit=200) in graphify/export.py hardcodes limit=200 as "safely under 255". That's correct for standard filesystems (ext4, APFS, NTFS), but eCryptfs (offered by Ubuntu's installer as "Encrypt my home folder", still a mainstream option) reports a much lower NAME_MAX because of its filename-encryption encoding overhead:

$ stat -f .
  ID: ...  Longnombre: 143     Tipo: ecryptfs
$ python3 -c "import os; print(os.pathconf('.', 'PC_NAME_MAX'))"
143

A node label of ~155-170 bytes (well under the 200-byte cap _cap_filename allows) still produces a filename that exceeds this filesystem's real 143-byte limit. _owned_write's target.exists() check raises OSError: [Errno 36] File name too long before any truncation logic gets a chance to help, because the cap itself was already too generous for this filesystem.

Reproduce

  1. On a Linux machine with an eCryptfs-encrypted home directory (or any filesystem with a lower-than-255 NAME_MAX — check with stat -f <path>), point the export at a source doc with a heading/label ~155-170 bytes long, e.g.:
    ### [arxiv] A Multi-Year Survey of Something With A Genuinely Long Title That Exceeds Common Encrypted Filesystem Name Limits Even Though It Fits Comfortably Under Two Hundred Bytes
    
  2. graphify update <dir>
  3. graphify export obsidian <out>
  4. Crash: OSError: [Errno 36] File name too long: '.../A Multi-Year Survey ... Bytes.md'

Suggested fix

_cap_filename (and the sibling in wiki.py, if it makes the same assumption) could query the target filesystem's real limit at runtime instead of hardcoding 200:

import os

def _effective_name_max(out_dir: str, fallback: int = 255) -> int:
    try:
        return os.pathconf(out_dir, "PC_NAME_MAX")
    except (OSError, ValueError, AttributeError):
        return fallback  # Windows / unsupported platform

...and derive the cap from that (e.g. _effective_name_max(out_dir) - 9 to leave room for the hash suffix), computed once per export run and threaded through to _cap_filename.

Environment

  • graphify (PyPI graphifyy) v0.9.11
  • Ubuntu 24.04, Linux 7.0 kernel
  • $HOME on eCryptfs (ecryptfs_cipher=aes, ecryptfs_key_bytes=16) — a standard Ubuntu "encrypt my home folder" install option, not an exotic setup
  • stat -f reports Longnombre: 143 (PC_NAME_MAX=143) on this mount vs the 255 _cap_filename assumes

Related: #1094 fixed the unbounded case; this is the wrong bound case — the cap needs to be filesystem-aware, not just present. Happy to test a fix if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions