filer/src/node.js

136 lines
3.7 KiB
JavaScript

const {
NODE_TYPE_FILE,
NODE_TYPE_DIRECTORY,
NODE_TYPE_SYMBOLIC_LINK,
DEFAULT_FILE_PERMISSIONS,
DEFAULT_DIR_PERMISSIONS
} = require('./constants');
const {
S_IFREG,
S_IFDIR,
S_IFLNK
} = require('./constants').fsConstants;
/**
* Make sure the options object has an id on property,
* either from caller or one we generate using supplied guid fn.
*/
function ensureID(options, prop, callback) {
if(options[prop]) {
return callback();
}
options.guid(function(err, id) {
if(err) {
return callback(err);
}
options[prop] = id;
callback();
});
}
/**
* 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:
return (modePermissions || DEFAULT_DIR_PERMISSIONS) | S_IFDIR;
case NODE_TYPE_SYMBOLIC_LINK:
return (modePermissions || DEFAULT_FILE_PERMISSIONS) | S_IFLNK;
case NODE_TYPE_FILE:
// falls through
default:
return (modePermissions || DEFAULT_FILE_PERMISSIONS) | S_IFREG;
}
}
/**
* Common properties for the layout of a Node
*/
class Node {
constructor(options) {
var now = Date.now();
this.id = options.id;
this.data = options.data; // id for data object
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.ctime = options.ctime || now; // creation/change time
this.mtime = options.mtime || now; // modified time
this.flags = options.flags || []; // file flags
this.xattrs = options.xattrs || {}; // extended attributes
this.nlinks = options.nlinks || 0; // links count
// Historically, Filer's node layout has referred to the
// 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.gid = options.gid || 0x0; // group name
}
/**
* Serialize a Node to JSON. Everything is as expected except
* that we use `mode` for `type` to maintain backward compatibility.
*/
toJSON() {
return {
id: this.id,
data: this.data,
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;
}
}
module.exports.create = function create(options, callback) {
// We expect both options.id and options.data to be provided/generated.
ensureID(options, 'id', function(err) {
if(err) {
return callback(err);
}
ensureID(options, 'data', function(err) {
if(err) {
return callback(err);
}
callback(null, new Node(options));
});
});
};