|
2 | 2 |
|
3 | 3 | const fs = require('graceful-fs')
|
4 | 4 | const path = require('path')
|
5 |
| -const mkdirp = require('../mkdirs').mkdirs |
| 5 | +const mkdirs = require('../mkdirs').mkdirs |
6 | 6 | const pathExists = require('../path-exists').pathExists
|
7 | 7 | const utimesMillis = require('../util/utimes').utimesMillis
|
8 | 8 | const stat = require('../util/stat')
|
@@ -43,7 +43,7 @@ function checkParentDir (destStat, src, dest, opts, cb) {
|
43 | 43 | pathExists(destParent, (err, dirExists) => {
|
44 | 44 | if (err) return cb(err)
|
45 | 45 | if (dirExists) return startCopy(destStat, src, dest, opts, cb)
|
46 |
| - mkdirp(destParent, err => { |
| 46 | + mkdirs(destParent, err => { |
47 | 47 | if (err) return cb(err)
|
48 | 48 | return startCopy(destStat, src, dest, opts, cb)
|
49 | 49 | })
|
@@ -92,64 +92,69 @@ function mayCopyFile (srcStat, src, dest, opts, cb) {
|
92 | 92 | }
|
93 | 93 |
|
94 | 94 | function copyFile (srcStat, src, dest, opts, cb) {
|
95 |
| - if (typeof fs.copyFile === 'function') { |
96 |
| - return fs.copyFile(src, dest, err => { |
| 95 | + fs.copyFile(src, dest, err => { |
| 96 | + if (err) return cb(err) |
| 97 | + if (opts.preserveTimestamps) return handleTimestampsAndMode(srcStat.mode, src, dest, cb) |
| 98 | + return setDestMode(dest, srcStat.mode, cb) |
| 99 | + }) |
| 100 | +} |
| 101 | + |
| 102 | +function handleTimestampsAndMode (srcMode, src, dest, cb) { |
| 103 | + // Make sure the file is writable before setting the timestamp |
| 104 | + // otherwise open fails with EPERM when invoked with 'r+' |
| 105 | + // (through utimes call) |
| 106 | + if (fileIsNotWritable(srcMode)) { |
| 107 | + return makeFileWritable(dest, srcMode, err => { |
97 | 108 | if (err) return cb(err)
|
98 |
| - if (opts.preserveTimestamps && (srcStat.mode & 0o200) === 0) { |
99 |
| - // Make sure the file is writable before setting the timestamp |
100 |
| - // otherwise openSync fails with EPERM when invoked with 'r+' |
101 |
| - // (through utimes call) |
102 |
| - return fs.chmod(dest, srcStat.mode | 0o200, (err) => { |
103 |
| - if (err) return cb(err) |
104 |
| - return setDestTimestampsAndMode(srcStat, src, dest, opts, cb) |
105 |
| - }) |
106 |
| - } |
107 |
| - return setDestTimestampsAndMode(srcStat, src, dest, opts, cb) |
| 109 | + return setDestTimestampsAndMode(srcMode, src, dest, cb) |
108 | 110 | })
|
109 | 111 | }
|
110 |
| - return copyFileFallback(srcStat, src, dest, opts, cb) |
| 112 | + return setDestTimestampsAndMode(srcMode, src, dest, cb) |
111 | 113 | }
|
112 | 114 |
|
113 |
| -function copyFileFallback (srcStat, src, dest, opts, cb) { |
114 |
| - const rs = fs.createReadStream(src) |
115 |
| - rs.on('error', err => cb(err)).once('open', () => { |
116 |
| - // explicitly not setting srcStat mode - will do that last |
117 |
| - const ws = fs.createWriteStream(dest) |
118 |
| - ws.on('error', err => cb(err)) |
119 |
| - .on('open', () => rs.pipe(ws)) |
120 |
| - .once('close', () => setDestTimestampsAndMode(srcStat, src, dest, opts, cb)) |
| 115 | +function fileIsNotWritable (srcMode) { |
| 116 | + return (srcMode & 0o200) === 0 |
| 117 | +} |
| 118 | + |
| 119 | +function makeFileWritable (dest, srcMode, cb) { |
| 120 | + return setDestMode(dest, srcMode | 0o200, cb) |
| 121 | +} |
| 122 | + |
| 123 | +function setDestTimestampsAndMode (srcMode, src, dest, cb) { |
| 124 | + setDestTimestamps(src, dest, err => { |
| 125 | + if (err) return cb(err) |
| 126 | + return setDestMode(dest, srcMode, cb) |
121 | 127 | })
|
122 | 128 | }
|
123 | 129 |
|
124 |
| -function setDestTimestampsAndMode (srcStat, src, dest, opts, cb) { |
125 |
| - if (opts.preserveTimestamps) { |
126 |
| - // The initial srcStat.atime cannot be trusted because it is modified by the read(2) system call |
127 |
| - // (See https://nodejs.org/api/fs.html#fs_stat_time_values) |
128 |
| - return fs.stat(src, (err, updatedSrcStat) => { |
129 |
| - if (err) return cb(err) |
130 |
| - return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, (err2) => { |
131 |
| - if (err2) return cb(err2) |
132 |
| - return fs.chmod(dest, srcStat.mode, cb) |
133 |
| - }) |
134 |
| - }) |
135 |
| - } |
136 |
| - return fs.chmod(dest, srcStat.mode, cb) |
| 130 | +function setDestMode (dest, srcMode, cb) { |
| 131 | + return fs.chmod(dest, srcMode, cb) |
| 132 | +} |
| 133 | + |
| 134 | +function setDestTimestamps (src, dest, cb) { |
| 135 | + // The initial srcStat.atime cannot be trusted |
| 136 | + // because it is modified by the read(2) system call |
| 137 | + // (See https://nodejs.org/api/fs.html#fs_stat_time_values) |
| 138 | + fs.stat(src, (err, updatedSrcStat) => { |
| 139 | + if (err) return cb(err) |
| 140 | + return utimesMillis(dest, updatedSrcStat.atime, updatedSrcStat.mtime, cb) |
| 141 | + }) |
137 | 142 | }
|
138 | 143 |
|
139 | 144 | function onDir (srcStat, destStat, src, dest, opts, cb) {
|
140 |
| - if (!destStat) return mkDirAndCopy(srcStat, src, dest, opts, cb) |
| 145 | + if (!destStat) return mkDirAndCopy(srcStat.mode, src, dest, opts, cb) |
141 | 146 | if (destStat && !destStat.isDirectory()) {
|
142 | 147 | return cb(new Error(`Cannot overwrite non-directory '${dest}' with directory '${src}'.`))
|
143 | 148 | }
|
144 | 149 | return copyDir(src, dest, opts, cb)
|
145 | 150 | }
|
146 | 151 |
|
147 |
| -function mkDirAndCopy (srcStat, src, dest, opts, cb) { |
| 152 | +function mkDirAndCopy (srcMode, src, dest, opts, cb) { |
148 | 153 | fs.mkdir(dest, err => {
|
149 | 154 | if (err) return cb(err)
|
150 | 155 | copyDir(src, dest, opts, err => {
|
151 | 156 | if (err) return cb(err)
|
152 |
| - return fs.chmod(dest, srcStat.mode, cb) |
| 157 | + return setDestMode(dest, srcMode, cb) |
153 | 158 | })
|
154 | 159 | })
|
155 | 160 | }
|
|
0 commit comments