CLI tool
winfsp/cgofuse avatar
winfsp/cgofuse

cgofuse: one FUSE binding for Go across six operating systems

Cross-platform FUSE library for Go - Works on Windows, macOS, Linux, FreeBSD, NetBSD, OpenBSD

647 stars103 forksGoMIT

At a glance

What is it?
cgofuse is a Go library for writing file systems on top of FUSE, with build variants for cgo, a no-cgo path for Windows, FUSE2 and FUSE3, covering Windows, macOS, Linux, FreeBSD, NetBSD and OpenBSD. The API is small, a FileSystemInterface plus a base class and a host, and the platform caveats are documented rather than discovered.
Who is it for?
Adopt cgofuse when you are writing a file system in Go and need it to work on more than one operating system, particularly on Windows alongside Unix, where the no-cgo path removes a compiler dependency. Do not adopt it if your file system needs to be high performance, since every path in the library crosses into C, or if you need NetBSD or OpenBSD to be production quality, since the README marks both as experimental.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 145 days ago.
What is it written in?
Mainly Go, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

One library, four build variants, six operating systems

The design of cgofuse is a single API with the platform differences pushed into build tags, and the README's opening paragraph states the approach. It is a cross-platform FUSE library for Go, supported on multiple platforms and portable to any platform that has a FUSE implementation, with cgo, !cgo (nocgo) and FUSE, also called FUSE2, and FUSE3 variants depending on the platform.

The matrix at the top of the README is the whole story of the project in one table. Windows supports cgo, !cgo and FUSE. macOS supports cgo and FUSE. Linux and FreeBSD support cgo, FUSE and FUSE3. NetBSD and OpenBSD support cgo and FUSE only, with no FUSE3 column ticked.

That grid is the practical answer to a problem Go developers hit constantly. The FUSE API is a C API, so every binding either uses cgo, which requires a C toolchain, or uses the Windows DLL mechanism, which does not. The !cgo column is the one that matters most on Windows, since it removes gcc from the build requirement entirely.

For Unix, the choice is between the two libfuse generations. FUSE2 is the older API that most existing code targets, and FUSE3 is the current one. Supporting both, and allowing a build to pick with the `fuse3` tag, means a library can be compiled against whichever libfuse a given distribution ships without maintaining two code paths.

The module is `github.com/winfsp/cgofuse` and the go.mod declares a Go language version of 1.17, which is a low floor and says the project has not needed a recent toolchain to stay compatible.

The API is a file system interface, a base class and a host

The usage section is short because the surface is small, and the three pieces it names are the whole model.

User mode file systems are expected to implement `fuse.FileSystemInterface`. To make implementation simpler, a file system can embed or inherit `fuse.FileSystemBase`, which provides default implementations for all operations. And to mount a file system you instantiate a `fuse.FileSystemHost` using `fuse.NewFileSystemHost`.

That is a familiar shape for anyone who has used a Go interface with an embeddable default implementation, and the choice of an interface plus a base struct is deliberate. A file system only has to implement the operations it cares about, and the base class returns an error for the rest, which is the right default for a file system where an unimplemented operation should be a failure rather than a silent success.

The host is the piece that talks to the platform. It is what the `fuse.NewFileSystemHost` constructor produces, and it is the object that owns the mount and the connection back to the kernel.

The full documentation is at GoDoc.org for the `fuse` package, and the README links it directly. That is where the interface methods, the base class defaults and the host configuration are described, since the README itself only names the three elements and stops there.

Four example file systems, each isolating one capability

The examples are the fastest way to understand the library, and the README lists four of them, each of which isolates a different part of the FUSE surface.

`Hellofs` is described as an extremely simple file system, and it runs on all operating systems. `Memfs` is an in-memory file system, also running on all operating systems. `Passthrough` passes all operations to the underlying file system, and runs on all operating systems except Windows. `Notifyfs` can issue file change notifications, and runs on Windows only.

