Skip to content

Instantly share code, notes, and snippets.

@igorzi
Created November 25, 2011 03:27
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save igorzi/1392759 to your computer and use it in GitHub Desktop.
Save igorzi/1392759 to your computer and use it in GitHub Desktop.
From 03df19d1564fddb4209f33a93d751aee7667f3d7 Mon Sep 17 00:00:00 2001
From: Igor Zinkovsky <igorzi@microsoft.com>
Date: Thu, 24 Nov 2011 12:36:46 -0800
Subject: [PATCH] enable long paths on windows
---
lib/fs.js | 91 ++++++++++++++++++++-----------------
lib/path.js | 21 +++++++++
src/node_file.cc | 18 +++++++-
test/simple/test-fs-long-path.js | 49 ++++++++++++++++++++
test/simple/test-path-makelong.js | 38 +++++++++++++++
5 files changed, 174 insertions(+), 43 deletions(-)
create mode 100644 test/simple/test-fs-long-path.js
create mode 100644 test/simple/test-path-makelong.js
diff --git a/lib/fs.js b/lib/fs.js
index 4ef2398..dabc351 100644
--- a/lib/fs.js
+++ b/lib/fs.js
@@ -26,6 +26,7 @@
// var mode = 438; /* mode=0666 */
var util = require('util');
+var pathModule = require('path');
var binding = process.binding('fs');
var constants = process.binding('constants');
@@ -220,12 +221,13 @@ fs.open = function(path, flags, mode, callback) {
mode = modeNum(mode, 438 /*=0666*/);
- binding.open(path, stringToFlags(flags), mode, callback);
+ binding.open(pathModule._makeLong(path), stringToFlags(flags), mode,
+ callback);
};
fs.openSync = function(path, flags, mode) {
mode = modeNum(mode, 438 /*=0666*/);
- return binding.open(path, stringToFlags(flags), mode);
+ return binding.open(pathModule._makeLong(path), stringToFlags(flags), mode);
};
fs.read = function(fd, buffer, offset, length, position, callback) {
@@ -320,11 +322,13 @@ fs.writeSync = function(fd, buffer, offset, length, position) {
};
fs.rename = function(oldPath, newPath, callback) {
- binding.rename(oldPath, newPath, callback || noop);
+ binding.rename(pathModule._makeLong(oldPath), pathModule._makeLong(newPath),
+ callback || noop);
};
fs.renameSync = function(oldPath, newPath) {
- return binding.rename(oldPath, newPath);
+ return binding.rename(pathModule._makeLong(oldPath),
+ pathModule._makeLong(newPath));
};
fs.truncate = function(fd, len, callback) {
@@ -336,11 +340,11 @@ fs.truncateSync = function(fd, len) {
};
fs.rmdir = function(path, callback) {
- binding.rmdir(path, callback || noop);
+ binding.rmdir(pathModule._makeLong(path), callback || noop);
};
fs.rmdirSync = function(path) {
- return binding.rmdir(path);
+ return binding.rmdir(pathModule._makeLong(path));
};
fs.fdatasync = function(fd, callback) {
@@ -361,11 +365,13 @@ fs.fsyncSync = function(fd) {
fs.mkdir = function(path, mode, callback) {
if (typeof mode === 'function') callback = mode;
- binding.mkdir(path, modeNum(mode, 511 /*=0777*/), callback || noop);
+ binding.mkdir(pathModule._makeLong(path), modeNum(mode, 511 /*=0777*/),
+ callback || noop);
};
fs.mkdirSync = function(path, mode) {
- return binding.mkdir(path, modeNum(mode, 511 /*=0777*/));
+ return binding.mkdir(pathModule._makeLong(path),
+ modeNum(mode, 511 /*=0777*/));
};
fs.sendfile = function(outFd, inFd, inOffset, length, callback) {
@@ -377,11 +383,11 @@ fs.sendfileSync = function(outFd, inFd, inOffset, length) {
};
fs.readdir = function(path, callback) {
- binding.readdir(path, callback || noop);
+ binding.readdir(pathModule._makeLong(path), callback || noop);
};
fs.readdirSync = function(path) {
- return binding.readdir(path);
+ return binding.readdir(pathModule._makeLong(path));
};
fs.fstat = function(fd, callback) {
@@ -389,11 +395,11 @@ fs.fstat = function(fd, callback) {
};
fs.lstat = function(path, callback) {
- binding.lstat(path, callback || noop);
+ binding.lstat(pathModule._makeLong(path), callback || noop);
};
fs.stat = function(path, callback) {
- binding.stat(path, callback || noop);
+ binding.stat(pathModule._makeLong(path), callback || noop);
};
fs.fstatSync = function(fd) {
@@ -401,46 +407,50 @@ fs.fstatSync = function(fd) {
};
fs.lstatSync = function(path) {
- return binding.lstat(path);
+ return binding.lstat(pathModule._makeLong(path));
};
fs.statSync = function(path) {
- return binding.stat(path);
+ return binding.stat(pathModule._makeLong(path));
};
fs.readlink = function(path, callback) {
- binding.readlink(path, callback || noop);
+ binding.readlink(pathModule._makeLong(path), callback || noop);
};
fs.readlinkSync = function(path) {
- return binding.readlink(path);
+ return binding.readlink(pathModule._makeLong(path));
};
fs.symlink = function(destination, path, mode_, callback) {
var mode = (typeof(mode_) == 'string' ? mode_ : null);
var callback_ = arguments[arguments.length - 1];
callback = (typeof(callback_) == 'function' ? callback_ : null);
- binding.symlink(destination, path, mode, callback);
+ binding.symlink(pathModule._makeLong(destination),
+ pathModule._makeLong(path), mode, callback);
};
fs.symlinkSync = function(destination, path, mode) {
- return binding.symlink(destination, path, mode);
+ return binding.symlink(pathModule._makeLong(destination),
+ pathModule._makeLong(path), mode);
};
fs.link = function(srcpath, dstpath, callback) {
- binding.link(srcpath, dstpath, callback || noop);
+ binding.link(pathModule._makeLong(srcpath), pathModule._makeLong(dstpath),
+ callback || noop);
};
fs.linkSync = function(srcpath, dstpath) {
- return binding.link(srcpath, dstpath);
+ return binding.link(pathModule._makeLong(srcpath),
+ pathModule._makeLong(dstpath));
};
fs.unlink = function(path, callback) {
- binding.unlink(path, callback || noop);
+ binding.unlink(pathModule._makeLong(path), callback || noop);
};
fs.unlinkSync = function(path) {
- return binding.unlink(path);
+ return binding.unlink(pathModule._makeLong(path));
};
fs.fchmod = function(fd, mode, callback) {
@@ -492,11 +502,11 @@ if (constants.hasOwnProperty('O_SYMLINK')) {
fs.chmod = function(path, mode, callback) {
- binding.chmod(path, modeNum(mode), callback || noop);
+ binding.chmod(pathModule._makeLong(path), modeNum(mode), callback || noop);
};
fs.chmodSync = function(path, mode) {
- return binding.chmod(path, modeNum(mode));
+ return binding.chmod(pathModule._makeLong(path), modeNum(mode));
};
if (constants.hasOwnProperty('O_SYMLINK')) {
@@ -526,11 +536,11 @@ fs.fchownSync = function(fd, uid, gid) {
};
fs.chown = function(path, uid, gid, callback) {
- binding.chown(path, uid, gid, callback || noop);
+ binding.chown(pathModule._makeLong(path), uid, gid, callback || noop);
};
fs.chownSync = function(path, uid, gid) {
- return binding.chown(path, uid, gid);
+ return binding.chown(pathModule._makeLong(path), uid, gid);
};
// converts Date or number to a fractional UNIX timestamp
@@ -551,13 +561,13 @@ fs._toUnixTimestamp = toUnixTimestamp;
fs.utimes = function(path, atime, mtime, callback) {
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);
- binding.utimes(path, atime, mtime, callback || noop);
+ binding.utimes(pathModule._makeLong(path), atime, mtime, callback || noop);
};
fs.utimesSync = function(path, atime, mtime) {
atime = toUnixTimestamp(atime);
mtime = toUnixTimestamp(mtime);
- binding.utimes(path, atime, mtime);
+ binding.utimes(pathModule._makeLong(path), atime, mtime);
};
fs.futimes = function(fd, atime, mtime, callback) {
@@ -646,7 +656,7 @@ function FSWatcher() {
util.inherits(FSWatcher, EventEmitter);
FSWatcher.prototype.start = function(filename, persistent) {
- var r = this._handle.start(filename, persistent);
+ var r = this._handle.start(pathModule._makeLong(filename), persistent);
if (r) {
this._handle.close();
@@ -703,7 +713,7 @@ util.inherits(StatWatcher, EventEmitter);
StatWatcher.prototype.start = function(filename, persistent, interval) {
- this._handle.start(filename, persistent, interval);
+ this._handle.start(pathModule._makeLong(filename), persistent, interval);
};
@@ -766,8 +776,7 @@ fs.unwatchFile = function(filename) {
// Not using realpath(2) because it's bad.
// See: http://insanecoding.blogspot.com/2007/11/pathmax-simply-isnt.html
-var path = require('path'),
- normalize = path.normalize,
+var normalize = pathModule.normalize,
isWindows = process.platform === 'win32';
if (isWindows) {
@@ -776,7 +785,7 @@ if (isWindows) {
// windows version
fs.realpathSync = function realpathSync(p, cache) {
- p = path.resolve(p);
+ p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return cache[p];
}
@@ -791,7 +800,7 @@ if (isWindows) {
cb = cache;
cache = null;
}
- p = path.resolve(p);
+ p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return cb(null, cache[p]);
}
@@ -812,7 +821,7 @@ if (isWindows) {
// posix version
fs.realpathSync = function realpathSync(p, cache) {
// make p is absolute
- p = path.resolve(p);
+ p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return cache[p];
@@ -865,14 +874,14 @@ if (isWindows) {
if (!seenLinks[id]) {
fs.statSync(base);
seenLinks[id] = fs.readlinkSync(base);
- resolvedLink = path.resolve(previous, seenLinks[id]);
+ resolvedLink = pathModule.resolve(previous, seenLinks[id]);
// track this, if given a cache.
if (cache) cache[base] = resolvedLink;
}
}
// resolve the link, then start over
- p = path.resolve(resolvedLink, p.slice(pos));
+ p = pathModule.resolve(resolvedLink, p.slice(pos));
pos = 0;
previous = base = current = '';
}
@@ -891,7 +900,7 @@ if (isWindows) {
}
// make p is absolute
- p = path.resolve(p);
+ p = pathModule.resolve(p);
if (cache && Object.prototype.hasOwnProperty.call(cache, p)) {
return cb(null, cache[p]);
@@ -969,7 +978,7 @@ if (isWindows) {
function gotTarget(err, target, base) {
if (err) return cb(err);
- var resolvedLink = path.resolve(previous, target);
+ var resolvedLink = pathModule.resolve(previous, target);
if (cache) cache[base] = resolvedLink;
gotResolvedLink(resolvedLink);
}
@@ -977,7 +986,7 @@ if (isWindows) {
function gotResolvedLink(resolvedLink) {
// resolve the link, then start over
- p = path.resolve(resolvedLink, p.slice(pos));
+ p = pathModule.resolve(resolvedLink, p.slice(pos));
pos = 0;
previous = base = current = '';
@@ -1425,4 +1434,4 @@ SyncWriteStream.prototype.destroy = function() {
return true;
};
-SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
+SyncWriteStream.prototype.destroySoon = SyncWriteStream.prototype.destroy;
\ No newline at end of file
diff --git a/lib/path.js b/lib/path.js
index 162a15f..03d0807 100644
--- a/lib/path.js
+++ b/lib/path.js
@@ -417,3 +417,24 @@ exports.existsSync = function(path) {
return false;
}
};
+
+
+exports._makeLong = isWindows ?
+ function(path) {
+ var resolvedPath = exports.resolve(path);
+
+ if (resolvedPath.match(/^[a-zA-Z]\:\\/)) {
+ // path is local filesystem path, which needs to be converted
+ // to long UNC path.
+ return '\\\\?\\' + resolvedPath;
+ } else if (resolvedPath.match(/^\\\\[^?.]/)) {
+ // path is network UNC path, which needs to be converted
+ // to long UNC path.
+ return '\\\\?\\UNC\\' + resolvedPath.substring(2);
+ }
+
+ return path;
+ } :
+ function(path) {
+ return path;
+ };
diff --git a/src/node_file.cc b/src/node_file.cc
index c6dcdda..411f86c 100644
--- a/src/node_file.cc
+++ b/src/node_file.cc
@@ -252,9 +252,23 @@ Local<Value> FSError(int errorno,
Local<Value> e;
+ Local<String> path_str;
+
if (path) {
+#ifdef _WIN32
+ if (memcmp(path, "\\\\?\\UNC\\", 8) == 0) {
+ path_str = String::Concat(String::New("\\\\"), String::New(path + 8));
+ } else if (memcmp(path, "\\\\?\\", 4) == 0) {
+ path_str = String::New(path + 4);
+ } else {
+ path_str = String::New(path);
+ }
+#else
+ path_str = String::New(path);
+#endif
+
Local<String> cons3 = String::Concat(cons2, String::NewSymbol(" '"));
- Local<String> cons4 = String::Concat(cons3, String::New(path));
+ Local<String> cons4 = String::Concat(cons3, path_str);
Local<String> cons5 = String::Concat(cons4, String::NewSymbol("'"));
e = Exception::Error(cons5);
} else {
@@ -266,7 +280,7 @@ Local<Value> FSError(int errorno,
// TODO errno should probably go
obj->Set(errno_symbol, Integer::New(errorno));
obj->Set(code_symbol, estring);
- if (path) obj->Set(errpath_symbol, String::New(path));
+ if (path) obj->Set(errpath_symbol, path_str);
if (syscall) obj->Set(syscall_symbol, String::NewSymbol(syscall));
return e;
}
diff --git a/test/simple/test-fs-long-path.js b/test/simple/test-fs-long-path.js
new file mode 100644
index 0000000..dfd6880
--- /dev/null
+++ b/test/simple/test-fs-long-path.js
@@ -0,0 +1,49 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+var fs = require('fs');
+var path = require('path');
+var assert = require('assert');
+
+var successes = 0;
+
+// make a path that will be at least 260 chars long.
+var cwd = process.cwd();
+var fileNameLen = Math.max(260 - cwd.length - 1, 1);
+var fileName = new Array(fileNameLen + 1).join('x');
+var fullPath = path.resolve(fileName);
+
+console.log({ filenameLength: fileName.length,
+ fullPathLength: fullPath.length });
+
+fs.writeFile(fullPath, 'ok', function(err) {
+ if (err) throw err;
+ successes++;
+
+ fs.stat(fullPath, function(err, stats) {
+ if (err) throw err;
+ successes++;
+ });
+});
+
+process.on('exit', function() {
+ assert.equal(2, successes);
+});
diff --git a/test/simple/test-path-makelong.js b/test/simple/test-path-makelong.js
new file mode 100644
index 0000000..885eb12
--- /dev/null
+++ b/test/simple/test-path-makelong.js
@@ -0,0 +1,38 @@
+// Copyright Joyent, Inc. and other Node contributors.
+//
+// Permission is hereby granted, free of charge, to any person obtaining a
+// copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to permit
+// persons to whom the Software is furnished to do so, subject to the
+// following conditions:
+//
+// The above copyright notice and this permission notice shall be included
+// in all copies or substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
+// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
+// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
+// USE OR OTHER DEALINGS IN THE SOFTWARE.
+
+if (process.platform === 'win32') {
+ var assert = require('assert');
+ var path = require('path');
+ var common = require('../common');
+
+ var file = path.join(common.fixturesDir, 'a.js');
+ var resolvedFile = path.resolve(file);
+ var networkFile = '\\\\someserver\\someshare\\somefile';
+
+ assert.equal('\\\\?\\' + resolvedFile, path._makeLong(file));
+ assert.equal('\\\\?\\' + resolvedFile, path._makeLong('\\\\?\\' + file));
+ assert.equal('\\\\?\\UNC\\someserver\\someshare\\somefile',
+ path._makeLong('\\\\someserver\\someshare\\somefile'));
+ assert.equal('\\\\?\\UNC\\someserver\\someshare\\somefile',
+ path._makeLong('\\\\?\\UNC\\someserver\\someshare\\somefile'));
+ assert.equal('\\\\.\\pipe\\somepipe', path._makeLong('\\\\.\\pipe\\somepipe'));
+}
--
1.7.4.msysgit.0
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment