Skip to content

Repository files navigation

Linuxulator Filesystem Compatibility Shim

An independent compatibility shim for running the Dropbox desktop app through the Linux binary compatibility layer of the FreeBSD® operating system when filesystem information is not represented as the app expects.

This repository contains a small LD_PRELOAD shim, wrapper examples, probe tests, and notes from a working setup. It is not a port of the Dropbox software.

Dropbox is a trademark of Dropbox, Inc. FreeBSD is a registered trademark of The FreeBSD Foundation. This independent project is not affiliated with, sponsored by, or endorsed by Dropbox, Inc. or The FreeBSD Foundation. Third-party trademarks are used solely to identify compatibility. See NOTICE.

Made with AI.

Related Projects

What This Solves

The Dropbox desktop app for Linux performs several filesystem checks. Although Dropbox currently lists ZFS among its supported Linux filesystems, a FreeBSD-backed home directory exposed through Linuxulator may not present consistent Linux filesystem metadata. The app can start, show a tray icon, and still refuse or fail to sync because different probes disagree.

The shim makes the observed Dropbox probes internally consistent:

  • statfs, statfs64, fstatfs, fstatfs64, and direct syscall(SYS_statfs/SYS_fstatfs) report EXT4_SUPER_MAGIC.
  • /proc/mounts, /proc/self/mounts, /proc/self/mountinfo, /proc/<pid>/mounts, /proc/<pid>/mountinfo, and /etc/mtab are redirected to generated ext4-looking files.
  • getmntent and getmntent_r rewrite matching mount entries to ext4.
  • statx and direct syscall(SYS_statx) report mount IDs and major/minor values matching the generated mountinfo.
  • listxattr, llistxattr, flistxattr, and direct list-xattr syscalls work around Linuxulator/ZFS cases where xattrs can be set/read but not listed.
  • direct syscall(SYS_fallocate) is emulated for normal allocation and FALLOC_FL_KEEP_SIZE.

The SYS_fallocate piece was the final upload fix in the reference setup. Dropbox detected and hashed local changes, but uploads stayed stuck until raw SYS_fallocate stopped returning EOPNOTSUPP.

Architecture

flowchart TD
    User["FreeBSD user shell<br/>(uid=alice)"]
    Wrapper["~/.local/bin/dropbox-linux<br/>validate shim owner + mode"]
    Chroot["chroot -n -u alice -g alice /compat/ubuntu<br/>(no new privileges)"]
    Env["env: LD_PRELOAD=shim.so<br/>DROPBOX_EXT4_SHIM_PATH, MOUNTS, MOUNTINFO"]
    Dropbox["/usr/bin/dropbox CLI<br/>~/.dropbox-dist/dropboxd daemon"]
    Shim["dropbox_ext4_shim.so<br/>(LD_PRELOAD)"]
    Libc["glibc<br/>statfs, open, getmntent,<br/>statx, listxattr, fallocate, syscall"]
    Kernel["FreeBSD Linuxulator<br/>(Linux syscall ABI on ZFS/UFS)"]
    State["user-owned state:<br/>fake mounts, fake mountinfo"]
    Cloud[("Dropbox cloud")]

    User --> Wrapper
    Wrapper -->|reads| State
    Wrapper --> Chroot --> Env --> Dropbox
    Dropbox -->|every libc call goes through| Shim
    Shim -->|forwards or rewrites| Libc
    Libc --> Kernel
    Dropbox <-->|HTTPS| Cloud
Loading

The shim sits between the Dropbox process and glibc. It only rewrites the small set of probes Dropbox uses to validate the sync filesystem; everything else passes through unchanged.

How the Dropbox Linux Client Sees the Filesystem

Dropbox for Linux is a proprietary daemon plus a Python CLI wrapper. The daemon does not trust a single filesystem signal. It cross-checks several independent surfaces and refuses to sync (or refuses to upload) when they disagree or when any of them reports an unsupported filesystem.

