Refactor Node to support layout changes with backwad compatibility

This commit is contained in:
David Humphrey 2018-12-08 21:59:04 -05:00
parent f738cbc17d
commit d0178539f5
3 changed files with 119 additions and 61 deletions

View File

@ -201,7 +201,7 @@ function find_node(context, path, callback) {
} else if(!rootDirectoryNode) { } else if(!rootDirectoryNode) {
callback(new Errors.ENOENT()); callback(new Errors.ENOENT());
} else { } else {
callback(null, rootDirectoryNode); Node.create(rootDirectoryNode, callback);
} }
} }
@ -227,11 +227,18 @@ function find_node(context, path, callback) {
callback(new Errors.ENOENT(null, path)); callback(new Errors.ENOENT(null, path));
} else { } else {
var nodeId = parentDirectoryData[name].id; var nodeId = parentDirectoryData[name].id;
context.getObject(nodeId, is_symbolic_link); context.getObject(nodeId, create_node);
} }
} }
} }
function create_node(error, data) {
if(error) {
return callback(error);
}
Node.create(data, is_symbolic_link);
}
function is_symbolic_link(error, node) { function is_symbolic_link(error, node) {
if(error) { if(error) {
callback(error); callback(error);
@ -664,7 +671,7 @@ function open_file(context, path, flags, mode, callback) {
fileNode = result; fileNode = result;
fileNode.nlinks += 1; fileNode.nlinks += 1;
if(mode){ if(mode){
Node.setMode(mode, fileNode); fileNode.mode = mode;
} }
context.putObject(fileNode.id, fileNode, write_file_data); context.putObject(fileNode.id, fileNode, write_file_data);
}); });
@ -2006,7 +2013,7 @@ function chmod_file(context, path, mode, callback) {
if (error) { if (error) {
callback(error); callback(error);
} else { } else {
Node.setMode(mode, node); node.mode = mode;
update_node_times(context, path, node, { mtime: Date.now() }, callback); update_node_times(context, path, node, { mtime: Date.now() }, callback);
} }
} }

View File

