Introduce forwarding inodes. Implement fs.Mount
Add forward_path info to fs.SearchPath. Handle forwarding for fs.Search.
This commit is contained in:
parent
070c38c6a1
commit
e6ef68b334
1 changed files with 216 additions and 7 deletions
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue