source: node_modules/tar-fs/index.js@ 81bc7da

finki-main main
Last change on this file since 81bc7da was 81bc7da, checked in by Klimentina Efremova <klimentina08642@…>, 3 months ago

Initial commit

  • Property mode set to 100644
File size: 9.8 KB
Line 
1var chownr = require('chownr')
2var tar = require('tar-stream')
3var pump = require('pump')
4var mkdirp = require('mkdirp-classic')
5var fs = require('fs')
6var path = require('path')
7var os = require('os')
8
9var win32 = os.platform() === 'win32'
10
11var noop = function () {}
12
13var echo = function (name) {
14 return name
15}
16
17var normalize = !win32 ? echo : function (name) {
18 return name.replace(/\\/g, '/').replace(/[:?<>|]/g, '_')
19}
20
21var statAll = function (fs, stat, cwd, ignore, entries, sort) {
22 var queue = entries || ['.']
23
24 return function loop (callback) {
25 if (!queue.length) return callback()
26 var next = queue.shift()
27 var nextAbs = path.join(cwd, next)
28
29 stat.call(fs, nextAbs, function (err, stat) {
30 if (err) return callback(err)
31
32 if (!stat.isDirectory()) return callback(null, next, stat)
33
34 fs.readdir(nextAbs, function (err, files) {
35 if (err) return callback(err)
36
37 if (sort) files.sort()
38 for (var i = 0; i < files.length; i++) {
39 if (!ignore(path.join(cwd, next, files[i]))) queue.push(path.join(next, files[i]))
40 }
41
42 callback(null, next, stat)
43 })
44 })
45 }
46}
47
48var strip = function (map, level) {
49 return function (header) {
50 header.name = header.name.split('/').slice(level).join('/')
51
52 var linkname = header.linkname
53 if (linkname && (header.type === 'link' || path.isAbsolute(linkname))) {
54 header.linkname = linkname.split('/').slice(level).join('/')
55 }
56
57 return map(header)
58 }
59}
60
61exports.pack = function (cwd, opts) {
62 if (!cwd) cwd = '.'
63 if (!opts) opts = {}
64
65 var xfs = opts.fs || fs
66 var ignore = opts.ignore || opts.filter || noop
67 var map = opts.map || noop
68 var mapStream = opts.mapStream || echo
69 var statNext = statAll(xfs, opts.dereference ? xfs.stat : xfs.lstat, cwd, ignore, opts.entries, opts.sort)
70 var strict = opts.strict !== false
71 var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask()
72 var dmode = typeof opts.dmode === 'number' ? opts.dmode : 0
73 var fmode = typeof opts.fmode === 'number' ? opts.fmode : 0
74 var pack = opts.pack || tar.pack()
75 var finish = opts.finish || noop
76
77 if (opts.strip) map = strip(map, opts.strip)
78
79 if (opts.readable) {
80 dmode |= parseInt(555, 8)
81 fmode |= parseInt(444, 8)
82 }
83 if (opts.writable) {
84 dmode |= parseInt(333, 8)
85 fmode |= parseInt(222, 8)
86 }
87
88 var onsymlink = function (filename, header) {
89 xfs.readlink(path.join(cwd, filename), function (err, linkname) {
90 if (err) return pack.destroy(err)
91 header.linkname = normalize(linkname)
92 pack.entry(header, onnextentry)
93 })
94 }
95
96 var onstat = function (err, filename, stat) {
97 if (err) return pack.destroy(err)
98 if (!filename) {
99 if (opts.finalize !== false) pack.finalize()
100 return finish(pack)
101 }
102
103 if (stat.isSocket()) return onnextentry() // tar does not support sockets...
104
105 var header = {
106 name: normalize(filename),
107 mode: (stat.mode | (stat.isDirectory() ? dmode : fmode)) & umask,
108 mtime: stat.mtime,
109 size: stat.size,
110 type: 'file',
111 uid: stat.uid,
112 gid: stat.gid
113 }
114
115 if (stat.isDirectory()) {
116 header.size = 0
117 header.type = 'directory'
118 header = map(header) || header
119 return pack.entry(header, onnextentry)
120 }
121
122 if (stat.isSymbolicLink()) {
123 header.size = 0
124 header.type = 'symlink'
125 header = map(header) || header
126 return onsymlink(filename, header)
127 }
128
129 // TODO: add fifo etc...
130
131 header = map(header) || header
132
133 if (!stat.isFile()) {
134 if (strict) return pack.destroy(new Error('unsupported type for ' + filename))
135 return onnextentry()
136 }
137
138 var entry = pack.entry(header, onnextentry)
139 if (!entry) return
140
141 var rs = mapStream(xfs.createReadStream(path.join(cwd, filename), { start: 0, end: header.size > 0 ? header.size - 1 : header.size }), header)
142
143 rs.on('error', function (err) { // always forward errors on destroy
144 entry.destroy(err)
145 })
146
147 pump(rs, entry)
148 }
149
150 var onnextentry = function (err) {
151 if (err) return pack.destroy(err)
152 statNext(onstat)
153 }
154
155 onnextentry()
156
157 return pack
158}
159
160var head = function (list) {
161 return list.length ? list[list.length - 1] : null
162}
163
164var processGetuid = function () {
165 return process.getuid ? process.getuid() : -1
166}
167
168var processUmask = function () {
169 return process.umask ? process.umask() : 0
170}
171
172exports.extract = function (cwd, opts) {
173 if (!cwd) cwd = '.'
174 if (!opts) opts = {}
175
176 var xfs = opts.fs || fs
177 var ignore = opts.ignore || opts.filter || noop
178 var map = opts.map || noop
179 var mapStream = opts.mapStream || echo
180 var own = opts.chown !== false && !win32 && processGetuid() === 0
181 var extract = opts.extract || tar.extract()
182 var stack = []
183 var now = new Date()
184 var umask = typeof opts.umask === 'number' ? ~opts.umask : ~processUmask()
185 var dmode = typeof opts.dmode === 'number' ? opts.dmode : 0
186 var fmode = typeof opts.fmode === 'number' ? opts.fmode : 0
187 var strict = opts.strict !== false
188
189 if (opts.strip) map = strip(map, opts.strip)
190
191 if (opts.readable) {
192 dmode |= parseInt(555, 8)
193 fmode |= parseInt(444, 8)
194 }
195 if (opts.writable) {
196 dmode |= parseInt(333, 8)
197 fmode |= parseInt(222, 8)
198 }
199
200 var utimesParent = function (name, cb) { // we just set the mtime on the parent dir again everytime we write an entry
201 var top
202 while ((top = head(stack)) && name.slice(0, top[0].length) !== top[0]) stack.pop()
203 if (!top) return cb()
204 xfs.utimes(top[0], now, top[1], cb)
205 }
206
207 var utimes = function (name, header, cb) {
208 if (opts.utimes === false) return cb()
209
210 if (header.type === 'directory') return xfs.utimes(name, now, header.mtime, cb)
211 if (header.type === 'symlink') return utimesParent(name, cb) // TODO: how to set mtime on link?
212
213 xfs.utimes(name, now, header.mtime, function (err) {
214 if (err) return cb(err)
215 utimesParent(name, cb)
216 })
217 }
218
219 var chperm = function (name, header, cb) {
220 var link = header.type === 'symlink'
221
222 /* eslint-disable node/no-deprecated-api */
223 var chmod = link ? xfs.lchmod : xfs.chmod
224 var chown = link ? xfs.lchown : xfs.chown
225 /* eslint-enable node/no-deprecated-api */
226
227 if (!chmod) return cb()
228
229 var mode = (header.mode | (header.type === 'directory' ? dmode : fmode)) & umask
230
231 if (chown && own) chown.call(xfs, name, header.uid, header.gid, onchown)
232 else onchown(null)
233
234 function onchown (err) {
235 if (err) return cb(err)
236 if (!chmod) return cb()
237 chmod.call(xfs, name, mode, cb)
238 }
239 }
240
241 extract.on('entry', function (header, stream, next) {
242 header = map(header) || header
243 header.name = normalize(header.name)
244 var name = path.join(cwd, path.join('/', header.name))
245
246 if (ignore(name, header)) {
247 stream.resume()
248 return next()
249 }
250
251 var stat = function (err) {
252 if (err) return next(err)
253 utimes(name, header, function (err) {
254 if (err) return next(err)
255 if (win32) return next()
256 chperm(name, header, next)
257 })
258 }
259
260 var onsymlink = function () {
261 if (win32) return next() // skip symlinks on win for now before it can be tested
262 xfs.unlink(name, function () {
263 var dst = path.resolve(path.dirname(name), header.linkname)
264 if (!inCwd(dst, cwd)) return next(new Error(name + ' is not a valid symlink'))
265
266 xfs.symlink(header.linkname, name, stat)
267 })
268 }
269
270 var onlink = function () {
271 if (win32) return next() // skip links on win for now before it can be tested
272 xfs.unlink(name, function () {
273 var srcpath = path.join(cwd, path.join('/', header.linkname))
274
275 xfs.realpath(srcpath, function (err, dst) {
276 if (err || !inCwd(dst, cwd)) return next(new Error(name + ' is not a valid hardlink'))
277
278 xfs.link(dst, name, function (err) {
279 if (err && err.code === 'EPERM' && opts.hardlinkAsFilesFallback) {
280 stream = xfs.createReadStream(srcpath)
281 return onfile()
282 }
283
284 stat(err)
285 })
286 })
287 })
288 }
289
290 var onfile = function () {
291 var ws = xfs.createWriteStream(name)
292 var rs = mapStream(stream, header)
293
294 ws.on('error', function (err) { // always forward errors on destroy
295 rs.destroy(err)
296 })
297
298 pump(rs, ws, function (err) {
299 if (err) return next(err)
300 ws.on('close', stat)
301 })
302 }
303
304 if (header.type === 'directory') {
305 stack.push([name, header.mtime])
306 return mkdirfix(name, {
307 fs: xfs, own: own, uid: header.uid, gid: header.gid
308 }, stat)
309 }
310
311 var dir = path.dirname(name)
312
313 validate(xfs, dir, path.join(cwd, '.'), function (err, valid) {
314 if (err) return next(err)
315 if (!valid) return next(new Error(dir + ' is not a valid path'))
316
317 mkdirfix(dir, {
318 fs: xfs, own: own, uid: header.uid, gid: header.gid
319 }, function (err) {
320 if (err) return next(err)
321
322 switch (header.type) {
323 case 'file': return onfile()
324 case 'link': return onlink()
325 case 'symlink': return onsymlink()
326 }
327
328 if (strict) return next(new Error('unsupported type for ' + name + ' (' + header.type + ')'))
329
330 stream.resume()
331 next()
332 })
333 })
334 })
335
336 if (opts.finish) extract.on('finish', opts.finish)
337
338 return extract
339}
340
341function validate (fs, name, root, cb) {
342 if (name === root) return cb(null, true)
343 fs.lstat(name, function (err, st) {
344 if (err && err.code !== 'ENOENT') return cb(err)
345 if (err || st.isDirectory()) return validate(fs, path.join(name, '..'), root, cb)
346 cb(null, false)
347 })
348}
349
350function mkdirfix (name, opts, cb) {
351 mkdirp(name, { fs: opts.fs }, function (err, made) {
352 if (!err && made && opts.own) {
353 chownr(made, opts.uid, opts.gid, cb)
354 } else {
355 cb(err)
356 }
357 })
358}
359
360function inCwd (dst, cwd) {
361 cwd = path.resolve(cwd)
362 return cwd === dst || dst.startsWith(cwd + path.sep)
363}
Note: See TracBrowser for help on using the repository browser.