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

Commit

Permalink
docs: add mode and mtime and .touch/.chmod mfs commands (#572)
Browse files Browse the repository at this point in the history
* 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 <[email protected]>

* 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 <[email protected]>
  • Loading branch information
achingbrain and Stebalien authored Jan 9, 2020
1 parent f75c682 commit e10e85d
Show file tree
Hide file tree
Showing 17 changed files with 852 additions and 232 deletions.
128 changes: 122 additions & 6 deletions SPEC/FILES.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ The regular, top-level API for add, cat, get and ls Files on IPFS
The Files API, aka MFS (Mutable File System)

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

### ⚠️ Note
Expand All @@ -58,6 +60,8 @@ Where `data` may be:
{
path: '/tmp/myfile.txt', // The file path
content: <data> // A Buffer, Readable Stream, Pull Stream or File with the contents of the file
mode: <Number> // optional integer mode to store the entry with
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()`
}
```
If no `content` is passed, then the path is treated as an empty directory
Expand Down Expand Up @@ -98,6 +102,8 @@ an array of objects is returned, each of the form:
{
path: '/tmp/myfile.txt',
hash: 'QmHash', // base58 encoded multihash
mode: Number,
mtime: { secs: Number, nsecs: Number },
size: 123
}
```
Expand Down Expand Up @@ -130,16 +136,20 @@ const results = await ipfs.add(files)

The `results` array:

```json
```javascript
[
{
"path": "tmp",
"hash": "QmWXdjNC362aPDtwHPUE9o2VMqPeNeCQuTBTv1NsKtwypg",
"mode": 493,
"mtime": { secs: Number, nsecs: Number },
"size": 67
},
{
"path": "/tmp/myfile.txt",
"hash": "QmNz1UBzpdd4HfZ3qir3aPiRdX5a93XwTuDNyXRc6PKhWW",
"mode": 420,
"mtime": { secs: Number, nsecs: Number },
"size": 11
}
]
Expand Down Expand Up @@ -179,6 +189,8 @@ stream.on('data', function (file) {
// {
// path: '/tmp/myfile.txt',
// hash: 'QmHash' // base58 encoded multihash
// mode: Number,
// mtime: { secs: Number, nsecs: Number },
// size: 123
// }
})
Expand Down Expand Up @@ -207,6 +219,8 @@ Returns a Pull Stream, where objects can be written of the forms
{
path: '/tmp/myfile.txt', // The file path
content: <data> // A Buffer, Readable Stream, Pull Stream or File with the contents of the file
mode: <Number> // optional integer mode to store the entry with
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()`
}
```

Expand All @@ -233,6 +247,8 @@ pull(
// {
// path: '/tmp/myfile.txt',
// hash: 'QmHash' // base58 encoded multihash
// mode: Number
// mtime: { secs: Number, nsecs: Number }
// size: 123
// }
})
Expand Down Expand Up @@ -264,6 +280,8 @@ an array of objects is returned, each of the form:
{
path: 'test-folder',
hash: 'QmRNjDeKStKGTQXnJ2NFqeQ9oW23WcpbmvCVrpDHgDg3T6',
mode: Number
mtime: Date
size: 123
}
```
Expand Down Expand Up @@ -318,6 +336,8 @@ an array of objects is returned, each of the form:
{
path: '/tmp/myfile.txt',
hash: 'QmHash', // base58 encoded multihash
mode: Number,
mtime: { secs: Number, nsecs: Number },
size: 123
}
```
Expand Down Expand Up @@ -511,7 +531,9 @@ the yielded objects are of the form:
```js
{
path: '/tmp/myfile.txt',
content: <Readable stream>
content: <Readable stream>,
mode: Number,
mtime: { secs: Number, nsecs: Number }
}
```

Expand Down Expand Up @@ -563,7 +585,9 @@ the yielded objects are of the form:
```js
{
path: '/tmp/myfile.txt',
content: <Pull Stream>
content: <Pull Stream>,
mode: Number,
mtime: { secs: Number, nsecs: Number }
}
```

Expand Down Expand Up @@ -624,7 +648,9 @@ an array of objects is returned, each of the form:
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
size: 11696,
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
type: 'file'
type: 'file',
mode: Number,
mtime: { secs: Number, nsecs: Number }
}
```

Expand Down Expand Up @@ -674,7 +700,9 @@ the yielded objects are of the form:
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
size: 11696,
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
type: 'file'
type: 'file',
mode: Number,
mtime: { secs: Number, nsecs: Number }
}
```

Expand Down Expand Up @@ -727,7 +755,9 @@ the yielded objects are of the form:
path: 'QmVvjDy7yF7hdnqE8Hrf4MHo5ABDtb5AbX6hWbD3Y42bXP/alice.txt',
size: 11696,
hash: 'QmZyUEQVuRK3XV7L9Dk26pg6RVSgaYkiSTEdnT2kZZdwoi',
type: 'file'
type: 'file',
mode: Number,
mtime: { secs: Number, nsecs: Number }
}
```

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

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.

#### `files.chmod`

> Change mode for files and directories
##### `ipfs.files.chmod(path, mode, [options])`

Where:

