Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit bad24b3

Browse files
committed
feat: mfs ls and mkdir commands
1 parent b232a09 commit bad24b3

File tree

13 files changed

+656
-14
lines changed

13 files changed

+656
-14
lines changed

package.json

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,21 @@
3838
"aegir": "^13.0.6",
3939
"chai": "^4.1.2",
4040
"dirty-chai": "^2.0.1",
41+
"ipfs": "^0.28.2",
4142
"pre-commit": "^1.2.2",
42-
"safe-buffer": "^5.1.1"
43+
"safe-buffer": "^5.1.1",
44+
"tmp": "0.0.33"
4345
},
4446
"dependencies": {
45-
47+
"async": "^2.6.0",
48+
"bs58": "^4.0.1",
49+
"cids": "~0.5.3",
50+
"debug": "^3.1.0",
51+
"interface-datastore": "^0.4.2",
52+
"ipfs-unixfs": "^0.1.14",
53+
"ipfs-unixfs-engine": "~0.27.0",
54+
"promisify-es6": "^1.0.3",
55+
"pull-stream": "^3.6.7"
4656
},
4757
"pre-commit": [
4858
"lint",

src/cli/index.js

Whitespace-only changes.

src/cli/ls.js

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
'use strict'
2+
3+
const {
4+
print
5+
} = require('./utils')
6+
7+
module.exports = {
8+
command: 'ls <path>',
9+
10+
describe: 'List directories in the local mutable namespace.',
11+
12+
builder: {
13+
long: {
14+
alias: 'l',
15+
type: 'boolean',
16+
default: false,
17+
describe: 'Use long listing format.'
18+
}
19+
},
20+
21+
handler (argv) {
22+
let {
23+
path,
24+
ipfs,
25+
long
26+
} = argv
27+
28+
ipfs.mfs.ls(path, {
29+
long
30+
}, (error, files) => {
31+
if (error) {
32+
throw error
33+
}
34+
35+
files.forEach(link => {
36+
if (long) {
37+
return print(`${link.name} ${link.hash} ${link.size}`)
38+
}
39+
40+
print(link.name)
41+
})
42+
})
43+
}
44+
}

src/cli/mkdir.js

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
'use strict'
2+
3+
const {
4+
print
5+
} = require('./utils')
6+
7+
module.exports = {
8+
command: 'mkdir <path>',
9+
10+
describe: 'Make directories.',
11+
12+
builder: {
13+
parents: {
14+
alias: 'p',
15+
type: 'boolean',
16+
default: false,
17+
describe: 'No error if existing, make parent directories as needed.'
18+
},
19+
cidVersion: {
20+
alias: ['cid-ver', 'cid-version'],
21+
type: 'integer',
22+
describe: 'Cid version to use. (experimental).'
23+
},
24+
hash: {
25+
type: 'string',
26+
describe: 'Hash function to use. Will set Cid version to 1 if used. (experimental).'
27+
},
28+
flush: {
29+
alias: 'f',
30+
type: 'boolean',
31+
describe: 'Weird undocumented option'
32+
}
33+
},
34+
35+
handler (argv) {
36+
let {
37+
path,
38+
ipfs,
39+
parents,
40+
cidVersion,
41+
hash,
42+
flush
43+
} = argv
44+
45+
ipfs.mfs.mkdir(path, {
46+
parents,
47+
cidVersion,
48+
hash,
49+
flush
50+
}, (error, result) => {
51+
if (error) {
52+
throw error
53+
}
54+
55+
print(result)
56+
})
57+
}
58+
}

src/cli/utils.js

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
'use strict'
2+
3+
let visible = true
4+
5+
const disablePrinting = () => {
6+
visible = false
7+
}
8+
9+
const print = (msg = '', newline = true) => {
10+
if (!visible) {
11+
return
12+
}
13+
14+
msg = newline ? msg + '\n' : msg
15+
process.stdout.write(msg)
16+
}
17+
18+
module.exports = {
19+
disablePrinting,
20+
print
21+
}

src/core/index.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
'use strict'
2+
3+
module.exports = {
4+
ls: require('./ls'),
5+
mkdir: require('./mkdir')
6+
}

src/core/ls.js

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
'use strict'
2+
3+
const exporter = require('ipfs-unixfs-engine').exporter
4+
const promisify = require('promisify-es6')
5+
const pull = require('pull-stream')
6+
const {
7+
collect
8+
} = pull
9+
const {
10+
withMfsRoot,
11+
validatePath
12+
} = require('./utils')
13+
14+
const defaultOptions = {
15+
long: false
16+
}
17+
18+
module.exports = function mfsLs (ipfs) {
19+
return promisify((path, options, callback) => {
20+
withMfsRoot(ipfs, (error, root) => {
21+
if (error) {
22+
return callback(error)
23+
}
24+
25+
if (typeof options === 'function') {
26+
callback = options
27+
options = {}
28+
}
29+
30+
options = Object.assign({}, defaultOptions, options)
31+
32+
try {
33+
path = validatePath(path)
34+
root = root.toBaseEncodedString()
35+
} catch (error) {
36+
return callback(error)
37+
}
38+
39+
pull(
40+
exporter(`/ipfs/${root}${path}`, ipfs._ipld),
41+
collect((error, results) => {
42+
if (error) {
43+
return callback(error)
44+
}
45+
46+
if (!results || !results.length) {
47+
return callback(new Error('file does not exist'))
48+
}
49+
50+
const files = (results[0].links || []).map(link => ({
51+
name: link.name,
52+
type: link.type,
53+
size: link.size,
54+
hash: link.multihash
55+
}))
56+
57+
callback(null, files)
58+
})
59+
)
60+
})
61+
})
62+
}

src/core/mkdir.js

Lines changed: 159 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,159 @@
1+
'use strict'
2+
3+
const UnixFS = require('ipfs-unixfs')
4+
const promisify = require('promisify-es6')
5+
const CID = require('cids')
6+
const bs58 = require('bs58')
7+
const log = require('debug')('mfs:mkdir')
8+
const dagPb = require('ipld-dag-pb')
9+
const {
10+
DAGNode,
11+
DAGLink
12+
} = dagPb
13+
const {
14+
waterfall,
15+
reduce
16+
} = require('async')
17+
const {
18+
withMfsRoot,
19+
updateMfsRoot,
20+
validatePath,
21+
FILE_SEPARATOR
22+
} = require('./utils')
23+
24+
const defaultOptions = {
25+
parents: true,
26+
hash: undefined,
27+
cidVersion: undefined
28+
}
29+
30+
const addLink = (ipfs, parent, child, name, callback) => {
31+
waterfall([
32+
(done) => {
33+
DAGNode.rmLink(parent, name, done)
34+
},
35+
(parent, done) => {
36+
DAGNode.addLink(parent, new DAGLink(name, child.size, child.hash || child.multihash), done)
37+
},
38+
(parent, done) => {
39+
ipfs.dag.put(parent, {
40+
cid: new CID(parent.hash || parent.multihash)
41+
}, (error) => done(error, parent))
42+
}
43+
], callback)
44+
}
45+
46+
module.exports = function mfsMkdir (ipfs) {
47+
return promisify((path, options, callback) => {
48+
withMfsRoot(ipfs, (error, root) => {
49+
if (error) {
50+
return callback(error)
51+
}
52+
53+
if (typeof options === 'function') {
54+
callback = options
55+
options = {}
56+
}
57+
58+
options = Object.assign({}, defaultOptions, options)
59+
60+
if (!path) {
61+
return callback(new Error('no path given to Mkdir'))
62+
}
63+
64+
const pathSegments = validatePath(path)
65+
.split(FILE_SEPARATOR)
66+
.filter(Boolean)
67+
68+
if (pathSegments.length === 0) {
69+
return callback(options.parents ? null : new Error(`cannot create directory '${FILE_SEPARATOR}': Already exists`))
70+
}
71+
72+
const trail = []
73+
74+
waterfall([
75+
(cb) => ipfs.dag.get(root, cb),
76+
(result, cb) => {
77+
const rootNode = result.value
78+
79+
trail.push({
80+
name: FILE_SEPARATOR,
81+
node: rootNode,
82+
parent: null
83+
})
84+
85+
reduce(pathSegments.map((pathSegment, index) => ({pathSegment, index})), {
86+
name: FILE_SEPARATOR,
87+
node: rootNode,
88+
parent: null
89+
}, (parent, {pathSegment, index}, done) => {
90+
const lastPathSegment = index === pathSegments.length - 1
91+
const existingLink = parent.node.links.find(link => link.name === pathSegment)
92+
93+
log(`Looking for ${pathSegment} in ${parent.name}`)
94+
95+
if (!existingLink) {
96+
if (!lastPathSegment && !options.parents) {
97+
return done(new Error(`Cannot create ${path} - intermediate directory '${pathSegment}' did not exist: Try again with the --parents flag`))
98+
}
99+
100+
log(`Adding empty directory '${pathSegment}' to parent ${parent.name}`)
101+
102+
return waterfall([
103+
(next) => DAGNode.create(new UnixFS('directory').marshal(), [], next),
104+
(emptyDirectory, next) => {
105+
addLink(ipfs, parent.node, emptyDirectory, pathSegment, (error, updatedParent) => {
106+
parent.node = updatedParent
107+
108+
next(error, {
109+
name: pathSegment,
110+
node: emptyDirectory,
111+
parent: parent
112+
})
113+
})
114+
}
115+
], done)
116+
}
117+
118+
if (lastPathSegment && existingLink && !options.parents) {
119+
return done(new Error(`Cannot create directory '${path}': Already exists`))
120+
}
121+
122+
let hash = existingLink.hash || existingLink.multihash
123+
124+
if (Buffer.isBuffer(hash)) {
125+
hash = bs58.encode(hash)
126+
}
127+
128+
// child existed, fetch it
129+
ipfs.dag.get(hash, (error, result) => {
130+
const child = {
131+
name: pathSegment,
132+
node: result && result.value,
133+
parent: parent
134+
}
135+
136+
trail.push(child)
137+
138+
done(error, child)
139+
})
140+
}, cb)
141+
},
142+
(result, cb) => {
143+
// replace all links and store in the repo
144+
reduce(pathSegments, result, (child, pathSegment, next) => {
145+
addLink(ipfs, child.parent.node, child.node, child.name, (error, updatedParent) => {
146+
child.parent.node = updatedParent
147+
148+
next(error, child.parent)
149+
})
150+
}, cb)
151+
},
152+
(result, cb) => {
153+
// update new MFS root CID
154+
updateMfsRoot(ipfs, result.node.multihash, cb)
155+
}
156+
], callback)
157+
})
158+
})
159+
}

0 commit comments

Comments
 (0)