The split is instructive. Hellofs and Memfs are pure user-space implementations that need no backing store, so they work everywhere, and they are the pair to read first since they show the shape of implementing the interface and handling the basic operations. Memfs is also the one that most resembles a real use case, since an in-memory file system is a plausible product rather than a toy.

Passthrough is the one that shows the file system sitting on top of another file system, forwarding every operation. Its Windows exclusion is not arbitrary, since forwarding to the underlying file system on Windows requires talking to the WinFsp layer in a way that the other examples do not.

Notifyfs is the inverse. It exists to show the notification mechanism, and it is Windows only, which reflects where change notification support is most mature in this stack.

The repository has an `examples/shared/` directory as well, and the examples directory is one of the top level entries, along with the `fuse/` package directory, a `Dockerfile`, a `Changelog.md` and build configuration for AppVeyor and the PMCI continuous integration service.

The build instructions differ on every platform, and the differences are the documentation

The how-to-build section is the longest part of the README, and the length is justified, because the prerequisites and the build command genuinely differ per platform.

On Windows the prerequisites are WinFsp and gcc, for example from Mingw-builds. The cgo build sets `CPATH` to the WinFsp include directory and then runs `go install -v ./fuse ./examples/memfs`. The !cgo build sets `CGO_ENABLED=0` and runs the same install command.

On macOS the prerequisites are macFUSE and the command line tools, and the build installs the fuse package plus the memfs and passthrough examples. On Linux the prerequisites are libfuse-dev, libfuse3-dev and gcc, with a plain build for FUSE2 and a build with `-tags=fuse3` for FUSE3. FreeBSD follows the same shape with fusefs-libs and fusefs-libs3 as prerequisites.

The three Unix build commands are worth reading together:

bash
    $ go install -v ./fuse ./examples/memfs ./examples/passthrough
bash
    $ go install -tags=fuse3 -v ./fuse ./examples/memfs ./examples/passthrough

NetBSD and OpenBSD have no prerequisites at all, which is worth noticing since it means the pure-Go path works there without any C library installed.

The two run-time notes are the ones that cause real friction. On FreeBSD you may need to add `fuse_load="YES"` to `/boot/loader.conf`, set `vfs.usermount=1` to allow user mounts, and add the user to the operator group so it can open `/dev/fuse`, and those commands must be run as root. On NetBSD you need `chmod go+rw /dev/puffs` and `sysctl -w vfs.generic.usermount=1`, also as root.

OpenBSD has a harder constraint. OpenBSD 6 removed the `kern.usermount` option, which allowed non-root users to mount file systems, so you must be root in order to use FUSE and cgofuse at all.

NetBSD and OpenBSD are marked experimental, and the reason is specific

Both BSD notes carry the same warning, and it is worth reading precisely.

For NetBSD, the README says support is experimental and that there are known issues that stem from the differences in the NetBSD librefuse implementation from the reference libfuse implementation. For OpenBSD, the same wording applies, referring to the differences in the OpenBSD libfuse implementation.

The distinction is that librefuse and the OpenBSD libfuse are not the upstream libfuse. They are platform-specific reimplementations, and cgofuse binds against the upstream API. Where the two diverge, cgofuse inherits the divergence, which is why the note points at the differences rather than at a bug in the binding.

That is a fair and honest statement, and it also tells you where the risk sits. A file system implemented on NetBSD or OpenBSD through cgofuse is running on a binding whose contract is not the same as the one the same code gets on Linux, and the difference is in the platform layer, not in your code.

The practical reading is that Linux, FreeBSD, macOS and Windows are the platforms to develop and test on, and the BSDs are for people who need them and are prepared to test. The OpenBSD root requirement makes that concrete, since there is no non-root path at all.

