make find_node symbolic link aware
This adds a new constant SYMLOOP_MAX, which corresponds to the POSIX variable of the number of symbolic links that may be followed. A new error Eloop was added to signal when SYMLOOP_MAX is exceeded. SYMLOOP_MAX has been arbitrarily set to 10 while on linux it is set to 40 and the POSIX minimum is 8. find_node when encountering a symbolic link anywhere in the given path will attempt to follow it. Note that SYMLOOP_MAX is only the limit of symbolic links to follow per symbolic link. There is currently no attempt to limit the total number of symbolic links followed when resolving a path. This adds tests for path resolution of symbolic links as well.
This commit is contained in:
parent
85a804cc0c
commit
5ceff20b12
|
@ -18,6 +18,8 @@ define(function(require) {
|
||||||
MODE_DIRECTORY: 'DIRECTORY',
|
MODE_DIRECTORY: 'DIRECTORY',
|
||||||
MODE_SYMBOLIC_LINK: 'SYMLINK',
|
MODE_SYMBOLIC_LINK: 'SYMLINK',
|
||||||
|
|
||||||
|
SYMLOOP_MAX: 10,
|
||||||
|
|
||||||
BINARY_MIME_TYPE: 'application/octet-stream',
|
BINARY_MIME_TYPE: 'application/octet-stream',
|
||||||
JSON_MIME_TYPE: 'application/json',
|
JSON_MIME_TYPE: 'application/json',
|
||||||
|
|
||||||
|
|
10
src/error.js
10
src/error.js
|
@ -91,6 +91,13 @@ define(function(require) {
|
||||||
EIO.prototype.name = "EIO";
|
EIO.prototype.name = "EIO";
|
||||||
EIO.prototype.constructor = EIO;
|
EIO.prototype.constructor = EIO;
|
||||||
|
|
||||||
|
function ELoop(message){
|
||||||
|
this.message = message || '';
|
||||||
|
}
|
||||||
|
ELoop.prototype = new Error();
|
||||||
|
ELoop.prototype.name = "ELoop";
|
||||||
|
ELoop.prototype.constructor = ELoop;
|
||||||
|
|
||||||
function EFileSystemError(message){
|
function EFileSystemError(message){
|
||||||
this.message = message || '';
|
this.message = message || '';
|
||||||
}
|
}
|
||||||
|
@ -109,7 +116,8 @@ define(function(require) {
|
||||||
ENotImplemented: ENotImplemented,
|
ENotImplemented: ENotImplemented,
|
||||||
ENotMounted: ENotMounted,
|
ENotMounted: ENotMounted,
|
||||||
EInvalid: EInvalid,
|
EInvalid: EInvalid,
|
||||||
EIO: EIO
|
EIO: EIO,
|
||||||
|
ELoop: ELoop
|
||||||
};
|
};
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
39
src/fs.js
39
src/fs.js
|
@ -28,6 +28,7 @@ define(function(require) {
|
||||||
var ENotMounted = require('src/error').ENotMounted;
|
var ENotMounted = require('src/error').ENotMounted;
|
||||||
var EInvalid = require('src/error').EInvalid;
|
var EInvalid = require('src/error').EInvalid;
|
||||||
var EIO = require('src/error').EIO;
|
var EIO = require('src/error').EIO;
|
||||||
|
var ELoop = require('src/error').ELoop;
|
||||||
var EFileSystemError = require('src/error').EFileSystemError;
|
var EFileSystemError = require('src/error').EFileSystemError;
|
||||||
|
|
||||||
var FS_FORMAT = require('src/constants').FS_FORMAT;
|
var FS_FORMAT = require('src/constants').FS_FORMAT;
|
||||||
|
@ -40,6 +41,7 @@ define(function(require) {
|
||||||
var IDB_RO = require('src/constants').IDB_RO;
|
var IDB_RO = require('src/constants').IDB_RO;
|
||||||
var FILE_STORE_NAME = require('src/constants').FILE_STORE_NAME;
|
var FILE_STORE_NAME = require('src/constants').FILE_STORE_NAME;
|
||||||
var METADATA_STORE_NAME = require('src/constants').METADATA_STORE_NAME;
|
var METADATA_STORE_NAME = require('src/constants').METADATA_STORE_NAME;
|
||||||
|
var SYMLOOP_MAX = require('src/constants').SYMLOOP_MAX;
|
||||||
var FS_READY = require('src/constants').FS_READY;
|
var FS_READY = require('src/constants').FS_READY;
|
||||||
var FS_PENDING = require('src/constants').FS_PENDING;
|
var FS_PENDING = require('src/constants').FS_PENDING;
|
||||||
var FS_ERROR = require('src/constants').FS_ERROR;
|
var FS_ERROR = require('src/constants').FS_ERROR;
|
||||||
|
@ -120,6 +122,7 @@ define(function(require) {
|
||||||
}
|
}
|
||||||
var name = basename(path);
|
var name = basename(path);
|
||||||
var parentPath = dirname(path);
|
var parentPath = dirname(path);
|
||||||
|
var followedCount = 0;
|
||||||
|
|
||||||
function check_root_directory_node(error, rootDirectoryNode) {
|
function check_root_directory_node(error, rootDirectoryNode) {
|
||||||
if(error) {
|
if(error) {
|
||||||
|
@ -139,13 +142,13 @@ define(function(require) {
|
||||||
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) {
|
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) {
|
||||||
callback(new ENotDirectory('a component of the path prefix is not a directory'));
|
callback(new ENotDirectory('a component of the path prefix is not a directory'));
|
||||||
} else {
|
} else {
|
||||||
read_object(objectStore, parentDirectoryNode.data, get_node_id_from_parent_directory_data);
|
read_object(objectStore, parentDirectoryNode.data, get_node_from_parent_directory_data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// in: parent directory data
|
// in: parent directory data
|
||||||
// out: searched node id
|
// out: searched node
|
||||||
function get_node_id_from_parent_directory_data(error, parentDirectoryData) {
|
function get_node_from_parent_directory_data(error, parentDirectoryData) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
} else {
|
} else {
|
||||||
|
@ -153,11 +156,39 @@ define(function(require) {
|
||||||
callback(new ENoEntry('path does not exist'));
|
callback(new ENoEntry('path does not exist'));
|
||||||
} else {
|
} else {
|
||||||
var nodeId = parentDirectoryData[name].id;
|
var nodeId = parentDirectoryData[name].id;
|
||||||
read_object(objectStore, nodeId, callback);
|
read_object(objectStore, nodeId, is_symbolic_link);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function is_symbolic_link(error, node) {
|
||||||
|
if(error) {
|
||||||
|
callback(error);
|
||||||
|
} else {
|
||||||
|
if(node.mode == MODE_SYMBOLIC_LINK) {
|
||||||
|
followedCount++;
|
||||||
|
if(followedCount > SYMLOOP_MAX){
|
||||||
|
callback(new ELoop('too many symbolic links were encountered'));
|
||||||
|
} else {
|
||||||
|
follow_symbolic_link(node.data);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
callback(undefined, node);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function follow_symbolic_link(data) {
|
||||||
|
data = normalize(data);
|
||||||
|
parentPath = dirname(data);
|
||||||
|
name = basename(data);
|
||||||
|
if(ROOT_DIRECTORY_NAME == name) {
|
||||||
|
read_object(objectStore, ROOT_NODE_ID, check_root_directory_node);
|
||||||
|
} else {
|
||||||
|
find_node(objectStore, parentPath, read_parent_directory_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if(ROOT_DIRECTORY_NAME == name) {
|
if(ROOT_DIRECTORY_NAME == name) {
|
||||||
read_object(objectStore, ROOT_NODE_ID, check_root_directory_node);
|
read_object(objectStore, ROOT_NODE_ID, check_root_directory_node);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -1508,3 +1508,348 @@ describe('fs.readlink', function() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('path resolution', function() {
|
||||||
|
beforeEach(function() {
|
||||||
|
this.db_name = mk_db_name();
|
||||||
|
this.fs = new IDBFS.FileSystem(this.db_name, 'FORMAT');
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function() {
|
||||||
|
indexedDB.deleteDatabase(this.db_name);
|
||||||
|
delete this.fs;
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow a symbolic link to the root directory', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.symlink('/', '/mydirectorylink', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
_node = result['node'];
|
||||||
|
that.fs.stat('/mydirectorylink', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).toBeDefined();
|
||||||
|
expect(_node).toBeDefined();
|
||||||
|
expect(_error).not.toBeDefined();
|
||||||
|
expect(_result['node']).toEqual(_node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow a symbolic link to a directory', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.mkdir('/mydir', function(error) {
|
||||||
|
that.fs.symlink('/mydir', '/mydirectorylink', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/mydir', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
_node = result['node'];
|
||||||
|
that.fs.stat('/mydirectorylink', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).toBeDefined();
|
||||||
|
expect(_node).toBeDefined();
|
||||||
|
expect(_error).not.toBeDefined();
|
||||||
|
expect(_result['node']).toEqual(_node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow a symbolic link to a file', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.open('/myfile', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.stat('/myfile', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
_node = result['node'];
|
||||||
|
that.fs.symlink('/myfile', '/myfilelink', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/myfilelink', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).toBeDefined();
|
||||||
|
expect(_node).toBeDefined();
|
||||||
|
expect(_error).not.toBeDefined();
|
||||||
|
expect(_result['node']).toEqual(_node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow multiple symbolic links to a file', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.open('/myfile', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.stat('/myfile', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
_node = result['node'];
|
||||||
|
that.fs.symlink('/myfile', '/myfilelink1', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.symlink('/myfilelink1', '/myfilelink2', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/myfilelink2', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).toBeDefined();
|
||||||
|
expect(_node).toBeDefined();
|
||||||
|
expect(_error).not.toBeDefined();
|
||||||
|
expect(_result['node']).toEqual(_node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if symbolic link leads to itself', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.symlink('/mylink1', '/mylink2', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/mylink2', '/mylink1', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/myfilelink1', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_error).toBeDefined();
|
||||||
|
expect(_result).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if it follows more than 10 symbolic links', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.open('/myfile', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.stat('/myfile', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfile', '/myfilelink1', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.symlink('/myfilelink1', '/myfilelink2', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink2', '/myfilelink3', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink3', '/myfilelink4', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink4', '/myfilelink5', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink5', '/myfilelink6', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink6', '/myfilelink7', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink7', '/myfilelink8', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink8', '/myfilelink9', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink9', '/myfilelink10', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.symlink('/myfilelink10', '/myfilelink11', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/myfilelink11', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).not.toBeDefined();
|
||||||
|
expect(_error).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should follow a symbolic link in the path to a file', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _node, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.open('/myfile', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.stat('/myfile', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
_node = result['node'];
|
||||||
|
that.fs.symlink('/', '/mydirlink', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/mydirlink/myfile', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_result).toBeDefined();
|
||||||
|
expect(_node).toBeDefined();
|
||||||
|
expect(_error).not.toBeDefined();
|
||||||
|
expect(_result['node']).toEqual(_node);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should error if a symbolic link in the path to a file is itself a file', function() {
|
||||||
|
var complete = false;
|
||||||
|
var _error, _result;
|
||||||
|
var that = this;
|
||||||
|
|
||||||
|
that.fs.open('/myfile', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.stat('/myfile', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.open('/myfile2', 'w', function(error, result) {
|
||||||
|
if(error) throw error;
|
||||||
|
var fd = result;
|
||||||
|
that.fs.close(fd, function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
that.fs.symlink('/myfile2', '/mynotdirlink', function(error) {
|
||||||
|
if(error) throw error;
|
||||||
|
|
||||||
|
that.fs.stat('/mynotdirlink/myfile', function(error, result) {
|
||||||
|
_error = error;
|
||||||
|
_result = result;
|
||||||
|
complete = true;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
waitsFor(function() {
|
||||||
|
return complete;
|
||||||
|
}, 'test to complete', DEFAULT_TIMEOUT);
|
||||||
|
|
||||||
|
runs(function() {
|
||||||
|
expect(_error).toBeDefined();
|
||||||
|
expect(_result).not.toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
Loading…
Reference in New Issue