-
-
Notifications
You must be signed in to change notification settings - Fork 156
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature metadata #144
Feature metadata #144
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,23 @@ | ||
'use strict'; | ||
|
||
var fs = require('fs'); | ||
var fs = require('graceful-fs'); | ||
var assign = require('object-assign'); | ||
|
||
var writeDir = require('./writeDir'); | ||
var writeStream = require('./writeStream'); | ||
var writeBuffer = require('./writeBuffer'); | ||
var writeSymbolicLink = require('./writeSymbolicLink'); | ||
|
||
// TODO include sticky/setuid/setgid, i.e. 7777? | ||
var MASK_MODE = parseInt('0777', 8); | ||
|
||
|
||
// http://stackoverflow.com/a/10589791/586382 | ||
function validDate(date) { | ||
return date instanceof Date && !isNaN(date.valueOf()); | ||
} | ||
|
||
|
||
function writeContents(writePath, file, cb) { | ||
// If directory then mkdirp it | ||
if (file.isDirectory()) { | ||
|
@@ -29,49 +41,124 @@ function writeContents(writePath, file, cb) { | |
|
||
// If no contents then do nothing | ||
if (file.isNull()) { | ||
return complete(); | ||
return finish(); | ||
} | ||
|
||
function complete(err) { | ||
cb(err, file); | ||
} | ||
|
||
function written(err) { | ||
// This is invoked by the various writeXxx modules when they've finished | ||
// writing the contents. | ||
// The third argument, if present, should be invoked to indicate we're done | ||
// with the file descriptor. | ||
function written(err, fd, close) { | ||
close = close || function(err, cb) { | ||
cb(err); | ||
}; | ||
|
||
if (err && !(err.code === 'EEXIST' && file.flag === 'wx')) { | ||
return close(err, finish); | ||
} | ||
|
||
if (isErrorFatal(err)) { | ||
return complete(err); | ||
// TODO handle symlinks properly | ||
if (!file.stat || file.symlink) { | ||
return close(null, finish); | ||
} | ||
|
||
if (!file.stat || typeof file.stat.mode !== 'number' || file.symlink) { | ||
return complete(); | ||
if (typeof fd === 'number') { | ||
return stat(fd, close); | ||
} | ||
|
||
fs.stat(writePath, function(err, st) { | ||
// No file descriptor given (writeDir or writeSymbolicLink) so create one. | ||
fs.open(writePath, 'r', function(err, fd) { | ||
if (err) { | ||
return complete(err); | ||
return close(err, finish); | ||
} | ||
var currentMode = (st.mode & parseInt('0777', 8)); | ||
var expectedMode = (file.stat.mode & parseInt('0777', 8)); | ||
if (currentMode === expectedMode) { | ||
return complete(); | ||
} | ||
fs.chmod(writePath, expectedMode, complete); | ||
|
||
stat(fd, function(err1) { | ||
fs.close(fd, function(err2) { | ||
finish(err1 || err2); | ||
}); | ||
}); | ||
}); | ||
} | ||
|
||
function isErrorFatal(err) { | ||
if (!err) { | ||
return false; | ||
} | ||
|
||
if (err.code === 'EEXIST' && file.flag === 'wx') { | ||
// Handle scenario for file overwrite failures. | ||
return false; // "These aren't the droids you're looking for" | ||
} | ||
// Set mode and/or atime, mtime. | ||
function stat(fd, close) { | ||
fs.fstat(fd, function(err, stat) { | ||
if (err) { | ||
return close(err, finish); | ||
} | ||
|
||
// Check if mode needs to be updated | ||
var modeDiff = 0; | ||
if (typeof file.stat.mode === 'number') { | ||
modeDiff = (file.stat.mode ^ stat.mode) & MASK_MODE; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what is the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Heh, I guess I betrayed my C origins there. It's a nice operator to get bit-difference. |
||
} | ||
|
||
// Check if atime/mtime need to be updated | ||
var timesDiff = null; | ||
if (validDate(file.stat.mtime)) { | ||
timesDiff = { | ||
mtime: file.stat.mtime, | ||
atime: validDate(file.stat.atime) ? file.stat.atime : stat.atime, | ||
}; | ||
} | ||
|
||
// Set file.stat to the reflect current state on disk | ||
assign(file.stat, stat); | ||
|
||
// Nothing to do | ||
if (!modeDiff && !timesDiff) { | ||
return close(null, finish); | ||
} | ||
|
||
// Check access, `futimes` and `fchmod` only work if we own the file, | ||
// or if we are effectively root. | ||
var uid = process.geteuid ? process.geteuid() : process.getuid(); | ||
if (stat.uid !== uid && uid !== 0) { | ||
return close(null, finish); | ||
} | ||
|
||
if (modeDiff) { | ||
return mode(); | ||
} | ||
times(); | ||
|
||
function mode() { | ||
var mode = stat.mode ^ modeDiff; | ||
fs.fchmod(fd, mode, function(err) { | ||
if (!err) { | ||
file.stat.mode = mode; | ||
file.stat.ctime.setTime(Date.now()); | ||
} | ||
if (timesDiff) { | ||
return times(err); | ||
} | ||
close(err, finish); | ||
}); | ||
} | ||
|
||
// Otherwise, this is a fatal error | ||
return true; | ||
function times(err1) { | ||
fs.futimes(fd, timesDiff.atime, timesDiff.mtime, function(err2) { | ||
if (!err2) { | ||
file.stat.atime = timesDiff.atime; | ||
file.stat.mtime = timesDiff.mtime; | ||
file.stat.ctime.setTime(Date.now()); | ||
} | ||
close(err1 || err2, finish); | ||
}); | ||
} | ||
}); | ||
} | ||
|
||
|
||
// This is invoked by the close callbacks; we're all done. | ||
function finish(err) { | ||
cb(err, file); | ||
} | ||
|
||
|
||
|
||
} | ||
|
||
module.exports = writeContents; |
This file was deleted.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@erikkemperman what would happen if this was just
'777'
like we did at https://github.com/gulpjs/vinyl-fs/blob/master/test/dest.js#L38There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
see #103 and #134
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@phated Not sure what you mean... If you meant dropping the zero, so it would leave
parseInt('777', 8)
-- that would do the exact same thing, since it is the same number...I added the leading zero to emphasize we are now actively ignoring any sticky/setuid/setgid bits that users might have set on
stat.mode
. Notice that this does not set those bits on extant files on disk to 0 -- all this mask does is ignore any bits onstat.mode
that are not rwx flags in building the argument tofchmod
. And the TODO comment questioned if we wanted to allow users to set those other bits as well.Either way, it is separate from the issue you linked, although if we were to allow these bits to be set by the user, and reflected on disk by
dest
, then there should be a test for it which doesn't use therealMode
function of course (awkward description of what it does, acually).