Update Filer for things needed for Plan 9 sharing
This commit is contained in:
parent
b026537f09
commit
2e627cfe5b
|
@ -76,5 +76,43 @@ module.exports = {
|
||||||
ENVIRONMENT: {
|
ENVIRONMENT: {
|
||||||
TMP: '/tmp',
|
TMP: '/tmp',
|
||||||
PATH: ''
|
PATH: ''
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plan 9 related
|
||||||
|
* - https://github.com/chaos/diod/blob/master/libnpfs/9p.h
|
||||||
|
* - https://en.wikibooks.org/wiki/C_Programming/POSIX_Reference/sys/stat.h
|
||||||
|
*/
|
||||||
|
P9: {
|
||||||
|
/**
|
||||||
|
* QID types
|
||||||
|
*
|
||||||
|
* @P9_QTDIR: directory
|
||||||
|
* @P9_QTAPPEND: append-only
|
||||||
|
* @P9_QTEXCL: excluse use (only one open handle allowed)
|
||||||
|
* @P9_QTMOUNT: mount points
|
||||||
|
* @P9_QTAUTH: authentication file
|
||||||
|
* @P9_QTTMP: non-backed-up files
|
||||||
|
* @P9_QTSYMLINK: symbolic links (9P2000.u)
|
||||||
|
* @P9_QTLINK: hard-link (9P2000.u)
|
||||||
|
* @P9_QTFILE: normal files
|
||||||
|
*/
|
||||||
|
QTDIR: 0x80,
|
||||||
|
QTAPPEND: 0x40,
|
||||||
|
QTEXCL: 0x20,
|
||||||
|
QTMOUNT: 0x10,
|
||||||
|
QTAUTH: 0x08,
|
||||||
|
QTTMP: 0x04,
|
||||||
|
QTSYMLINK: 0x02,
|
||||||
|
QTLINK: 0x01,
|
||||||
|
QTFILE: 0x00,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* POSIX System Values
|
||||||
|
*/
|
||||||
|
S_IFMT: 0xF000, // mask for file type
|
||||||
|
S_IFLNK: 0xA000, // symbolic link
|
||||||
|
S_IFDIR: 0x4000, // directory
|
||||||
|
S_IFREG: 0x8000 // regular file
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -7,7 +7,23 @@ function encode(string) {
|
||||||
return new Buffer(string, 'utf8');
|
return new Buffer(string, 'utf8');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// https://github.com/darkskyapp/string-hash
|
||||||
|
function hash32(string) {
|
||||||
|
var hash = 5381;
|
||||||
|
var i = string.length;
|
||||||
|
|
||||||
|
while(i) {
|
||||||
|
hash = (hash * 33) ^ string.charCodeAt(--i);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* JavaScript does bitwise operations (like XOR, above) on 32-bit signed
|
||||||
|
* integers. Since we want the results to be always positive, convert the
|
||||||
|
* signed int to an unsigned by doing an unsigned bitshift. */
|
||||||
|
return hash >>> 0;
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
encode: encode,
|
encode: encode,
|
||||||
decode: decode
|
decode: decode,
|
||||||
|
hash32: hash32
|
||||||
};
|
};
|
||||||
|
|
|
@ -12,6 +12,9 @@ var MODE_FILE = Constants.MODE_FILE;
|
||||||
var MODE_DIRECTORY = Constants.MODE_DIRECTORY;
|
var MODE_DIRECTORY = Constants.MODE_DIRECTORY;
|
||||||
var MODE_SYMBOLIC_LINK = Constants.MODE_SYMBOLIC_LINK;
|
var MODE_SYMBOLIC_LINK = Constants.MODE_SYMBOLIC_LINK;
|
||||||
var MODE_META = Constants.MODE_META;
|
var MODE_META = Constants.MODE_META;
|
||||||
|
var P9_QTDIR = Constants.P9.QTDIR;
|
||||||
|
var P9_QTFILE = Constants.P9.QTFILE;
|
||||||
|
var P9_QTSYMLINK = Constants.P9.QTSYMLINK;
|
||||||
|
|
||||||
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
|
var ROOT_DIRECTORY_NAME = Constants.ROOT_DIRECTORY_NAME;
|
||||||
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
|
var SUPER_NODE_ID = Constants.SUPER_NODE_ID;
|
||||||
|
@ -44,17 +47,24 @@ var Buffer = require('../buffer.js');
|
||||||
* and filesystem flags are examined in order to override update logic.
|
* and filesystem flags are examined in order to override update logic.
|
||||||
*/
|
*/
|
||||||
function update_node_times(context, path, node, times, callback) {
|
function update_node_times(context, path, node, times, callback) {
|
||||||
|
var update = false;
|
||||||
|
|
||||||
|
if(times.ctime || times.mtime) {
|
||||||
|
// Update the qid's version field, since node has changed.
|
||||||
|
// TODO: this might more than I need to do...
|
||||||
|
node.p9.qid.version = times.ctime || times.mtime;
|
||||||
|
update = true;
|
||||||
|
}
|
||||||
|
|
||||||
// Honour mount flags for how we update times
|
// Honour mount flags for how we update times
|
||||||
var flags = context.flags;
|
var flags = context.flags;
|
||||||
if(_(flags).contains(FS_NOCTIME)) {
|
if(_(flags).contains(FS_NOCTIME)) {
|
||||||
delete times.ctime;
|
delete times.ctime;
|
||||||
}
|
}
|
||||||
if(_(flags).contains(FS_NOMTIME)) {
|
if(_(flags).contains(FS_NOMTIME)) {
|
||||||
delete times.mtime;
|
delete times.mtime;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Only do the update if required (i.e., times are still present)
|
|
||||||
var update = false;
|
|
||||||
if(times.ctime) {
|
if(times.ctime) {
|
||||||
node.ctime = times.ctime;
|
node.ctime = times.ctime;
|
||||||
// We don't do atime tracking for perf reasons, but do mirror ctime
|
// We don't do atime tracking for perf reasons, but do mirror ctime
|
||||||
|
@ -69,6 +79,8 @@ function update_node_times(context, path, node, times, callback) {
|
||||||
}
|
}
|
||||||
if(times.mtime) {
|
if(times.mtime) {
|
||||||
node.mtime = times.mtime;
|
node.mtime = times.mtime;
|
||||||
|
// Also update the qid's version filed, since file has changed.
|
||||||
|
node.p9.qid.version = times.mtime;
|
||||||
update = true;
|
update = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -133,7 +145,11 @@ function make_node(context, path, mode, callback) {
|
||||||
callback(error);
|
callback(error);
|
||||||
} else {
|
} else {
|
||||||
parentNodeData = result;
|
parentNodeData = result;
|
||||||
Node.create({guid: context.guid, mode: mode}, function(error, result) {
|
Node.create({
|
||||||
|
path: path,
|
||||||
|
guid: context.guid,
|
||||||
|
mode: mode
|
||||||
|
}, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
@ -311,7 +327,7 @@ function ensure_root_directory(context, callback) {
|
||||||
} else if(error && !(error instanceof Errors.ENOENT)) {
|
} else if(error && !(error instanceof Errors.ENOENT)) {
|
||||||
callback(error);
|
callback(error);
|
||||||
} else {
|
} else {
|
||||||
SuperNode.create({guid: context.guid}, function(error, result) {
|
SuperNode.create({ guid: context.guid }, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
@ -326,7 +342,12 @@ function ensure_root_directory(context, callback) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
} else {
|
} else {
|
||||||
Node.create({guid: context.guid, id: superNode.rnode, mode: MODE_DIRECTORY}, function(error, result) {
|
Node.create({
|
||||||
|
guid: context.guid,
|
||||||
|
id: superNode.rnode,
|
||||||
|
mode: MODE_DIRECTORY,
|
||||||
|
path: ROOT_DIRECTORY_NAME
|
||||||
|
}, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
@ -387,7 +408,11 @@ function make_directory(context, path, callback) {
|
||||||
callback(error);
|
callback(error);
|
||||||
} else {
|
} else {
|
||||||
parentDirectoryData = result;
|
parentDirectoryData = result;
|
||||||
Node.create({guid: context.guid, mode: MODE_DIRECTORY}, function(error, result) {
|
Node.create({
|
||||||
|
guid: context.guid,
|
||||||
|
mode: MODE_DIRECTORY,
|
||||||
|
path: path
|
||||||
|
}, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
@ -624,7 +649,11 @@ function open_file(context, path, flags, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function write_file_node() {
|
function write_file_node() {
|
||||||
Node.create({guid: context.guid, mode: MODE_FILE}, function(error, result) {
|
Node.create({
|
||||||
|
guid: context.guid,
|
||||||
|
mode: MODE_FILE,
|
||||||
|
path: path
|
||||||
|
}, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
@ -1111,7 +1140,11 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function write_file_node() {
|
function write_file_node() {
|
||||||
Node.create({guid: context.guid, mode: MODE_SYMBOLIC_LINK}, function(error, result) {
|
Node.create({
|
||||||
|
guid: context.guid,
|
||||||
|
mode: MODE_SYMBOLIC_LINK,
|
||||||
|
path: dstpath
|
||||||
|
}, function(error, result) {
|
||||||
if(error) {
|
if(error) {
|
||||||
callback(error);
|
callback(error);
|
||||||
return;
|
return;
|
||||||
|
|
80
src/node.js
80
src/node.js
|
@ -1,4 +1,46 @@
|
||||||
|
var path = require('./path.js');
|
||||||
|
var hash32 = require('./encoding.js').hash32;
|
||||||
|
|
||||||
var MODE_FILE = require('./constants.js').MODE_FILE;
|
var MODE_FILE = require('./constants.js').MODE_FILE;
|
||||||
|
var MODE_DIRECTORY = require('./constants.js').MODE_DIRECTORY;
|
||||||
|
var MODE_SYMBOLIC_LINK = require('./constants.js').MODE_SYMBOLIC_LINK;
|
||||||
|
var MODE_META = require('./constants.js').MODE_META;
|
||||||
|
|
||||||
|
var P9_QTFILE = require('./constants.js').P9.QTFILE;
|
||||||
|
var P9_QTDIR = require('./constants.js').P9.QTDIR;
|
||||||
|
var P9_QTSYMLINK = require('./constants.js').P9.QTSYMLINK;
|
||||||
|
|
||||||
|
var S_IFLNK = require('./constants.js').P9.S_IFLNK;
|
||||||
|
var S_IFDIR = require('./constants.js').P9.S_IFDIR;
|
||||||
|
var S_IFREG = require('./constants.js').P9.S_IFREG;
|
||||||
|
|
||||||
|
var ROOT_DIRECTORY_NAME = require('./constants.js').ROOT_DIRECTORY_NAME;
|
||||||
|
|
||||||
|
function getQType(mode) {
|
||||||
|
switch(mode) {
|
||||||
|
case MODE_FILE:
|
||||||
|
return P9_QTFILE;
|
||||||
|
case MODE_DIRECTORY:
|
||||||
|
return P9_QTDIR;
|
||||||
|
case MODE_SYMBOLIC_LINK:
|
||||||
|
return P9_QTSYMLINK;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPOSIXMode(mode) {
|
||||||
|
switch(mode) {
|
||||||
|
case MODE_FILE:
|
||||||
|
return S_IFREG;
|
||||||
|
case MODE_DIRECTORY:
|
||||||
|
return S_IFDIR;
|
||||||
|
case MODE_SYMBOLIC_LINK:
|
||||||
|
return S_IFLNK;
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function Node(options) {
|
function Node(options) {
|
||||||
var now = Date.now();
|
var now = Date.now();
|
||||||
|
@ -16,6 +58,44 @@ function Node(options) {
|
||||||
this.blksize = undefined; // block size
|
this.blksize = undefined; // block size
|
||||||
this.nblocks = 1; // blocks count
|
this.nblocks = 1; // blocks count
|
||||||
this.data = options.data; // id for data object
|
this.data = options.data; // id for data object
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Plan 9 related metadata:
|
||||||
|
* https://web.archive.org/web/20170601072902/http://plan9.bell-labs.com/magic/man2html/5/0intro
|
||||||
|
*
|
||||||
|
* "The qid represents the server's unique identification for the file being
|
||||||
|
* accessed: two files on the same server hierarchy are the same if and only
|
||||||
|
* if their qids are the same. (The client may have multiple fids pointing to
|
||||||
|
* a single file on a server and hence having a single qid.) The thirteen–byte
|
||||||
|
* qid fields hold a one–byte type, specifying whether the file is a directory,
|
||||||
|
* append–only file, etc., and two unsigned integers: first the four–byte qid
|
||||||
|
* version, then the eight–byte qid path. The path is an integer unique among
|
||||||
|
* all files in the hierarchy. If a file is deleted and recreated with the same
|
||||||
|
* name in the same directory, the old and new path components of the qids
|
||||||
|
* should be different. The version is a version number for a file; typically,
|
||||||
|
* it is incremented every time the file is modified."
|
||||||
|
*/
|
||||||
|
this.p9 = {
|
||||||
|
qid: {
|
||||||
|
type: getQType(this.mode) || P9_QTFILE,
|
||||||
|
// use mtime for version info, since we already keep that updated
|
||||||
|
version: now,
|
||||||
|
// files have a unique `path` number, which takes into account files with same
|
||||||
|
// name but created at different times.
|
||||||
|
path: hash32(options.path + this.ctime)
|
||||||
|
},
|
||||||
|
// permissions and flags
|
||||||
|
// TODO: I don't think I'm doing this correctly yet...
|
||||||
|
mode: getPOSIXMode(this.mode) || S_IFREG,
|
||||||
|
// Name of file/dir. Must be / if the file is the root directory of the server
|
||||||
|
// TODO: do I need this or can I derive it from abs path?
|
||||||
|
name: options.path === ROOT_DIRECTORY_NAME ? ROOT_DIRECTORY_NAME : path.basename(options.path),
|
||||||
|
uid: 0x0, // owner name
|
||||||
|
gid: 0x0, // group name
|
||||||
|
muid: 0x0// name of the user who last modified the file
|
||||||
|
};
|
||||||
|
|
||||||
|
console.log('Node', this);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Make sure the options object has an id on property,
|
// Make sure the options object has an id on property,
|
||||||
|
|
Loading…
Reference in New Issue