flowchart LR
    subgraph Probes["Dropbox filesystem probes"]
        direction LR
        P1["statfs / statfs64"]
        P2["fstatfs / fstatfs64"]
        P3["raw syscall(SYS_statfs /<br/>SYS_fstatfs / SYS_statx)"]
        P4["open / fopen of<br/>/proc/mounts,<br/>/proc/self/mountinfo,<br/>/etc/mtab"]
        P5["getmntent /<br/>getmntent_r"]
        P6["statx + statx(fd,'',AT_EMPTY_PATH)"]
        P7["setxattr / getxattr /<br/>listxattr"]
        P8["raw syscall(SYS_fallocate)<br/>for upload block staging"]
    end
    Decision{"All probes<br/>agree on<br/>ext4?"}
    OutcomeOK["Sync allowed,<br/>uploads complete"]
    OutcomeBad["Popup:<br/>'unsupported filesystem'<br/>or silent upload stall"]

    Probes --> Decision
    Decision -->|yes| OutcomeOK
    Decision -->|no| OutcomeBad
Loading

In the reference setup each probe hit a different FreeBSD/Linuxulator mismatch and the shim had to make every surface report the same consistent ext4-compatible view:

Probe What Linuxulator/ZFS returned What the shim answers
statfs(path) f_type = 0x0 (unknown) EXT4_SUPER_MAGIC (0xEF53) for paths under DROPBOX_EXT4_SHIM_PATH
fstatfs(fd) same as above EXT4_SUPER_MAGIC while the env var is set
syscall(SYS_statfs) same as above patched in the raw syscall path
/proc/mounts, /etc/mtab real FreeBSD mounts (zfs, nullfs, linprocfs) redirected to dropbox_fake_mounts
/proc/self/mountinfo, /proc/<pid>/mountinfo same redirected to dropbox_fake_mountinfo
getmntent / getmntent_r iterates the real table entries under the sync prefix rewritten to ext4
statx(path) and statx(fd, "", AT_EMPTY_PATH) real device and mount IDs rewritten to match the fake mountinfo
listxattr family EOPNOTSUPP even when setxattr worked returns user.com.dropbox.attrs when that attribute exists
raw syscall(SYS_fallocate) EOPNOTSUPP from Linuxulator/ZFS emulated for mode=0 (via ftruncate) and FALLOC_FL_KEEP_SIZE

The final blocker was SYS_fallocate. Dropbox detected local changes, hashed them, and queued uploads, but the local block cache stayed empty because raw SYS_fallocate returned EOPNOTSUPP. Once the shim emulated the two modes Dropbox uses, the stuck queue drained.

See the design notes for how the compatibility surfaces fit together and the troubleshooting guide for common failure modes.

Status

Known-good reference behavior:

  • Dropbox daemon starts through the wrapper.
  • Tray icon appears in KDE Plasma.
  • Remote-to-local sync works.
  • Local-to-remote add/edit/delete sync works.
  • dropbox status returns Up to date after test uploads.

Known limitations:

  • This is a compatibility shim for one class of FreeBSD/Linuxulator behavior, not a guarantee for future Dropbox builds.
  • It does not implement a full Linux filesystem layer.
  • Unsupported fallocate modes such as hole punching still return unsupported.
  • FALLOC_FL_KEEP_SIZE is a compatibility acknowledgement, not a real disk-space reservation. Keep sufficient free space and backups.
  • This is not a security sandbox. Run the wrapper only as the normal Dropbox user, never as root.
  • rules.dropboxignore is not a Dropbox feature; Dropbox will not read it unless another helper translates it into real excludes or ignore metadata.

Prerequisites

  • FreeBSD with Linuxulator configured.
  • An Ubuntu or similar Linux userland under /compat/ubuntu.
  • The official Dropbox Linux package installed inside that userland.
  • clang on FreeBSD.
  • /home or the selected sync path visible inside the Linux chroot.

The examples assume:

