2014-05-23 18:14:06 +00:00
|
|
|
var _ = require('../../lib/nodash.js');
|
|
|
|
|
|
|
|
var Path = require('../path.js');
|
|
|
|
var normalize = Path.normalize;
|
|
|
|
var dirname = Path.dirname;
|
|
|
|
var basename = Path.basename;
|
|
|
|
var isAbsolutePath = Path.isAbsolute;
|
|
|
|
var isNullPath = Path.isNull;
|
|
|
|
|
|
|
|
var Constants = require('../constants.js');
|
|
|
|
var MODE_FILE = Constants.MODE_FILE;
|
|
|
|
var MODE_DIRECTORY = Constants.MODE_DIRECTORY;
|
|
|
|
var MODE_SYMBOLIC_LINK = Constants.MODE_SYMBOLIC_LINK;
|
|
|
|
var MODE_META = Constants.MODE_META;
|
|
|
|
|
|
|
|
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
|
|
|
|
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
|
|
|
|
var SYMLOOP_MAX = Constants.SYMLOOP_MAX;
|
|
|
|
|
|
|
|
var O_READ = Constants.O_READ;
|
|
|
|
var O_WRITE = Constants.O_WRITE;
|
|
|
|
var O_CREATE = Constants.O_CREATE;
|
|
|
|
var O_EXCLUSIVE = Constants.O_EXCLUSIVE;
|
|
|
|
var O_TRUNCATE = Constants.O_TRUNCATE;
|
|
|
|
var O_APPEND = Constants.O_APPEND;
|
|
|
|
var O_FLAGS = Constants.O_FLAGS;
|
|
|
|
|
|
|
|
var XATTR_CREATE = Constants.XATTR_CREATE;
|
|
|
|
var XATTR_REPLACE = Constants.XATTR_REPLACE;
|
|
|
|
var FS_NOMTIME = Constants.FS_NOMTIME;
|
|
|
|
var FS_NOCTIME = Constants.FS_NOCTIME;
|
|
|
|
|
2014-06-04 19:52:08 +00:00
|
|
|
var Encoding = require('../encoding.js');
|
2014-05-23 18:14:06 +00:00
|
|
|
var Errors = require('../errors.js');
|
|
|
|
var DirectoryEntry = require('../directory-entry.js');
|
|
|
|
var OpenFileDescription = require('../open-file-description.js');
|
|
|
|
var SuperNode = require('../super-node.js');
|
|
|
|
var Node = require('../node.js');
|
|
|
|
var Stats = require('../stats.js');
|
2014-08-16 20:22:41 +00:00
|
|
|
var Buffer = require('../buffer.js');
|
2014-05-23 18:14:06 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Many functions below use this callback pattern. If it's not
|
|
|
|
* re-defined, we use this to generate a callback. NOTE: this
|
|
|
|
* can be use for callbacks of both forms without problem (i.e.,
|
|
|
|
* since result will be undefined if not returned):
|
|
|
|
* - callback(error)
|
|
|
|
* - callback(error, result)
|
|
|
|
*/
|
|
|
|
function standard_check_result_cb(callback) {
|
|
|
|
return function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
callback(null, result);
|
2014-05-13 22:10:11 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Update node times. Only passed times are modified (undefined times are ignored)
|
|
|
|
* and filesystem flags are examined in order to override update logic.
|
|
|
|
*/
|
|
|
|
function update_node_times(context, path, node, times, callback) {
|
|
|
|
// Honour mount flags for how we update times
|
|
|
|
var flags = context.flags;
|
|
|
|
if(_(flags).contains(FS_NOCTIME)) {
|
|
|
|
delete times.ctime;
|
|
|
|
}
|
|
|
|
if(_(flags).contains(FS_NOMTIME)) {
|
|
|
|
delete times.mtime;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Only do the update if required (i.e., times are still present)
|
|
|
|
var update = false;
|
|
|
|
if(times.ctime) {
|
|
|
|
node.ctime = times.ctime;
|
|
|
|
// We don't do atime tracking for perf reasons, but do mirror ctime
|
|
|
|
node.atime = times.ctime;
|
|
|
|
update = true;
|
|
|
|
}
|
|
|
|
if(times.atime) {
|
|
|
|
// The only time we explicitly pass atime is when utimes(), futimes() is called.
|
|
|
|
// Override ctime mirror here if so
|
|
|
|
node.atime = times.atime;
|
|
|
|
update = true;
|
|
|
|
}
|
|
|
|
if(times.mtime) {
|
|
|
|
node.mtime = times.mtime;
|
|
|
|
update = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
function complete(error) {
|
|
|
|
// Queue this change so we can send watch events.
|
|
|
|
// Unlike node.js, we send the full path vs. basename/dirname only.
|
|
|
|
context.changes.push({ event: 'change', path: path });
|
|
|
|
callback(error);
|
|
|
|
}
|
|
|
|
|
|
|
|
if(update) {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(node.id, node, complete);
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
complete();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* make_node()
|
|
|
|
*/
|
|
|
|
// in: file or directory path
|
|
|
|
// out: new node representing file/directory
|
|
|
|
function make_node(context, path, mode, callback) {
|
|
|
|
if(mode !== MODE_DIRECTORY && mode !== MODE_FILE) {
|
|
|
|
return callback(new Errors.EINVAL('mode must be a directory or file'));
|
|
|
|
}
|
|
|
|
|
|
|
|
path = normalize(path);
|
|
|
|
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
|
|
|
var parentNode;
|
|
|
|
var parentNodeData;
|
|
|
|
var node;
|
|
|
|
|
|
|
|
// Check if the parent node exists
|
|
|
|
function create_node_in_parent(error, parentDirectoryNode) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY) {
|
|
|
|
callback(new Errors.ENOTDIR('a component of the path prefix is not a directory'));
|
|
|
|
} else {
|
|
|
|
parentNode = parentDirectoryNode;
|
|
|
|
find_node(context, path, check_if_node_exists);
|
2014-05-13 22:10:11 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Check if the node to be created already exists
|
|
|
|
function check_if_node_exists(error, result) {
|
|
|
|
if(!error && result) {
|
|
|
|
callback(new Errors.EEXIST('path name already exists'));
|
|
|
|
} else if(error && !(error instanceof Errors.ENOENT)) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(parentNode.data, create_node);
|
2014-05-13 22:10:11 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Create the new node
|
|
|
|
function create_node(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentNodeData = result;
|
2014-06-02 20:44:20 +00:00
|
|
|
Node.create({guid: context.guid, mode: mode}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
node = result;
|
|
|
|
node.nlinks += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(node.id, node, update_parent_node_data);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-05-13 22:10:11 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
// Update parent node time
|
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, node, { mtime: now, ctime: now }, callback);
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
// Update the parent nodes data
|
|
|
|
function update_parent_node_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentNodeData[name] = new DirectoryEntry(node.id, mode);
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(parentNode.data, parentNodeData, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
// Find the parent node
|
|
|
|
find_node(context, parentPath, create_node_in_parent);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* find_node
|
|
|
|
*/
|
|
|
|
// in: file or directory path
|
|
|
|
// out: node structure, or error
|
|
|
|
function find_node(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
if(!path) {
|
|
|
|
return callback(new Errors.ENOENT('path is an empty string'));
|
|
|
|
}
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
|
|
|
var followedCount = 0;
|
|
|
|
|
|
|
|
function read_root_directory_node(error, superNode) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(!superNode || superNode.mode !== MODE_META || !superNode.rnode) {
|
|
|
|
callback(new Errors.EFILESYSTEMERROR());
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(superNode.rnode, check_root_directory_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_root_directory_node(error, rootDirectoryNode) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(!rootDirectoryNode) {
|
|
|
|
callback(new Errors.ENOENT());
|
|
|
|
} else {
|
|
|
|
callback(null, rootDirectoryNode);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
// in: parent directory node
|
|
|
|
// out: parent directory data
|
|
|
|
function read_parent_directory_data(error, parentDirectoryNode) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(parentDirectoryNode.mode !== MODE_DIRECTORY || !parentDirectoryNode.data) {
|
|
|
|
callback(new Errors.ENOTDIR('a component of the path prefix is not a directory'));
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(parentDirectoryNode.data, get_node_from_parent_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
// in: parent directory data
|
|
|
|
// out: searched node
|
|
|
|
function get_node_from_parent_directory_data(error, parentDirectoryData) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
if(!_(parentDirectoryData).has(name)) {
|
|
|
|
callback(new Errors.ENOENT());
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
var nodeId = parentDirectoryData[name].id;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(nodeId, is_symbolic_link);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function is_symbolic_link(error, node) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
if(node.mode == MODE_SYMBOLIC_LINK) {
|
|
|
|
followedCount++;
|
|
|
|
if(followedCount > SYMLOOP_MAX){
|
|
|
|
callback(new Errors.ELOOP());
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
follow_symbolic_link(node.data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null, node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function follow_symbolic_link(data) {
|
|
|
|
data = normalize(data);
|
|
|
|
parentPath = dirname(data);
|
|
|
|
name = basename(data);
|
2014-03-18 18:16:12 +00:00
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(SUPER_NODE_ID, read_root_directory_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
|
|
|
find_node(context, parentPath, read_parent_directory_data);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(SUPER_NODE_ID, read_root_directory_node);
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
find_node(context, parentPath, read_parent_directory_data);
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
/**
|
|
|
|
* set extended attribute (refactor)
|
|
|
|
*/
|
|
|
|
function set_extended_attribute (context, path_or_fd, name, value, flag, callback) {
|
|
|
|
var path;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function set_xattr (error, node) {
|
|
|
|
var xattr = (node ? node.xattrs[name] : null);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
2014-03-18 18:16:12 +00:00
|
|
|
callback(error);
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
update_node_times(context, path, node, { ctime: Date.now() }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
}
|
|
|
|
else if (flag === XATTR_CREATE && node.xattrs.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.EEXIST('attribute already exists'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else if (flag === XATTR_REPLACE && !node.xattrs.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.ENOATTR());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-05-23 18:14:06 +00:00
|
|
|
node.xattrs[name] = value;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(node.id, node, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof path_or_fd == 'string') {
|
|
|
|
path = path_or_fd;
|
|
|
|
find_node(context, path_or_fd, set_xattr);
|
|
|
|
}
|
|
|
|
else if (typeof path_or_fd == 'object' && typeof path_or_fd.id == 'string') {
|
|
|
|
path = path_or_fd.path;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(path_or_fd.id, set_xattr);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
callback(new Errors.EINVAL('path or file descriptor of wrong type'));
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
/**
|
2014-07-07 21:02:42 +00:00
|
|
|
* ensure_root_directory. Creates a root node if necessary.
|
|
|
|
*
|
|
|
|
* Note: this should only be invoked when formatting a new file system.
|
|
|
|
* Multiple invocations of this by separate instances will still result
|
|
|
|
* in only a single super node.
|
2014-05-23 18:14:06 +00:00
|
|
|
*/
|
2014-07-07 21:02:42 +00:00
|
|
|
function ensure_root_directory(context, callback) {
|
2014-05-23 18:14:06 +00:00
|
|
|
var superNode;
|
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-07-07 21:02:42 +00:00
|
|
|
function ensure_super_node(error, existingNode) {
|
2014-05-23 18:14:06 +00:00
|
|
|
if(!error && existingNode) {
|
2014-07-07 21:02:42 +00:00
|
|
|
// Another instance has beat us and already created the super node.
|
|
|
|
callback();
|
2014-05-23 18:14:06 +00:00
|
|
|
} else if(error && !(error instanceof Errors.ENOENT)) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-06-02 20:44:20 +00:00
|
|
|
SuperNode.create({guid: context.guid}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
superNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(superNode.id, superNode, write_directory_node);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_directory_node(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-06-02 20:44:20 +00:00
|
|
|
Node.create({guid: context.guid, id: superNode.rnode, mode: MODE_DIRECTORY}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
directoryNode = result;
|
|
|
|
directoryNode.nlinks += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.id, directoryNode, write_directory_data);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = {};
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.data, directoryData, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(SUPER_NODE_ID, ensure_super_node);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
/**
|
|
|
|
* make_directory
|
|
|
|
*/
|
|
|
|
function make_directory(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
var parentDirectoryNode;
|
|
|
|
var parentDirectoryData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_directory_exists(error, result) {
|
|
|
|
if(!error && result) {
|
|
|
|
callback(new Errors.EEXIST());
|
|
|
|
} else if(error && !(error instanceof Errors.ENOENT)) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
find_node(context, parentPath, read_parent_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_parent_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentDirectoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(parentDirectoryNode.data, write_directory_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_directory_node(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentDirectoryData = result;
|
2014-06-02 20:44:20 +00:00
|
|
|
Node.create({guid: context.guid, mode: MODE_DIRECTORY}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
directoryNode = result;
|
|
|
|
directoryNode.nlinks += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.id, directoryNode, write_directory_data);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = {};
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.data, directoryData, update_parent_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_parent_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentDirectoryData[name] = new DirectoryEntry(directoryNode.id, MODE_DIRECTORY);
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, path, check_if_directory_exists);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
/**
|
|
|
|
* remove_directory
|
|
|
|
*/
|
|
|
|
function remove_directory(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
var parentDirectoryNode;
|
|
|
|
var parentDirectoryData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_parent_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
parentDirectoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(parentDirectoryNode.data, check_if_node_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_node_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(ROOT_DIRECTORY_NAME == name) {
|
|
|
|
callback(new Errors.EBUSY());
|
|
|
|
} else if(!_(result).has(name)) {
|
|
|
|
callback(new Errors.ENOENT());
|
|
|
|
} else {
|
|
|
|
parentDirectoryData = result;
|
|
|
|
directoryNode = parentDirectoryData[name].id;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode, check_if_node_is_directory);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_node_is_directory(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(result.mode != MODE_DIRECTORY) {
|
|
|
|
callback(new Errors.ENOTDIR());
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_directory_is_empty);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_directory_is_empty(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(_(directoryData).size() > 0) {
|
|
|
|
callback(new Errors.ENOTEMPTY());
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
remove_directory_entry_from_parent_directory_node();
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, parentDirectoryNode, { mtime: now, ctime: now }, remove_directory_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function remove_directory_entry_from_parent_directory_node() {
|
|
|
|
delete parentDirectoryData[name];
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(parentDirectoryNode.data, parentDirectoryData, update_time);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function remove_directory_node(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
context.delete(directoryNode.id, remove_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function remove_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
context.delete(directoryNode.data, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, parentPath, read_parent_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function open_file(context, path, flags, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
var directoryEntry;
|
|
|
|
var fileNode;
|
|
|
|
var fileData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var followedCount = 0;
|
|
|
|
|
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
2014-08-18 15:03:46 +00:00
|
|
|
if(_(flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set'));
|
|
|
|
} else {
|
|
|
|
find_node(context, path, set_file_node);
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
find_node(context, parentPath, read_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-07-16 20:54:02 +00:00
|
|
|
} else if(result.mode !== MODE_DIRECTORY) {
|
2014-07-16 19:40:40 +00:00
|
|
|
callback(new Errors.ENOENT());
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(_(directoryData).has(name)) {
|
|
|
|
if(_(flags).contains(O_EXCLUSIVE)) {
|
|
|
|
callback(new Errors.ENOENT('O_CREATE and O_EXCLUSIVE are set, and the named file exists'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
directoryEntry = directoryData[name];
|
2014-08-18 15:03:46 +00:00
|
|
|
if(directoryEntry.type == MODE_DIRECTORY && _(flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryEntry.id, check_if_symbolic_link);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
if(!_(flags).contains(O_CREATE)) {
|
|
|
|
callback(new Errors.ENOENT('O_CREATE is not set and the named file does not exist'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
write_file_node();
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_symbolic_link(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var node = result;
|
|
|
|
if(node.mode == MODE_SYMBOLIC_LINK) {
|
|
|
|
followedCount++;
|
|
|
|
if(followedCount > SYMLOOP_MAX){
|
|
|
|
callback(new Errors.ELOOP());
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
follow_symbolic_link(node.data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
set_file_node(undefined, node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function follow_symbolic_link(data) {
|
|
|
|
data = normalize(data);
|
|
|
|
parentPath = dirname(data);
|
|
|
|
name = basename(data);
|
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
|
|
|
if(_(flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EISDIR('the named file is a directory and O_WRITE is set'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, path, set_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, parentPath, read_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function set_file_node(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
|
|
|
callback(null, fileNode);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_file_node() {
|
2014-06-02 20:44:20 +00:00
|
|
|
Node.create({guid: context.guid, mode: MODE_FILE}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fileNode = result;
|
|
|
|
fileNode.nlinks += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, write_file_data);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_file_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-06-04 19:52:08 +00:00
|
|
|
fileData = new Buffer(0);
|
2014-06-06 15:14:52 +00:00
|
|
|
fileData.fill(0);
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putBuffer(fileNode.data, fileData, update_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, handle_update_result);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_FILE);
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.data, directoryData, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function handle_update_result(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
callback(null, fileNode);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function replace_data(context, ofd, buffer, offset, length, callback) {
|
|
|
|
var fileNode;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function return_nbytes(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
callback(null, length);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_file_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
2014-06-04 19:52:08 +00:00
|
|
|
var newData = new Buffer(length);
|
2014-06-06 15:14:52 +00:00
|
|
|
newData.fill(0);
|
|
|
|
buffer.copy(newData, 0, offset, offset + length);
|
2014-05-23 18:14:06 +00:00
|
|
|
ofd.position = length;
|
|
|
|
|
|
|
|
fileNode.size = length;
|
|
|
|
fileNode.version += 1;
|
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putBuffer(fileNode.data, newData, update_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, write_file_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_data(context, ofd, buffer, offset, length, position, callback) {
|
|
|
|
var fileNode;
|
|
|
|
var fileData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function return_nbytes(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
callback(null, length);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, return_nbytes);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-08-16 21:02:18 +00:00
|
|
|
fileData = result;
|
2014-06-25 17:13:55 +00:00
|
|
|
if(!fileData) {
|
|
|
|
return callback(new Errors.EIO('Expected Buffer'));
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var _position = (!(undefined === position || null === position)) ? position : ofd.position;
|
|
|
|
var newSize = Math.max(fileData.length, _position + length);
|
2014-06-04 19:52:08 +00:00
|
|
|
var newData = new Buffer(newSize);
|
2014-06-06 15:14:52 +00:00
|
|
|
newData.fill(0);
|
2014-05-23 18:14:06 +00:00
|
|
|
if(fileData) {
|
2014-06-06 15:14:52 +00:00
|
|
|
fileData.copy(newData);
|
2014-06-04 19:52:08 +00:00
|
|
|
}
|
2014-06-06 15:14:52 +00:00
|
|
|
buffer.copy(newData, _position, offset, offset + length);
|
2014-05-23 18:14:06 +00:00
|
|
|
if(undefined === position) {
|
|
|
|
ofd.position += length;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
fileNode.size = newSize;
|
|
|
|
fileNode.version += 1;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putBuffer(fileNode.data, newData, update_file_node);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_file_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getBuffer(fileNode.data, update_file_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, read_file_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_data(context, ofd, buffer, offset, length, position, callback) {
|
|
|
|
var fileNode;
|
|
|
|
var fileData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function handle_file_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 21:02:18 +00:00
|
|
|
fileData = result;
|
2014-06-25 17:13:55 +00:00
|
|
|
if(!fileData) {
|
|
|
|
return callback(new Errors.EIO('Expected Buffer'));
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var _position = (!(undefined === position || null === position)) ? position : ofd.position;
|
|
|
|
length = (_position + length > buffer.length) ? length - _position : length;
|
2014-06-06 15:14:52 +00:00
|
|
|
fileData.copy(buffer, offset, _position, _position + length);
|
2014-05-23 18:14:06 +00:00
|
|
|
if(undefined === position) {
|
|
|
|
ofd.position += length;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null, length);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_file_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getBuffer(fileNode.data, handle_file_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, read_file_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function stat_file(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
find_node(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fstat_file(context, ofd, callback) {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, standard_check_result_cb(callback));
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function lstat_file(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
|
|
|
find_node(context, path, standard_check_result_cb(callback));
|
|
|
|
} else {
|
|
|
|
find_node(context, parentPath, read_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(!_(directoryData).has(name)) {
|
|
|
|
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryData[name].id, standard_check_result_cb(callback));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function link_node(context, oldpath, newpath, callback) {
|
|
|
|
oldpath = normalize(oldpath);
|
|
|
|
var oldname = basename(oldpath);
|
|
|
|
var oldParentPath = dirname(oldpath);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
newpath = normalize(newpath);
|
|
|
|
var newname = basename(newpath);
|
|
|
|
var newParentPath = dirname(newpath);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var oldDirectoryNode;
|
|
|
|
var oldDirectoryData;
|
|
|
|
var newDirectoryNode;
|
|
|
|
var newDirectoryData;
|
|
|
|
var fileNode;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
update_node_times(context, newpath, fileNode, { ctime: Date.now() }, callback);
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
|
|
|
fileNode.nlinks += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_entry(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(newDirectoryData[newname].id, update_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_new_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
newDirectoryData = result;
|
|
|
|
if(_(newDirectoryData).has(newname)) {
|
|
|
|
callback(new Errors.EEXIST('newpath resolves to an existing file'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
newDirectoryData[newname] = oldDirectoryData[oldname];
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(newDirectoryNode.data, newDirectoryData, read_directory_entry);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_new_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
newDirectoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(newDirectoryNode.data, check_if_new_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_old_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
oldDirectoryData = result;
|
|
|
|
if(!_(oldDirectoryData).has(oldname)) {
|
|
|
|
callback(new Errors.ENOENT('a component of either path prefix does not exist'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, newParentPath, read_new_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_old_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
oldDirectoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(oldDirectoryNode.data, check_if_old_file_exists);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, oldParentPath, read_old_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function unlink_node(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
var fileNode;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
delete directoryData[name];
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.data, directoryData, function(error) {
|
2014-05-23 18:14:06 +00:00
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback);
|
|
|
|
});
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function delete_file_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
context.delete(fileNode.data, update_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode = result;
|
|
|
|
fileNode.nlinks -= 1;
|
|
|
|
if(fileNode.nlinks < 1) {
|
|
|
|
context.delete(fileNode.id, delete_file_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, function(error) {
|
2014-05-23 18:14:06 +00:00
|
|
|
update_node_times(context, path, fileNode, { ctime: Date.now() }, update_directory_data);
|
|
|
|
});
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(!_(directoryData).has(name)) {
|
|
|
|
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryData[name].id, update_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, parentPath, read_directory_data);
|
|
|
|
}
|
|
|
|
|
|
|
|
function read_directory(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
|
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
|
|
|
|
function handle_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
var files = Object.keys(directoryData);
|
|
|
|
callback(null, files);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, handle_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, path, read_directory_data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function make_symbolic_link(context, srcpath, dstpath, callback) {
|
|
|
|
dstpath = normalize(dstpath);
|
|
|
|
var name = basename(dstpath);
|
|
|
|
var parentPath = dirname(dstpath);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
|
|
|
var fileNode;
|
|
|
|
|
|
|
|
if(ROOT_DIRECTORY_NAME == name) {
|
|
|
|
callback(new Errors.EEXIST());
|
|
|
|
} else {
|
2014-03-18 18:16:12 +00:00
|
|
|
find_node(context, parentPath, read_directory_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(_(directoryData).has(name)) {
|
|
|
|
callback(new Errors.EEXIST());
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
write_file_node();
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function write_file_node() {
|
2014-06-02 20:44:20 +00:00
|
|
|
Node.create({guid: context.guid, mode: MODE_SYMBOLIC_LINK}, function(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fileNode = result;
|
|
|
|
fileNode.nlinks += 1;
|
|
|
|
fileNode.size = srcpath.length;
|
|
|
|
fileNode.data = srcpath;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_directory_data);
|
2014-06-02 20:44:20 +00:00
|
|
|
});
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, parentPath, directoryNode, { mtime: now, ctime: now }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_directory_data(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData[name] = new DirectoryEntry(fileNode.id, MODE_SYMBOLIC_LINK);
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(directoryNode.data, directoryData, update_time);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_link(context, path, callback) {
|
|
|
|
path = normalize(path);
|
|
|
|
var name = basename(path);
|
|
|
|
var parentPath = dirname(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var directoryNode;
|
|
|
|
var directoryData;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
find_node(context, parentPath, read_directory_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_directory_data(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryNode = result;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryNode.data, check_if_file_exists);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_file_exists(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
directoryData = result;
|
|
|
|
if(!_(directoryData).has(name)) {
|
|
|
|
callback(new Errors.ENOENT('a component of the path does not name an existing file'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(directoryData[name].id, check_if_symbolic);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_if_symbolic(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
if(result.mode != MODE_SYMBOLIC_LINK) {
|
|
|
|
callback(new Errors.EINVAL("path not a symbolic link"));
|
|
|
|
} else {
|
|
|
|
callback(null, result.data);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function truncate_file(context, path, length, callback) {
|
|
|
|
path = normalize(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var fileNode;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_file_data (error, node) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(node.mode == MODE_DIRECTORY ) {
|
|
|
|
callback(new Errors.EISDIR());
|
|
|
|
} else{
|
|
|
|
fileNode = node;
|
2014-08-16 21:02:18 +00:00
|
|
|
context.getBuffer(fileNode.data, truncate_file_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function truncate_file_data(error, fileData) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-06-25 17:13:55 +00:00
|
|
|
if(!fileData) {
|
|
|
|
return callback(new Errors.EIO('Expected Buffer'));
|
|
|
|
}
|
2014-06-04 19:52:08 +00:00
|
|
|
var data = new Buffer(length);
|
2014-06-06 15:14:52 +00:00
|
|
|
data.fill(0);
|
2014-05-23 18:14:06 +00:00
|
|
|
if(fileData) {
|
2014-06-04 19:52:08 +00:00
|
|
|
fileData.copy(data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putBuffer(fileNode.data, data, update_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, path, fileNode, { mtime: now, ctime: now }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node (error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode.size = length;
|
|
|
|
fileNode.version += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(length < 0) {
|
|
|
|
callback(new Errors.EINVAL('length cannot be negative'));
|
|
|
|
} else {
|
|
|
|
find_node(context, path, read_file_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function ftruncate_file(context, ofd, length, callback) {
|
|
|
|
var fileNode;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read_file_data (error, node) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else if(node.mode == MODE_DIRECTORY ) {
|
|
|
|
callback(new Errors.EISDIR());
|
|
|
|
} else{
|
|
|
|
fileNode = node;
|
2014-08-16 21:02:18 +00:00
|
|
|
context.getBuffer(fileNode.data, truncate_file_data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function truncate_file_data(error, fileData) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-06-04 19:52:08 +00:00
|
|
|
var data;
|
2014-06-25 17:13:55 +00:00
|
|
|
if(!fileData) {
|
|
|
|
return callback(new Errors.EIO('Expected Buffer'));
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
if(fileData) {
|
2014-06-04 19:52:08 +00:00
|
|
|
data = fileData.slice(0, length);
|
|
|
|
} else {
|
|
|
|
data = new Buffer(length);
|
2014-06-06 15:14:52 +00:00
|
|
|
data.fill(0);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putBuffer(fileNode.data, data, update_file_node);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var now = Date.now();
|
|
|
|
update_node_times(context, ofd.path, fileNode, { mtime: now, ctime: now }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_file_node (error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
fileNode.size = length;
|
|
|
|
fileNode.version += 1;
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(fileNode.id, fileNode, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(length < 0) {
|
|
|
|
callback(new Errors.EINVAL('length cannot be negative'));
|
|
|
|
} else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, read_file_data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function utimes_file(context, path, atime, mtime, callback) {
|
|
|
|
path = normalize(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function update_times(error, node) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
update_node_times(context, path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof atime != 'number' || typeof mtime != 'number') {
|
|
|
|
callback(new Errors.EINVAL('atime and mtime must be number'));
|
|
|
|
}
|
|
|
|
else if (atime < 0 || mtime < 0) {
|
|
|
|
callback(new Errors.EINVAL('atime and mtime must be positive integers'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
find_node(context, path, update_times);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function futimes_file(context, ofd, atime, mtime, callback) {
|
|
|
|
|
|
|
|
function update_times (error, node) {
|
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
update_node_times(context, ofd.path, node, { atime: atime, ctime: mtime, mtime: mtime }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof atime != 'number' || typeof mtime != 'number') {
|
|
|
|
callback(new Errors.EINVAL('atime and mtime must be a number'));
|
|
|
|
}
|
|
|
|
else if (atime < 0 || mtime < 0) {
|
|
|
|
callback(new Errors.EINVAL('atime and mtime must be positive integers'));
|
|
|
|
}
|
|
|
|
else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, update_times);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function setxattr_file(context, path, name, value, flag, callback) {
|
|
|
|
path = normalize(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL('attribute name must be a string'));
|
|
|
|
}
|
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
|
|
|
}
|
|
|
|
else if (flag !== null &&
|
|
|
|
flag !== XATTR_CREATE && flag !== XATTR_REPLACE) {
|
|
|
|
callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_extended_attribute(context, path, name, value, flag, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function fsetxattr_file (context, ofd, name, value, flag, callback) {
|
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL('attribute name must be a string'));
|
|
|
|
}
|
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
|
|
|
}
|
|
|
|
else if (flag !== null &&
|
|
|
|
flag !== XATTR_CREATE && flag !== XATTR_REPLACE) {
|
|
|
|
callback(new Errors.EINVAL('invalid flag, must be null, XATTR_CREATE or XATTR_REPLACE'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
set_extended_attribute(context, ofd, name, value, flag, callback);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getxattr_file (context, path, name, callback) {
|
|
|
|
path = normalize(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function get_xattr(error, node) {
|
|
|
|
var xattr = (node ? node.xattrs[name] : null);
|
|
|
|
|
|
|
|
if (error) {
|
|
|
|
callback (error);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else if (!node.xattrs.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.ENOATTR());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null, node.xattrs[name]);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL('attribute name must be a string'));
|
|
|
|
}
|
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
find_node(context, path, get_xattr);
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fgetxattr_file (context, ofd, name, callback) {
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function get_xattr (error, node) {
|
|
|
|
var xattr = (node ? node.xattrs[name] : null);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else if (!node.xattrs.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.ENOATTR());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null, node.xattrs[name]);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL());
|
|
|
|
}
|
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
|
|
|
}
|
|
|
|
else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, get_xattr);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function removexattr_file (context, path, name, callback) {
|
|
|
|
path = normalize(path);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function remove_xattr (error, node) {
|
|
|
|
var xattr = (node ? node.xattrs : null);
|
|
|
|
|
|
|
|
function update_time(error) {
|
|
|
|
if(error) {
|
2014-03-18 18:16:12 +00:00
|
|
|
callback(error);
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
update_node_times(context, path, node, { ctime: Date.now() }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else if (!xattr.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.ENOATTR());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
else {
|
2014-05-23 18:14:06 +00:00
|
|
|
delete node.xattrs[name];
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(node.id, node, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL('attribute name must be a string'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else {
|
|
|
|
find_node(context, path, remove_xattr);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fremovexattr_file (context, ofd, name, callback) {
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function remove_xattr (error, node) {
|
|
|
|
function update_time(error) {
|
2014-03-18 18:16:12 +00:00
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
update_node_times(context, ofd.path, node, { ctime: Date.now() }, callback);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (error) {
|
|
|
|
callback(error);
|
|
|
|
}
|
|
|
|
else if (!node.xattrs.hasOwnProperty(name)) {
|
|
|
|
callback(new Errors.ENOATTR());
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
delete node.xattrs[name];
|
2014-08-16 20:22:41 +00:00
|
|
|
context.putObject(node.id, node, update_time);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (typeof name != 'string') {
|
|
|
|
callback(new Errors.EINVAL('attribute name must be a string'));
|
|
|
|
}
|
|
|
|
else if (!name) {
|
|
|
|
callback(new Errors.EINVAL('attribute name cannot be an empty string'));
|
|
|
|
}
|
|
|
|
else {
|
2014-08-16 20:22:41 +00:00
|
|
|
context.getObject(ofd.id, remove_xattr);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function validate_flags(flags) {
|
|
|
|
if(!_(O_FLAGS).has(flags)) {
|
|
|
|
return null;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
return O_FLAGS[flags];
|
|
|
|
}
|
|
|
|
|
|
|
|
function validate_file_options(options, enc, fileMode){
|
|
|
|
if(!options) {
|
|
|
|
options = { encoding: enc, flag: fileMode };
|
|
|
|
} else if(typeof options === "function") {
|
|
|
|
options = { encoding: enc, flag: fileMode };
|
|
|
|
} else if(typeof options === "string") {
|
|
|
|
options = { encoding: options, flag: fileMode };
|
2014-05-13 22:10:11 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
return options;
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function pathCheck(path, callback) {
|
|
|
|
var err;
|
|
|
|
if(isNullPath(path)) {
|
|
|
|
err = new Error('Path must be a string without null bytes.');
|
|
|
|
} else if(!isAbsolutePath(path)) {
|
|
|
|
err = new Error('Path must be absolute.');
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(err) {
|
|
|
|
callback(err);
|
|
|
|
return false;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function open(fs, context, path, flags, mode, callback) {
|
|
|
|
// NOTE: we support the same signature as node with a `mode` arg,
|
|
|
|
// but ignore it.
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
|
|
|
|
function check_result(error, fileNode) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var position;
|
|
|
|
if(_(flags).contains(O_APPEND)) {
|
|
|
|
position = fileNode.size;
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
position = 0;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var openFileDescription = new OpenFileDescription(path, fileNode.id, flags, position);
|
|
|
|
var fd = fs.allocDescriptor(openFileDescription);
|
|
|
|
callback(null, fd);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
flags = validate_flags(flags);
|
|
|
|
if(!flags) {
|
|
|
|
callback(new Errors.EINVAL('flags is not valid'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
open_file(context, path, flags, check_result);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function close(fs, context, fd, callback) {
|
|
|
|
if(!_(fs.openFiles).has(fd)) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else {
|
|
|
|
fs.releaseDescriptor(fd);
|
|
|
|
callback(null);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function mknod(fs, context, path, mode, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
make_node(context, path, mode, callback);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function mkdir(fs, context, path, mode, callback) {
|
|
|
|
// NOTE: we support passing a mode arg, but we ignore it internally for now.
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
make_directory(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function rmdir(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
remove_directory(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 20:34:47 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function stat(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function check_result(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
var stats = new Stats(result, fs.name);
|
|
|
|
callback(null, stats);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
stat_file(context, path, check_result);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fstat(fs, context, fd, callback) {
|
|
|
|
function check_result(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var stats = new Stats(result, fs.name);
|
|
|
|
callback(null, stats);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else {
|
|
|
|
fstat_file(context, ofd, check_result);
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function link(fs, context, oldpath, newpath, callback) {
|
|
|
|
if(!pathCheck(oldpath, callback)) return;
|
|
|
|
if(!pathCheck(newpath, callback)) return;
|
|
|
|
link_node(context, oldpath, newpath, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function unlink(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
unlink_node(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function read(fs, context, fd, buffer, offset, length, position, callback) {
|
|
|
|
// Follow how node.js does this
|
|
|
|
function wrapped_cb(err, bytesRead) {
|
|
|
|
// Retain a reference to buffer so that it can't be GC'ed too soon.
|
|
|
|
callback(err, bytesRead || 0, buffer);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
offset = (undefined === offset) ? 0 : offset;
|
|
|
|
length = (undefined === length) ? buffer.length - offset : length;
|
|
|
|
callback = arguments[arguments.length - 1];
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else if(!_(ofd.flags).contains(O_READ)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit reading'));
|
|
|
|
} else {
|
|
|
|
read_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(wrapped_cb));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function readFile(fs, context, path, options, callback) {
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
options = validate_file_options(options, null, 'r');
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(!pathCheck(path, callback)) return;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var flags = validate_flags(options.flag || 'r');
|
|
|
|
if(!flags) {
|
2014-08-18 15:03:46 +00:00
|
|
|
return callback(new Errors.EINVAL('flags is not valid'));
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
open_file(context, path, flags, function(err, fileNode) {
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = new OpenFileDescription(path, fileNode.id, flags, 0);
|
|
|
|
var fd = fs.allocDescriptor(ofd);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-08-18 15:03:46 +00:00
|
|
|
function cleanup() {
|
|
|
|
fs.releaseDescriptor(fd);
|
|
|
|
}
|
|
|
|
|
|
|
|
fstat_file(context, ofd, function(err, fstatResult) {
|
|
|
|
if(err) {
|
|
|
|
cleanup();
|
|
|
|
return callback(err);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var stats = new Stats(fstatResult, fs.name);
|
2014-08-18 15:03:46 +00:00
|
|
|
|
|
|
|
if(stats.isDirectory()) {
|
|
|
|
cleanup();
|
|
|
|
return callback(new Errors.EISDIR('illegal operation on directory'));
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var size = stats.size;
|
2014-06-04 19:52:08 +00:00
|
|
|
var buffer = new Buffer(size);
|
2014-06-06 15:14:52 +00:00
|
|
|
buffer.fill(0);
|
2014-05-23 18:14:06 +00:00
|
|
|
|
2014-08-18 15:03:46 +00:00
|
|
|
read_data(context, ofd, buffer, 0, size, 0, function(err, nbytes) {
|
|
|
|
cleanup();
|
|
|
|
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
|
|
|
|
var data;
|
|
|
|
if(options.encoding === 'utf8') {
|
2014-06-04 19:52:08 +00:00
|
|
|
data = Encoding.decode(buffer);
|
2014-05-23 18:14:06 +00:00
|
|
|
} else {
|
|
|
|
data = buffer;
|
|
|
|
}
|
|
|
|
callback(null, data);
|
2014-03-18 18:16:12 +00:00
|
|
|
});
|
|
|
|
});
|
2014-05-23 18:14:06 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
function write(fs, context, fd, buffer, offset, length, position, callback) {
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
offset = (undefined === offset) ? 0 : offset;
|
|
|
|
length = (undefined === length) ? buffer.length - offset : length;
|
|
|
|
|
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else if(!_(ofd.flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
|
|
|
} else if(buffer.length - offset < length) {
|
|
|
|
callback(new Errors.EIO('intput buffer is too small'));
|
|
|
|
} else {
|
|
|
|
write_data(context, ofd, buffer, offset, length, position, standard_check_result_cb(callback));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function writeFile(fs, context, path, data, options, callback) {
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
options = validate_file_options(options, 'utf8', 'w');
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(!pathCheck(path, callback)) return;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var flags = validate_flags(options.flag || 'w');
|
|
|
|
if(!flags) {
|
|
|
|
callback(new Errors.EINVAL('flags is not valid'));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
data = data || '';
|
|
|
|
if(typeof data === "number") {
|
|
|
|
data = '' + data;
|
|
|
|
}
|
|
|
|
if(typeof data === "string" && options.encoding === 'utf8') {
|
2014-06-04 19:52:08 +00:00
|
|
|
data = Encoding.encode(data);
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
open_file(context, path, flags, function(err, fileNode) {
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = new OpenFileDescription(path, fileNode.id, flags, 0);
|
|
|
|
var fd = fs.allocDescriptor(ofd);
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-08-18 15:15:48 +00:00
|
|
|
replace_data(context, ofd, data, 0, data.length, function(err, nbytes) {
|
2014-05-23 18:14:06 +00:00
|
|
|
fs.releaseDescriptor(fd);
|
2014-08-18 15:15:48 +00:00
|
|
|
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null);
|
2014-03-18 18:16:12 +00:00
|
|
|
});
|
2014-05-23 18:14:06 +00:00
|
|
|
});
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function appendFile(fs, context, path, data, options, callback) {
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
options = validate_file_options(options, 'utf8', 'a');
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
|
|
|
|
var flags = validate_flags(options.flag || 'a');
|
|
|
|
if(!flags) {
|
|
|
|
callback(new Errors.EINVAL('flags is not valid'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
data = data || '';
|
|
|
|
if(typeof data === "number") {
|
|
|
|
data = '' + data;
|
|
|
|
}
|
|
|
|
if(typeof data === "string" && options.encoding === 'utf8') {
|
2014-06-04 19:52:08 +00:00
|
|
|
data = Encoding.encode(data);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
open_file(context, path, flags, function(err, fileNode) {
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
2014-03-18 20:34:47 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = new OpenFileDescription(path, fileNode.id, flags, fileNode.size);
|
|
|
|
var fd = fs.allocDescriptor(ofd);
|
|
|
|
|
2014-08-18 15:15:48 +00:00
|
|
|
write_data(context, ofd, data, 0, data.length, ofd.position, function(err, nbytes) {
|
2014-05-23 18:14:06 +00:00
|
|
|
fs.releaseDescriptor(fd);
|
2014-08-18 15:15:48 +00:00
|
|
|
|
|
|
|
if(err) {
|
|
|
|
return callback(err);
|
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
callback(null);
|
|
|
|
});
|
|
|
|
});
|
|
|
|
}
|
2014-03-18 20:34:47 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function exists(fs, context, path, callback) {
|
|
|
|
function cb(err, stats) {
|
|
|
|
callback(err ? false : true);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
stat(fs, context, path, cb);
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function getxattr(fs, context, path, name, callback) {
|
|
|
|
if (!pathCheck(path, callback)) return;
|
|
|
|
getxattr_file(context, path, name, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 20:34:47 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fgetxattr(fs, context, fd, name, callback) {
|
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if (!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
else {
|
|
|
|
fgetxattr_file(context, ofd, name, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function setxattr(fs, context, path, name, value, flag, callback) {
|
|
|
|
if(typeof flag === 'function') {
|
|
|
|
callback = flag;
|
|
|
|
flag = null;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if (!pathCheck(path, callback)) return;
|
|
|
|
setxattr_file(context, path, name, value, flag, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
function fsetxattr(fs, context, fd, name, value, flag, callback) {
|
|
|
|
if(typeof flag === 'function') {
|
|
|
|
callback = flag;
|
|
|
|
flag = null;
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if (!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
}
|
|
|
|
else if (!_(ofd.flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fsetxattr_file(context, ofd, name, value, flag, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function removexattr(fs, context, path, name, callback) {
|
|
|
|
if (!pathCheck(path, callback)) return;
|
|
|
|
removexattr_file(context, path, name, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function fremovexattr(fs, context, fd, name, callback) {
|
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if (!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
}
|
|
|
|
else if (!_(ofd.flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
fremovexattr_file(context, ofd, name, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function lseek(fs, context, fd, offset, whence, callback) {
|
|
|
|
function update_descriptor_position(error, stats) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
if(stats.size + offset < 0) {
|
2014-03-18 18:16:12 +00:00
|
|
|
callback(new Errors.EINVAL('resulting file offset would be negative'));
|
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
ofd.position = stats.size + offset;
|
2014-03-18 18:16:12 +00:00
|
|
|
callback(null, ofd.position);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
if('SET' === whence) {
|
|
|
|
if(offset < 0) {
|
|
|
|
callback(new Errors.EINVAL('resulting file offset would be negative'));
|
|
|
|
} else {
|
|
|
|
ofd.position = offset;
|
|
|
|
callback(null, ofd.position);
|
|
|
|
}
|
|
|
|
} else if('CUR' === whence) {
|
|
|
|
if(ofd.position + offset < 0) {
|
|
|
|
callback(new Errors.EINVAL('resulting file offset would be negative'));
|
2014-03-18 18:16:12 +00:00
|
|
|
} else {
|
2014-05-23 18:14:06 +00:00
|
|
|
ofd.position += offset;
|
|
|
|
callback(null, ofd.position);
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
} else if('END' === whence) {
|
|
|
|
fstat_file(context, ofd, update_descriptor_position);
|
|
|
|
} else {
|
|
|
|
callback(new Errors.EINVAL('whence argument is not a proper value'));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function readdir(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
read_directory(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function utimes(fs, context, path, atime, mtime, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
var currentTime = Date.now();
|
|
|
|
atime = (atime) ? atime : currentTime;
|
|
|
|
mtime = (mtime) ? mtime : currentTime;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
utimes_file(context, path, atime, mtime, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function futimes(fs, context, fd, atime, mtime, callback) {
|
|
|
|
var currentTime = Date.now();
|
|
|
|
atime = (atime) ? atime : currentTime;
|
|
|
|
mtime = (mtime) ? mtime : currentTime;
|
|
|
|
|
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else if(!_(ofd.flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
|
|
|
} else {
|
|
|
|
futimes_file(context, ofd, atime, mtime, standard_check_result_cb(callback));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function rename(fs, context, oldpath, newpath, callback) {
|
|
|
|
if(!pathCheck(oldpath, callback)) return;
|
|
|
|
if(!pathCheck(newpath, callback)) return;
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function unlink_old_node(error) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
unlink_node(context, oldpath, standard_check_result_cb(callback));
|
2014-03-18 18:16:12 +00:00
|
|
|
}
|
2014-05-23 18:14:06 +00:00
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
link_node(context, oldpath, newpath, unlink_old_node);
|
|
|
|
}
|
|
|
|
|
|
|
|
function symlink(fs, context, srcpath, dstpath, type, callback) {
|
|
|
|
// NOTE: we support passing the `type` arg, but ignore it.
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
if(!pathCheck(srcpath, callback)) return;
|
|
|
|
if(!pathCheck(dstpath, callback)) return;
|
|
|
|
make_symbolic_link(context, srcpath, dstpath, standard_check_result_cb(callback));
|
|
|
|
}
|
2014-03-18 18:16:12 +00:00
|
|
|
|
2014-05-23 18:14:06 +00:00
|
|
|
function readlink(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
read_link(context, path, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
function lstat(fs, context, path, callback) {
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
|
|
|
|
function check_result(error, result) {
|
|
|
|
if(error) {
|
|
|
|
callback(error);
|
|
|
|
} else {
|
|
|
|
var stats = new Stats(result, fs.name);
|
|
|
|
callback(null, stats);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
lstat_file(context, path, check_result);
|
|
|
|
}
|
|
|
|
|
|
|
|
function truncate(fs, context, path, length, callback) {
|
|
|
|
// NOTE: length is optional
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
length = length || 0;
|
|
|
|
|
|
|
|
if(!pathCheck(path, callback)) return;
|
|
|
|
truncate_file(context, path, length, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
|
|
|
|
function ftruncate(fs, context, fd, length, callback) {
|
|
|
|
// NOTE: length is optional
|
|
|
|
callback = arguments[arguments.length - 1];
|
|
|
|
length = length || 0;
|
|
|
|
|
|
|
|
var ofd = fs.openFiles[fd];
|
|
|
|
if(!ofd) {
|
|
|
|
callback(new Errors.EBADF());
|
|
|
|
} else if(!_(ofd.flags).contains(O_WRITE)) {
|
|
|
|
callback(new Errors.EBADF('descriptor does not permit writing'));
|
|
|
|
} else {
|
|
|
|
ftruncate_file(context, ofd, length, standard_check_result_cb(callback));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
module.exports = {
|
2014-07-07 21:02:42 +00:00
|
|
|
ensureRootDirectory: ensure_root_directory,
|
2014-05-23 18:14:06 +00:00
|
|
|
open: open,
|
|
|
|
close: close,
|
|
|
|
mknod: mknod,
|
|
|
|
mkdir: mkdir,
|
|
|
|
rmdir: rmdir,
|
|
|
|
unlink: unlink,
|
|
|
|
stat: stat,
|
|
|
|
fstat: fstat,
|
|
|
|
link: link,
|
|
|
|
read: read,
|
|
|
|
readFile: readFile,
|
|
|
|
write: write,
|
|
|
|
writeFile: writeFile,
|
|
|
|
appendFile: appendFile,
|
|
|
|
exists: exists,
|
|
|
|
getxattr: getxattr,
|
|
|
|
fgetxattr: fgetxattr,
|
|
|
|
setxattr: setxattr,
|
|
|
|
fsetxattr: fsetxattr,
|
|
|
|
removexattr: removexattr,
|
|
|
|
fremovexattr: fremovexattr,
|
|
|
|
lseek: lseek,
|
|
|
|
readdir: readdir,
|
|
|
|
utimes: utimes,
|
|
|
|
futimes: futimes,
|
|
|
|
rename: rename,
|
|
|
|
symlink: symlink,
|
|
|
|
readlink: readlink,
|
|
|
|
lstat: lstat,
|
|
|
|
truncate: truncate,
|
|
|
|
ftruncate: ftruncate
|
|
|
|
};
|