diff --git a/AUTHORS b/AUTHORS index 9eea44b..e218b72 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,3 @@ Alan K (blog.modeswitch.org) David Humphrey (@humphd) +Abir Viqar diff --git a/README.md b/README.md index 2f1c8db..20232fb 100644 --- a/README.md +++ b/README.md @@ -160,3 +160,8 @@ Asynchronous lseek(2), where `whence` can be `SET`, `CUR`, or `END`. Callback ge #### fs.readdir(path, callback) Asynchronous readdir(3). Reads the contents of a directory. Callback gets `(error, files)`, where `files` is an array containing the names of each file in the directory, excluding `.` and `..`. + +#### fs.readlink(path, callback) + +Asynchronous readlink(2). Callback gets `(error, linkContents)`, where `linkContents` is a string containing the path to which the symbolic link links to. + diff --git a/src/fs.js b/src/fs.js index 21ef81a..94c9c93 100644 --- a/src/fs.js +++ b/src/fs.js @@ -33,6 +33,7 @@ define(function(require) { var FS_FORMAT = require('src/constants').FS_FORMAT; var MODE_FILE = require('src/constants').MODE_FILE; var MODE_DIRECTORY = require('src/constants').MODE_DIRECTORY; + var MODE_SYMBOLIC_LINK = require('src/constants').MODE_SYMBOLIC_LINK; var ROOT_DIRECTORY_NAME = require('src/constants').ROOT_DIRECTORY_NAME; var ROOT_NODE_ID = require('src/constants').ROOT_NODE_ID; var IDB_RW = require('src/constants').IDB_RW; @@ -752,6 +753,51 @@ define(function(require) { } } + function read_link(objectStore, path, callback) { + path = normalize(path); + var name = basename(path); + var parentPath = dirname(path); + + var directoryNode; + var directoryData; + + find_node(objectStore, parentPath, read_directory_data); + + function read_directory_data(error, result) { + if(error) { + callback(error); + } else { + directoryNode = result; + read_object(objectStore, directoryNode.data, check_if_file_exists); + } + } + + function check_if_file_exists(error, result) { + if(error) { + callback(error); + } else { + directoryData = result; + if(!_(directoryData).has(name)) { + callback(new ENoEntry('a component of the path does not name an existing file')); + } else { + read_object(objectStore, directoryData[name].id, check_if_symbolic); + } + } + } + + function check_if_symbolic(error, result) { + if(error) { + callback(error); + } else { + if(result.mode != MODE_SYMBOLIC_LINK) { + callback(new EInvalid("path not a symbolic link")); + } else { + callback(undefined, result.data); + } + } + } + } + function validate_flags(flags) { if(!_(O_FLAGS).has(flags)) { return null; @@ -1169,8 +1215,19 @@ define(function(require) { FileSystem.prototype._symlink = function _symlink(fd, length, callback) { }; - FileSystem.prototype._readlink = function _readlink(fd, length, callback) { + FileSystem.prototype._readlink = function _readlink(context, path, callback) { + var that = this; + function check_result(error, result) { + if(error) { + // if(transaction.error) transaction.abort(); + callback(error); + } else { + callback(undefined, result); + } + } + + read_link(context, path, check_result); }; FileSystem.prototype._realpath = function _realpath(fd, length, callback) { @@ -1458,6 +1515,18 @@ define(function(require) { ); if(error) callback(error); }; + IndexedDBFileSystem.prototype.readlink = function readlink(path, callback) { + var fs = this; + var error = this._queueOrRun( + function() { + var transaction = fs.db.transaction([FILE_STORE_NAME], IDB_RW); + var files = transaction.objectStore(FILE_STORE_NAME); + var context = new IndexedDBContext(files); + fs._readlink(context, path, callback); + } + ); + if(error) callback(error); + }; // FIXME: WebSQL stuff, this needs implementation function WebSQLContext(transaction) { diff --git a/tests/spec/idbfs.spec.js b/tests/spec/idbfs.spec.js index 2cdc912..8f1a74d 100644 --- a/tests/spec/idbfs.spec.js +++ b/tests/spec/idbfs.spec.js @@ -1350,3 +1350,84 @@ describe('fs.lseek', function() { }); }); }); + +describe('fs.readlink', 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 be a function', function() { + expect(typeof this.fs.readlink).toEqual('function'); + }); + + it('should return an error if part of the parent destination path does not exist', function() { + var complete = false; + var _error; + var that = this; + + that.fs.readlink('/tmp/mydir', function(error) { + _error = error; + + complete = true; + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toBeDefined(); + }); + }); + + it('should return an error if the path is not a symbolic link', function() { + var complete = false; + var _error; + var that = this; + + that.fs.readlink('/', function(error) { + _error = error; + + complete = true; + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).toBeDefined(); + }); + }); + + xit('should return the contents of a symbolic link', function() { + var complete = false; + var _error, _result; + var that = this; + + that.fs.symlink('/', '/myfile', function(error) { + if(error) throw error; + + that.fs.readlink('/myfile', function(error, result) { + _error = error; + _result = result; + complete = true; + }); + }); + + waitsFor(function() { + return complete; + }, 'test to complete', DEFAULT_TIMEOUT); + + runs(function() { + expect(_error).not.toBeDefined(); + expect(_result).toEqual('/'); + }); + }); +});