kopia lustrzana https://github.com/c9/core
backport localfs changes from v4
rodzic
086d4293e7
commit
7e90154196
|
@ -114,8 +114,9 @@ define(function(require, exports, module) {
|
|||
});
|
||||
});
|
||||
}
|
||||
onload && onload();
|
||||
|
||||
setTimeout(function() {
|
||||
onload && onload();
|
||||
});
|
||||
});
|
||||
if (app) {
|
||||
app.on("service", function(name, plugin) {
|
||||
|
|
|
@ -14,9 +14,9 @@ var getMime = (function(simpleMime) {
|
|||
return function(path) {
|
||||
var mime = simpleMime(path);
|
||||
if (typeof mime != "string")
|
||||
return "application/octet-stream"
|
||||
return "application/octet-stream";
|
||||
return mime;
|
||||
}
|
||||
};
|
||||
})(require("simple-mime")());
|
||||
var vm = require("vm");
|
||||
var exists = fs.exists || require("path").exists;
|
||||
|
@ -69,9 +69,16 @@ function logToFile(message){
|
|||
});
|
||||
}
|
||||
|
||||
var auditLog = function(description, data) {
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
module.exports = function setup(fsOptions) {
|
||||
var debug = fsOptions.debug || false;
|
||||
|
||||
auditLog = fsOptions.auditLog || auditLog;
|
||||
|
||||
var pty;
|
||||
if (fsOptions.nodePath) {
|
||||
process.env.NODE_PATH = fsOptions.nodePath;
|
||||
|
@ -80,7 +87,7 @@ module.exports = function setup(fsOptions) {
|
|||
if (!fsOptions.nopty) {
|
||||
// on darwin trying to load binary for a wrong version crashes the process
|
||||
[(fsOptions.nodePath || process.env.HOME + "/.c9/node_modules") + "/pty.js",
|
||||
"pty.js", "pty.nw.js"].some(function(p) {
|
||||
"pty.js"].some(function(p) {
|
||||
try {
|
||||
pty = require(p);
|
||||
return true;
|
||||
|
@ -510,19 +517,27 @@ module.exports = function setup(fsOptions) {
|
|||
});
|
||||
}
|
||||
|
||||
function getMetadata(path, options, callback){
|
||||
function resolveMetaPath(path, options, callback) {
|
||||
if (path.charAt(0) == "~")
|
||||
path = join(process.env.HOME, path.substr(1));
|
||||
|
||||
var metaPath = join(WSMETAPATH, path);
|
||||
|
||||
resolvePath(metaPath, options, function (err, path) {
|
||||
if (err) return callback(err);
|
||||
callback(null, path);
|
||||
});
|
||||
}
|
||||
|
||||
function getMetadata(path, options, callback){
|
||||
resolveMetaPath(path, options, function (err, path) {
|
||||
if (err) return callback(err);
|
||||
fs.readFile(path, callback);
|
||||
});
|
||||
}
|
||||
|
||||
function readfile(path, options, callback) {
|
||||
auditLog("readfile", path);
|
||||
var meta = {};
|
||||
var originalPath = path;
|
||||
|
||||
|
@ -535,20 +550,83 @@ module.exports = function setup(fsOptions) {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
var metadata = options.metadata && originalPath.indexOf(WSMETAPATH) == -1;
|
||||
|
||||
// Basic file info
|
||||
meta.mime = getMime(path);
|
||||
meta.size = stat.size;
|
||||
meta.etag = calcEtag(stat);
|
||||
|
||||
calcEtagConsideringMetadata(metadata, function(err, etag) {
|
||||
if (err) return callback(err);
|
||||
|
||||
meta.etag = etag;
|
||||
|
||||
// ETag support
|
||||
if ((TESTING || stat.mtime % 1000) && options.etag === meta.etag) {
|
||||
meta.notModified = true;
|
||||
fs.close(fd);
|
||||
return callback(null, meta);
|
||||
}
|
||||
|
||||
supportRange(meta, function(_, stop) {
|
||||
if (stop) {
|
||||
fs.close(fd);
|
||||
return callback(null, meta);
|
||||
}
|
||||
|
||||
if (metadata) {
|
||||
addMetadata(meta);
|
||||
return;
|
||||
}
|
||||
done();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
// ETag support
|
||||
if ((TESTING || stat.mtime % 1000) && options.etag === meta.etag) {
|
||||
meta.notModified = true;
|
||||
fs.close(fd);
|
||||
return callback(null, meta);
|
||||
function calcEtagConsideringMetadata(metadata, callback) {
|
||||
if (!metadata) {
|
||||
return callback(null, calcEtag(stat));
|
||||
}
|
||||
|
||||
resolveMetaPath(originalPath, options, function (err, path) {
|
||||
if (err) return callback(null, calcEtag(stat));
|
||||
|
||||
fs.stat(path, function (err, metaStat) {
|
||||
if (err) return callback(null, calcEtag(stat));
|
||||
|
||||
var previousMtime = stat.mtime;
|
||||
var previousSize = stat.size;
|
||||
var mtime = stat.mtime < metaStat.mtime ? metaStat.mtime : stat.mtime;
|
||||
var size = parseInt(stat.size.toString() + metaStat.size.toString());
|
||||
stat.mtime = mtime;
|
||||
stat.size = size;
|
||||
var etag = calcEtag(stat);
|
||||
stat.mtime = previousMtime;
|
||||
stat.size = previousSize;
|
||||
callback(null, etag);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function addMetadata(meta) {
|
||||
getMetadata(originalPath, options, function (err, data) {
|
||||
if (err) return done();
|
||||
|
||||
try {
|
||||
meta.metadataSize = data.length;
|
||||
meta.metadataStringLength = data.toString("utf8").length;
|
||||
done(data);
|
||||
} catch (e) {
|
||||
fs.close(fd);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Range support
|
||||
if (options.hasOwnProperty('range') && !(options.range.etag && options.range.etag !== meta.etag)) {
|
||||
function supportRange(meta, callback) {
|
||||
if (!(options.hasOwnProperty('range') && !(options.range.etag && options.range.etag !== meta.etag)))
|
||||
return callback(null, false);
|
||||
|
||||
var range = options.range;
|
||||
var start, end;
|
||||
if (range.hasOwnProperty("start")) {
|
||||
|
@ -562,42 +640,21 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
else {
|
||||
meta.rangeNotSatisfiable = "Invalid Range";
|
||||
fs.close(fd);
|
||||
return callback(null, meta);
|
||||
return callback(null, true);
|
||||
}
|
||||
}
|
||||
if (end < start || start < 0 || end >= stat.size) {
|
||||
meta.rangeNotSatisfiable = "Range out of bounds";
|
||||
fs.close(fd);
|
||||
return callback(null, meta);
|
||||
return callback(null, true);
|
||||
}
|
||||
options.start = start;
|
||||
options.end = end;
|
||||
meta.size = end - start + 1;
|
||||
meta.partialContent = { start: start, end: end, size: stat.size };
|
||||
}
|
||||
|
||||
var metaData;
|
||||
if (options.hasOwnProperty("metadata") && originalPath.indexOf(WSMETAPATH) == -1) {
|
||||
getMetadata(originalPath, options, function (err, data) {
|
||||
if (err)
|
||||
return done();
|
||||
try {
|
||||
meta.metadataSize = data.length;
|
||||
meta.metadataStringLength = data.toString("utf8").length;
|
||||
metaData = data;
|
||||
done(true);
|
||||
} catch (e) {
|
||||
fs.close(fd);
|
||||
done();
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
done();
|
||||
callback(null, false);
|
||||
}
|
||||
|
||||
function done(fakeStream){
|
||||
function done(metadata){
|
||||
// HEAD request support
|
||||
if (options.hasOwnProperty("head")) {
|
||||
fs.close(fd);
|
||||
|
@ -613,7 +670,7 @@ module.exports = function setup(fsOptions) {
|
|||
return callback(err);
|
||||
}
|
||||
|
||||
if (fakeStream) {
|
||||
if (metadata) {
|
||||
var readStream = meta.stream;
|
||||
meta.stream = new Stream();
|
||||
meta.stream.readable = true;
|
||||
|
@ -627,7 +684,7 @@ module.exports = function setup(fsOptions) {
|
|||
};
|
||||
readStream.pipe(meta.stream, { end : false });
|
||||
readStream.on("end", function(){
|
||||
meta.stream.write(metaData);
|
||||
meta.stream.write(metadata);
|
||||
meta.stream.end();
|
||||
});
|
||||
meta.stream.destroy = function () {
|
||||
|
@ -710,6 +767,7 @@ module.exports = function setup(fsOptions) {
|
|||
// file and then renames to the final destination.
|
||||
// It will copy the properties of the existing file is there is one.
|
||||
function mkfile(path, options, realCallback) {
|
||||
auditLog("mkfile", path);
|
||||
var meta = {};
|
||||
var called;
|
||||
var callback = function (err) {
|
||||
|
@ -1014,10 +1072,12 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function rmfile(path, options, callback) {
|
||||
auditLog("rmfile", path);
|
||||
remove(path, fs.unlink, options, callback);
|
||||
}
|
||||
|
||||
function rmdir(path, options, callback) {
|
||||
auditLog("rmdir", path);
|
||||
if (options.recursive) {
|
||||
remove(path, function(path, callback) {
|
||||
spawn("rm", {args: ["-rf", path], stdio: 'ignore'}, function(err, child) {
|
||||
|
@ -1039,6 +1099,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function rename(path, options, callback) {
|
||||
auditLog("rename", path);
|
||||
var from, to;
|
||||
if (options.from) {
|
||||
from = options.from; to = path;
|
||||
|
@ -1103,6 +1164,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function copy(path, options, callback) {
|
||||
auditLog("copy", path);
|
||||
var from, to;
|
||||
if (options.from) {
|
||||
from = options.from; to = path;
|
||||
|
@ -1191,6 +1253,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function symlink(path, options, callback) {
|
||||
auditLog("symlink", path);
|
||||
if (!options.target) return callback(new Error("options.target is required"));
|
||||
var meta = {};
|
||||
// Get real path to target dir
|
||||
|
@ -1523,6 +1586,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function chmod(path, options, callback) {
|
||||
auditLog("chmod", path);
|
||||
resolvePath(path, options, function(err, path){
|
||||
if (err) return callback(err);
|
||||
|
||||
|
@ -1540,6 +1604,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function spawn(executablePath, options, callback) {
|
||||
auditLog("spawn", executablePath);
|
||||
if (waitForEnv)
|
||||
return waitForEnv.push(spawn.bind(null, executablePath, options, callback));
|
||||
|
||||
|
@ -1559,6 +1624,14 @@ module.exports = function setup(fsOptions) {
|
|||
} catch (err) {
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (!child.pid) {
|
||||
err = new Error("spawn " + executablePath+ " ENOENT");
|
||||
err.code = "ENOENT";
|
||||
err.errno = "ENOENT";
|
||||
return callback(err);
|
||||
}
|
||||
|
||||
if (options.resumeStdin) child.stdin.resume();
|
||||
if (options.hasOwnProperty('stdoutEncoding')) {
|
||||
child.stdout && child.stdout.setEncoding(options.stdoutEncoding);
|
||||
|
@ -1571,7 +1644,7 @@ module.exports = function setup(fsOptions) {
|
|||
child.on("error", function(err) {
|
||||
child.emit("exit", 127);
|
||||
});
|
||||
|
||||
|
||||
callback(null, {
|
||||
process: child
|
||||
});
|
||||
|
@ -2306,6 +2379,7 @@ module.exports = function setup(fsOptions) {
|
|||
}
|
||||
|
||||
function execFile(executablePath, options, callback) {
|
||||
auditLog("execFile", executablePath);
|
||||
if (waitForEnv)
|
||||
return waitForEnv.push(execFile.bind(null, executablePath, options, callback));
|
||||
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
some meta data
|
|
@ -1 +1 @@
|
|||
This is a simple file!
|
||||
This is a file with exactly 42 characters!!
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
juhu kinners
|
|
@ -4,7 +4,8 @@
|
|||
"use mocha";
|
||||
|
||||
require("c9/inline-mocha")(module);
|
||||
|
||||
const async = require("async");
|
||||
const assert = require("assert");
|
||||
var expect = require('chai').expect;
|
||||
|
||||
describe('vfs-local', function () {
|
||||
|
@ -17,7 +18,8 @@ describe('vfs-local', function () {
|
|||
root: root,
|
||||
testing: true,
|
||||
defaultEnv: { CUSTOM: 43 },
|
||||
checkSymlinks: true
|
||||
checkSymlinks: true,
|
||||
wsmetapath: ".c9/metadata",
|
||||
}));
|
||||
|
||||
var vfsLoose = require('vfs-lint')(require("vfs-local")({
|
||||
|
@ -38,7 +40,6 @@ describe('vfs-local', function () {
|
|||
});
|
||||
});
|
||||
});
|
||||
|
||||
it('should prepend root when resolving virtual paths', function (done) {
|
||||
var vpath = "/dir/stuff.json";
|
||||
vfs.resolve(vpath, {}, function (err, meta) {
|
||||
|
@ -82,7 +83,7 @@ describe('vfs-local', function () {
|
|||
vfs.stat("/file.txt", {}, function (err, stat) {
|
||||
if (err) throw err;
|
||||
expect(stat).property("name").equal("file.txt");
|
||||
expect(stat).property("size").equal(23);
|
||||
expect(stat).property("size").equal(44);
|
||||
expect(stat).property("mime").equal("text/plain");
|
||||
done();
|
||||
});
|
||||
|
@ -96,11 +97,86 @@ describe('vfs-local', function () {
|
|||
});
|
||||
|
||||
describe('vfs.readfile()', function () {
|
||||
|
||||
it("should work if metadata is requested but there is none", function(done) {
|
||||
vfs.readfile("/file2.txt", { metadata: true }, function(err, meta) {
|
||||
if (err) throw err;
|
||||
var stream = meta.stream;
|
||||
var chunks = [];
|
||||
var length = 0;
|
||||
stream.on("data", function (chunk) {
|
||||
chunks.push(chunk);
|
||||
length += chunk.length;
|
||||
});
|
||||
stream.on("end", function () {
|
||||
expect(length).equal(12);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should work if metadata is requested but there is none, don't check symlinks", function(done) {
|
||||
vfs.readfile("/file2.txt", { metadata: true, checkSymlinks: false }, function(err, meta) {
|
||||
if (err) throw err;
|
||||
var stream = meta.stream;
|
||||
var chunks = [];
|
||||
var length = 0;
|
||||
stream.on("data", function (chunk) {
|
||||
chunks.push(chunk);
|
||||
length += chunk.length;
|
||||
});
|
||||
stream.on("end", function () {
|
||||
expect(length).equal(12);
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("should return metadata", function(done) {
|
||||
vfs.readfile("/file.txt", { metadata: true }, function(err, meta) {
|
||||
if (err) throw err;
|
||||
expect(meta).property("metadataSize").equals(14);
|
||||
var stream = meta.stream;
|
||||
var chunks = [];
|
||||
var length = 0;
|
||||
stream.on("data", function (chunk) {
|
||||
chunks.push(chunk);
|
||||
length += chunk.length;
|
||||
});
|
||||
stream.on("end", function () {
|
||||
expect(length).equal(58);
|
||||
var body = chunks.join("");
|
||||
expect(body).equal("This is a file with exactly 42 characters!!\nsome meta data");
|
||||
done();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
it("etag determined by latest mtime", function(done) {
|
||||
async.series({
|
||||
withMeta: (next) => {
|
||||
vfs.readfile("/file.txt", { metadata: true }, function(err, meta) {
|
||||
next(err, meta.etag);
|
||||
})
|
||||
},
|
||||
withoutMeta: (next) => {
|
||||
vfs.readfile("/file.txt", { }, function(err, meta) {
|
||||
next(err, meta.etag);
|
||||
})
|
||||
}
|
||||
},
|
||||
function(err, values) {
|
||||
if (err) throw err;
|
||||
assert(values.withMeta !== values.withoutMeta);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
it("should read the text file", function (done) {
|
||||
vfs.readfile("/file.txt", {}, function (err, meta) {
|
||||
if (err) throw err;
|
||||
expect(meta).property("mime").equals("text/plain");
|
||||
expect(meta).property("size").equals(23);
|
||||
expect(meta).property("size").equals(44);
|
||||
expect(meta).property("etag");
|
||||
expect(meta).property("stream").property("readable");
|
||||
var stream = meta.stream;
|
||||
|
@ -111,9 +187,9 @@ describe('vfs-local', function () {
|
|||
length += chunk.length;
|
||||
});
|
||||
stream.on("end", function () {
|
||||
expect(length).equal(23);
|
||||
expect(length).equal(44);
|
||||
var body = chunks.join("");
|
||||
expect(body).equal("This is a simple file!\n");
|
||||
expect(body).equal("This is a file with exactly 42 characters!!\n");
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -134,7 +210,7 @@ describe('vfs-local', function () {
|
|||
vfs.readfile("/file.txt", {head:true}, function (err, meta) {
|
||||
if (err) throw err;
|
||||
expect(meta).property("mime").equal("text/plain");
|
||||
expect(meta).property("size").equal(23);
|
||||
expect(meta).property("size").equal(44);
|
||||
expect(meta).property("mime").ok;
|
||||
expect(meta.stream).not.ok;
|
||||
done();
|
||||
|
@ -148,7 +224,7 @@ describe('vfs-local', function () {
|
|||
vfs.readfile("/file.txt", {etag:etag}, function (err, meta) {
|
||||
if (err) throw err;
|
||||
expect(meta).property("mime").equal("text/plain");
|
||||
expect(meta).property("size").equal(23);
|
||||
expect(meta).property("size").equal(44);
|
||||
expect(meta).property("notModified").ok;
|
||||
expect(meta.stream).not.ok;
|
||||
done();
|
||||
|
@ -161,7 +237,7 @@ describe('vfs-local', function () {
|
|||
expect(meta).property("mime").equal("text/plain");
|
||||
expect(meta).property("size").equal(3);
|
||||
expect(meta).property("etag").ok;
|
||||
expect(meta).property("partialContent").deep.equal({ start: 1, end: 3, size: 23 });
|
||||
expect(meta).property("partialContent").deep.equal({ start: 1, end: 3, size: 44 });
|
||||
expect(meta).property("stream").ok;
|
||||
var stream = meta.stream;
|
||||
var chunks = [];
|
||||
|
@ -180,7 +256,7 @@ describe('vfs-local', function () {
|
|||
if (err) throw err;
|
||||
expect(meta).property("size").equal(10);
|
||||
expect(meta).property("etag").ok;
|
||||
expect(meta).property("partialContent").deep.equal({ start: 13, end: 22, size: 23 });
|
||||
expect(meta).property("partialContent").deep.equal({ start: 34, end: 43, size: 44 });
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -212,7 +288,7 @@ describe('vfs-local', function () {
|
|||
parts.push(part);
|
||||
});
|
||||
stream.on("end", function () {
|
||||
expect(parts).length(5);
|
||||
expect(parts).length(7);
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
@ -340,8 +416,10 @@ describe('vfs-local', function () {
|
|||
});
|
||||
});
|
||||
it("should create intermediate directories", function(done) {
|
||||
vfs.execFile("rm", {args: ["-rf", root + "/nested"]}, function() {
|
||||
vfs.execFile("rm", {args: ["-rf", root + "/nested"]}, function(err) {
|
||||
assert(!err, err);
|
||||
vfs.mkfile("/nested/dir/file.txt", { parents: true }, function(err, meta) {
|
||||
assert(!err, err);
|
||||
meta.stream.write("juhu");
|
||||
meta.stream.end();
|
||||
|
||||
|
@ -627,6 +705,7 @@ describe('vfs-local', function () {
|
|||
|
||||
var inner = false;
|
||||
vfs.mkfile(vpath, {}, function(err, meta){
|
||||
assert(!err, err);
|
||||
var stream = meta.stream;
|
||||
stream.write("Change!");
|
||||
stream.end();
|
||||
|
@ -1016,7 +1095,8 @@ describe('vfs-local', function () {
|
|||
});
|
||||
|
||||
var inner = false;
|
||||
vfs.mkfile(vpath, { sandbox: sandbox }, function(err, meta){
|
||||
vfs.mkfile(vpath, { sandbox: sandbox }, function(err, meta) {
|
||||
assert(!err, err);
|
||||
var stream = meta.stream;
|
||||
stream.write("Change!");
|
||||
stream.end();
|
||||
|
|
Ładowanie…
Reference in New Issue