Skip to content

Commit 2f02eb5

Browse files
authored
Merge pull request #38 from ipfs/make-mtime-and-mode-optional
fix: allow mtime and mode to be optional
2 parents 46d15cd + d93b32b commit 2f02eb5

File tree

5 files changed

+304
-195
lines changed

5 files changed

+304
-195
lines changed

README.md

Lines changed: 60 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
# ipfs-unixfs JavaScript Implementation
1+
# ipfs-unixfs JavaScript Implementation <!-- omit in toc -->
22

33
[![](https://img.shields.io/badge/made%20by-Protocol%20Labs-blue.svg?style=flat-square)](http://ipn.io)
44
[![](https://img.shields.io/badge/project-IPFS-blue.svg?style=flat-square)](http://ipfs.io/)
@@ -10,36 +10,34 @@
1010
![](https://img.shields.io/badge/npm-%3E%3D3.0.0-orange.svg?style=flat-square)
1111
![](https://img.shields.io/badge/Node.js-%3E%3D8.0.0-orange.svg?style=flat-square)
1212

13-
> JavaScript implementation of IPFS' unixfs (a Unix FileSystem files representation on top of a MerkleDAG)
13+
> JavaScript implementation of IPFS' UnixFS (a Unix FileSystem files representation on top of a MerkleDAG)
1414
15-
[The unixfs spec can be found inside the ipfs/specs repository](http://github.com/ipfs/specs)
15+
The UnixFS spec can be found inside the [ipfs/specs repository](http://github.com/ipfs/specs)
1616

17-
## Lead Maintainer
17+
## Lead Maintainer <!-- omit in toc -->
1818

1919
[Alex Potsides](https://github.com/achingbrain)
2020

21-
## Table of Contents
22-
23-
- [ipfs-unixfs JavaScript Implementation](#ipfs-unixfs-javascript-implementation)
24-
- [Lead Maintainer](#lead-maintainer)
25-
- [Table of Contents](#table-of-contents)
26-
- [Install](#install)
27-
- [npm](#npm)
28-
- [Use in Node.js](#use-in-nodejs)
29-
- [Use in a browser with browserify, webpack or any other bundler](#use-in-a-browser-with-browserify--webpack-or-any-other-bundler)
30-
- [Use in a browser Using a script tag](#use-in-a-browser-using-a-script-tag)
31-
- [Usage](#usage)
32-
- [Examples](#examples)
33-
- [Create a file composed by several blocks](#create-a-file-composed-by-several-blocks)
34-
- [Create a directory that contains several files](#create-a-directory-that-contains-several-files)
35-
- [API](#api)
36-
- [unixfs Data Structure](#unixfs-data-structure)
37-
- [create an unixfs Data element](#create-an-unixfs-data-element)
38-
- [add and remove a block size to the block size list](#add-and-remove-a-block-size-to-the-block-size-list)
39-
- [get total fileSize](#get-total-filesize)
40-
- [marshal and unmarshal](#marshal-and-unmarshal)
41-
- [Contribute](#contribute)
42-
- [License](#license)
21+
## Table of Contents <!-- omit in toc -->
22+
23+
- [Install](#install)
24+
- [npm](#npm)
25+
- [Use in Node.js](#use-in-nodejs)
26+
- [Use in a browser with browserify, webpack or any other bundler](#use-in-a-browser-with-browserify-webpack-or-any-other-bundler)
27+
- [Use in a browser Using a script tag](#use-in-a-browser-using-a-script-tag)
28+
- [Usage](#usage)
29+
- [Examples](#examples)
30+
- [Create a file composed by several blocks](#create-a-file-composed-by-several-blocks)
31+
- [Create a directory that contains several files](#create-a-directory-that-contains-several-files)
32+
- [API](#api)
33+
- [UnixFS Data Structure](#unixfs-data-structure)
34+
- [create an unixfs Data element](#create-an-unixfs-data-element)
35+
- [add and remove a block size to the block size list](#add-and-remove-a-block-size-to-the-block-size-list)
36+
- [get total fileSize](#get-total-filesize)
37+
- [marshal and unmarshal](#marshal-and-unmarshal)
38+
- [is this UnixFS entry a directory?](#is-this-unixfs-entry-a-directory)
39+
- [Contribute](#contribute)
40+
- [License](#license)
4341

4442
## Install
4543

@@ -52,20 +50,20 @@
5250
### Use in Node.js
5351

5452
```JavaScript
55-
var Unixfs = require('ipfs-unixfs')
53+
var UnixFS = require('ipfs-unixfs')
5654
```
5755

5856
### Use in a browser with browserify, webpack or any other bundler
5957

6058
The code published to npm that gets loaded on require is in fact a ES5 transpiled version with the right shims added. This means that you can require it and use with your favourite bundler without having to adjust asset management process.
6159

6260
```JavaScript
63-
var Unixfs = require('ipfs-unixfs')
61+
var UnixFS = require('ipfs-unixfs')
6462
```
6563

6664
### Use in a browser Using a script tag
6765

68-
Loading this module through a script tag will make the `Unixfs` obj available in the global namespace.
66+
Loading this module through a script tag will make the `UnixFS` obj available in the global namespace.
6967

7068
```html
7169
<script src="https://npmcdn.com/ipfs-unixfs/dist/index.min.js"></script>
@@ -80,7 +78,7 @@ Loading this module through a script tag will make the `Unixfs` obj available in
8078
#### Create a file composed by several blocks
8179

8280
```JavaScript
83-
var data = new Unixfs('file')
81+
const data = new UnixFS({ type: 'file' })
8482
data.addBlockSize(256) // add the size of each block
8583
data.addBlockSize(256)
8684
// ...
@@ -91,14 +89,16 @@ data.addBlockSize(256)
9189
Creating a directory that contains several files is achieve by creating a unixfs element that identifies a MerkleDAG node as a directory. The links of that MerkleDAG node are the files that are contained in this directory.
9290

9391
```JavaScript
94-
var data = new Unixfs('directory')
92+
const data = new UnixFS({ type: 'directory' })
9593
```
9694

9795
## API
9896

99-
#### unixfs Data Structure
97+
#### UnixFS Data Structure
10098

10199
```protobuf
100+
syntax = "proto2";
101+
102102
message Data {
103103
enum DataType {
104104
Raw = 0;
@@ -113,9 +113,10 @@ message Data {
113113
optional bytes Data = 2;
114114
optional uint64 filesize = 3;
115115
repeated uint64 blocksizes = 4;
116-
117116
optional uint64 hashType = 5;
118117
optional uint64 fanout = 6;
118+
optional uint32 mode = 7;
119+
optional int64 mtime = 8;
119120
}
120121
121122
message Metadata {
@@ -126,10 +127,22 @@ message Metadata {
126127
#### create an unixfs Data element
127128

128129
```JavaScript
129-
var data = new UnixFS(<type>, [<content>])
130+
const data = new UnixFS([options])
130131
```
131132

132-
Type can be: `['raw', 'directory', 'file', 'metadata', 'symlink', 'hamt-sharded-directory']`
133+
`options` is an optional object argument that might include the following keys:
134+
135+
- type (string, default `file`): The type of UnixFS entry. Can be:
136+
- `raw`
137+
- `directory`
138+
- `file`
139+
- `metadata`
140+
- `symlink`
141+
- `hamt-sharded-directory`
142+
- data (Buffer): The optional data field for this node
143+
- blockSizes (Array, default: `[]`): If this is a `file` node that is made up of multiple blocks, `blockSizes` is a list numbers that represent the size of the file chunks stored in each child node. It is used to calculate the total file size.
144+
- mode (Number, default `0644` for files, `0755` for directories/hamt-sharded-directories) file mode
145+
- mtime (Date, default `0`): The modification time of this node
133146

134147
#### add and remove a block size to the block size list
135148

@@ -149,9 +162,19 @@ data.fileSize() // => size in bytes
149162

150163
#### marshal and unmarshal
151164

165+
```javascript
166+
const marshaled = data.marshal()
167+
const unmarshaled = Unixfs.unmarshal(marshaled)
152168
```
153-
var marshaled = data.marshal()
154-
var unmarshaled = Unixfs.unmarshal(marshaled)
169+
170+
#### is this UnixFS entry a directory?
171+
172+
```JavaScript
173+
const dir = new Data({ type: 'directory' })
174+
dir.isDirectory() // true
175+
176+
const file = new Data({ type: 'file' })
177+
file.isDirectory() // false
155178
```
156179

157180
## Contribute

package.json

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,10 @@
3838
"devDependencies": {
3939
"aegir": "^20.4.1",
4040
"chai": "^4.2.0",
41-
"dirty-chai": "^2.0.1",
42-
"safe-buffer": "^5.1.2"
41+
"dirty-chai": "^2.0.1"
4342
},
4443
"dependencies": {
45-
"protons": "^1.0.1"
44+
"protons": "^1.1.0"
4645
},
4746
"contributors": [
4847
"David Dias <daviddias.p@gmail.com>",

src/index.js

Lines changed: 101 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@
22

33
const protons = require('protons')
44
const pb = protons(require('./unixfs.proto'))
5-
// encode/decode
65
const unixfsData = pb.Data
7-
// const unixfsMetadata = pb.MetaData // encode/decode
86

97
const types = [
108
'raw',
@@ -20,37 +18,94 @@ const dirTypes = [
2018
'hamt-sharded-directory'
2119
]
2220

23-
function Data (type, data) {
24-
if (!(this instanceof Data)) {
25-
return new Data(type, data)
21+
const DEFAULT_FILE_MODE = parseInt('0644', 8)
22+
const DEFAULT_DIRECTORY_MODE = parseInt('0755', 8)
23+
24+
function parseArgs (args) {
25+
if (args.length === 0) {
26+
return {
27+
type: 'file'
28+
}
29+
}
30+
31+
if (args.length === 2) {
32+
// support old-style constructor
33+
return {
34+
type: args[0],
35+
data: args[1]
36+
}
37+
}
38+
39+
if (typeof args[0] === 'string' || args[0] instanceof String) {
40+
return {
41+
type: args[0]
42+
}
2643
}
27-
if (types.indexOf(type) === -1) {
28-
throw new Error('Type: ' + type + ' is not valid')
44+
45+
return args[0]
46+
}
47+
48+
class Data {
49+
// decode from protobuf https://github.com/ipfs/specs/blob/master/UNIXFS.md
50+
static unmarshal (marshaled) {
51+
const decoded = unixfsData.decode(marshaled)
52+
53+
return new Data({
54+
type: types[decoded.Type],
55+
data: decoded.hasData() ? decoded.Data : undefined,
56+
blockSizes: decoded.blocksizes,
57+
mode: decoded.hasMode() ? decoded.mode : undefined,
58+
mtime: decoded.hasMtime() ? new Date(decoded.mtime * 1000) : undefined
59+
})
2960
}
3061

31-
this.type = type
32-
this.data = data
33-
this.blockSizes = []
62+
constructor (...args) {
63+
const {
64+
type,
65+
data,
66+
blockSizes,
67+
hashType,
68+
fanout,
69+
mtime,
70+
mode
71+
} = parseArgs(args)
72+
73+
if (!types.includes(type)) {
74+
throw new Error('Type: ' + type + ' is not valid')
75+
}
76+
77+
this.type = type
78+
this.data = data
79+
this.hashType = hashType
80+
this.fanout = fanout
81+
this.blockSizes = blockSizes || []
82+
this.mtime = mtime || new Date(0)
83+
this.mode = mode
84+
85+
if (this.mode === undefined && type === 'file') {
86+
this.mode = DEFAULT_FILE_MODE
87+
}
3488

35-
if (this.type === 'file') {
36-
this.mode = parseInt('0644', 8)
89+
if (this.mode === undefined && this.isDirectory()) {
90+
this.mode = DEFAULT_DIRECTORY_MODE
91+
}
3792
}
3893

39-
if (this.type === 'directory' || this.type === 'hamt-sharded-directory') {
40-
this.mode = parseInt('0755', 8)
94+
isDirectory () {
95+
return dirTypes.includes(this.type)
4196
}
4297

43-
this.addBlockSize = (size) => {
98+
addBlockSize (size) {
4499
this.blockSizes.push(size)
45100
}
46101

47-
this.removeBlockSize = (index) => {
102+
removeBlockSize (index) {
48103
this.blockSizes.splice(index, 1)
49104
}
50105

51106
// data.length + blockSizes
52-
this.fileSize = () => {
53-
if (dirTypes.indexOf(this.type) >= 0) {
107+
fileSize () {
108+
if (this.isDirectory()) {
54109
// dirs don't have file size
55110
return undefined
56111
}
@@ -59,14 +114,16 @@ function Data (type, data) {
59114
this.blockSizes.forEach((size) => {
60115
sum += size
61116
})
62-
if (data) {
63-
sum += data.length
117+
118+
if (this.data) {
119+
sum += this.data.length
64120
}
121+
65122
return sum
66123
}
67124

68125
// encode to protobuf
69-
this.marshal = () => {
126+
marshal () {
70127
let type
71128

72129
switch (this.type) {
@@ -92,12 +149,28 @@ function Data (type, data) {
92149
blockSizes = undefined
93150
}
94151

95-
if ((this.type === 'directory' || this.type === 'hamt-sharded-directory') && this.mode === parseInt('0755', 8)) {
96-
delete this.mode
152+
let mode
153+
154+
if (!isNaN(parseInt(this.mode))) {
155+
mode = this.mode
156+
157+
if (mode === DEFAULT_FILE_MODE && this.type === 'file') {
158+
mode = undefined
159+
}
160+
161+
if (mode === DEFAULT_DIRECTORY_MODE && this.isDirectory()) {
162+
mode = undefined
163+
}
97164
}
98165

99-
if (this.type === 'file' && this.mode === parseInt('0644', 8)) {
100-
delete this.mode
166+
let mtime
167+
168+
if (this.mtime) {
169+
mtime = Math.round(this.mtime.getTime() / 1000)
170+
171+
if (mtime === 0) {
172+
mtime = undefined
173+
}
101174
}
102175

103176
return unixfsData.encode({
@@ -107,30 +180,10 @@ function Data (type, data) {
107180
blocksizes: blockSizes,
108181
hashType: this.hashType,
109182
fanout: this.fanout,
110-
mode: this.mode,
111-
mtime: this.mtime
183+
mode,
184+
mtime
112185
})
113186
}
114187
}
115188

116-
// decode from protobuf https://github.com/ipfs/go-ipfs/blob/master/unixfs/format.go#L24
117-
Data.unmarshal = (marsheled) => {
118-
const decoded = unixfsData.decode(marsheled)
119-
if (!decoded.Data) {
120-
decoded.Data = undefined
121-
}
122-
const obj = new Data(types[decoded.Type], decoded.Data)
123-
obj.blockSizes = decoded.blocksizes
124-
125-
if (decoded.mode) {
126-
obj.mode = decoded.mode
127-
}
128-
129-
if (decoded.mtime) {
130-
obj.mtime = decoded.mtime
131-
}
132-
133-
return obj
134-
}
135-
136-
exports = module.exports = Data
189+
module.exports = Data

0 commit comments

Comments
 (0)