jam/js/dos/afs_srv_rpc.js

1623 lines
57 KiB
JavaScript

/**
** ==============================
** O O O OOOO
** O O O O O O
** O O O O O O
** OOOO OOOO O OOO OOOO
** O O O O O O O
** O O O O O O O
** OOOO OOOO O O OOOO
** ==============================
** Dr. Stefan Bosse http://www.bsslab.de
**
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
** BY THE AUTHOR(S).
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
** OUTSIDE OF THE SOFTWARE SYSTEM.
**
** $AUTHORS: Stefan Bosse
** $INITIAL: (C) 2015-2017 bLAB
** $CREATED: 6-7-15.
** $VERSION: 1.2.2
**
** $INFO:
* AFS Server and RPC Interface
*
** $ENDOFINFO
*/
/*
*********************
** PUBLIC INTERFACE
*********************
*/
"use strict";
var log = 0;
var util = Require('util');
var Io = Require('com/io');
var Net = Require('dos/network');
var Status = Net.Status;
var Command = Net.Command;
var Rights = Net.Rights;
var Std = Require('dos/std');
var Sch = Require('dos/scheduler');
var Buf = Require('dos/buf');
var Rpc = Require('dos/rpc');
var Fcache = Require('dos/fcache');
var Flist = Require('dos/freelist');
var Fs = Require('fs');
var Comp = Require('com/compat');
var String = Comp.string;
var Array = Comp.array;
var Perv = Comp.pervasives;
var Hashtbl = Comp.hashtbl;
var assert = Comp.assert;
var div = Comp.div;
var Afs = Require('dos/afs_srv_common');
var Afs_file_state = Afs.Afs_file_state;
var Afs_commit_flag= Afs.Afs_commit_flag;
/**
* @augments afs_server
* @augments afs_server_emb
* @see afs_server
* @see afs_server_emb
* @see afs_server_rpc~meth
* @constructor
*/
var afs_server_rpc = function() {
};
/**
* @typedef {{
* afs_create_file:afs_server_rpc.afs_create_file,
* afs_modify_file:afs_server_rpc.afs_modify_file,
* afs_read_file:afs_server_rpc.afs_read_file,
* afs_file_size:afs_server_rpc.afs_file_size,
* afs_touch_file:afs_server_rpc.afs_touch_file,
* afs_age:afs_server_rpc.afs_age,
* afs_insert_data:afs_server_rpc.afs_insert_data,
* afs_delete_data:afs_server_rpc.afs_delete_data,
* afs_destroy_file:afs_server_rpc.afs_destroy_file,
* afs_info:afs_server_rpc.afs_info,
* afs_exit:afs_server_rpc.afs_exit,
* afs_stat:afs_server_rpc.afs_stat,
* afs_start_server:afs_server_rpc.afs_start_server
* }} afs_server_rpc~meth
*/
/** AFS server create request.
** Create a new file.
**
** Args:
** priv: the request private field
** buf: the write buffer
** size: the initial file size [bytes] (can be 0: only object creation)
** commit: the commit flag
**
** Return:
** status
** newcap
**
*
* @param {privat} priv
* @param {buffer|rpcio} buf
* @param {number} size
* @param {Afs_commit_flag} commit
* @param {function((Status.STD_OK|*),capability|undefined)} callback
*/
afs_server_rpc.prototype.afs_create_file =function (priv,buf,size,commit,callback) {
var self=this;
var stat=Status.STD_UNKNOWN;
var file;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
if (obj > 0) {
/*
** It's a file, used only for authorization.
*/
self.acquire_file(obj, function (_stat, _file) {
stat = _stat;
file = _file;
});
} else {
file=undefined;
if (Net.prv_decode(priv, self.afs_super.afs_checkfield) == false ||
Net.rights_req(rights, [Rights.AFS_RGT_CREATE]) == false)
stat = Status.STD_DENIED;
else stat = Status.STD_OK;
}
},
function () {
if (stat != Status.STD_OK)
throw stat;
if (file!=undefined) {
if (Net.prv_decode(priv, file.ff_random) == false ||
Net.rights_req(rights, [Rights.AFS_RGT_CREATE]) == false) {
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
throw Status.STD_DENIED;
}
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
}
/*
** First get a new i-node==object number
*/
var objnew = self.get_freeobjnum();
file = Afs.Afs_file(objnew,Net.uniqport(),size, // initial size
self.time(),Afs.AFS_MAXLIVE,
Afs_file_state.FF_unlocked);
file.lock();
var final = commit > 0;
stat = self.create_file_inode(file,final);
if (stat != Status.STD_OK) {
file.unlock();
throw stat;
}
var cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(objnew,Rights.PRV_ALL_RIGHTS,file.ff_random));
self.touch(file);
if (size>0) {
stat=self.modify_file(file,0,size,buf);
}
if (stat != Status.STD_OK) {
file.unlock();
throw stat;
}
stat = self.release_file(file,commit);
//Io.out(file.print());
if (stat != Status.STD_OK) {
file.unlock();
throw stat;
}
callback(stat,cap);
}
],function (e) {
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_create_file');}
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
});
};
/** AFS server modify request.
** Morify an existing file.
**
** Args:
** priv: the request private field
** buf: the write buffer
** size: the initial file size [bytes] (can be 0: only object creation)
** commit: the commit flag
**
** Return:
** status
** newcap
**
*
* @param {privat} priv
* @param {buffer|rpcio} buf
* @param {number} off
* @param {number} size
* @param {(Afs_commit_flag.AFS_UNCOMMIT|*)} commit
* @param {function((Status.STD_OK|*),capability|undefined)} callback
*/
afs_server_rpc.prototype.afs_modify_file =function (priv,buf,off,size,commit,callback) {
var self=this;
var stat=Status.STD_UNKNOWN;
var file=undefined;
var file2=undefined;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Io.log((log<1)||('afs_server_rpc.afs_modify_file: obj='+obj+' off='+off+' size='+size+' commit='+Afs.Afs_commit_flag.print(commit)));
if (off<0 || size<0) callback(Status.STD_ARGBAD,undefined);
else Sch.ScheduleBlock([
function () {
if (obj > 0) {
/*
*/
self.acquire_file(obj, function (_stat, _file) {
stat = _stat;
file = _file;
});
} else {
stat = Status.STD_CAPBAD;
}
},
function () {
if (stat != Status.STD_OK)
throw stat;
if (Net.prv_decode(priv, file.ff_random) == false ||
Net.rights_req(rights, [Rights.AFS_RGT_MODIFY]) == false) {
throw Status.STD_DENIED;
}
var cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj,Rights.PRV_ALL_RIGHTS,file.ff_random));
if (size==0 && file.ff_state == Afs.Afs_file_state.FF_unlocked) {
/*
** Only commit the file.
*/
stat = self.release_file(file, commit);
file=undefined;
} else if (size==0 && (file.ff_state == Afs.Afs_file_state.FF_locked||
file.ff_state == Afs.Afs_file_state.FF_commit)) {
/*
** Only unlocked files can be committed!
** Or must we create a copy here?
*/
throw Status.STD_ARGBAD;
} else {
/*
** The file exists, the request is authorized, modify content.
*/
if (file.ff_state == Afs.Afs_file_state.FF_unlocked) {
/*
** New data beyond the current file size ?
*/
if (file.ff_size < (off+size)) {
stat=self.modify_size(file,(off+size));
if (stat!=Status.STD_OK) throw stat;
}
stat=self.modify_file(file,off,size,buf);
if (stat!=Status.STD_OK) throw stat;
stat = self.release_file(file, commit);
if (stat!=Status.STD_OK) throw stat;
file=undefined;
} else {
/*
** Create a new file. Copy the content of the
** original one. Modify the new one.
*/
/*
** First get a new i-node==object number
*/
var objnew = self.get_freeobjnum();
var size2;
if (file.ff_size < off+size) size2=off+size; else size2=file.ff_size;
file2 = Afs.Afs_file(objnew,Net.uniqport(),size2, // initial size
self.time(),Afs.AFS_MAXLIVE,
Afs_file_state.FF_unlocked);
file2.lock();
if (stat != Status.STD_OK) {
file.unlock();
throw stat;
}
cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(objnew,Rights.PRV_ALL_RIGHTS,file2.ff_random));
var buf2=Buf.Buffer();
var cs = Perv.min(file.ff_size,1000);
var co = 0;
while (cs>0) {
stat=self.read_file(file,co,cs,buf2);
if (stat!=Status.STD_OK) {
file.unlock();
throw stat;
}
stat=self.modify_file(file2,co,cs,buf2);
if (stat!=Status.STD_OK) {
file.unlock();
throw stat;
}
co=Perv.min(co+cs,file.ff_size);
cs=Perv.min(file.ff_size-co,1000);
}
stat=self.modify_file(file2,off,size,buf);
if (stat!=Status.STD_OK) {
file2.unlock();
throw stat;
}
stat = self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
stat = self.release_file(file2,commit);
if (stat!=Status.STD_OK) {
file2.unlock();
throw stat;
}
}
}
if (stat != Status.STD_OK) {
throw stat;
}
callback(stat,cap);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_modify_file');}
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
});
};
/**
*
* @param {privat} priv
* @param {buffer|rpcio} buf
* @param off
* @param size
* @param {function((Status.STD_OK|*),number)} callback return status,size
*/
afs_server_rpc.prototype.afs_read_file =function (priv,buf,off,size,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var file = undefined;
var size=size;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
if (obj > 0) {
/*
** It's a file
*/
self.acquire_file(obj, function (_stat, _file) {
stat = _stat;
file = _file;
});
} else
stat=Status.STD_CAPBAD;
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if ((Net.prv_decode(priv, file.ff_random) == false) ||
(Net.rights_req(rights,[Rights.AFS_RGT_READ])== false)) {
throw Status.STD_DENIED;
}
Io.log((log<1)||('afs_read_file off='+off+' ff_size='+file.ff_size));
if (off > file.ff_size) {
throw Status.STD_ARGBAD;
}
if ((size+off) > file.ff_size) size=file.ff_size-off;
stat=self.read_file(file,off,size,buf);
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
callback(stat,size);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_read_file');}
if (typeof e == 'number') callback(e,0); else callback(Status.STD_SYSERR,0);
});
};
/**
** AFS server size request.
**
**
*
* @param {private} priv
* @param {function((Status.STD_OK|*),number)} callback return status,size
*/
afs_server_rpc.prototype.afs_file_size =function (priv,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var file = undefined;
var size=0;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
if (obj > 0) {
/*
** It's a file
*/
self.acquire_file(obj, function (_stat, _file) {
stat = _stat;
file = _file;
});
} else
stat=Status.STD_CAPBAD;
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if ((Net.prv_decode(priv, file.ff_random) == false)) {
throw Status.STD_DENIED;
}
size=file.ff_size;
stat=Status.STD_OK;
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
callback(stat,size);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_file_size');}
if (typeof e == 'number') callback(e,0); else callback(Status.STD_SYSERR,0);
});
};
/**
** Touch file (update live time)
*
* @param {private} priv
* @param {function((Status.STD_OK|*))} callback return status
*/
afs_server_rpc.prototype.afs_touch_file =function (priv,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var file = undefined;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
self.acquire_file(obj,function (_stat,_file) {
stat=_stat;
file=_file;
});
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if (Net.prv_decode(priv, file.ff_random) == false) {
throw Status.STD_DENIED;
}
self.touch(file);
stat=Status.STD_OK;
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
callback(stat);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_touch_file');}
if (typeof e == 'number') callback(e); else callback(Status.STD_SYSERR);
});
};
/**
** Age a file (decrement live time)
*
* @param {private} priv
* @param {function((Status.STD_OK|*))} callback return status
*/
afs_server_rpc.prototype.afs_age_file =function (priv,callback) {
var self = this,
stat = Status.STD_UNKNOWN,
file = undefined,
obj = Net.prv_number(priv),
rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
self.acquire_file(obj,function (_stat,_file) {
stat=_stat;
file=_file;
});
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if (Net.prv_decode(priv, file.ff_random) == false) {
throw Status.STD_DENIED;
}
self.age_file(file);
stat=Status.STD_OK;
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
callback(stat);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_age_file');}
if (typeof e == 'number') callback(e); else callback(Status.STD_SYSERR);
});
};
/**
** Age all files (decrement live time)
*
* @param {function((Status.STD_OK|*))} callback return status
*/
afs_server_rpc.prototype.afs_age =function (priv,callback) {
var self = this,
stat = Status.STD_UNKNOWN,
obj = Net.prv_number(priv),
rights = Net.prv_rights(priv);
if (obj!=0 || !Net.prv_rights_check(priv, self.afs_super.afs_checkfield, Rights.AFS_RGT_MODIFY))
callback(Status.STD_DENIED);
else self.age(callback);
};
/**
** Restrict file capability
*
* @param {private} priv
* @param {number} mask
* @param {function((Status.STD_OK|*),(privat|undefined))} callback return status
*/
afs_server_rpc.prototype.afs_restrict =function (priv,mask,callback) {
var self = this;
var privres=undefined;
var stat = Status.STD_UNKNOWN;
var file = undefined;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
if (obj==0) {
if (Net.prv_decode(priv, self.afs_super.afs_checkfield) == false) {
stat= Status.STD_DENIED;
} else {
stat=Status.STD_OK;
privres=Net.prv_encode(obj,mask,self.afs_super.afs_checkfield);
}
callback(stat,privres);
}
else Sch.ScheduleBlock([
function () {
self.acquire_file(obj,function (_stat,_file) {
stat=_stat;
file=_file;
});
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if (Net.prv_decode(priv, file.ff_random) == false) {
throw Status.STD_DENIED;
}
stat=Status.STD_OK;
privres=Net.prv_encode(obj,mask,file.ff_random);
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
callback(stat,privres);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_restrict');}
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
});
};
/** AFS server Insert request.
**
** Args:
** server: the server structure
** priv: the request private field
** buf: the write buffer
** size: the requested size (<= file size) [bytes]
** off: the file offset (<= file size) [bytes]
** commit: the commit flag
**
** Return:
** status
** newcap (FF_unlocked -> oldcap)
*
* @param {privat} priv
* @param {buffer|rpcio} buf
* @param off
* @param size
* @param commit
* @param {function((Status.STD_OK|*),capability|undefined)} callback
*/
afs_server_rpc.prototype.afs_insert_data =function (priv,buf,off,size,commit,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var cap=undefined;
var file;
var size2,buf2,off2,obj2,file2;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
if (obj > 0) {
self.acquire_file(obj,function (_stat,_file) {
stat = _stat;
file = _file;
});
} else stat=Status.STD_CAPBAD;
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if ((Net.prv_decode(priv, file.ff_random) == false) ||
(Net.rights_req(rights, [Rights.AFS_RGT_MODIFY]) == false)) {
throw Status.STD_DENIED;
}
if (size == 0 && file.ff_state == Afs_file_state.FF_unlocked) {
/*
** Only commit the file.
*/
stat = self.release_file(file, commit);
if (stat != Status.STD_OK) {
throw stat;
}
cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj, Rights.PRV_ALL_RIGHTS, file.ff_random));
stat = Status.STD_OK;
} else if (size == 0 && (file.ff_state == Afs_file_state.FF_locked ||
file.ff_state == Afs_file_state.FF_commit)) {
/*
** Only unlocked files can be committed!
** Or must we create a copy here?
*/
self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
stat=Status.STD_ARGBAD;
} else {
/*
** The file exists, and the request is authorized.
*/
if (file.ff_state == Afs_file_state.FF_unlocked) {
/*
** Modify existing file.
*/
/*
** New data beyond the current file size ?
*/
if (file.ff_size < (off+size))
stat=self.modify_size(file,off+size);
else stat=Status.STD_OK;
if (stat != Status.STD_OK) {
self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
throw stat;
}
cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj, Rights.PRV_ALL_RIGHTS, file.ff_random));
if ((off+size) < file.ff_size) {
/*
** The data should be inserted in the middle
** or at the beginning of the file. Therefore, we must
** shift the data between this offset (off) and
** (off+size) to create the needed space.
*/
buf2 = Buf.Buffer(self.afs_super.afs_block_size);
size2 = Perv.min(self.afs_super.afs_block_size,size);
off2 = (file.ff_size - size2);
/*
** Start at the current end of the file position and
** work down to the specified insert offset (off).
*/
while ( off2 >= (off+size)) {
/*
** First the chunk to read
*/
stat = self.read_file(file, off2, size2, buf2);
if (stat != Status.STD_OK) {
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
throw stat;
}
/*
** Now write the buffer to the shifted
** position.
*/
stat = self.modify_file(file, (off2 + size), size2, buf2);
if (stat != Status.STD_OK) {
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
throw stat;
}
size2 = Perv.min((off2 - off), self.afs_super.afs_block_size);
off2 = Perv.max((off2 - size2), off);
}
}
/*
** Now write the data to be inserted.
*/
stat=self.modify_file(file,off,size,buf);
if (stat != Status.STD_OK) {
throw stat;
}
stat=self.release_file(file,commit);
file=undefined;
if (stat != Status.STD_OK) {
throw stat;
}
} else {
/*
** Create a new file, copy the content of the
** original one, but shift the part from (off)
** to (off+size).
*/
obj2 = self.get_freeobjnum();
size2 = file.ff_size< (off+size)?(off+size):file.ff_size;
file2 = Afs.Afs_file(obj2,Net.uniqport(),size2,self.time(),Afs.AFS_MAXLIVE,
Afs_file_state.FF_unlocked,Afs.Disk(),true);
// Non-blocking, we're the first one accessng this file
file2.lock();
stat= self.create_file_inode(file2,false);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
cap =
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj2, Rights.PRV_ALL_RIGHTS, file2.ff_random));
/*
** Copy the original data
*/
buf2 = Buf.Buffer(self.afs_super.afs_block_size);
var co = 0;
var cs = Perv.min(off,self.afs_super.afs_block_size);
/*
** First the part up to the off position.
** (Not shifted).
*/
while (cs>0) {
/*
** First the chunk to read
*/
stat =self.read_file(file,co,cs,buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
/*
** Now write the buffer
*/
stat = self.modify_file(file2,co,cs,buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
co = co + cs;
cs = Perv.min((off - co),self.afs_super.afs_block_size);
}
/*
** Now the shifted part up to the end of the file.
*/
cs = Perv.min((file.ff_size - co), self.afs_super.afs_block_size);
while (cs > 0) {
/*
** First the chunk to read
*/
stat = self.read_file(file, co, cs, buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
/*
** Now write the buffer to the shifted position.
*/
stat = self.modify_file(file2, (co + off + size), cs, buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
co = co + cs;
cs = Perv.min((file.ff_size - co), self.afs_super.afs_block_size);
}
/*
** Now write the data to be inserted.
*/
stat = self.modify_file(file2,off,size,buf);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
stat = self.release_file(file2,commit);
if (stat != Status.STD_OK) {
throw stat;
}
self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
}
}
callback(stat, cap);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_insert_data');}
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
});
};
/** AFS server Delete (part of the file data) request.
*
* Args:
* server: the server structure
* priv: the request private field
* size: the requested size (<= file size) [bytes]
* off: the file offset (<= file size) [bytes]
* commit: the commit flag
*
* Return:
* status
* newcap (FF_unlocked -> oldcap)
*
* @param {privat} priv
* @param off
* @param size
* @param commit
* @param {function((Status.STD_OK|*),capability|undefined)} callback
*/
afs_server_rpc.prototype.afs_delete_data =function (priv,off,size,commit,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var file=undefined;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
var cap;
var off2,size2,buf2,obj2,file2;
Sch.ScheduleBlock([
function () {
if (obj > 0) {
self.acquire_file(obj,function (_stat,_file) {
stat = _stat;
file = _file;
});
} else
stat=Status.STD_CAPBAD;
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if ((Net.prv_decode(priv, file.ff_random) == false) ||
(Net.rights_req(rights,[Rights.AFS_RGT_MODIFY])== false)) {
throw Status.STD_DENIED;
}
if (file.ff_state == Afs_file_state.FF_unlocked) {
cap=
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj,Rights.PRV_ALL_RIGHTS,file.ff_random));
buf2=Buf.Buffer(self.afs_super.afs_block_size);
size2 = Perv.min(file.ff_size - off + size, self.afs_super.afs_block_size);
off2 = (off+size);
while (off2 < file.ff_size) {
/*
** First the chunk to read
*/
stat = self.read_file(file, off2, size2, buf2);
if (stat != Status.STD_OK) {
throw stat;
}
/*
** Now write the buffer to the shifted
** position.
*/
stat = self.modify_file(file, (off2 - size), size2, buf2);
if (stat != Status.STD_OK) {
throw stat;
}
off2 = off2 + size2;
size2 = Perv.min(file.ff_size - off2, self.afs_super.afs_block_size);
}
/*
** Adjust the new size.
*/
self.modify_size(file,(file.ff_size - size));
stat = self.release_file(file,commit);
file=undefined;
if (stat != Status.STD_OK) {
throw stat;
}
} else {
/*
** Create a new file, copy the content of the
** original one, but shift the part from (off+size)
** to (off).
*/
/*
** First get a new i-node==object number
*/
obj2=self.get_freeobjnum();
size2 = file.ff_size - size;
file2 = Afs.Afs_file(obj2,Net.uniqport(),size2,self.time(),Afs.AFS_MAXLIVE,
Afs_file_state.FF_unlocked,Afs.Disk(),true);
file2.lock();
stat = self.create_file_inode(file2,false);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
cap=
Net.Capability(self.afs_super.afs_putport,
Net.prv_encode(obj2,Rights.PRV_ALL_RIGHTS,file2.ff_random));
/*
** Copy the original data
*/
buf2 = Buf.Buffer(self.afs_super.afs_block_size);
var co = 0;
var cs = Perv.min(off,self.afs_super.afs_block_size);
/*
** First the part up to the off position.
** (Not shifted).
*/
while (cs>0) {
/*
** First the chunk to read
*/
stat = self.read_file(file,co,cs,buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
/*
** Now write the buffer
*/
stat = self.modify_file(file2,co,cs,buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
co = co + cs;
cs = Perv.min((off - co),self.afs_super.afs_block_size);
}
/*
** Now the shifted part up to the end of the file.
*/
cs = Perv.min((file2.ff_size - co), self.afs_super.afs_block_size);
while (co < file2.ff_size) {
/*
** First the chunk to read
*/
stat = self.read_file(file, (co + size), cs, buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
/*
** Now write the buffer to the shifted position.
*/
stat = self.modify_file(file2, co, cs, buf2);
if (stat != Status.STD_OK) {
file2.unlock();
throw stat;
}
co = co + cs;
cs = Perv.min((file2.ff_size - co), self.afs_super.afs_block_size);
}
stat = self.release_file(file2, commit);
if (stat != Status.STD_OK) {
throw stat;
}
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
}
stat=Status.STD_OK;
callback(stat,cap);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_delete_data');}
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
});
};
/** Destroy a file object.
*
* @param {privat} priv
* @param {function((Status.STD_OK|*))} callback return status
*/
afs_server_rpc.prototype.afs_destroy_file =function (priv,callback) {
var self = this;
var stat = Status.STD_UNKNOWN;
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
var file;
Sch.ScheduleBlock([
function () {
if (obj > 0) {
self.acquire_file(obj,function (_stat,_file) {
stat = _stat;
file = _file;
});
} else
stat=Status.STD_CAPBAD;
},
function () {
if ((Net.prv_decode(priv, file.ff_random) == false) ||
(Net.rights_req(rights,[Rights.AFS_RGT_DESTROY])== false)) {
throw Status.STD_DENIED;
}
if (file.ff_state == Afs_file_state.FF_unlocked) {
stat = self.delete_file_inode(file);
if (stat != Status.STD_OK) {
throw stat;
}
stat = Status.STD_OK;
} else {
stat = self.delete_file_inode(file);
if (stat != Status.STD_OK) {
throw stat;
}
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
stat = Status.STD_OK;
}
callback(stat);
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_destroy_file');}
if (typeof e == 'number') callback(e); else callback(Status.STD_SYSERR);
});
};
/** AFS Info Request
*
* @param {privat} priv
* @param {function((Status.STD_OK|*),string)} callback return status,info
*/
afs_server_rpc.prototype.afs_info =function (priv,callback) {
var self=this;
var stat;
var file;
var info='';
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
Sch.ScheduleBlock([
function () {
if (obj > 0) {
self.acquire_file(obj,function (_stat,_file) {
stat = _stat;
file = _file;
});
} else {
file=undefined;
stat=Status.STD_OK;
}
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if (file!=undefined) {
if ((Net.prv_decode(priv, file.ff_random) == false)) {
throw Status.STD_DENIED;
}
info = '- '+file.ff_size+' bytes';
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
stat = Status.STD_OK;
} else {
/*
** Super capability
*/
if ((Net.prv_decode(priv, self.afs_super.afs_checkfield) == false)) {
throw Status.STD_DENIED;
}
info='Atomic Filesystem Server';
stat=Status.STD_OK;
}
callback(stat,info)
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e != 'number') {Io.printstack(e,'Afs_server_rpc.afs_info');}
if (typeof e == 'number') callback(e,''); else callback(Status.STD_SYSERR,'');
});
};
/** AFS Exit Request
* @param {privat} [priv]
*
* @returns {(Status.STD_OK|*)}
*/
afs_server_rpc.prototype.afs_exit =function (priv) {
var self=this;
var stat=Status.STD_OK;
if (priv!=undefined) {
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
if (obj!=0 || !Net.prv_decode(priv,self.afs_super.afs_checkfield)) return Status.STD_DENIED;
}
Io.out('[AFS] Writing live table...');
stat=self.live_write();
return stat;
};
/** AFS Synchronize Request
*
* @returns {Status.STD_OK|*}
*/
afs_server_rpc.prototype.afs_sync =function () {
var self=this;
var stat,stat1,stat2;
Io.out('[AFS] Syncing discs...');
self.afs_super.lock();
self.inode_cache_entry.fse_state = Afs_file_state.FF_locked;
stat1=self.cache_inode.cache_sync();
stat2=self.cache_data.cache_sync();
self.inode_cache_entry.fse_state = Afs_file_state.FF_unlocked;
Io.out('[AFS] Compacting freelist...');
self.freeblocks.free_compact();
/*
** Synchronize disk
*/
self.disk_sync();
self.afs_super.unlock();
if (stat1 != Status.STD_OK) return stat1; else return stat2;
};
/** AFS Status Request
*
* @param {privat} [priv]
* @param {function((Status.STD_OK|*),string)} [callback] return status,info
* @returns {string}
*/
afs_server_rpc.prototype.afs_stat = function (priv,callback) {
var self=this;
var stat;
function printstat() {
var block_size = self.afs_super.afs_block_size;
var str = '';
var res = self.freeblocks.free_info();
var holes = res[0];
var freespace = res[1];
var holestat = res[2];
var usedspace = self.afs_super.afs_nblocks - freespace;
var uncomm = 0;
var unlock = 0;
Hashtbl.iter(self.cache_data.fsc_table, function (addr, fse) {
if (fse.fse_state == Afs_file_state.FF_unlocked) unlock++;
if (fse.fse_state == Afs_file_state.FF_commit) uncomm++;
});
str = str + 'Cache statistics: Inode Cache\n' + self.cache_inode.cache_stat() + '\n';
str = str + 'Cache statistics: Data Cache\n' + self.cache_data.cache_stat() + '\n';
str = str + 'File system statistics\n';
str = str + ' Free: ' + (freespace * block_size) + ' bytes\n';
str = str + ' Used: ' + (usedspace * block_size) + ' bytes\n';
str = str + ' Files: ' + (self.afs_super.afs_nused) + '\n';
str = str + ' Uncommited: ' + (uncomm) + '\n';
str = str + ' Unlocked: ' + (unlock) + '\n';
str = str + 'Server statistics\n';
str = str + ' Read: ' + (self.stats.op_read) + '\n';
str = str + ' Modify: ' + (self.stats.op_modify) + '\n';
str = str + ' Create: ' + (self.stats.op_create) + '\n';
str = str + ' Touch: ' + (self.stats.op_touch) + '\n';
str = str + ' Age: ' + (self.stats.op_age) + '\n';
str = str + ' Destroy: ' + (self.stats.op_destroy) + '\n';
str = str + 'Free list statistics\n';
str = str + ' Comp. Thr.: ' + (self.freeblocks.fbs_compacthres) + '\n';
str = str + ' Holes: ' + (holes) + '\n';
for (var i = 0; i < holestat.length; i++) {
var hole = holestat[i];
var low, high, count;
low = hole[0];
high = hole[1];
count = hole[2];
str = str + ' Free holes from ' + (low * block_size) + ' to ' + (high * block_size) + ' bytes: ' + count + '\n';
}
self.afs_super.unlock();
return str;
}
if (!self.afs_super.try_lock()) {
if (callback) {
callback(Status.STD_NOTNOW,'');
return '';
}
else return '';
}
if (priv!=undefined) {
var obj = Net.prv_number(priv);
var rights = Net.prv_rights(priv);
var file;
Sch.ScheduleBlock([
function () {
if (obj > 0) {
self.acquire_file(obj,function (_stat,_file) {
stat = _stat;
file = _file;
});
} else {
file=undefined;
stat=Status.STD_OK;
}
},
function () {
if (stat != Status.STD_OK) {
throw stat;
}
if (file!=undefined) {
if ((Net.prv_decode(priv, file.ff_random) == false)) {
self.release_file(file, Afs_commit_flag.AFS_UNCOMMIT);
throw Status.STD_DENIED;
}
self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
file=undefined;
} else {
if ((Net.prv_decode(priv, self.afs_super.afs_checkfield) == false)) {
throw Status.STD_DENIED;
}
}
callback(stat,printstat());
}
],function (e) {
if (file!=undefined) self.release_file(file,Afs_commit_flag.AFS_UNCOMMIT);
if (typeof e == 'number') callback(e,''); else {
Io.printstack(e,'afs_srv_rpc.afs_stat');
callback(Status.STD_SYSERR,'');
}
});
return '';
} else {
return printstat();
}
};
/** AFS Start the RPC server
*
* @param {number} index
* @param {taskscheduler} scheduler
*/
afs_server_rpc.prototype.afs_start_server = function (index,scheduler) {
var afs = this;
var server = function () {
// Server
var self = this;
var rpc = afs.rpc;
var port = afs.afs_super.afs_getport;
var router = rpc.router;
var rpcio = router.pkt_get();
var dying=false;
this.init = function () {
Io.out('[AFS'+index+'] Starting RPC Server with public port '+Net.Print.port(afs.afs_super.afs_putport));
router.add_port(port);
};
this.request = function () {
Io.log((log<10)||('[AFS'+index+'] request'));
rpcio.init();
rpcio.operation = Rpc.Operation.GETREQ;
rpcio.header.h_port = port;
rpcio.header.h_status=undefined;
rpcio.header.h_command=undefined;
rpcio.header.h_priv=undefined;
rpc.getreq(rpcio);
};
this.service = function () {
var ss,sc,stat,info,size,flag,off,buf;
buf=Buf.Buffer();
Io.log((log<1)||('[AFS'+index+'] service: '+Net.Print.header(rpcio.header)));
assert((rpcio.index!=-1) ||'RPCIO invalid');
switch (rpcio.header.h_command) {
case Command.AFS_SIZE:
/*
** +---
* |===
* | size(int32)
* +---
*/
Sch.ScheduleBlock([
function () {
afs.afs_file_size(rpcio.header.h_priv,function (_stat,_size) {
rpcio.header.h_status=_stat;
Buf.buf_init(rpcio);
Buf.buf_put_int32(rpcio,_size);
});
}
]);
break;
case Command.AFS_CREATE:
/*
* +---
* | off(int32)
* | size(int32)
* | flag(int16)
* | data
* |===
* +---
*/
rpcio.pos=0;
off = Buf.buf_get_int32(rpcio);
size = Buf.buf_get_int32(rpcio);
flag = Buf.buf_get_int16(rpcio);
Buf.buf_init(buf);
Buf.buf_get_buf(rpcio,buf,0,size);
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_create_file(rpcio.header.h_priv, buf, size, flag, function (_stat, _cap) {
if (_stat == Status.STD_OK) Net.Copy.private(_cap.cap_priv, rpcio.header.h_priv);
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.AFS_MODIFY:
/*
* +---
* | off(int32)
* | size(int32)
* | flag(int16)
* | data
* |===
* +---
*/
rpcio.pos=0;
off = Buf.buf_get_int32(rpcio);
size = Buf.buf_get_int32(rpcio);
flag = Buf.buf_get_int16(rpcio);
Buf.buf_init(buf);
Buf.buf_get_buf(rpcio,buf,0,size);
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_modify_file(rpcio.header.h_priv, buf, off, size, flag, function (_stat, _cap) {
if (_stat == Status.STD_OK) Net.Copy.private(_cap.cap_priv, rpcio.header.h_priv);
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.AFS_READ:
/*
** +---
* | off(int32)
* | size(int32)
* |===
* | size(int32)
* | data
* +---
*/
rpcio.pos=0;
off = Buf.buf_get_int32(rpcio);
size = Buf.buf_get_int32(rpcio);
Io.log((log<1)||('AFS_READ: off='+off+' size='+size));
Buf.buf_init(rpcio);
Buf.buf_init(buf);
Sch.ScheduleBlock([
function () {
afs.afs_read_file(rpcio.header.h_priv,buf,off,size,function (_stat,_size) {
Buf.buf_put_int32(rpcio,_size);
Buf.buf_put_buf(rpcio,buf);
rpcio.header.h_status=_stat;
});
}]);
break;
case Command.AFS_INSERT:
/*
** +---
* | off(int32)
* | size(int32)
* | flag(int16)
* | data
* |===
* +---
*/
rpcio.pos=0;
off = Buf.buf_get_int32(rpcio);
size = Buf.buf_get_int32(rpcio);
flag = Buf.buf_get_int16(rpcio);
Buf.buf_init(buf);
Buf.buf_get_buf(rpcio,buf,0,size);
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_insert_data(rpcio.header.h_priv, buf, off, size, flag, function (_stat, _cap) {
if (_stat == Status.STD_OK) Net.Copy.private(_cap.cap_priv, rpcio.header.h_priv);
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.AFS_DELETE:
/*
** +---
* | off(int32)
* | size(int32)
* | flag(int16)
* |===
* +---
*/
rpcio.pos=0;
off = Buf.buf_get_int32(rpcio);
size = Buf.buf_get_int32(rpcio);
flag = Buf.buf_get_int16(rpcio);
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_delete_data(rpcio.header.h_priv,off,size,flag,function(_stat,_cap) {
if (_stat==Status.STD_OK) Net.Copy.private(_cap.cap_priv,rpcio.header.h_priv);
rpcio.header.h_status=sc.stat;
});
}]);
break;
case Command.AFS_SYNC:
/*
** +---
* |===
* +---
*/
stat = afs.afs_sync();
rpcio.header.h_status=stat;
break;
case Command.STD_DESTROY:
/*
** +---
* |===
* +---
*/
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_destroy_file(rpcio.header.h_priv, function (_stat) {
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.STD_INFO:
/*
** +---
* |===
* | info(string)
* +---
*/
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_info(rpcio.header.h_priv, function (_stat, _info) {
rpcio.header.h_status = _stat;
if (_stat == Status.STD_OK) Buf.buf_put_string(rpcio, _info);
});
}]);
break;
case Command.STD_STATUS:
/*
** +---
* |===
* | info(string)
* +---
*/
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_stat(rpcio.header.h_priv, function (_stat, _info) {
rpcio.header.h_status = _stat;
if (_stat == Status.STD_OK) Buf.buf_put_string(rpcio, _info);
});
}]);
break;
case Command.STD_RESTRICT:
/*
* ----------
* mask (int16)
* ----------
* priv (privat)
* ----------
*/
var mask = Buf.buf_get_int16(rpcio);
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_restrict(rpcio.header.h_priv, mask, function (_stat, _priv) {
rpcio.header.h_status = _stat;
if (_stat == Status.STD_OK) Buf.buf_put_priv(rpcio, _priv);
});
}]);
break;
case Command.STD_TOUCH:
/*
** +---
* |===
* +---
*/
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_touch_file(rpcio.header.h_priv, function (_stat) {
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.STD_AGE:
/*
** +---
* |===
* +---
*/
Buf.buf_init(rpcio);
Sch.ScheduleBlock([
function () {
afs.afs_age(rpcio.header.h_priv, function (_stat) {
rpcio.header.h_status = _stat;
});
}]);
break;
case Command.STD_EXIT:
/*
** +---
* |===
* +---
*/
Buf.buf_init(rpcio);
stat=afs.afs_exit(rpcio.header.h_priv);
rpcio.header.h_status=stat;
if (stat==Status.STD_OK) dying=true;
break;
default:
rpcio.header.h_status=Status.STD_COMBAD;
Buf.buf_init(rpcio);
}
};
this.reply = function () {
Io.log((log<1)||('[AFS'+index+'] reply: '+Net.Print.header(rpcio.header)));
rpc.putrep(rpcio);
assert((rpcio.index!=-1) ||'RPCIO invalid');
};
this.onexit = function () {
// TODO exit context?
};
this.transitions = function () {
var trans;
trans =
[
[undefined, this.init],
[this.init, this.request],
[this.request, this.service],
[this.service, this.reply],
[this.reply, this.request, function (self) {return !dying}],
[this.reply, this.onexit, function (self) {return dying}]
];
return trans;
};
this.context = Sch.TaskContext('AFS'+index+' '+Net.Print.port(port), self);
};
var proc1 = new server();
scheduler.Add(proc1.context);
};
module.exports = {
afs_server_rpc:afs_server_rpc
};