- `path` is the path to the entry to modify. It might be:
- An existing MFS path to a file or a directory (e.g. `/my-dir/my-file.txt`)
- An IPFS path (e.g. `/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks`)
- A [CID][cid] instance (e.g. `new CID('QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks')`)
- `mode` is the new file mode. It might be:
- A string modification of the existing mode, e.g. `'a+x'`, `'g-w'`, etc
- An integer, e.g. the returned value from `parseInt('0755', 8)` or `0o755`
- `options` is an optional Object that might contain the following keys:
- `recursive` is a Boolean value that indicates if `mode` should be applied to all sub files/directories of `path` (default: false)
- `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)
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
- `cidVersion`: the CID version to use for any updated entries (integer, default 0)

**Returns**

| Type | Description |
| -------- | -------- |
| `Promise<void>` | If action is successfully completed. Otherwise an error will be thrown |

**Example:**

```JavaScript
// To give a file -rwxrwxrwx permissions
await ipfs.files.chmod('/path/to/file.txt', parseInt('0777', 8))

// Alternatively
await ipfs.files.chmod('/path/to/file.txt', '+rwx')

// You can omit the leading `0` too
await ipfs.files.chmod('/path/to/file.txt', '777')
```

#### `files.cp`

> Copy files.
Expand Down Expand Up @@ -819,6 +889,8 @@ Where:
- `parents` is a Boolean value to decide whether or not to make the parent directories if they don't exist (default: false)
- `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)
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
- `mode`: optional UnixFS mode to create the directory with - a number or a string that will be interpreted as a base 8 number
- `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)

**Returns**

Expand Down Expand Up @@ -882,6 +954,42 @@ console.log(stats)
// }
```

#### `files.touch`

> Update the mtime of a file or directory
##### `ipfs.files.touch(path, [options])`

Where:

- `path` is the path to the file or directory to update. It might be:
- An existing MFS path to a file or directory (e.g. `/my-dir/a.txt`)
- An IPFS path (e.g. `/ipfs/QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks`)
- A [CID][cid] instance (e.g. `new CID('QmWGeRAEgtsHW3ec7U4qW2CyVy7eA2mFRVbk1nb24jFyks')`)
- `options` is an optional Object that might contain the following keys:
- `mtime` Either a ` Date` object, an object with `{ sec, nsecs }` properties or the output of `process.hrtime()` (default: now)
- `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)
- `flush` is a Boolean value to decide whether or not to immediately flush MFS changes to disk (default: true)
- `cidVersion`: the CID version to use for any updated entries (integer, default 0)

**Returns**

| Type | Description |
| -------- | -------- |
| `Promise<void>` | If action is successfully completed. Otherwise an error will be thrown |

**Example:**

```JavaScript
// set the mtime to the current time
await ipfs.files.touch('/path/to/file.txt')

// set the mtime to a specific time
await ipfs.files.touch('/path/to/file.txt', {
mtime: new Date('May 23, 2014 14:45:14 -0700')
})
```

#### `files.rm`

> Remove a file or directory.
Expand Down Expand Up @@ -1034,6 +1142,8 @@ Where:
- `length` is an Integer with the maximum number of bytes to read (default: Read all bytes from `content`)
- `rawLeaves`: if true, DAG leaves will contain raw file data and not be wrapped in a protobuf (boolean, default false)
- `cidVersion`: the CID version to use when storing the data (storage keys are based on the CID, including its version) (integer, default 0)
- `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
- `mtime`: A Date object, an object with `{ sec, nsecs }` properties or the output of `process.hrtime()` or `process.hrtime.bigint()`

**Returns**

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

**Example:**

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

**Example:**

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

**Example:**

Expand Down
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@
"get-stream": "^5.1.0",
"hat": "0.0.3",
"ipfs-block": "~0.8.0",
"ipfs-unixfs": "~0.1.16",
"ipfs-utils": "~0.4.0",
"ipfs-unixfs": "^0.3.0",
"ipfs-utils": "^0.4.2",
"ipld-dag-cbor": "~0.15.0",
"ipld-dag-pb": "^0.18.1",
"is-ipfs": "~0.6.1",
Expand Down
62 changes: 62 additions & 0 deletions src/files-mfs/chmod.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/* eslint-env mocha */
'use strict'

const hat = require('hat')
const { getDescribe, getIt, expect } = require('../utils/mocha')

module.exports = (common, options) => {
const describe = getDescribe(options)
const it = getIt(options)

describe('.files.chmod', function () {
this.timeout(40 * 1000)

let ipfs

async function testMode (mode, expectedMode) {
const testPath = `/test-${hat()}`

await ipfs.files.write(testPath, Buffer.from('Hello, world!'), {
create: true
})
await ipfs.files.chmod(testPath, mode)

const stat = await ipfs.files.stat(testPath)
expect(stat).to.have.property('mode').that.equals(expectedMode)
}

before(async () => {
ipfs = (await common.spawn()).api
})

after(() => common.clean())

it('should change file mode', async function () {
const mode = parseInt('544', 8)
await testMode(mode, mode)
})

it('should change file mode as string', async function () {
const mode = parseInt('544', 8)
await testMode('544', mode)
})

it('should change file mode to 0', async function () {
const mode = 0
await testMode(mode, mode)
})

it('should change directory mode', async function () {
const testPath = `/test-${hat()}`
const mode = parseInt('544', 8)

await ipfs.files.mkdir(testPath, {
create: true
})
await ipfs.files.chmod(testPath, mode)

const stat = await ipfs.files.stat(testPath)
expect(stat).to.have.property('mode').that.equals(mode)
})
})
}
Loading

0 comments on commit e10e85d

Please sign in to comment.