Skip to content

Commit 81651c0

Browse files
committed
Transform : into f03a on Windows
Also, strip Windows-absolute paths from tarballs, even on Unix (same as bsdtar and gnutar do), and be careful not to pass the drive letter to the winchars.encode function so that the : there isn't converted. Close isaacs#111
1 parent b5dc46c commit 81651c0

File tree

5 files changed

+57
-14
lines changed

5 files changed

+57
-14
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -657,7 +657,7 @@ It has the following fields:
657657
- `strict` Treat warnings as crash-worthy errors. Default false.
658658
- `win32` True if on a windows platform. Causes behavior where paths
659659
replace `\` with `/` and filenames containing the windows-compatible
660-
forms of `<|>?` characters are converted to actual `<|>?` characters
660+
forms of `<|>?:` characters are converted to actual `<|>?:` characters
661661
in the archive.
662662
- `noPax` Suppress pax extended headers. Note that this means that
663663
long paths and linkpaths will be truncated, and large or negative

lib/unpack.js

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const fs = require('fs')
77
const path = require('path')
88
const mkdir = require('./mkdir.js')
99
const mkdirSync = mkdir.sync
10-
const winchars = require('./winchars.js')
10+
const wc = require('./winchars.js')
1111

1212
const ONENTRY = Symbol('onEntry')
1313
const CHECKFS = Symbol('checkFs')
@@ -98,15 +98,22 @@ class Unpack extends Parser {
9898
this.warn('path contains \'..\'', p)
9999
return false
100100
}
101-
if (path.isAbsolute(p)) {
102-
const parsed = path.parse(p)
101+
102+
// absolutes on posix are also absolutes on win32
103+
// so we only need to test this one to get both
104+
if (path.win32.isAbsolute(p)) {
105+
const parsed = path.win32.parse(p)
103106
this.warn('stripping ' + parsed.root + ' from absolute path', p)
104107
entry.path = p.substr(parsed.root.length)
105108
}
106109
}
107110

108-
if (this.win32)
109-
entry.path = winchars.encode(entry.path)
111+
// only encode : chars that aren't drive letter indicators
112+
if (this.win32) {
113+
const parsed = path.win32.parse(entry.path)
114+
entry.path = parsed.root === '' ? wc.encode(entry.path)
115+
: parsed.root + wc.encode(entry.path.substr(parsed.root.length))
116+
}
110117

111118
if (path.isAbsolute(entry.path))
112119
entry.absolute = entry.path

lib/winchars.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ const raw = [
77
'|',
88
'<',
99
'>',
10-
'?'
10+
'?',
11+
':'
1112
]
1213

1314
const win = raw.map(char =>

lib/write-entry.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,8 +47,10 @@ const WriteEntry = warner(class WriteEntry extends MiniPass {
4747
if (typeof opt.onwarn === 'function')
4848
this.on('warn', opt.onwarn)
4949

50-
if (path.isAbsolute(p) && !this.preservePaths) {
51-
const parsed = path.parse(p)
50+
if (!this.preservePaths && path.win32.isAbsolute(p)) {
51+
// absolutes on posix are also absolutes on win32
52+
// so we only need to test this one to get both
53+
const parsed = path.win32.parse(p)
5254
this.warn('stripping ' + parsed.root + ' from absolute path', p)
5355
this.path = p.substr(parsed.root.length)
5456
}

test/unpack.js

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1622,11 +1622,11 @@ t.test('unpack when dir is not writable', t => {
16221622
t.test('transmute chars on windows', t => {
16231623
const data = makeTar([
16241624
{
1625-
path: '<|>?.txt',
1626-
size: 4,
1625+
path: '<|>?:.txt',
1626+
size: 5,
16271627
type: 'File'
16281628
},
1629-
'<|>?',
1629+
'<|>?:',
16301630
'',
16311631
''
16321632
])
@@ -1635,12 +1635,13 @@ t.test('transmute chars on windows', t => {
16351635
t.beforeEach(cb => mkdirp(dir, cb))
16361636
t.afterEach(cb => rimraf(dir, cb))
16371637

1638-
const uglyName = new Buffer('ef80bcef81bcef80beef80bf2e747874', 'hex').toString()
1638+
const hex = 'ef80bcef81bcef80beef80bfef80ba2e747874'
1639+
const uglyName = new Buffer(hex, 'hex').toString()
16391640
const ugly = path.resolve(dir, uglyName)
16401641

16411642
const check = t => {
16421643
t.same(fs.readdirSync(dir), [ uglyName ])
1643-
t.equal(fs.readFileSync(ugly, 'utf8'), '<|>?')
1644+
t.equal(fs.readFileSync(ugly, 'utf8'), '<|>?:')
16441645
t.end()
16451646
}
16461647

@@ -1664,3 +1665,35 @@ t.test('transmute chars on windows', t => {
16641665

16651666
t.end()
16661667
})
1668+
1669+
t.test('safely transmute chars on windows with absolutes', t => {
1670+
// don't actually make the directory
1671+
const poop = new Error('poop')
1672+
t.teardown(mutateFS.fail('mkdir', poop))
1673+
1674+
const data = makeTar([
1675+
{
1676+
path: 'c:/x/y/z/<|>?:.txt',
1677+
size: 5,
1678+
type: 'File'
1679+
},
1680+
'<|>?:',
1681+
'',
1682+
''
1683+
])
1684+
1685+
const hex = 'ef80bcef81bcef80beef80bfef80ba2e747874'
1686+
const uglyName = new Buffer(hex, 'hex').toString()
1687+
const uglyPath = 'c:/x/y/z/' + uglyName
1688+
1689+
const u = new Unpack({
1690+
win32: true,
1691+
preservePaths: true
1692+
})
1693+
u.on('entry', entry => {
1694+
t.equal(entry.path, uglyPath)
1695+
t.end()
1696+
})
1697+
1698+
u.end(data)
1699+
})

0 commit comments

Comments
 (0)