Awesome
fuse-bindings
NOTICE
We published the successor module to this at https://github.com/fuse-friends/fuse-native
Fully maintained fuse bindings for Node that aims to cover the entire FUSE api
npm install fuse-bindings
Compared to fuse4js these bindings cover almost the entire FUSE api (except for locking) and doesn't do any buffer copys in read/write. It also supports unmount and mouting of multiple fuse drives.
Requirements
You need to have FUSE installed (or Dokany on Windows)
- On Linux/Ubuntu
sudo apt-get install libfuse-dev
- On OSX
- if you use Brew, install OSXFuse and
brew install pkg-config
- if you use MacPorts,
sudo port install osxfuse +devel
- if you use Brew, install OSXFuse and
- On Windows install Dokany
Windows
WARNING: Dokany is still not quite stable. It can cause BSODs. Be careful.
Using this on Windows is slightly more complicated. You need to install Dokany (for dokanfuse.lib
, dokanctl.exe
, driver and service) and clone its repo (for the headers).
Once the Dokany repo is cloned, you also need to set environment variable DOKAN_INSTALL_DIR
to the path to DokenLibrary
of your Dokany installaton, and DOKAN_FUSE_INCLUDE
to the path to *dokany repo*\dokan_fuse\include
.
EDIT: Dokany now includes needed headers and sets proper environment variables when installing! Just install Dokany and this module should install and work just fine! (Drop an issue otherwise)
Usage
Try creating an empty folder called mnt
and run the below example
var fuse = require('fuse-bindings')
var mountPath = process.platform !== 'win32' ? './mnt' : 'M:\\'
fuse.mount(mountPath, {
readdir: function (path, cb) {
console.log('readdir(%s)', path)
if (path === '/') return cb(0, ['test'])
cb(0)
},
getattr: function (path, cb) {
console.log('getattr(%s)', path)
if (path === '/') {
cb(0, {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
nlink: 1,
size: 100,
mode: 16877,
uid: process.getuid ? process.getuid() : 0,
gid: process.getgid ? process.getgid() : 0
})
return
}
if (path === '/test') {
cb(0, {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
nlink: 1,
size: 12,
mode: 33188,
uid: process.getuid ? process.getuid() : 0,
gid: process.getgid ? process.getgid() : 0
})
return
}
cb(fuse.ENOENT)
},
open: function (path, flags, cb) {
console.log('open(%s, %d)', path, flags)
cb(0, 42) // 42 is an fd
},
read: function (path, fd, buf, len, pos, cb) {
console.log('read(%s, %d, %d, %d)', path, fd, len, pos)
var str = 'hello world\n'.slice(pos, pos + len)
if (!str) return cb(0)
buf.write(str)
return cb(str.length)
}
}, function (err) {
if (err) throw err
console.log('filesystem mounted on ' + mountPath)
})
process.on('SIGINT', function () {
fuse.unmount(mountPath, function (err) {
if (err) {
console.log('filesystem at ' + mountPath + ' not unmounted', err)
} else {
console.log('filesystem at ' + mountPath + ' unmounted')
}
})
})
See also
fs-fuse is a wrapper module build on top of fuse-bindings
that allow you to export and mount any fs
-like object as a FUSE filesystem.
API
fuse.mount(mnt, ops, [cb])
Mount a new filesystem on mnt
.
Pass the FUSE operations you want to support as the ops
argument.
fuse.unmount(mnt, [cb])
Unmount a filesystem
fuse.context()
Returns the current fuse context (pid, uid, gid). Must be called inside a fuse callback.
Mount options
ops.options
Set mount options
ops.options = ['direct_io'] // set the direct_io option
ops.displayFolder
Set to true
to make OSX display a folder icon and the folder name as the mount point in finder
ops.force
Set to true
to force mount the filesystem (will do an unmount first)
FUSE operations
Most of the FUSE api is supported. In general the callback for each op should be called with cb(returnCode, [value])
where the return code is a number (0
for OK and < 0
for errors). See below for a list of POSIX error codes.
ops.init(cb)
Called on filesystem init.
ops.access(path, mode, cb)
Called before the filesystem accessed a file
ops.statfs(path, cb)
Called when the filesystem is being stat'ed. Accepts a fs stat object after the return code in the callback.
ops.statfs = function (path, cb) {
cb(0, {
bsize: 1000000,
frsize: 1000000,
blocks: 1000000,
bfree: 1000000,
bavail: 1000000,
files: 1000000,
ffree: 1000000,
favail: 1000000,
fsid: 1000000,
flag: 1000000,
namemax: 1000000
})
}
ops.getattr(path, cb)
Called when a path is being stat'ed. Accepts a stat object (similar to the one returned in fs.stat(path, cb)
) after the return code in the callback.
ops.getattr = function (path, cb) {
cb(0, {
mtime: new Date(),
atime: new Date(),
ctime: new Date(),
size: 100,
mode: 16877,
uid: process.getuid(),
gid: process.getgid()
})
}
ops.fgetattr(path, fd, cb)
Same as above but is called when someone stats a file descriptor
ops.flush(path, fd, cb)
Called when a file descriptor is being flushed
ops.fsync(path, fd, datasync, cb)
Called when a file descriptor is being fsync'ed.
ops.fsyncdir(path, fd, datasync, cb)
Same as above but on a directory
ops.readdir(path, cb)
Called when a directory is being listed. Accepts an array of file/directory names after the return code in the callback
ops.readdir = function (path, cb) {
cb(0, ['file-1.txt', 'dir'])
}
ops.truncate(path, size, cb)
Called when a path is being truncated to a specific size
ops.ftruncate(path, fd, size, cb)
Same as above but on a file descriptor
ops.readlink(path, cb)
Called when a symlink is being resolved. Accepts a pathname (that the link should resolve to) after the return code in the callback
ops.readlink = function (path, cb) {
cb(null, 'file.txt') // make link point to file.txt
}
ops.chown(path, uid, gid, cb)
Called when ownership of a path is being changed
ops.chmod(path, mode, cb)
Called when the mode of a path is being changed
ops.mknod(path, mode, dev, cb)
Called when the a new device file is being made.
ops.setxattr(path, name, buffer, length, offset, flags, cb)
Called when extended attributes is being set (see the extended docs for your platform).
Currently you can read the attribute value being set in buffer
at offset
.
ops.getxattr(path, name, buffer, length, offset, cb)
Called when extended attributes is being read.
Currently you have to write the result to the provided buffer
at offset
.
ops.listxattr(path, buffer, length, cb)
Called when extended attributes of a path are being listed.
buffer
should be filled with the extended attribute names as null-terminated strings, one after the other, up to a total of length
in length. (ERANGE
should be passed to the callback if length
is insufficient.)
The size of buffer required to hold all the names should be passed to the callback either on success, or if the supplied length
was zero.
ops.removexattr(path, name, cb)
Called when an extended attribute is being removed.
ops.open(path, flags, cb)
Called when a path is being opened. flags
in a number containing the permissions being requested. Accepts a file descriptor after the return code in the callback.
var toFlag = function(flags) {
flags = flags & 3
if (flags === 0) return 'r'
if (flags === 1) return 'w'
return 'r+'
}
ops.open = function (path, flags, cb) {
var flag = toFlag(flags) // convert flags to a node style string
...
cb(0, 42) // 42 is a file descriptor
}
ops.opendir(path, flags, cb)
Same as above but for directories
ops.read(path, fd, buffer, length, position, cb)
Called when contents of a file is being read. You should write the result of the read to the buffer
and return the number of bytes written as the first argument in the callback.
If no bytes were written (read is complete) return 0 in the callback.
var data = new Buffer('hello world')
ops.read = function (path, fd, buffer, length, position, cb) {
if (position >= data.length) return cb(0) // done
var part = data.slice(position, position + length)
part.copy(buffer) // write the result of the read to the result buffer
cb(part.length) // return the number of bytes read
}
ops.write(path, fd, buffer, length, position, cb)
Called when a file is being written to. You can get the data being written in buffer
and you should return the number of bytes written in the callback as the first argument.
ops.write = function (path, fd, buffer, length, position, cb) {
console.log('writing', buffer.slice(0, length))
cb(length) // we handled all the data
}
ops.release(path, fd, cb)
Called when a file descriptor is being released. Happens when a read/write is done etc.
ops.releasedir(path, fd, cb)
Same as above but for directories
ops.create(path, mode, cb)
Called when a new file is being opened.
ops.utimens(path, atime, mtime, cb)
Called when the atime/mtime of a file is being changed.
ops.unlink(path, cb)
Called when a file is being unlinked.
ops.rename(src, dest, cb)
Called when a file is being renamed.
ops.link(src, dest, cb)
Called when a new link is created.
ops.symlink(src, dest, cb)
Called when a new symlink is created
ops.mkdir(path, mode, cb)
Called when a new directory is being created
ops.rmdir(path, cb)
Called when a directory is being removed
ops.destroy(cb)
Both read
and write
passes the underlying fuse buffer without copying them to be as fast as possible.
Error codes
The available error codes are exposes as well as properties. These include
fuse.EPERM === -1
fuse.ENOENT === -2
fuse.ESRCH === -3
fuse.EINTR === -4
fuse.EIO === -5
fuse.ENXIO === -6
fuse.E2BIG === -7
fuse.ENOEXEC === -8
fuse.EBADF === -9
fuse.ECHILD === -10
fuse.EAGAIN === -11
fuse.ENOMEM === -12
fuse.EACCES === -13
fuse.EFAULT === -14
fuse.ENOTBLK === -15
fuse.EBUSY === -16
fuse.EEXIST === -17
fuse.EXDEV === -18
fuse.ENODEV === -19
fuse.ENOTDIR === -20
fuse.EISDIR === -21
fuse.EINVAL === -22
fuse.ENFILE === -23
fuse.EMFILE === -24
fuse.ENOTTY === -25
fuse.ETXTBSY === -26
fuse.EFBIG === -27
fuse.ENOSPC === -28
fuse.ESPIPE === -29
fuse.EROFS === -30
fuse.EMLINK === -31
fuse.EPIPE === -32
fuse.EDOM === -33
fuse.ERANGE === -34
fuse.EDEADLK === -35
fuse.ENAMETOOLONG === -36
fuse.ENOLCK === -37
fuse.ENOSYS === -38
fuse.ENOTEMPTY === -39
fuse.ELOOP === -40
fuse.EWOULDBLOCK === -11
fuse.ENOMSG === -42
fuse.EIDRM === -43
fuse.ECHRNG === -44
fuse.EL2NSYNC === -45
fuse.EL3HLT === -46
fuse.EL3RST === -47
fuse.ELNRNG === -48
fuse.EUNATCH === -49
fuse.ENOCSI === -50
fuse.EL2HLT === -51
fuse.EBADE === -52
fuse.EBADR === -53
fuse.EXFULL === -54
fuse.ENOANO === -55
fuse.EBADRQC === -56
fuse.EBADSLT === -57
fuse.EDEADLOCK === -35
fuse.EBFONT === -59
fuse.ENOSTR === -60
fuse.ENODATA === -61
fuse.ETIME === -62
fuse.ENOSR === -63
fuse.ENONET === -64
fuse.ENOPKG === -65
fuse.EREMOTE === -66
fuse.ENOLINK === -67
fuse.EADV === -68
fuse.ESRMNT === -69
fuse.ECOMM === -70
fuse.EPROTO === -71
fuse.EMULTIHOP === -72
fuse.EDOTDOT === -73
fuse.EBADMSG === -74
fuse.EOVERFLOW === -75
fuse.ENOTUNIQ === -76
fuse.EBADFD === -77
fuse.EREMCHG === -78
fuse.ELIBACC === -79
fuse.ELIBBAD === -80
fuse.ELIBSCN === -81
fuse.ELIBMAX === -82
fuse.ELIBEXEC === -83
fuse.EILSEQ === -84
fuse.ERESTART === -85
fuse.ESTRPIPE === -86
fuse.EUSERS === -87
fuse.ENOTSOCK === -88
fuse.EDESTADDRREQ === -89
fuse.EMSGSIZE === -90
fuse.EPROTOTYPE === -91
fuse.ENOPROTOOPT === -92
fuse.EPROTONOSUPPORT === -93
fuse.ESOCKTNOSUPPORT === -94
fuse.EOPNOTSUPP === -95
fuse.EPFNOSUPPORT === -96
fuse.EAFNOSUPPORT === -97
fuse.EADDRINUSE === -98
fuse.EADDRNOTAVAIL === -99
fuse.ENETDOWN === -100
fuse.ENETUNREACH === -101
fuse.ENETRESET === -102
fuse.ECONNABORTED === -103
fuse.ECONNRESET === -104
fuse.ENOBUFS === -105
fuse.EISCONN === -106
fuse.ENOTCONN === -107
fuse.ESHUTDOWN === -108
fuse.ETOOMANYREFS === -109
fuse.ETIMEDOUT === -110
fuse.ECONNREFUSED === -111
fuse.EHOSTDOWN === -112
fuse.EHOSTUNREACH === -113
fuse.EALREADY === -114
fuse.EINPROGRESS === -115
fuse.ESTALE === -116
fuse.EUCLEAN === -117
fuse.ENOTNAM === -118
fuse.ENAVAIL === -119
fuse.EISNAM === -120
fuse.EREMOTEIO === -121
fuse.EDQUOT === -122
fuse.ENOMEDIUM === -123
fuse.EMEDIUMTYPE === -124
License
MIT