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

Commit e10e85d

Browse files
docs: add mode and mtime and .touch/.chmod mfs commands (#572)
* docs: add mode and mtime and .touch/.chmod mfs commands * docs: update arg/return types * test: updates tests to use timespecs * fix: remove cumulativeSize as it changes based on timestamps * docs: update SPEC/FILES.md * docs: update SPEC/FILES.md * docs: remove format * Update SPEC/FILES.md Co-Authored-By: Steven Allen <steven@stebalien.com> * docs: more detail about mtime values * docs: remove octal as string * docs: remove mode as strings * fix: fix up tests for optional mtime Co-authored-by: Steven Allen <steven@stebalien.com>
1 parent f75c682 commit e10e85d

17 files changed

+852
-232
lines changed

SPEC/FILES.md

+122-6
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ The regular, top-level API for add, cat, get and ls Files on IPFS
2424
The Files API, aka MFS (Mutable File System)
2525

2626
_Explore the Mutable File System through interactive coding challenges in our [ProtoSchool tutorial](https://proto.school/#/mutable-file-system/)._
27+
- [files.chmod](#fileschmod)
2728
- [files.cp](#filescp)
2829
- [files.flush](#filesflush)
2930
- [files.ls](#filesls)
@@ -36,6 +37,7 @@ _Explore the Mutable File System through interactive coding challenges in our [P
3637
- [files.readReadableStream](#filesreadreadablestream)
3738
- [files.rm](#filesrm)
3839
- [files.stat](#filesstat)
40+
- [files.touch](#filestouch)
3941
- [files.write](#fileswrite)
4042

4143
### ⚠️ Note
@@ -58,6 +60,8 @@ Where `data` may be:
5860
{
5961
path: '/tmp/myfile.txt', // The file path
6062
content: <data> // A Buffer, Readable Stream, Pull Stream or File with the contents of the file
63+
mode: <Number> // optional integer mode to store the entry with
64+
mtime: <time> // optional value representing the modification time of the entry - either a `Date` object, an object with `{ secs, nsecs }` properties where `secs` is the number of seconds since (positive) or before (negative) the Unix Epoch began and `nsecs` is the number of nanoseconds since the last full second, or the output of `process.hrtime()`
6165
}
6266
```
6367
If no `content` is passed, then the path is treated as an empty directory
@@ -98,6 +102,8 @@ an array of objects is returned, each of the form:
98102
{
99103
path: '/tmp/myfile.txt',
100104
hash: 'QmHash', // base58 encoded multihash
105+
mode: Number,
106+
mtime: { secs: Number, nsecs: Number },
101107
size: 123
102108
}
103109
```
@@ -130,16 +136,20 @@ const results = await ipfs.add(files)
130136

131137
The `results` array:
132138

133-
```json
139+
```javascript
134140
[
135141
{
136142
"path": "tmp",
137143
"hash": "QmWXdjNC362aPDtwHPUE9o2VMqPeNeCQuTBTv1NsKtwypg",
144+
"mode": 493,
145+
"mtime": { secs: Number, nsecs: Number },
138146
"size": 67
139147
},
140148
{
141149
"path": "/tmp/myfile.txt",
142150
"hash": "QmNz1UBzpdd4HfZ3qir3aPiRdX5a93XwTuDNyXRc6PKhWW",
151+
"mode": 420,
152+
"mtime": { secs: Number, nsecs: Number },
143153
"size": 11
144154
}
145155
]
@@ -179,6 +189,8 @@ stream.on('data', function (file) {
179189
// {
180190
// path: '/tmp/myfile.txt',
181191
// hash: 'QmHash' // base58 encoded multihash
192+
// mode: Number,
193+
// mtime: { secs: Number, nsecs: Number },
182194
// size: 123
183195
// }
184196
})
@@ -207,6 +219,8 @@ Returns a Pull Stream, where objects can be written of the forms
207219
{
208220
path: '/tmp/myfile.txt', // The file path
209221
content: <data> // A Buffer, Readable Stream, Pull Stream or File with the contents of the file
222+
mode: <Number> // optional integer mode to store the entry with
223+
mtime: <time> // optional value representing the modification time of the entry - either a `Date` object, an object with `{ secs, nsecs }` properties where `secs` is the number of seconds since (positive) or before (negative) the Unix Epoch began and `nsecs` is the number of nanoseconds since the last full second, or the output of `process.hrtime()`
210224
}
211225
```
212226

@@ -233,6 +247,8 @@ pull(
233247
// {
234248
// path: '/tmp/myfile.txt',
235249
// hash: 'QmHash' // base58 encoded multihash
250+
// mode: Number
251+
// mtime: { secs: Number, nsecs: Number }
236252
// size: 123
237253
// }
238254
})
@@ -264,6 +280,8 @@ an array of objects is returned, each of the form:
264280
{
265281
path: 'test-folder',
266282
hash: 'QmRNjDeKStKGTQXnJ2NFqeQ9oW23WcpbmvCVrpDHgDg3T6',
283+
mode: Number
284+
mtime: Date
267285
size: 123
268286
}
269287
```
@@ -318,6 +336,8 @@ an array of objects is returned, each of the form:
318336
{
319337
path: '/tmp/myfile.txt',
320338
hash: 'QmHash', // base58 encoded multihash
339+
mode: Number,
340+
mtime: { secs: Number, nsecs: Number },
321341
size: 123
322342
}
323343
```
@@ -511,7 +531,9 @@ the yielded objects are of the form:
511531
```js
512532
{
513533
path: '/tmp/myfile.txt',
514-
content: <Readable stream>
534+
content: <Readable stream>,
535+
mode: Number,
536+
mtime: { secs: Number, nsecs: Number }
515537
}
516538
```
517539

@@ -563,7 +585,9 @@ the yielded objects are of the form:
563585
```js
564586
{
565587
path: '/tmp/myfile.txt',
566-
content: <Pull Stream>
588+
content: <Pull Stream>,
589+
mode: Number,
590+
mtime: { secs: Number, nsecs: Number }
567591
}
568592
```
569593

@@ -624,7 +648,9 @@ an array of objects is returned, each of the form:
624648
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
625649
size: 11696,
626650
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
627-
type: 'file'
651+
type: 'file',
652+
mode: Number,
653+
mtime: { secs: Number, nsecs: Number }
628654
}
629655
```
630656

@@ -674,7 +700,9 @@ the yielded objects are of the form:
674700
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
675701
size: 11696,
676702
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
677-
type: 'file'
703+
type: 'file',
704+
mode: Number,
705+
mtime: { secs: Number, nsecs: Number }
678706
}
679707
```
680708

@@ -727,7 +755,9 @@ the yielded objects are of the form:
727755
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
728756
size: 11696,
729757
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
730-
type: 'file'
758+
type: 'file',
759+
mode: Number,
760+
mtime: { secs: Number, nsecs: Number }
731761
}
732762
```
733763

@@ -759,6 +789,46 @@ A great source of [examples][] can be found in the tests for this API.
759789

760790
The Mutable File System (MFS) is a virtual file system on top of IPFS that exposes a Unix like API over a virtual directory. It enables users to write and read from paths without having to worry about updating the graph. It enables things like [ipfs-blob-store](https://github.com/ipfs/ipfs-blob-store) to exist.
761791

792+
#### `files.chmod`
793+
794+
> Change mode for files and directories
795+
796+
##### `ipfs.files.chmod(path, mode, [options])`
797+
798+
Where:
799+
800+
- `path` is the path to the entry to modify. It might be:
801+
- An existing MFS path to a file or a directory (e.g. `/my-dir/my-file.txt`)
802+
- An IPFS path (e.g. `/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks`)
803+
- A [CID][cid] instance (e.g. `new CID('QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks')`)
804+
- `mode` is the new file mode. It might be:
805+
- A string modification of the existing mode, e.g. `'a+x'`, `'g-w'`, etc
806+
- An integer, e.g. the returned value from `parseInt('0755', 8)` or `0o755`
807+
- `options` is an optional Object that might contain the following keys:
808+
- `recursive` is a Boolean value that indicates if `mode` should be applied to all sub files/directories of `path` (default: false)
809+
- `hashAlg` is which algorithm to use when creating CIDs for modified entries. (default: `sha2-256`) [The list of all possible values]( https://github.com/multiformats/js-multihash/blob/master/src/constants.js#L5-L343)
810+
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
811+
- `cidVersion`: the CID version to use for any updated entries (integer, default 0)
812+
813+
**Returns**
814+
815+
| Type | Description |
816+
| -------- | -------- |
817+
| `Promise<void>` | If action is successfully completed. Otherwise an error will be thrown |
818+
819+
**Example:**
820+
821+
```JavaScript
822+
// To give a file -rwxrwxrwx permissions
823+
await ipfs.files.chmod('/path/to/file.txt', parseInt('0777', 8))
824+
825+
// Alternatively
826+
await ipfs.files.chmod('/path/to/file.txt', '+rwx')
827+
828+
// You can omit the leading `0` too
829+
await ipfs.files.chmod('/path/to/file.txt', '777')
830+
```
831+
762832
#### `files.cp`
763833

764834
> Copy files.
@@ -819,6 +889,8 @@ Where:
819889
- `parents` is a Boolean value to decide whether or not to make the parent directories if they don't exist (default: false)
820890
- `hashAlg` is which algorithm to use when creating CIDs for newly created directories (default: `sha2-256`) [The list of all possible values]( https://github.com/multiformats/js-multihash/blob/master/src/constants.js#L5-L343)
821891
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
892+
- `mode`: optional UnixFS mode to create the directory with - a number or a string that will be interpreted as a base 8 number
893+
- `mtime`: A Date object, an object with `{ secs, nsecs }` properties where `secs` is the number of seconds since (positive) or before (negative) the Unix Epoch began and `nsecs` is the number of nanoseconds since the last full second, or the output of [`process.hrtime()`](https://nodejs.org/api/process.html#process_process_hrtime_time)
822894

823895
**Returns**
824896

@@ -882,6 +954,42 @@ console.log(stats)
882954
// }
883955
```
884956

957+
#### `files.touch`
958+
959+
> Update the mtime of a file or directory
960+
961+
##### `ipfs.files.touch(path, [options])`
962+
963+
Where:
964+
965+
- `path` is the path to the file or directory to update. It might be:
966+
- An existing MFS path to a file or directory (e.g. `/my-dir/a.txt`)
967+
- An IPFS path (e.g. `/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks`)
968+
- A [CID][cid] instance (e.g. `new CID('QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks')`)
969+
- `options` is an optional Object that might contain the following keys:
970+
- `mtime` Either a ` Date` object, an object with `{ sec, nsecs }` properties or the output of `process.hrtime()` (default: now)
971+
- `hashAlg` is which algorithm to use when creating CIDs for modified entries. (default: `sha2-256`) [The list of all possible values]( https://github.com/multiformats/js-multihash/blob/master/src/constants.js#L5-L343)
972+
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
973+
- `cidVersion`: the CID version to use for any updated entries (integer, default 0)
974+
975+
**Returns**
976+
977+
| Type | Description |
978+
| -------- | -------- |
979+
| `Promise<void>` | If action is successfully completed. Otherwise an error will be thrown |
980+
981+
**Example:**
982+
983+
```JavaScript
984+
// set the mtime to the current time
985+
await ipfs.files.touch('/path/to/file.txt')
986+
987+
// set the mtime to a specific time
988+
await ipfs.files.touch('/path/to/file.txt', {
989+
mtime: new Date('May 23, 2014 14:45:14 -0700')
990+
})
991+
```
992+
885993
#### `files.rm`
886994

887995
> Remove a file or directory.
@@ -1034,6 +1142,8 @@ Where:
10341142
- `length` is an Integer with the maximum number of bytes to read (default: Read all bytes from `content`)
10351143
- `rawLeaves`: if true, DAG leaves will contain raw file data and not be wrapped in a protobuf (boolean, default false)
10361144
- `cidVersion`: the CID version to use when storing the data (storage keys are based on the CID, including its version) (integer, default 0)
1145+
- `mode`: optional UnixFS mode to create or update the file with - a number or a string that will be interpreted as a base 8 number
1146+
- `mtime`: A Date object, an object with `{ sec, nsecs }` properties or the output of `process.hrtime()` or `process.hrtime.bigint()`
10371147

10381148
**Returns**
10391149

@@ -1141,6 +1251,8 @@ each object contains the following keys:
11411251
- `type` which is the object's type (`directory` or `file`)
11421252
- `size` the size of the file in bytes
11431253
- `hash` the hash of the file
1254+
- `mode` the UnixFS mode as a Number
1255+
- `mtime` an objects with numeric `secs` and `nsecs` properties
11441256

11451257
**Example:**
11461258

@@ -1185,6 +1297,8 @@ the yielded objects contain the following keys:
11851297
- `type` which is the object's type (`directory` or `file`)
11861298
- `size` the size of the file in bytes
11871299
- `hash` the hash of the file
1300+
- `mode` the UnixFS mode as a Number
1301+
- `mtime` an object with numeric `secs` and `nsecs` properties
11881302

11891303
**Example:**
11901304

@@ -1227,6 +1341,8 @@ the yielded objects contain the following keys:
12271341
- `type` which is the object's type (`directory` or `file`)
12281342
- `size` the size of the file in bytes
12291343
- `hash` the hash of the file
1344+
- `mode` the UnixFS mode as a Number
1345+
- `mtime` an object with numeric `secs` and `nsecs` properties
12301346

12311347
**Example:**
12321348

package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -49,8 +49,8 @@
4949
"get-stream": "^5.1.0",
5050
"hat": "0.0.3",
5151
"ipfs-block": "~0.8.0",
52-
"ipfs-unixfs": "~0.1.16",
53-
"ipfs-utils": "~0.4.0",
52+
"ipfs-unixfs": "^0.3.0",
53+
"ipfs-utils": "^0.4.2",
5454
"ipld-dag-cbor": "~0.15.0",
5555
"ipld-dag-pb": "^0.18.1",
5656
"is-ipfs": "~0.6.1",

src/files-mfs/chmod.js

+62
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
/* eslint-env mocha */
2+
'use strict'
3+
4+
const hat = require('hat')
5+
const { getDescribe, getIt, expect } = require('../utils/mocha')
6+
7+
module.exports = (common, options) => {
8+
const describe = getDescribe(options)
9+
const it = getIt(options)
10+
11+
describe('.files.chmod', function () {
12+
this.timeout(40 * 1000)
13+
14+
let ipfs
15+
16+
async function testMode (mode, expectedMode) {
17+
const testPath = `/test-${hat()}`
18+
19+
await ipfs.files.write(testPath, Buffer.from('Hello, world!'), {
20+
create: true
21+
})
22+
await ipfs.files.chmod(testPath, mode)
23+
24+
const stat = await ipfs.files.stat(testPath)
25+
expect(stat).to.have.property('mode').that.equals(expectedMode)
26+
}
27+
28+
before(async () => {
29+
ipfs = (await common.spawn()).api
30+
})
31+
32+
after(() => common.clean())
33+
34+
it('should change file mode', async function () {
35+
const mode = parseInt('544', 8)
36+
await testMode(mode, mode)
37+
})
38+
39+
it('should change file mode as string', async function () {
40+
const mode = parseInt('544', 8)
41+
await testMode('544', mode)
42+
})
43+
44+
it('should change file mode to 0', async function () {
45+
const mode = 0
46+
await testMode(mode, mode)
47+
})
48+
49+
it('should change directory mode', async function () {
50+
const testPath = `/test-${hat()}`
51+
const mode = parseInt('544', 8)
52+
53+
await ipfs.files.mkdir(testPath, {
54+
create: true
55+
})
56+
await ipfs.files.chmod(testPath, mode)
57+
58+
const stat = await ipfs.files.stat(testPath)
59+
expect(stat).to.have.property('mode').that.equals(mode)
60+
})
61+
})
62+
}

0 commit comments

Comments
 (0)