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.
- FreeBSD and its Linux binary compatibility layer (Linuxulator)
- The official Dropbox for Linux client
- Ubuntu as the reference Linux userland
- LLVM/Clang as the cross-compiling toolchain
- OpenZFS as the backing filesystem in the reference setup
- KDE Plasma for the optional tray and autostart integration
- rclone as an alternative Dropbox client when third-party OAuth applications are permitted
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 directsyscall(SYS_statfs/SYS_fstatfs)reportEXT4_SUPER_MAGIC./proc/mounts,/proc/self/mounts,/proc/self/mountinfo,/proc/<pid>/mounts,/proc/<pid>/mountinfo, and/etc/mtabare redirected to generated ext4-looking files.getmntentandgetmntent_rrewrite matching mount entries toext4.statxand directsyscall(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 andFALLOC_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.
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
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.
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
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.
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 statusreturnsUp to dateafter 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
fallocatemodes such as hole punching still return unsupported. FALLOC_FL_KEEP_SIZEis 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.dropboxignoreis not a Dropbox feature; Dropbox will not read it unless another helper translates it into real excludes or ignore metadata.
- FreeBSD with Linuxulator configured.
- An Ubuntu or similar Linux userland under
/compat/ubuntu. - The official Dropbox Linux package installed inside that userland.
clangon FreeBSD./homeor 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.
git clone git@github.com:costis-t/linuxulator_filesystem_compatibility_shim.git ~/linuxulator_filesystem_compatibility_shim
cd ~/linuxulator_filesystem_compatibility_shim
./scripts/build.shEquivalent make command:
make UBUNTU_SYSROOT=/compat/ubuntuThe shared object is written to:
build/dropbox_ext4_shim.so
Generate fake mount files:
export DROPBOX_EXT4_SHIM_PATH="$HOME/Dropbox"
export DROPBOX_EXT4_SHIM_HOME_PREFIX=/home
./scripts/generate-fake-mounts.shBy 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_IDCopy and edit the wrapper example:
mkdir -p ~/.local/bin
cp examples/dropbox-linux.example ~/.local/bin/dropbox-linux
chmod 755 ~/.local/bin/dropbox-linuxEdit 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=/homeThen 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-restartOptional KDE autostart:
mkdir -p ~/.config/autostart
cp examples/dropbox-linux-autostart.desktop ~/.config/autostart/dropbox-linux.desktopRun 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'
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.
The public design overview is in docs/design.md.
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.sois 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_mountinfofiles with the same ownership and mode rule. The generator usesumask 077; keep it that way. - The generator refuses
DROPBOX_EXT4_SHIM_PATHorDROPBOX_EXT4_SHIM_HOME_PREFIXvalues 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.
- Default
DROPBOX_EXT4_SHIM_PATHis$HOMEor narrower, never/. -
examples/dropbox-linux.exampleis the only entry point; the stockdropbox start -idesktop entry is hidden or overridden. -
build/dropbox_ext4_shim.sois owned by the user and has mode0755or stricter. - The state directory (
~/.local/share/linuxulator_filesystem_compatibility_shimby 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 usesumask 077. -
DROPBOX_EXT4_SHIM_PATHandDROPBOX_EXT4_SHIM_HOME_PREFIXdo 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.pythrough the wrapper and seenrc 0 errno 0for the syscall probes you rely on. - You have read SECURITY.md and confirmed your deployment matches the documented trust model.
Tagged releases attach archives produced by scripts/package-release.sh and include SHA256SUMS.