@ -1,34 +1,60 @@
var Constants = require('./constants.js'); const {
var NODE_TYPE_FILE = Constants.NODE_TYPE_FILE; NODE_TYPE_FILE,
var NODE_TYPE_DIRECTORY = Constants.NODE_TYPE_DIRECTORY; NODE_TYPE_DIRECTORY,
var NODE_TYPE_SYMBOLIC_LINK = Constants.NODE_TYPE_SYMBOLIC_LINK; NODE_TYPE_SYMBOLIC_LINK,
DEFAULT_FILE_PERMISSIONS,
DEFAULT_DIR_PERMISSIONS
} = require('./constants');
const {
S_IFREG,
S_IFDIR,
S_IFLNK
} = require('./constants').fsConstants;
var S_IFREG = Constants.fsConstants.S_IFREG; /**
var S_IFDIR = Constants.fsConstants.S_IFDIR; * Make sure the options object has an id on property,
var S_IFLNK = Constants.fsConstants.S_IFLNK; * either from caller or one we generate using supplied guid fn.
*/
function ensureID(options, prop, callback) {
if(options[prop]) {
return callback();
}
var DEFAULT_FILE_PERMISSIONS = Constants.DEFAULT_FILE_PERMISSIONS; options.guid(function(err, id) {
var DEFAULT_DIR_PERMISSIONS = Constants.DEFAULT_DIR_PERMISSIONS; if(err) {
return callback(err);
}
options[prop] = id;
callback();
});
}
function getMode(type, mode) { /**
switch(type) { * Generate a POSIX mode (integer) for the node type and permissions.
* Use default permissions if we aren't passed any.
*/
function generateMode(nodeType, modePermissions) {
switch(nodeType) {
case NODE_TYPE_DIRECTORY: case NODE_TYPE_DIRECTORY:
return (mode || DEFAULT_DIR_PERMISSIONS) | S_IFDIR; return (modePermissions || DEFAULT_DIR_PERMISSIONS) | S_IFDIR;
case NODE_TYPE_SYMBOLIC_LINK: case NODE_TYPE_SYMBOLIC_LINK:
return (mode || DEFAULT_FILE_PERMISSIONS) | S_IFLNK; return (modePermissions || DEFAULT_FILE_PERMISSIONS) | S_IFLNK;
/* jshint -W086 */
case NODE_TYPE_FILE: case NODE_TYPE_FILE:
// falls through // falls through
default: default:
return (mode || DEFAULT_FILE_PERMISSIONS) | S_IFREG; return (modePermissions || DEFAULT_FILE_PERMISSIONS) | S_IFREG;
} }
} }
function Node(options) { /**
* Common properties for the layout of a Node
*/
class Node {
constructor(options) {
var now = Date.now(); var now = Date.now();
this.id = options.id; this.id = options.id;
this.type = options.type || NODE_TYPE_FILE; // node type (file, directory, etc) this.data = options.data; // id for data object
this.size = options.size || 0; // size (bytes for files, entries for directories) this.size = options.size || 0; // size (bytes for files, entries for directories)
this.atime = options.atime || now; // access time (will mirror ctime after creation) this.atime = options.atime || now; // access time (will mirror ctime after creation)
this.ctime = options.ctime || now; // creation/change time this.ctime = options.ctime || now; // creation/change time
@ -36,50 +62,74 @@ function Node(options) {
this.flags = options.flags || []; // file flags this.flags = options.flags || []; // file flags
this.xattrs = options.xattrs || {}; // extended attributes this.xattrs = options.xattrs || {}; // extended attributes
this.nlinks = options.nlinks || 0; // links count this.nlinks = options.nlinks || 0; // links count
this.data = options.data; // id for data object
this.version = options.version || 1;
// permissions and flags // Historically, Filer's node layout has referred to the
this.mode = options.mode || (getMode(this.type)); // node type as `mode`, and done so using a String. In
// a POSIX filesystem, the mode is a number that combines
// both node type and permission bits. Internal we use `type`,
// but store it in the database as `mode` for backward
// compatibility.
if(typeof options.type === 'string') {
this.type = options.type;
} else if(typeof options.mode === 'string') {
this.type = options.mode;
} else {
this.type = NODE_TYPE_FILE;
}
// Extra mode permissions and ownership info
this.permissions = options.permissions || generateMode(this.type);
this.uid = options.uid || 0x0; // owner name this.uid = options.uid || 0x0; // owner name
this.gid = options.gid || 0x0; // group name this.gid = options.gid || 0x0; // group name
} }
// Make sure the options object has an id on property, /**
// either from caller or one we generate using supplied guid fn. * Serialize a Node to JSON. Everything is as expected except
function ensureID(options, prop, callback) { * that we use `mode` for `type` to maintain backward compatibility.
if(options[prop]) { */
callback(null); toJSON() {
} else { return {
options.guid(function(err, id) { id: this.id,
options[prop] = id; data: this.data,
callback(err); size: this.size,
}); atime: this.atime,
ctime: this.ctime,
mtime: this.ctime,
flags: this.flags,
xattrs: this.xattrs,
nlinks: this.nlinks,
// Use `mode` for `type` to keep backward compatibility
mode: this.type,
permissions: this.permissions,
uid: this.uid,
gid: this.gid
};
}
// Return complete POSIX `mode` for node type + permissions. See:
// http://man7.org/linux/man-pages/man2/chmod.2.html
get mode() {
return generateMode(this.type, this.permissions);
}
// When setting the `mode` we assume permissions bits only (not changing type)
set mode(value) {
this.permissions = value;
} }
} }
Node.create = function(options, callback) { module.exports.create = function create(options, callback) {
// We expect both options.id and options.data to be provided/generated. // We expect both options.id and options.data to be provided/generated.
ensureID(options, 'id', function(err) { ensureID(options, 'id', function(err) {
if(err) { if(err) {
callback(err); return callback(err);
return;
} }
ensureID(options, 'data', function(err) { ensureID(options, 'data', function(err) {
if(err) { if(err) {
callback(err); return callback(err);
return;
} }
callback(null, new Node(options)); callback(null, new Node(options));
}); });
}); });
}; };
// Update the node's mode (permissions), taking file type bits into account.
Node.setMode = function(mode, node) {
node.mode = getMode(node.type, mode);
};
module.exports = Node;

View File

@ -1,4 +1,5 @@
var Errors = require('./errors.js'); const Errors = require('./errors.js');
const Node = require('./node');
function OpenFileDescription(path, id, flags, position) { function OpenFileDescription(path, id, flags, position) {
this.path = path; this.path = path;
@ -22,7 +23,7 @@ OpenFileDescription.prototype.getNode = function(context, callback) {
return callback(new Errors.EBADF('file descriptor refers to unknown node', path)); return callback(new Errors.EBADF('file descriptor refers to unknown node', path));
} }
callback(null, node); Node.create(node, callback);
} }
context.getObject(id, check_if_node_exists); context.getObject(id, check_if_node_exists);