@@ -12,6 +12,13 @@ const updateMfsRoot = require('./utils/update-mfs-root')
12
12
const { DAGNode } = require ( 'ipld-dag-pb' )
13
13
const mc = require ( 'multicodec' )
14
14
const mh = require ( 'multihashes' )
15
+ const pipe = require ( 'it-pipe' )
16
+ const importer = require ( 'ipfs-unixfs-importer' )
17
+ const exporter = require ( 'ipfs-unixfs-exporter' )
18
+ const last = require ( 'it-last' )
19
+ const cp = require ( './cp' )
20
+ const rm = require ( './rm' )
21
+ const persist = require ( 'ipfs-unixfs-importer/src/utils/persist' )
15
22
16
23
const defaultOptions = {
17
24
flush : true ,
@@ -21,10 +28,10 @@ const defaultOptions = {
21
28
recursive : false
22
29
}
23
30
24
- function calculateModification ( mode ) {
31
+ function calculateModification ( mode , originalMode , isDirectory ) {
25
32
let modification = 0
26
33
27
- if ( mode . includes ( 'x' ) ) {
34
+ if ( mode . includes ( 'x' ) || ( mode . includes ( 'X' ) && ( isDirectory || ( originalMode & 0o1 || originalMode & 0o10 || originalMode & 0o100 ) ) ) ) {
28
35
modification += 1
29
36
}
30
37
@@ -76,7 +83,7 @@ function calculateSpecial (references, mode, modification) {
76
83
}
77
84
78
85
// https://en.wikipedia.org/wiki/Chmod#Symbolic_modes
79
- function parseSymbolicMode ( input , originalMode ) {
86
+ function parseSymbolicMode ( input , originalMode , isDirectory ) {
80
87
if ( ! originalMode ) {
81
88
originalMode = 0
82
89
}
@@ -98,7 +105,7 @@ function parseSymbolicMode (input, originalMode) {
98
105
references = 'ugo'
99
106
}
100
107
101
- let modification = calculateModification ( mode )
108
+ let modification = calculateModification ( mode , originalMode , isDirectory )
102
109
modification = calculateUGO ( references , modification )
103
110
modification = calculateSpecial ( references , mode , modification )
104
111
@@ -139,6 +146,20 @@ function parseSymbolicMode (input, originalMode) {
139
146
}
140
147
}
141
148
149
+ function calculateMode ( mode , metadata ) {
150
+ if ( typeof mode === 'string' || mode instanceof String ) {
151
+ if ( mode . match ( / ^ \d + $ / g) ) {
152
+ mode = parseInt ( mode , 8 )
153
+ } else {
154
+ mode = mode . split ( ',' ) . reduce ( ( curr , acc ) => {
155
+ return parseSymbolicMode ( acc , curr , metadata . isDirectory ( ) )
156
+ } , metadata . mode )
157
+ }
158
+ }
159
+
160
+ return mode
161
+ }
162
+
142
163
module . exports = ( context ) => {
143
164
return async function mfsChmod ( path , mode , options ) {
144
165
options = applyDefaultOptions ( options , defaultOptions )
@@ -155,20 +176,54 @@ module.exports = (context) => {
155
176
throw errCode ( new Error ( `${ path } was not a UnixFS node` ) , 'ERR_NOT_UNIXFS' )
156
177
}
157
178
158
- let node = await context . ipld . get ( cid )
159
- const metadata = UnixFS . unmarshal ( node . Data )
160
-
161
- if ( typeof mode === 'string' || mode instanceof String ) {
162
- if ( mode . match ( / ^ \d + $ / g) ) {
163
- mode = parseInt ( mode , 8 )
164
- } else {
165
- mode = mode . split ( ',' ) . reduce ( ( curr , acc ) => {
166
- return parseSymbolicMode ( acc , curr )
167
- } , metadata . mode )
168
- }
179
+ if ( options . recursive ) {
180
+ // recursively export from root CID, change perms of each entry then reimport
181
+ // but do not reimport files, only manipulate dag-pb nodes
182
+ const root = await pipe (
183
+ async function * ( ) {
184
+ for await ( const entry of exporter . recursive ( cid , context . ipld ) ) {
185
+ let node = await context . ipld . get ( entry . cid )
186
+ entry . unixfs . mode = calculateMode ( mode , entry . unixfs )
187
+ node = new DAGNode ( entry . unixfs . marshal ( ) , node . Links )
188
+
189
+ yield {
190
+ path : entry . path ,
191
+ content : node
192
+ }
193
+ }
194
+ } ,
195
+ ( source ) => importer ( source , context . ipld , {
196
+ ...options ,
197
+ dagBuilder : async function * ( source , ipld , options ) {
198
+ for await ( const entry of source ) {
199
+ yield async function ( ) {
200
+ const cid = await persist ( entry . content , ipld , options )
201
+
202
+ return {
203
+ cid,
204
+ path : entry . path ,
205
+ unixfs : UnixFS . unmarshal ( entry . content . Data ) ,
206
+ node : entry . content
207
+ }
208
+ }
209
+ }
210
+ }
211
+ } ) ,
212
+ ( nodes ) => last ( nodes )
213
+ )
214
+
215
+ // remove old path from mfs
216
+ await rm ( context ) ( path , options )
217
+
218
+ // add newly created tree to mfs at path
219
+ await cp ( context ) ( `/ipfs/${ root . cid } ` , path , options )
220
+
221
+ return
169
222
}
170
223
171
- metadata . mode = mode
224
+ let node = await context . ipld . get ( cid )
225
+ const metadata = UnixFS . unmarshal ( node . Data )
226
+ metadata . mode = calculateMode ( mode , metadata )
172
227
node = new DAGNode ( metadata . marshal ( ) , node . Links )
173
228
174
229
const updatedCid = await context . ipld . put ( node , mc . DAG_PB , {
0 commit comments