The repository is MIT licensed with the terms in `LICENSE.txt`, the project is not archived, and the last push was on 2026-05-31. The release history is sparse by comparison, with v1.6.0 on 2025-01-13, v1.5.0 on 2021-03-09 and v1.4.0 on 2020-07-24, so a great many years separate the current source from the last tagged release.

Compared with writing to libfuse directly and with a Go-native VFS library

The alternatives divide into two families, and the difference in approach is visible in what each one asks of you.

Writing to libfuse directly from Go, through cgo, is the no-library route. You write your own cgo bindings, your own struct-to-C conversion, and your own handling of the FUSE low-level API. You get full control and no abstraction, and you get to maintain the parts of the binding that are the most tedious and the most likely to be wrong, including the memory handling around paths and buffers, which is where cgo bugs live.

cgofuse is that work done once, for several platforms and both libfuse generations at the same time. The cost is the abstraction itself, and specifically the fact that the API is a Go interface, which means every path crossing the boundary is a cgo call.

The second family is the Go-native virtual filesystem libraries, the ones that build an in-process file system in pure Go without the kernel involved at all. Those are a different tool entirely. They are what you use when you want to present a virtual tree to code inside the same process, such as an embedded database or an archive, with no mount, no privileges and no kernel module. cgofuse is the opposite: it produces a real mount that other programs and the operating system can see, which is what you need when the point is to make a cloud bucket, an archive or a custom store look like a disk.

So the choice is not really cgofuse against a Go VFS library. It is cgofuse against writing your own cgo binding, and the deciding factor is how many platforms you have to support and whether you can live with the boundary-crossing cost on every operation.

Editorial conclusion

Adopt cgofuse when you are writing a file system in Go and need it to work on more than one operating system, particularly on Windows alongside Unix, where the no-cgo path removes a compiler dependency. Do not adopt it if your file system needs to be high performance, since every path in the library crosses into C, or if you need NetBSD or OpenBSD to be production quality, since the README marks both as experimental. Verify first which build variant your target platform supports, since the matrix differs per operating system, and confirm the FUSE version on the host matches the FUSE2 or FUSE3 tag you build with.

Frequently asked questions

What platforms does cgofuse support?

Windows, macOS, Linux, FreeBSD, NetBSD and OpenBSD. The README's matrix shows which build variant works where: Windows supports cgo, !cgo and FUSE, macOS supports cgo and FUSE, Linux and FreeBSD support cgo, FUSE and FUSE3, and NetBSD and OpenBSD support cgo and FUSE only.

How do I implement a file system with cgofuse?

Implement `fuse.FileSystemInterface`, optionally embedding `fuse.FileSystemBase` to inherit default implementations for all operations, then instantiate a `fuse.FileSystemHost` with `fuse.NewFileSystemHost` to mount it. The full reference is the package fuse documentation on GoDoc.org.

How do I build cgofuse against FUSE3 instead of FUSE2?

Add the `fuse3` build tag. The Linux and FreeBSD instructions give `go install -tags=fuse3 -v ./fuse ./examples/memfs ./examples/passthrough` for the FUSE3 variant, and those platforms need libfuse3-dev or fusefs-libs3 as a prerequisite.

Can I build cgofuse on Windows without a C compiler?

Yes. The README documents a !cgo build that sets `CGO_ENABLED=0` and then runs the same go install command, with WinFsp as the prerequisite. The cgo build instead sets `CPATH` to the WinFsp include directory and needs gcc, for example from Mingw-builds.

Is cgofuse stable on NetBSD and OpenBSD?

The README marks support on both as experimental, with known issues stemming from the differences in their librefuse and libfuse implementations compared with the reference libfuse. On OpenBSD you must also be root, because OpenBSD 6 removed the kern.usermount option that allowed non-root users to mount file systems.

Official sources

  1. License: MIT
  2. Project website
  3. README
  4. Releases
  5. winfsp/cgofuse on GitHub
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/winfsp-cgofuse.svg)](https://hysenlabs.com/projects/winfsp-cgofuse)

Was this useful?