Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 19 additions & 8 deletions INSTALL.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,25 +10,36 @@ sudo apt-get install fuse

**macOS**

Download and install from https://osxfuse.github.io/ or:

```sh
brew install fuse
```
Download and install from https://osxfuse.github.io/

## Node.js

Install Node.js from https://nodejs.org/.

## IPFS

Install and run the `go-ipfs` daemon from https://ipfs.io/. I know, I know, but until `js-ipfs` gets MFS this project is not useable with it.
OPTIONAL! You do not need to install IPFS at all because `ipfs-fuse` comes bundled with a JS IPFS that will run by default.

`ipfs-fuse` also works with both `go-ipfs` and `js-ipfs` daemons:

### JS

Install and run the `js-ipfs` daemon:

```sh
ipfs daemon
npm install ipfs
jsipfs daemon
```

Note, currently your daemon API needs to be running on the default port.
Note: to connect to a JS daemon, you must pass the API multiaddr to `ipfs-fuse` when starting. e.g. `--api-addr=/ip4/127.0.0.1/tcp/5002`

### Go

Install and run the `go-ipfs` daemon by downloading from https://ipfs.io/.

```sh
ipfs daemon
```

## IPFS FUSE

Expand Down
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,28 @@ npm_config_python=/usr/local/opt/python2/bin/python2 npm i -g ipfs-fuse

## Usage

Mount IPFS MFS on `~/IPFS` or `I://` (windows).
Mount JS IPFS MFS on `~/IPFS` or `I://` (windows).

```sh
ipfs-fuse
```

### Connect to running daemon

```sh
ipfs-fuse --daemon
```

You can also specify the multiaddr your daemon is running on for if you're connecting to a JS IPFS daemon for example:

```sh
ipfs-fuse --daemon --api-addr=/ip4/127.0.0.1/tcp/5002
```

## Contribute

