aboutsummaryrefslogtreecommitdiff
path: root/node_modules/vinyl-fs/lib/fileOperations.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/vinyl-fs/lib/fileOperations.js')
-rw-r--r--node_modules/vinyl-fs/lib/fileOperations.js206
1 files changed, 206 insertions, 0 deletions
diff --git a/node_modules/vinyl-fs/lib/fileOperations.js b/node_modules/vinyl-fs/lib/fileOperations.js
new file mode 100644
index 000000000..e323fb70e
--- /dev/null
+++ b/node_modules/vinyl-fs/lib/fileOperations.js
@@ -0,0 +1,206 @@
+'use strict';
+
+var fs = require('graceful-fs');
+var assign = require('object-assign');
+var isEqual = require('lodash.isequal');
+var isValidDate = require('vali-date');
+
+// TODO shared module
+// TODO include sticky/setuid/setgid, i.e. 7777?
+var MASK_MODE = parseInt('0777', 8);
+var DEFAULT_FILE_MODE = parseInt('0666', 8);
+var APPEND_MODE_REGEXP = /a/;
+
+function closeFd(propagatedErr, fd, callback) {
+ if (typeof fd !== 'number') {
+ return callback(propagatedErr);
+ }
+
+ fs.close(fd, onClosed);
+
+ function onClosed(closeErr) {
+ if (propagatedErr || closeErr) {
+ return callback(propagatedErr || closeErr);
+ }
+
+ callback();
+ }
+}
+
+function getModeDiff(fsMode, vinylMode) {
+ var modeDiff = 0;
+
+ if (typeof vinylMode === 'number') {
+ modeDiff = (vinylMode ^ fsMode) & MASK_MODE;
+ }
+
+ return modeDiff;
+}
+
+function getTimesDiff(fsStat, vinylStat) {
+
+ if (!isValidDate(vinylStat.mtime)) {
+ return;
+ }
+
+ if (isEqual(vinylStat.mtime, fsStat.mtime) &&
+ isEqual(vinylStat.atime, fsStat.atime)) {
+ return;
+ }
+
+ var atime;
+ if (isValidDate(vinylStat.atime)) {
+ atime = vinylStat.atime;
+ } else {
+ atime = fsStat.atime;
+ }
+
+ if (!isValidDate(atime)) {
+ atime = undefined;
+ }
+
+ var timesDiff = {
+ mtime: vinylStat.mtime,
+ atime: atime,
+ };
+
+ return timesDiff;
+}
+
+function isOwner(fsStat) {
+ var hasGetuid = (typeof process.getuid === 'function');
+ var hasGeteuid = (typeof process.geteuid === 'function');
+
+ // If we don't have either, assume we don't have permissions.
+ // This should only happen on Windows.
+ // Windows basically noops fchmod and errors on futimes called on directories.
+ if (!hasGeteuid && !hasGetuid) {
+ return false;
+ }
+
+ var uid;
+ if (hasGeteuid) {
+ uid = process.geteuid();
+ } else {
+ uid = process.getuid();
+ }
+
+ if (fsStat.uid !== uid && uid !== 0) {
+ return false;
+ }
+
+ return true;
+}
+
+function updateMetadata(fd, file, callback) {
+
+ fs.fstat(fd, onStat);
+
+ function onStat(err, stat) {
+ if (err) {
+ return callback(err, fd);
+ }
+
+ // Check if mode needs to be updated
+ var modeDiff = getModeDiff(stat.mode, file.stat.mode);
+
+ // Check if atime/mtime need to be updated
+ var timesDiff = getTimesDiff(stat, file.stat);
+
+ // Set file.stat to the reflect current state on disk
+ assign(file.stat, stat);
+
+ // Nothing to do
+ if (!modeDiff && !timesDiff) {
+ return callback(null, fd);
+ }
+
+ // Check access, `futimes` and `fchmod` only work if we own the file,
+ // or if we are effectively root.
+ if (!isOwner(stat)) {
+ return callback(null, fd);
+ }
+
+ if (modeDiff) {
+ return mode();
+ }
+ times();
+
+ function mode() {
+ var mode = stat.mode ^ modeDiff;
+
+ fs.fchmod(fd, mode, onFchmod);
+
+ function onFchmod(fchmodErr) {
+ if (!fchmodErr) {
+ file.stat.mode = mode;
+ }
+ if (timesDiff) {
+ return times(fchmodErr);
+ }
+ callback(fchmodErr, fd);
+ }
+ }
+
+ function times(fchmodErr) {
+ fs.futimes(fd, timesDiff.atime, timesDiff.mtime, onFutimes);
+
+ function onFutimes(futimesErr) {
+ if (!futimesErr) {
+ file.stat.atime = timesDiff.atime;
+ file.stat.mtime = timesDiff.mtime;
+ }
+ callback(fchmodErr || futimesErr, fd);
+ }
+ }
+ }
+}
+
+/*
+ Custom writeFile implementation because we need access to the
+ file descriptor after the write is complete.
+ Most of the implementation taken from node core.
+ */
+function writeFile(path, data, options, callback) {
+ if (typeof options === 'function') {
+ callback = options;
+ options = {};
+ }
+
+ if (!Buffer.isBuffer(data)) {
+ callback(new TypeError('Data must be a Buffer'));
+ return;
+ }
+
+ if (!options) {
+ options = {};
+ }
+
+ // Default the same as node
+ var mode = options.mode || DEFAULT_FILE_MODE;
+ var flag = options.flag || 'w';
+ var position = APPEND_MODE_REGEXP.test(flag) ? null : 0;
+
+ fs.open(path, flag, mode, onOpen);
+
+ function onOpen(err, fd) {
+ if (err) {
+ return onComplete(err);
+ }
+
+ fs.write(fd, data, 0, data.length, position, onComplete);
+
+ function onComplete(err) {
+ callback(err, fd);
+ }
+ }
+}
+
+module.exports = {
+ closeFd: closeFd,
+ getModeDiff: getModeDiff,
+ getTimesDiff: getTimesDiff,
+ isOwner: isOwner,
+ updateMetadata: updateMetadata,
+ writeFile: writeFile,
+};