Update Filer for things needed for Plan 9 sharing

This commit is contained in:
David Humphrey 2018-05-15 13:33:45 -04:00
parent b026537f09
commit 2e627cfe5b
4 changed files with 177 additions and 10 deletions

View File

@ -76,5 +76,43 @@ module.exports = {
ENVIRONMENT: {
TMP: '/tmp',
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
}
};

View File

@ -7,7 +7,23 @@ function encode(string) {
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 = {
encode: encode,
decode: decode
decode: decode,
hash32: hash32
};

View File

@ -12,6 +12,9 @@ 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 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 SUPER_NODE_ID = Constants.SUPER_NODE_ID;
@ -44,6 +47,15 @@ var Buffer = require('../buffer.js');
* and filesystem flags are examined in order to override update logic.
*/
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
var flags = context.flags;
if(_(flags).contains(FS_NOCTIME)) {
@ -53,8 +65,6 @@ function update_node_times(context, path, node, times, callback) {
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
@ -69,6 +79,8 @@ function update_node_times(context, path, node, times, callback) {
}
if(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;
}
@ -133,7 +145,11 @@ function make_node(context, path, mode, callback) {
callback(error);
} else {
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) {
callback(error);
return;
@ -326,7 +342,12 @@ function ensure_root_directory(context, callback) {
if(error) {
callback(error);
} 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) {
callback(error);
return;
@ -387,7 +408,11 @@ function make_directory(context, path, callback) {
callback(error);
} else {
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) {
callback(error);
return;
@ -624,7 +649,11 @@ function open_file(context, path, flags, callback) {
}
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) {
callback(error);
return;
@ -1111,7 +1140,11 @@ function make_symbolic_link(context, srcpath, dstpath, callback) {
}
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) {
callback(error);
return;

View File

@ -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_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) {
var now = Date.now();
@ -16,6 +58,44 @@ function Node(options) {
this.blksize = undefined; // block size
this.nblocks = 1; // blocks count
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 thirteenbyte
* qid fields hold a onebyte type, specifying whether the file is a directory,
* appendonly file, etc., and two unsigned integers: first the fourbyte qid
* version, then the eightbyte 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,