Feel free to dive in! [Open an issue](https://github.com/alanshaw/ipfs-fuse/issues/new) or submit PRs.

## License

[MIT](LICENSE) © Alan Shaw
15 changes: 11 additions & 4 deletions bin.js
100644 → 100755
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,23 @@

const Os = require('os')
const Path = require('path')
const argv = require('minimist')(process.argv.slice(2))
const IpfsFuse = require('./')

const mountPath = process.platform !== 'win32'
? Path.join(Os.homedir(), 'IPFS')
: 'I:\\'

let fileSystem

IpfsFuse.mount(mountPath, {
ipfs: {},
fuse: { displayFolder: true, force: true }
}, (err) => {
ipfs: argv['api-addr'] || {},
fuse: { displayFolder: true, force: true },
daemon: argv.daemon
}, (err, fs) => {
if (err) return console.error(err.message)
console.log(`Mounted IPFS filesystem on ${mountPath}`)
fileSystem = fs
})

let destroyed = false
Expand All @@ -23,7 +28,9 @@ process.on('SIGINT', () => {

destroyed = true

IpfsFuse.unmount(mountPath, (err) => {
if (!fileSystem) return

fileSystem.unmount(err => {
if (err) return console.error(err.message)
console.log(`Unmounted IPFS filesystem at ${mountPath}`)
})
Expand Down
77 changes: 56 additions & 21 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
const Fuse = require('fuse-bindings')
const debug = require('debug')('ipfs-fuse:index')
const IpfsApi = require('ipfs-api')
const mkdirp = require('mkdirp')
const Async = require('async')
const explain = require('explain-error')
Expand Down Expand Up @@ -28,48 +27,84 @@ exports.mount = (mountPath, opts, cb) => {
})
},
ipfs (cb) {
const ipfs = new IpfsApi(opts.ipfs)
if (opts.daemon) return cb(null, require('ipfs-api')(opts.ipfs))

ipfs.id((err, id) => {
const Ipfs = require('ipfs')
const ipfs = new Ipfs(opts.ipfs)

const onReady = () => {
ipfs.removeListener('ready', onReady)
ipfs.removeListener('error', onError)
cb(null, ipfs)
}

const onError = err => {
ipfs.removeListener('ready', onReady)
ipfs.removeListener('error', onError)
cb(err)
}

ipfs.on('ready', onReady).on('error', onError)
},
id: ['ipfs', (res, cb) => {
res.ipfs.id((err, id) => {
if (err) {
err = explain(err, 'Failed to connect to IPFS node')
debug(err)
return cb(err)
}

debug(id)
cb(null, ipfs)
cb(null, id)
})
},
mount: ['path', 'ipfs', (res, cb) => {
Fuse.mount(mountPath, createIpfsFuse(res.ipfs), opts.fuse, (err) => {
}],
mount: ['path', 'ipfs', 'id', (res, cb) => {
Fuse.mount(mountPath, createIpfsFuse(res.ipfs, res.id), opts.fuse, (err) => {
if (err) {
err = explain(err, 'Failed to mount IPFS FUSE volume')
debug(err)
return cb(err)
}

cb(null, {})
cb()
})
}]
}, (err) => {
}, (err, res) => {
if (err) {
debug(err)
return cb(err)
}
cb(null, {})
})
}

exports.unmount = (mountPath, cb) => {
cb = cb || (() => {})
cb(null, {
unmount (cb) {
cb = cb || (() => {})

Fuse.unmount(mountPath, (err) => {
if (err) {
err = explain(err, 'Failed to unmount IPFS FUSE volume')
debug(err)
return cb(err)
}
cb()
Async.parallel([
cb => {
Fuse.unmount(mountPath, err => {
if (err) {
err = explain(err, 'Failed to unmount IPFS FUSE volume')
debug(err)
return cb(err)
}
cb()
})
},
cb => {
// Do not stop a daemon...
if (opts.daemon) return cb()

res.ipfs.stop(err => {
if (err) {
err = explain(err, 'Failed to stop IPFS node')
debug(err)
return cb(err)
}
cb()
})
}
], cb)
}
})
})
}
30 changes: 15 additions & 15 deletions ipfs-fuse/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,19 +13,19 @@ const createUnlink = require('./unlink')
const createUtimens = require('./utimens')
const createWrite = require('./write')

module.exports = (ipfs) => Object.assign(
createCreate(ipfs),
createFtruncate(ipfs),
createGetattr(ipfs),
createMkdir(ipfs),
createMknod(ipfs),
createOpen(ipfs),
createRead(ipfs),
createReaddir(ipfs),
createRename(ipfs),
createRmdir(ipfs),
createStatfs(ipfs),
createUnlink(ipfs),
createUtimens(ipfs),
createWrite(ipfs)
module.exports = (ipfs, id) => Object.assign(
createCreate(ipfs, id),
createFtruncate(ipfs, id),
createGetattr(ipfs, id),
createMkdir(ipfs, id),
createMknod(ipfs, id),
createOpen(ipfs, id),
createRead(ipfs, id),
createReaddir(ipfs, id),
createRename(ipfs, id),
createRmdir(ipfs, id),
createStatfs(ipfs, id),
createUnlink(ipfs, id),
createUtimens(ipfs, id),
createWrite(ipfs, id)
)
8 changes: 7 additions & 1 deletion ipfs-fuse/read.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,19 @@ module.exports = (ipfs) => {
read (path, fd, buf, len, pos, reply) {
debug({ path, fd, len, pos })

ipfs.files.read(path, { offset: pos, count: len }, (err, part) => {
// FIXME: count and length because https://github.com/ipfs/js-ipfs-mfs/issues/21
ipfs.files.read(path, { offset: pos, count: len, length: len }, (err, part) => {
if (err) {
err = explain(err, 'Failed to read path')
debug(err)
return reply(Fuse.EREMOTEIO)
}

// FIXME: this should always return a buffer js-ipfs-api does not for empty
if (Array.isArray(part) && !part.length) {
part = Buffer.alloc(0)
}

part.copy(buf)
reply(part.length)
})
Expand Down
87 changes: 59 additions & 28 deletions ipfs-fuse/statfs.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,46 +2,77 @@ const Fuse = require('fuse-bindings')
const explain = require('explain-error')
const debug = require('debug')('ipfs-fuse:statfs')

module.exports = (ipfs) => {
module.exports = (ipfs, id) => {
return {
statfs (path, reply) {
debug({ path })

ipfs.repo.stat((err, stat) => {
ipfs.files.stat(path, (err, fstat) => {
if (err) {
err = explain(err, 'Failed to stat repo')
debug(err)
return reply(Fuse.EREMOTEIO)
}

// TODO: I have no idea what I'm doing.
// https://github.com/mafintosh/fuse-bindings#opsstatfspath-cb
// https://github.com/mafintosh/fuse-bindings/blob/032ed16e234f7379fbf421c12afef592ab2a292d/fuse-bindings.cc#L771-L783
// http://man7.org/linux/man-pages/man2/statfs.2.html
const data = {
bsize: 8, // TODO: Because 8 bits in a byte right?
frsize: 0, // TODO: No idea...
blocks: stat.repoSize.gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.repoSize.toFixed()), // If blocks are bytes?
bfree: stat.storageMax.minus(stat.repoSize).gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.storageMax.minus(stat.repoSize).toFixed()), // TODO: because blocks are bytes?
bavail: stat.storageMax.minus(stat.repoSize).gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.storageMax.minus(stat.repoSize).toFixed()), // TODO: because blocks are bytes?
files: stat.numObjects.gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.numObjects.toFixed()),
ffree: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
favail: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
fsid: 0, // TODO: WHAT IS?
flag: 0, // TODO: does fuse know this?
namemax: Number.MAX_SAFE_INTEGER // TODO: get from OS?
// FIXME: cannot repo.stat on JS IPFS it reads the whole repo into memory
// to count the blocks 😱 and takes 5s each time
if (id.agentVersion.includes('js-ipfs')) {
// TODO: I have no idea what I'm doing.
// https://github.com/mafintosh/fuse-bindings#opsstatfspath-cb
// https://github.com/mafintosh/fuse-bindings/blob/032ed16e234f7379fbf421c12afef592ab2a292d/fuse-bindings.cc#L771-L783
// http://man7.org/linux/man-pages/man2/statfs.2.html
const data = {
bsize: 8, // TODO: Because 8 bits in a byte right?
frsize: 0, // TODO: No idea...
blocks: Number.MAX_SAFE_INTEGER,
bfree: Number.MAX_SAFE_INTEGER,
bavail: Number.MAX_SAFE_INTEGER,
files: fstat.blocks,
ffree: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
favail: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
fsid: 0, // TODO: WHAT IS?
flag: 0, // TODO: does fuse know this?
namemax: Number.MAX_SAFE_INTEGER // TODO: get from OS?
}

debug(data)
reply(0, data)
}

debug(data)
reply(0, data)
ipfs.repo.stat((err, stat) => {
if (err) {
err = explain(err, 'Failed to stat repo')
debug(err)
return reply(Fuse.EREMOTEIO)
}

// TODO: I have no idea what I'm doing.
// https://github.com/mafintosh/fuse-bindings#opsstatfspath-cb
// https://github.com/mafintosh/fuse-bindings/blob/032ed16e234f7379fbf421c12afef592ab2a292d/fuse-bindings.cc#L771-L783
// http://man7.org/linux/man-pages/man2/statfs.2.html
const data = {
bsize: 8, // TODO: Because 8 bits in a byte right?
frsize: 0, // TODO: No idea...
blocks: stat.repoSize.gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.repoSize.toFixed()), // If blocks are bytes?
bfree: stat.storageMax.minus(stat.repoSize).gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.storageMax.minus(stat.repoSize).toFixed()), // TODO: because blocks are bytes?
bavail: stat.storageMax.minus(stat.repoSize).gt(Number.MAX_SAFE_INTEGER)
? Number.MAX_SAFE_INTEGER // TODO: have to reply with int32
: parseInt(stat.storageMax.minus(stat.repoSize).toFixed()), // TODO: because blocks are bytes?
files: fstat.blocks,
ffree: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
favail: Number.MAX_SAFE_INTEGER, // TODO: no idea how to work this out
fsid: 0, // TODO: WHAT IS?
flag: 0, // TODO: does fuse know this?
namemax: Number.MAX_SAFE_INTEGER // TODO: get from OS?
}

debug(data)
reply(0, data)
})
})
}
}
Expand Down
3 changes: 2 additions & 1 deletion ipfs-fuse/write.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ module.exports = (ipfs) => {
write (path, fd, buf, len, pos, reply) {
debug({ path })

ipfs.files.write(path, buf, { offset: pos, count: len }, (err) => {
// FIXME: count and length because https://github.com/ipfs/js-ipfs-mfs/issues/21
ipfs.files.write(path, buf, { offset: pos, count: len, length: len }, (err) => {
if (err) {
err = explain(err, 'Failed to write to file')
debug(err)
Expand Down
Loading