FreeBSD home:       /home/alice
Ubuntu chroot:      /compat/ubuntu
Linux user:         alice
Dropbox folder:     /home/alice/Dropbox
Repository checkout /home/alice/linuxulator_filesystem_compatibility_shim

Adjust paths for your machine.

Build

git clone git@github.com:costis-t/linuxulator_filesystem_compatibility_shim.git ~/linuxulator_filesystem_compatibility_shim
cd ~/linuxulator_filesystem_compatibility_shim
./scripts/build.sh

Equivalent make command:

make UBUNTU_SYSROOT=/compat/ubuntu

The shared object is written to:

build/dropbox_ext4_shim.so

Configure Mount Metadata

Generate fake mount files:

export DROPBOX_EXT4_SHIM_PATH="$HOME/Dropbox"
export DROPBOX_EXT4_SHIM_HOME_PREFIX=/home
./scripts/generate-fake-mounts.sh

By default this writes:

~/.local/share/linuxulator_filesystem_compatibility_shim/dropbox_fake_mounts
~/.local/share/linuxulator_filesystem_compatibility_shim/dropbox_fake_mountinfo

The default fake device/mount IDs are:

/             dev 8:1  mount id 100
/home         dev 8:2  mount id 101
$HOME/Dropbox dev 8:3  mount id 102

These do not need to be real block devices. They need to be consistent across fake mountinfo and shimmed statx results. Override them with:

DROPBOX_EXT4_SHIM_ROOT_DEV_MAJOR
DROPBOX_EXT4_SHIM_ROOT_DEV_MINOR
DROPBOX_EXT4_SHIM_ROOT_MNT_ID
DROPBOX_EXT4_SHIM_HOME_DEV_MAJOR
DROPBOX_EXT4_SHIM_HOME_DEV_MINOR
DROPBOX_EXT4_SHIM_HOME_MNT_ID
DROPBOX_EXT4_SHIM_SYNC_DEV_MAJOR
DROPBOX_EXT4_SHIM_SYNC_DEV_MINOR
DROPBOX_EXT4_SHIM_SYNC_MNT_ID

Install Wrapper

Copy and edit the wrapper example:

mkdir -p ~/.local/bin
cp examples/dropbox-linux.example ~/.local/bin/dropbox-linux
chmod 755 ~/.local/bin/dropbox-linux

Edit at least:

UBUNTU_CHROOT=/compat/ubuntu
LINUX_USER=alice
LINUXULATOR_FS_SHIM_DIR=/home/alice/linuxulator_filesystem_compatibility_shim
DROPBOX_EXT4_SHIM_PATH=/home/alice/Dropbox
DROPBOX_EXT4_SHIM_HOME_PREFIX=/home

Then use only the wrapper:

~/.local/bin/dropbox-linux start
~/.local/bin/dropbox-linux status
~/.local/bin/dropbox-linux filestatus "$HOME/Dropbox"

Do not start Dropbox directly with dropbox start -i or ~/.dropbox-dist/dropboxd, because those bypass the chroot and shim environment.

Optional restart helper:

cp examples/dropbox-linux-restart.example ~/.local/bin/dropbox-linux-restart
chmod 755 ~/.local/bin/dropbox-linux-restart

Optional KDE autostart:

mkdir -p ~/.config/autostart
cp examples/dropbox-linux-autostart.desktop ~/.config/autostart/dropbox-linux.desktop

Validate the Shim

Run the probe inside the Linux chroot through the same shim environment. Example:

/usr/sbin/chroot -n -u "$USER" -g "$USER" /compat/ubuntu /usr/bin/env \
  HOME="$HOME" \
  USER="$USER" \
  LOGNAME="$USER" \
  LD_PRELOAD="$HOME/linuxulator_filesystem_compatibility_shim/build/dropbox_ext4_shim.so" \
  DROPBOX_EXT4_SHIM_PATH="$HOME/Dropbox" \
  DROPBOX_EXT4_SHIM_HOME_PREFIX=/home \
  DROPBOX_EXT4_SHIM_MOUNTS="$HOME/.local/share/linuxulator_filesystem_compatibility_shim/dropbox_fake_mounts" \
  DROPBOX_EXT4_SHIM_MOUNTINFO="$HOME/.local/share/linuxulator_filesystem_compatibility_shim/dropbox_fake_mountinfo" \
  /usr/bin/python3 "$HOME/linuxulator_filesystem_compatibility_shim/tests/probe.py"

