Introduce forwarding inodes. Implement fs.Mount

Add forward_path info to fs.SearchPath.
Handle forwarding for fs.Search.
This commit is contained in:
Ernest Wong 2018-08-11 11:05:11 +12:00 committed by Fabian
commit e6ef68b334

View file

@ -29,6 +29,7 @@ var STATUS_OK = 0x0;
var STATUS_ON_SERVER = 0x2;
var STATUS_LOADING = 0x3;
var STATUS_UNLINKED = 0x4;
var STATUS_FORWARDING = 0x5;
/** @const */ var JSONFS_VERSION = 2;
@ -62,6 +63,9 @@ function FS(baseurl) {
this.total_size = 256 * 1024 * 1024 * 1024;
this.used_size = 0;
/** @type {!Array<!FSMountInfo>} */
this.mounts = [];
//RegisterMessage("LoadFilesystem", this.LoadFilesystem.bind(this) );
//RegisterMessage("MergeFile", this.MergeFile.bind(this) );
//RegisterMessage("tar",
@ -92,6 +96,8 @@ FS.prototype.get_state = function()
}
state[3] = this.total_size;
state[4] = this.used_size;
state[5] = this.baseurl;
state[6] = this.mounts.map(mnt => mnt.fs);
return state;
};
@ -113,6 +119,14 @@ FS.prototype.set_state = function(state)
}
this.total_size = state[3];
this.used_size = state[4];
this.baseurl = state[5];
this.mounts = [];
for(const mount_state of state[6])
{
const mount = new FSMountInfo(null);
mount.set_state(mount_state);
this.mounts.push(mount);
}
};
@ -341,6 +355,10 @@ function Inode(qidnumber)
this.nlinks = 1;
this.dirty = false; // has this file changed?
// For forwarders:
this.mount_id = -1; // which fs in this.mounts does this inode forward to?
this.foreign_id = -1; // which foreign inode id does it represent?
//this.qid_type = 0;
//this.qid_version = 0;
//this.qid_path = qidnumber;
@ -372,6 +390,8 @@ Inode.prototype.get_state = function()
state[20] = this.caps;
state[21] = this.nlinks;
state[22] = this.dirty;
state[23] = this.mount_id;
state[24] = this.foreign_id;
return state;
};
@ -400,6 +420,8 @@ Inode.prototype.set_state = function(state)
this.caps = state[20];
this.nlinks = state[21];
this.dirty = state[22];
this.mount_id = state[23];
this.foreign_id = state[24];
};
FS.prototype.CreateInode = function() {
@ -611,11 +633,21 @@ FS.prototype.Read = function(inodeid, offset, count)
};
FS.prototype.Search = function(parentid, name) {
var id = this.inodes[parentid].firstid;
const parent_inode = this.inodes[parentid];
if(this.is_forwarder(parent_inode))
{
const foreign_parentid = parent_inode.foreign_id;
const foreign_id = this.follow_fs(parent_inode).Search(foreign_parentid, name);
if(foreign_id === -1) return -1;
return this.get_forwarder(parent_inode.mount_id, foreign_id);
}
var id = parent_inode.firstid;
while(id != -1) {
if (this.inodes[id].parentid != parentid) { // consistency check
message.Debug("Error in Filesystem: Found inode with wrong parent id");
}
dbg_assert(this.GetParent(id) === parentid,
"Error in Filesystem: Found inode (" + this.inodes[id].name + ") with wrong parent id. " +
"Expected: " + parentid + ", Actual: " + this.GetParent(id));
if (this.inodes[id].name == name) return id;
id = this.inodes[id].nextid;
}
@ -763,15 +795,20 @@ FS.prototype.SearchPath = function(path) {
var parentid = 0;
var id = -1;
let forward_path = null;
for(var i=0; i<n; i++) {
id = this.Search(parentid, walk[i]);
if(!forward_path && this.is_forwarder(this.inodes[parentid]))
{
forward_path = "/" + walk.slice(i).join("/");
}
if (id == -1) {
if (i < n-1) return {id: -1, parentid: -1, name: walk[i]}; // one name of the path cannot be found
return {id: -1, parentid: parentid, name: walk[i]}; // the last element in the path does not exist, but the parent
if (i < n-1) return {id: -1, parentid: -1, name: walk[i], forward_path }; // one name of the path cannot be found
return {id: -1, parentid: parentid, name: walk[i], forward_path}; // the last element in the path does not exist, but the parent
}
parentid = id;
}
return {id: id, parentid: parentid, name: walk[i]};
return {id: id, parentid: parentid, name: walk[i], forward_path};
};
// -----------------------------------------------------
@ -988,3 +1025,175 @@ FS.prototype.PrepareCAPs = function(id) {
return inode.caps.length;
};
// -----------------------------------------------------
/**
* @constructor
* @param {FS} filesystem
*/
function FSMountInfo(filesystem)
{
/** @type {FS}*/
this.fs = filesystem;
/** @type {!Map<number,number>} */
this.backtrack = new Map();
}
FSMountInfo.prototype.get_state = function()
{
const state = [];
state[0] = this.fs;
state[1] = [];
for(const entry of this.backtrack.entries())
{
state[1].push(entry);
}
return state;
};
FSMountInfo.prototype.set_state = function(state)
{
this.fs = new FS(undefined);
fs.set_state(state[0]);
this.backtrack = new Map(state[1]);
};
/**
* @private
* @param {number} idx Local idx of inode.
* @param {number} mount_id Mount number of the destination fs.
* @param {number} foreign_id Foreign idx of destination inode.
*/
FS.prototype.set_forwarder = function(idx, mount_id, foreign_id)
{
const inode = this.inodes[idx];
if(this.is_forwarder(inode))
{
this.mounts[inode.mount_id].backtrack.delete(inode.foreign_id);
}
inode.status = STATUS_FORWARDING;
inode.mount_id = mount_id;
inode.foreign_id = foreign_id;
this.mounts[mount_id].backtrack.set(foreign_id, idx);
};
/**
* @private
* @param {number} mount_id Mount number of the destination fs.
* @param {number} foreign_id Foreign idx of destination inode.
* @return {number} Local idx of newly created forwarder.
*/
FS.prototype.create_forwarder = function(mount_id, foreign_id)
{
dbg_assert(foreign_id !== 0, "Filesystem: root forwarder should not be created.");
const inode = this.CreateInode();
const idx = this.inodes.length;
this.inodes.push(inode);
inode.fid = idx;
this.set_forwarder(idx, mount_id, foreign_id);
return idx;
};
/**
* @private
* @param {Inode} inode
* @return {boolean}
*/
FS.prototype.is_forwarder = function(inode)
{
return inode.status === STATUS_FORWARDING;
};
/**
* Ensures forwarder exists, and returns such forwarder, for the described foreign inode.
* @private
* @param {number} mount_id
* @param {number} foreign_id
* @return {number} Local idx of a forwarder to described inode.
*/
FS.prototype.get_forwarder = function(mount_id, foreign_id)
{
const mount = this.mounts[mount_id];
dbg_assert(foreign_id >= 0, "Filesystem get_forwarder: invalid foreign_id: " + foreign_id);
dbg_assert(mount, "Filesystem get_forwarder: invalid mount number: " + mount_id);
const result = mount.backtrack.get(foreign_id);
if(result === undefined)
{
// Create if not already exists.
return this.create_forwarder(mount_id, foreign_id);
}
return result;
};
/**
* @private
* @param {Inode} inode
*/
FS.prototype.delete_forwarder = function(inode)
{
dbg_assert(this.is_forwarder(inode), "Filesystem delete_forwarder: expected forwarder");
inode.status = STATUS_INVALID;
this.mounts[inode.mount_id].backtrack.delete(inode.foreign_id);
};
/**
* @private
* @param {Inode} inode
* @return {FS}
*/
FS.prototype.follow_fs = function(inode)
{
const mount = this.mounts[inode.mount_id];
dbg_assert(this.is_forwarder(inode),
"Filesystem follow_fs: inode should be a forwarding inode");
dbg_assert(mount, "Filesystem follow_fs: inode<id=" + inode.fid + ", name=" +
inode.name + "> should point to valid mounted FS");
return mount.fs;
};
/**
* Mount another filesystem onto an existing directory.
* @param {string} path Path to existing directrory relative to this filesystem.
* @param {FS} fs
*/
FS.prototype.Mount = function(path, fs)
{
const path_infos = this.SearchPath(path);
if(path_infos.id === -1)
{
dbg_log("Mount failed: path not found: " + path, LOG_9P);
return -1;
}
if(path_infos.forward_path)
{
const parent = this.inodes[path_infos.parentid];
this.follow_fs(parent).Mount(path_infos.forward_path, fs);
return;
}
const mount_id = this.mounts.length;
this.mounts.push(new FSMountInfo(fs));
// Existing inode is already linked. Just set forwarding information.
this.set_forwarder(path_infos.id, mount_id, 0);
return path_infos.id;
};