Expected shape:

SYS_fallocate mode=0 rc 0 errno 0
SYS_fallocate KEEP_SIZE rc 0 errno 0
SYS_statx fd-empty rc 0 errno 0 dev 8:3 mnt 102
SYS_listxattr rc ... errno 0 names b'user.com.dropbox.attrs\0'
SYS_flistxattr rc ... errno 0 names b'user.com.dropbox.attrs\0'

Debugging Upload Stalls

If Dropbox says Uploading N files... forever:

~/.local/bin/dropbox-linux status
~/.local/bin/dropbox-linux filestatus "$HOME/Dropbox/path/to/file"

Inspect sync history:

sqlite3 "$HOME/.dropbox/instance*/sync_history.db" \
  "select datetime(timestamp,'unixepoch'), event_type, file_event_type, direction, local_path from sync_history order by timestamp desc limit 20;"

Test raw fallocate inside the chroot. If direct SYS_fallocate returns errno 95 without the shim and succeeds with it, you are seeing the same failure mode.

See docs/troubleshooting.md for additional checks and guidance on reporting a new failure.

Design Notes

The public design overview is in docs/design.md.

Security

Read SECURITY.md before deploying or forking. It documents the threat model, the issues fixed in this review, the behaviors that are intentional and why, and the operating requirements.

Short version:

  • Run the wrapper only as the normal Dropbox user. Never as root. The wrapper and the generator both refuse uid 0.
  • The wrapper now refuses to start if build/dropbox_ext4_shim.so is missing, owned by another uid, or writable by group/other. Do not bypass that check.
  • The wrapper and the generator also validate the state directory and the generated dropbox_fake_mounts / dropbox_fake_mountinfo files with the same ownership and mode rule. The generator uses umask 077; keep it that way.
  • The generator refuses DROPBOX_EXT4_SHIM_PATH or DROPBOX_EXT4_SHIM_HOME_PREFIX values containing a newline, tab, carriage return, or backslash. Spaces are still escaped via \040.
  • The shim overrides only the small surface of filesystem information the Dropbox desktop app probes. It is not a sandbox and it is not a filesystem layer.
  • If your account is a Dropbox Business account, third-party API tools such as rclone may require admin approval. This shim avoids that by running the official Dropbox Linux client rather than a separate Dropbox API application.

Deployment checklist before you publicise a fork

  • Default DROPBOX_EXT4_SHIM_PATH is $HOME or narrower, never /.
  • examples/dropbox-linux.example is the only entry point; the stock dropbox start -i desktop entry is hidden or overridden.
  • build/dropbox_ext4_shim.so is owned by the user and has mode 0755 or stricter.
  • The state directory (~/.local/share/linuxulator_filesystem_compatibility_shim by default) and the fake-mount files inside it are owned by the user and not writable by group/other. The wrapper now checks this for you; the generator uses umask 077.
  • DROPBOX_EXT4_SHIM_PATH and DROPBOX_EXT4_SHIM_HOME_PREFIX do not contain newline, tab, carriage return, or backslash. The generator rejects them; if you need spaces, the existing escape (\040) handles it.
  • You have run tests/probe.py through the wrapper and seen rc 0 errno 0 for the syscall probes you rely on.
  • You have read SECURITY.md and confirmed your deployment matches the documented trust model.

Release Archives

Tagged releases attach archives produced by scripts/package-release.sh and include SHA256SUMS.

About

Filesystem compatibility shim for running the Dropbox Linux client through FreeBSD Linuxulator.

Topics

Resources

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages