diff --git a/js/dos/dns_srv.js b/js/dos/dns_srv.js
new file mode 100644
index 0000000..87eb757
--- /dev/null
+++ b/js/dos/dns_srv.js
@@ -0,0 +1,2169 @@
+/**
+ ** ==============================
+ ** 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
+ ** ==============================
+ ** BSSLAB, 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: 23-4-15 by sbosse.
+ ** $VERSION: 1.3.8
+ **
+ ** $INFO:
+ ** DNS Server with local file system storage and row capability cache.
+ *
+ * $ENDINFO
+ */
+"use strict";
+
+var util = Require('util');
+var Io = Require('com/io');
+var Net = Require('dos/network');
+var Std = Require('dos/std');
+var Sch = Require('dos/scheduler');
+var Buf = Require('dos/buf');
+var Rpc = Require('dos/rpc');
+var Cs = Require('dos/capset');
+var Comp = Require('com/compat');
+var String = Comp.string;
+var Array = Comp.array;
+var Perv = Comp.pervasives;
+var Printf = Comp.printf;
+var div = Perv.div;
+var assert = Comp.assert;
+var Rand = Comp.random;
+var Status = Net.Status;
+var Command = Net.Command;
+var Rights = Net.Rights;
+var AfsInt = Require('dos/afs');
+var Afs = Require('dos/afs_srv_common');
+var Dns = Require('dos/dns_srv_common');
+var DnsInt = Require('dos/dns');
+var Fs = Require('fs');
+var Fcache = Require('dos/fcache');
+var Cache = Require('dos/cache');
+var Afs_file_state = Afs.Afs_file_state;
+
+var log = 0;
+var CACHETMO = 30; // seconds
+
+/** DNS Dns_server
+ *
+ * @param {rpcint} rpc
+ * @constructor
+ * @typedef {{rpc,afs,std,cs,dns_super:dns_super,dns_part:string,dns_part_fd,cache_inode,cache_data,inode_cache_entry,
+ * rootdir:dns_dir,fs_default,
+ * block,to_block,of_block,
+ * live_set,live_get,live_read,live_write,
+ * read_inode,write_inode,sync_inode,
+ * inode_of_obj,cap_of_obj,update_inode,
+ * iocachebypass,sync_write_mode,
+ * stats}} dns_server~obj
+ * @see dns_server~obj
+ * @see dns_server~meth
+ */
+var dns_server = function (rpc,options) {
+ this.verbose=options.verbose||0;
+ this.rpc=rpc;
+ this.afs=AfsInt.AfsInt(rpc);
+ this.std=Std.StdInt(rpc);
+ this.cs=Cs.CsInt(rpc);
+ /**
+ *
+ * @type {dns_super|undefined}
+ */
+ this.dns_super=undefined;
+ this.dns_part=''; // DNS inode partition file path
+ this.dns_part_fd=undefined;
+
+ /**
+ *
+ * @type {fsc_cache|undefined}
+ */
+ this.cache_inode = undefined;
+ /**
+ *
+ * @type {cache|undefined}
+ */
+ this.cache_data = undefined;
+ this.inode_cache_entry=undefined;
+
+
+ /** Capability cache to speed-up row
+ * capability restriction using a column mask.
+ * Key: cap-key+mask
+ * Value: Restricted cap
+ *
+ */
+ this.cache_cap = Cache.Cache(1000);
+
+
+ this.rootdir=undefined;
+ this.fs_default=0;
+
+ this.block=function() {};
+ this.to_block=function() {};
+ this.of_block=function() {};
+
+ /**
+ *
+ * @param {number} obj
+ * @param {number} time
+ * @param {number} flag
+ */
+ this.live_set=function (obj,time,flag) {};
+ /**
+ *
+ * @param {number} obj
+ * @returns {{time: number, flag: number}}
+ */
+ this.live_get=function (obj) {return {time:0,flag:0}};
+ /**
+ *
+ * @returns {(Status.STD_OK|*)}
+ */
+ this.live_read=function () {return 0;};
+ /**
+ *
+ * @returns {(Status.STD_OK|*)}
+ */
+ this.live_write=function () {return 0;};
+
+ /**
+ *
+ * @param obj
+ * @param addr
+ * @param data
+ * @param size
+ * @returns {number}
+ */
+ this.read_inode=function(obj,addr,data,size) {return 0};
+ /**
+ *
+ * @param obj
+ * @param addr
+ * @param data
+ * @param size
+ * @returns {number}
+ */
+ this.write_inode=function(obj,addr,data,size) {return 0};
+ this.sync_inode=function() {};
+
+ /**
+ *
+ * @param {number} obj
+ * @returns {{stat:(Status.STD_OK|*),inode:(dns_inode|undefined)}}
+ */
+ this.inode_of_obj=function(obj){return {stat:0,inode:undefined}};
+ /**
+ *
+ * @param {number} obj
+ * @returns {{stat:(Status.STD_OK|*),cap:(capability|undefined),inode:(dns_inode|undefined)}}
+ */
+ this.cap_of_obj=function(obj){return {stat:0,cap:undefined,inode:undefined}};
+ this.update_inode=function(){};
+
+ this.iocache_bypass=false;
+ this.sync_write_mode=false;
+
+ this.stats = {
+ op_read:0,
+ op_modify:0,
+ op_create:0,
+ op_destroy:0,
+ op_touch:0,
+ op_age:0
+ }
+};
+
+/** Initialite and start up services
+ *
+ */
+
+dns_server.prototype.init = function () {
+ var self=this;
+ Sch.AddTimer(1000,'DNS Server Garbage Collector',function () {
+ self.cache_cap.refresh(function (key,data) {
+ if (data.tmo) { data.tmo--; return data.tmo>0}
+ else return true;
+ });
+ });
+}
+
+/** Get the current system time in 10s units
+ *
+ * @returns {number}
+ */
+dns_server.prototype.time =function () {
+ var self=this;
+ return div(Perv.time(),10);
+};
+
+
+/** Acquire and lock a directory [BLOCKING]
+ *
+ * @param {number} obj
+ * @param {function((Status.STD_OK|*),(dns_dir|undefined))} callback
+ */
+dns_server.prototype.acquire_dir = function (obj,callback) {
+ var self=this;
+ var dir=undefined;
+ var stat;
+ Sch.ScheduleBlock([
+ function () {self.dns_super.lock();},
+ function () {
+ var res;
+ res=self.read_dir(obj,function (_stat,_dir) {
+ stat=_stat;
+ dir=_dir;
+ });
+ if (stat==Status.STD_OK) {
+ dir.lock();
+ }
+ },
+ function () {
+ self.dns_super.unlock();
+ Io.log((log < 1) || ('Dns_srv.acquire_dir: done ' + obj));
+ callback(stat,dir);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e,undefined); else {
+ Io.printstack(e,'Dns_server.acquire_dir');
+ callback(Status.STD_SYSERR,undefined);
+ }
+ });
+};
+
+/** Release an acquired directory. Flush all pending writes - the
+ ** super structure and the i-node table, and the directory itself
+ ** if modified. Return the status of the operation. [BLOCKING]
+ *
+ *
+ * @param {dns_dir} dir
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.release_dir = function (dir,callback) {
+ var stat=Status.STD_OK;
+ Io.log((log < 1) || ('dns_srv.release_dir: ' + util.inspect(dir)));
+ if (dir != undefined) {
+ if ((dir.dd_state == Dns.Dns_dir_state.DD_unlocked ||
+ dir.dd_state == Dns.Dns_dir_state.DD_modified)) {
+ this.modify_dir(dir, function (_stat) {
+ callback(_stat);
+ });
+ }
+ else {
+ stat = Status.STD_OK;
+ dir.unlock();
+ if (callback) callback(stat);
+ }
+ } else {
+ stat=Status.STD_ARGBAD;
+ if (callback!=undefined) callback(stat);
+ }
+};
+/** Release an acquired but unmodified directory. Flush all pending writes - the
+ ** super structure and the i-node table.
+ ** Return the status of the operation.
+ *
+ *
+ * @param {dns_dir} dir
+ * @returns {(Status.STD_OK|*)}
+ *
+ * @param dir
+ */
+dns_server.prototype.release_unmodified_dir = function (dir) {
+ var stat=Status.STD_OK;
+ Io.log((log < 1) || ('dns_srv.release_unmodified_dir: ' + util.inspect(dir)));
+ if (dir != undefined) {
+ if ((dir.dd_state == Dns.Dns_dir_state.DD_unlocked ||
+ dir.dd_state == Dns.Dns_dir_state.DD_modified)) {
+ Io.out('Dns_srv.release_unmodified_dir: Directory '+dir.dd_objnum+' was modified!');
+ return Status.STD_SYSERR;
+ }
+ else {
+ stat = Status.STD_OK;
+ dir.unlock();
+ return stat;
+ }
+ } else {
+ return Status.STD_ARGBAD;
+ }
+};
+
+
+/** Lookup a directory object (priv.prv_obj) and check the required rights.
+ * The directory is returned unlocked!
+ *
+ * @param {privat} priv
+ * @param {number} req
+ * @param {function((Status.STD_OK|*),(dns_dir|undefined))} callback
+ */
+dns_server.prototype.lookup_dir = function(priv,req,callback) {
+ var self = this;
+ var obj = Net.prv_number(priv);
+ var stat;
+ var dir;
+
+ if (obj < 1 || obj >= this.dirs_top) {
+ callback(Status.STD_ARGBAD,undefined);
+ } else Sch.ScheduleBlock([
+ function () {
+ Io.log((log<1)||('Dns_server.lookup_dir: read_dir('+obj+')'));
+ self.read_dir(obj,function (_stat,_dir) {
+ stat=_stat;
+ dir=_dir;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ if (!Net.prv_decode(priv, dir.dd_random)) {
+ throw Status.STD_DENIED;
+ }
+ /*
+ ** When checking the presence of column rights, only take the
+ ** *actual*
+ ** columns present into acount (i.e., do not use dns_COLMASK here)
+ */
+ var ncols=dir.dd_ncols;
+ var rights = Net.prv_rights(priv);
+ var colbits = Dns.dns_col_bits[ncols];
+ var colmask = colbits-1;
+
+ Io.log((log<1)||('Dns_server.lookup_dir: '+Net.Print.private(priv))+' req='+req+' colmask='+colmask+' rights='+rights);
+
+ if ((rights & colmask) == 0 ||
+ (rights & req) != req) {
+ throw Status.STD_DENIED;
+ }
+ callback(Status.STD_OK,dir);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e,undefined); else {
+ Io.printstack(e,'Dns_server.lookup_dir');
+ callback(Status.STD_SYSERR,undefined);
+ }
+ });
+
+};
+
+/** A client request arrived. Enter the critical section. Check the capability.
+ ** If it is correct, check whether the required rights are present or not.
+ ** Return the directory structure, but only if the required rights are present.
+ *
+ * @param priv
+ * @param req
+ * @param callback
+ */
+dns_server.prototype.request_dir = function(priv,req,callback) {
+ var self = this;
+ var obj = Net.prv_number(priv);
+ var stat;
+ var dir;
+
+ if (obj < 1 || obj >= this.dirs_top) {
+ callback(Status.STD_ARGBAD,undefined);
+ } else Sch.ScheduleBlock([
+ function () {
+ Io.log((log<1)||('Dns_server.request_dir: acquire_dir('+obj+')'));
+ self.acquire_dir(obj,function (_stat,_dir) {
+ stat=_stat;
+ dir=_dir;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ if (!Net.prv_decode(priv, dir.dd_random)) {
+ self.release_unmodified_dir(dir);
+ throw Status.STD_DENIED;
+ }
+ /*
+ ** When checking the presence of column rights, only take the
+ ** *actual*
+ ** columns present into acount (i.e., do not use dns_COLMASK here)
+ */
+ var ncols=dir.dd_ncols;
+ var rights = Net.prv_rights(priv);
+ var colbits = Dns.dns_col_bits[ncols];
+ var colmask = colbits-1;
+
+ Io.log((log<1)||('Dns_server.request_dir: '+Net.Print.private(priv))+' req='+req+' colmask='+colmask+' rights='+rights);
+
+ if ((rights & colmask) == 0 ||
+ (rights & req) != req) {
+ self.release_unmodified_dir(dir);
+ throw Status.STD_DENIED;
+ }
+ callback(Status.STD_OK,dir);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e,undefined); else {
+ Io.printstack(e,'Dns_server.request_dir');
+ callback(Status.STD_SYSERR,undefined);
+ }
+ });
+};
+
+/**
+ ** With a given directory capability set check if this directory belongs
+ ** to this server.
+ *
+ *
+ * @param {capset} dir
+ * @returns {private|undefined}
+ */
+dns_server.prototype.check_dir = function(dir) {
+ var self=this;
+ Io.log((log<1)||('Dns_server.check_dir: '+Cs.Print.capset(dir)));
+ if (dir==undefined) return undefined;
+ for (var i = 0; i
0)||('Dns_srv.read_dir_file: inode.i_size is zero'));
+ Io.log((log<1)||('Dns_server.read_dir_file cap='+Net.Print.capability(inode.i_caps[fs_default])+' '+util.inspect(inode)));
+ self.afs.afs_read(inode.i_caps[fs_default],buf,0,inode.i_size, function (_stat) {
+ stat=_stat;
+ if (stat==Status.STD_OK) dir=Dns.buf_get_dir(buf); else dir=undefined;
+ })
+ },
+ function () {
+ callback(stat,dir);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e,undefined); else {
+ Io.printstack(e,'Dns_server.read_dir_file');
+ callback(Status.STD_SYSERR,undefined);
+ }
+ });
+
+};
+
+/** Read a directory specified with his object number (index) number.
+ * Either the directory is currently cached, or the directory is read from an AFS file.
+ *
+ * @param {number} obj
+ * @param {function((Status.STD_OK|*), dns_dir)} callback
+ */
+dns_server.prototype.read_dir = function (obj,callback) {
+ var self=this;
+ var stat,dir,inode;
+ this.stats.op_read++;
+ var si=this.inode_of_obj(obj);
+ stat=si.stat;inode=si.inode;
+ Io.log((log<1)||('Dns_server.read_dir obj='+obj));
+ if (stat!=Status.STD_OK) callback(stat,undefined);
+ else Sch.ScheduleBlock([
+ function () {
+ dir=self.cache_data.lookup(inode.i_objnum);
+ if(dir==undefined) stat=Status.STD_NOTFOUND; else stat=Status.STD_OK;
+ },
+ function () {
+ if (stat==Status.STD_NOTFOUND) self.read_dir_file(inode,function (_stat,_dir) {
+ stat = _stat;
+ dir = _dir;
+ })
+ },
+ function () {
+ if (stat==Status.STD_OK) {
+ var tf=self.live_get(dir.dd_objnum);
+ dir.dd_live=tf.time;
+ }
+ callback(stat,dir);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e,undefined); else {
+ Io.printstack(e,'Dns_server.read_dir');
+ callback(Status.STD_SYSERR,undefined);
+ }
+ });
+
+};
+
+/**
+ * Write an existing directory (only if modified).
+ *
+ * @param {dns_dir} dir
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.modify_dir = function (dir,callback) {
+ var i;
+ var stat;
+ var self = this;
+ var oldcaps;
+ var cap = undefined;
+ var obj = dir.dd_objnum;
+ var inode,inode2,dirsize;
+ var fs = this.dns_super.dns_fs;
+ var fs_default=fs.fs_default;
+ var buf = Buf.Buffer();
+ var si=this.inode_of_obj(obj);
+ stat=si.stat;inode=si.inode;
+ Io.log((log<1)||('Dns_server.modify_dir dir='+util.inspect(dir)));
+ this.stats.op_modify++;
+ /*
+ ** Distinguish these two cases:
+ ** dd_state = DD_modified -> a new AFS object must be created
+ ** dd_state = DD_unlocked -> commit the AFS object
+ */
+ if (stat!=Status.STD_OK) callback(stat);
+ else
+ {
+ if (dir.dd_state==Dns.Dns_dir_state.DD_modified)
+ Sch.ScheduleBlock([
+ /*
+ ** Create a new AFS object. If in two copy mode,
+ ** the copy will be duplicated later.
+ ** Destroy the old capabilities after this oepration
+ ** succeeded.
+ */
+ function () {
+ oldcaps = Array.map(inode.i_caps,function (cap) {return Net.Copy.capability(cap)});
+ dirsize=dir.size();
+ Dns.buf_put_dir(buf,dir);
+ self.afs.afs_create(fs.fs_cap[fs_default],buf,dirsize,Afs.Afs_commit_flag.AFS_SAFETY,function (_stat,_cap) {
+ stat=_stat;
+ cap=_cap;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ dir.dd_state=Dns.Dns_dir_state.DD_locked;
+ var caps=[Net.nilcap,Net.nilcap];
+ caps[fs_default]=cap;
+ inode2=Dns.Dns_inode(obj,caps,dirsize,Afs.Afs_file_state.FF_locked);
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf,inode2);
+ /*
+ ** Write the inode data through the cache to disk ...
+ */
+ stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
+ if (stat!=Status.STD_OK) throw stat;
+ i=0;
+ Sch.ScheduleLoop(function(index) {
+ i=index;
+ return index<2;
+ },[
+ /*
+ ** Destroy the old caps.
+ */
+ function () {
+ if(!Net.Equal.capability(oldcaps[i],Net.nilcap)) {
+ self.std.std_destroy(oldcaps[i],function (_stat) {
+ stat=_stat;
+ })
+ }
+ }
+ ],[
+ function () {
+ stat=Status.STD_OK;
+ }
+ ])
+ },
+ function () {
+ callback(stat)
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.modify_dir');
+ callback(Status.STD_SYSERR);
+ }
+ });
+ else // dir.dd_state = DD_unlocked
+ Sch.ScheduleBlock([
+ function () {
+ dirsize=self.dir_size(dir);
+ Dns.buf_put_dir(buf,dir);
+ self.afs.afs_modify(inode.i_caps[fs_default],buf,dirsize,0,Afs.Afs_commit_flag.AFS_SAFETY,function (_stat,_cap) {
+ stat=_stat;
+ cap=_cap;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ dir.dd_state=Dns.Dns_dir_state.DD_locked;
+ inode.i_size=dirsize;
+ inode.i_state=Afs.Afs_file_state.FF_locked;
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf,inode);
+ stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
+ },
+ function () {
+ callback(stat)
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.modify_dir');
+ callback(Status.STD_SYSERR);
+ }
+ });
+ }
+};
+
+/** Insert a new created directory into the DNS table.
+ *
+ * @param {dns_dir} dir
+ * @param {function((Status.STD_OK|*))} callback
+ */
+dns_server.prototype.create_dir = function (dir,callback) {
+ var stat;
+ var self = this;
+ var cap = undefined;
+ var obj = dir.dd_objnum;
+ var fs = this.dns_super.dns_fs;
+ var fs_default=fs.fs_default;
+ var buf = Buf.Buffer();
+
+ this.stats.op_create++;
+ Sch.ScheduleBlock([
+ function () {
+ /*
+ ** Create a new AFS object. If in two copy mode,
+ ** the copy will be duplicated later.
+ */
+ self.afs.afs_create(fs.fs_cap[fs_default],buf,0,Afs.Afs_commit_flag.AFS_UNCOMMIT,function (_stat,_cap) {
+ stat=_stat;
+ cap=_cap;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ dir.dd_state=Dns.Dns_dir_state.DD_unlocked;
+ self.live_set(obj,Dns.DNS_MAX_LIVE,1);
+ /*
+ ** First read the inode from disk to update
+ ** the inode cache (reads/writes always full blocks!).
+ */
+ stat=self.cache_inode.cache_read(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DEF_INODE_SIZE);
+
+ if (stat!=Status.STD_OK) throw stat;
+
+ var fs_caps = [Net.nilcap,Net.nilcap];
+ fs_caps[fs_default]=cap;
+ var inode = Dns.Dns_inode(obj,fs_caps,0,Afs.Afs_file_state.FF_unlocked);
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf,inode);
+ stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DEF_INODE_SIZE);
+ self.cache_data.add(obj,dir);
+
+ callback(stat);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.create_dir');
+ callback(Status.STD_SYSERR);
+ }
+ })
+};
+
+/** Create a new directory. Return the directory structure and
+ ** the status returned by the server create function. The super structure
+ ** is already modified by this function. The new directory remains
+ ** locked.
+ *
+ * @param colnames
+ * @param {function ((Status.STD_OK|*),(dns_dir|undefined))} callback
+ */
+dns_server.prototype.alloc_dir = function (colnames,callback) {
+ var self=this;
+ var sup=this.dns_super;
+ Sch.ScheduleBlock([
+ function () {
+ sup.lock();
+ },
+ function () {
+ var obj=self.get_freeobjnum();
+ var dir = Dns.Dns_dir(obj,colnames.length,0,colnames,Net.uniqport(),[],
+ Dns.Dns_dir_state.DD_unlocked,self.time(),Dns.DNS_MAX_LIVE);
+ assert(dir.try_lock());
+ sup.unlock();
+ self.create_dir(dir,function (_stat) {
+ if (_stat==Status.STD_OK) callback(_stat,dir);
+ else callback(_stat,undefined);
+ })
+ }
+ ]);
+};
+
+
+/** Delete a directory. Destroy associated AFS objects.
+ *
+ * @param {dns_dir} dir
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.delete_dir = function (dir,callback) {
+ var stat;
+ var i;
+ var self = this;
+ var cap = undefined;
+ var obj = dir.dd_objnum;
+ var fs = this.dns_super.dns_fs;
+ var fs_default=fs.fs_default;
+ var buf = Buf.Buffer();
+ var oldcaps;
+ /*
+ ** Get the i-node mapping
+ */
+ var inode;
+ this.stats.op_destroy++;
+ var si=this.inode_of_obj(obj);
+ stat=si.stat;inode=si.inode;
+ if (stat!=Status.STD_OK) callback(stat);
+ else {
+ self.live_set(obj,0,0);
+ oldcaps = Array.map(inode.i_caps,function (cap) {return Net.Copy.capability(cap)});
+ /*
+ ** Destroy first the old caps
+ */
+ i=0;
+ Sch.ScheduleLoop(function(index) {
+ i=index;
+ return index<2;
+ },[
+ /*
+ ** Destroy the old caps.
+ */
+ function () {
+ if(!Net.Equal.capability(oldcaps[i],Net.nilcap)) {
+ self.std.std_destroy(oldcaps[i],function (_stat) {
+ stat=_stat;
+ })
+ }
+ }
+ ],[
+ function () {
+ stat=Status.STD_OK;
+ /*
+ ** Now destroy ourself!
+ ** Write the i-node data through the cache to disk ...
+ */
+ inode.i_state=Afs_file_state.FF_invalid;
+ inode.i_size=0;
+ inode.i_caps=[Net.nilcap,Net.nilcap];
+ Dns.buf_put_inode(buf,inode);
+ stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
+ /*
+ ** Invalidate cache entry.
+ */
+ self.cache_data.invalidate(inode.i_objnum);
+ callback(stat);
+ }
+ ], function(e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.delete_dir');
+ callback(Status.STD_SYSERR);
+ }
+
+ })
+ }
+};
+dns_server.prototype.dir_modified = function (dir) {
+ if (dir.dd_state == Dns.Dns_dir_state.DD_locked)
+ dir.dd_state = Dns.Dns_dir_state.DD_modified;
+};
+
+/**
+ * Delete an i-node without destroying AFS objects.
+ *
+ * @param {number} obj
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.delete_inode = function (obj,callback) {
+ var stat;
+ var self = this;
+ var buf = Buf.Buffer();
+ /*
+ ** Get the inode mapping
+ */
+ var inode;
+ this.stats.op_destroy++;
+ var si=this.inode_of_obj(obj);
+ stat=si.stat;inode=si.inode;
+ if (stat!=Status.STD_OK) callback(stat);
+ else {
+ /*
+ ** Destroy ourself without destroying AFS objects!
+ ** Write the i-node data through the cache to disk ...
+ */
+ inode.i_state=Afs_file_state.FF_invalid;
+ inode.i_size=0;
+ inode.i_caps=[Net.nilcap,Net.nilcap];
+ Dns.buf_put_inode(buf,inode);
+ stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
+ /*
+ ** Invalidate cache entry.
+ */
+ self.cache_data.invalidate(inode.i_objnum);
+ callback(stat);
+ }
+};
+
+/**
+ * Create generic server statistics informations.
+ *
+ * @returns {string}
+ */
+dns_server.prototype.stat = function () {
+ var self=this;
+ var stats,res;
+ stats=self.stats;
+ res = 'DNS server status\n';
+ res = res + 'Inode cache statistics:\n' +
+ (self.cache_inode.cache_stat()) +
+ '\nServer statistics\n\n'+
+ 'Read: '+Printf.sprintf2([['%8d',stats.op_read]])+
+ ' Modify: '+Printf.sprintf2([['%8d',stats.op_modify]])+
+ ' Create: '+Printf.sprintf2([['%8d',stats.op_create]])+'\n'+
+ 'Touch: '+Printf.sprintf2([['%8d',stats.op_touch]])+
+ ' Age: '+Printf.sprintf2([['%8d',stats.op_age]])+
+ ' Destroy:'+Printf.sprintf2([['%8d',stats.op_destroy]])+'\n';
+ return res;
+};
+
+/**
+ * Touch directory and AFS content table objects
+ *
+ * @param {dns_dir} dir
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.touch = function (dir,callback) {
+ var self=this,
+ stat,
+ cap,inode,
+ i;
+ this.stats.op_touch++;
+ /*
+ ** Keep object internally alive.
+ */
+ dir.dd_live=Dns.DNS_MAX_LIVE;
+ self.live_set(dir.dd_objnum,Dns.DNS_MAX_LIVE,1);
+ /*
+ ** Keep object(s) externally alive.
+ */
+ var sci = self.cap_of_obj(dir.dd_objnum);
+ stat=sci.stat;inode=sci.inode;
+ i=0;
+ if (stat!=Status.STD_OK) callback(stat);
+ else Sch.ScheduleLoop(function(index) {
+ i=index;
+ return index < 2;
+ },[
+ function () {
+ cap=inode.i_caps[i];
+ if (!Net.Equal.capability(cap,Net.nilcap)) {
+ self.std.std_touch(cap,function (_stat) {
+ stat=_stat;
+ })
+ }
+ }
+ ],[
+ function () {
+ callback(Status.STD_OK);
+ }
+ ],function(e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.touch');
+ callback(Status.STD_SYSERR);
+ }
+ });
+};
+
+/** Age all objects (used and unused inodes). Destroy directories
+ ** with live time equal zero (only valid files). Remember that
+ ** the root dirctory (obj=1) can't be aged or deleted.
+ *
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.age = function (callback) {
+ var self=this,
+ sup = this.dns_super,
+ stat=Status.STD_OK,
+ i=2,
+ lt,
+ dir,
+ gone=0,
+ aged=0,
+ scavenge=[];
+ this.stats.op_age++;
+ if (self.verbose) Io.out('[DNS] Aging ..');
+ for(i=2;i 1 ) {
+ self.live_set(i,lt.time-1,lt.flag);
+ aged++;
+ } else {
+ self.live_set(i,0,lt.flag);
+ }
+ if (lt.flag==1 && lt.time==0) scavenge.push(i);
+ }
+ if (scavenge.length) Sch.ScheduleLoop(function() {
+ return i < scavenge.length;
+ },[
+ function () {
+ dir=undefined;
+ self.acquire_dir(scavenge[i],function (_stat,_dir) {
+ stat=_stat;
+ dir=_dir;
+ });
+ },
+ function () {
+ if (stat==Status.STD_OK) {
+ self.delete_dir(dir,function (_stat) {
+ stat=_stat;
+ gone++;
+ })
+ } else if (stat!=Status.STD_NOTNOW) {
+ /*
+ ** Maybe already deleted AFS object.
+ */
+ self.delete_inode(scavenge[i],function (_stat) {
+ stat=_stat;
+ gone++;
+ })
+ }
+ },
+ function () {
+ i++;
+ if (dir!=undefined) self.release_dir(dir,function (_stat) {
+ stat=_stat;
+ });
+ }
+ ],[
+ function () {
+ // sup.unlock();
+ if (self.verbose) Io.out('[DNS] '+aged+' aged, '+gone+' directories(s) deleted.');
+ else if (gone) Io.out('[DNS] '+gone+' directories(s) deleted.');
+ callback(Status.STD_OK);
+ }
+ ],function(e) {
+ // sup.unlock();
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.age');
+ callback(Status.STD_SYSERR);
+ }
+ });
+ else callback(stat);
+};
+
+/** Append a row to a directory.
+ *
+ * @param {dns_dir} dir
+ * @param {dns_row} row
+ */
+dns_server.prototype.append_row = function (dir,row) {
+ var self=this;
+ dir.dd_rows.push(row);
+ dir.dd_nrows++;
+ this.dir_modified(dir);
+};
+
+/** Append a row to a directory.
+ *
+ * @param {dns_dir} dir
+ * @param {string} oldname
+ * @param {string} newname
+ * @returns {(Status.STD_OK|*)}
+ */
+dns_server.prototype.rename_row = function (dir,oldname,newname) {
+ var self=this;
+ var row = Array.find(dir.dd_rows,function(_row) {
+ return String.equal(_row.dr_name,oldname);
+ });
+ if (row!=undefined) {
+ row.dr_name=newname;
+ self.dir_modified(dir);
+ return Status.STD_OK;
+ } else return Status.STD_NOTFOUND;
+};
+
+/** Delete a row of a directory.
+ *
+ * @param {dns_dir} dir
+ * @param {string} name
+ */
+dns_server.prototype.delete_row = function (dir,name) {
+ var self=this;
+ dir.dd_rows=Array.filter(dir.dd_rows,function (_row) {
+ return !(String.equal(_row.dr_name,name));
+ });
+ dir.dd_nrows=Array.length(dir.dd_rows);
+ this.dir_modified(dir);
+};
+
+/** Replace a row in a directory.
+ *
+ * @param {dns_dir} dir
+ * @param {string} name
+ * @param {capset} newcs
+ * @returns {(Status.STD_OK|*)}
+ */
+dns_server.prototype.replace_row = function (dir,name,newcs) {
+ var self=this;
+ var row = Array.find(dir.dd_rows,function(_row) {
+ return String.equal(_row.dr_name,oldname);
+ });
+ if (row!=undefined) {
+ row.dr_capset=newcs;
+ this.dir_modified(dir);
+ } else return Status.STD_NOTFOUND;
+};
+
+
+/** Search a row in a directory.
+ *
+ * @param {dns_dir} dir
+ * @param {string} name
+ * @returns {(dns_row|undefined)}
+ */
+dns_server.prototype.search_row = function (dir,name) {
+ var self=this;
+ var row = Array.find(dir.dd_rows,function(_row) {
+ return String.equal(_row.dr_name,name);
+ });
+ return row;
+};
+/**
+ * @returns {dns_super}
+ */
+dns_server.prototype.read_super = function () {
+ return this.dns_super;
+};
+
+
+/** Returns (used,lifetime) tuple. This functions first probes for an
+ ** used i-node, and in the case of a used i-node was found, it ages the live time.
+ ** If the lifetime reaches zero, the object must be destroyed.
+ ** (by this module).
+ *
+ *
+ * @param {number} obj
+ * @param {function(number,number)} callback function(used,live)
+ * @returns {{used: boolean, live: number}}
+ */
+dns_server.prototype.age_obj = function (obj,callback) {
+ var used,live,dir;
+ this.stats.op_age++;
+ Sch.ScheduleBlock([
+ function () {
+ self.dns_super.lock();
+ },
+ function () {
+ var tf = self.live_get(obj);
+ if (tf.flag == 1 && tf.time>1) {
+ self.live_set(obj,tf.time-1,1);
+ used=true;live=tf.time-1;
+ } else if (tf.flag==1) {
+ self.live_set(obj,0,1);
+ used=true;live=0;
+ } else {
+ used=false;live=0;
+ }
+ self.dns_super.unlock();
+ callback (used,live);
+ }
+ ]);
+};
+
+/**
+ * Flush the caches (if any).
+ *
+ * @returns {(Status.STD_OK|*)}
+ */
+dns_server.prototype.sync = function () {
+ var stat;
+ // TODO
+ return stat;
+};
+
+/**
+ * Exit the server.
+ *
+ * @returns {(Status.STD_OK|*)}
+ */
+dns_server.prototype.exit = function () {
+ Io.out('[DNS] Exit. Writing live table...');
+ return this.live_write();
+};
+
+
+
+
+/** Create the directory filesystem (one partition = UNIX file). [BLOCKING]
+ **
+ ** Args:
+ ** label: the filesystem label string [max 256 chars]
+ ** ninodes: number of i-nodes ( = maximal number of directories)
+ ** cols: Column name array (dns_colnames)
+ ** colmasks: Column mask array (dns_generic_colmask)
+ ** part_inode: Partition path and name
+ ** fs_caps: AFS server(s). All directories are saved there. Up to
+ ** two file servers can be specified. The first is the default.
+ **
+ ** Return:
+ ** status
+ ** dns_super
+ *
+ *
+ * @param {string} label
+ * @param {number} ninodes
+ * @param {number} blocksize
+ * @param {string []} cols
+ * @param {number []} colmasks
+ * @param {string} part_inode
+ * @param {capability []} fs_caps
+ * @param {boolean} overwrite
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.create_fs = function (label,ninodes,blocksize,cols,colmasks,part_inode,fs_caps,overwrite,callback) {
+ var self = this;
+ function create (callback) {
+ var i, n;
+
+ var stat = Status.STD_OK;
+ if (fs_caps==undefined || Array.empty(fs_caps) || Net.Equal.capability(fs_caps[0],Net.nilcap)) {
+ Io.out('[DNS] Cannot create DNS file system. No file server capability provided. Fatal.');
+ throw Status.STD_ARGBAD;
+ }
+ Io.out('[DNS] Creating DNS tree...');
+ Io.out('[DNS] Blocksize: ' + blocksize + ' [bytes]');
+ Io.out('[DNS] Number of total inodes (dirs): ' + ninodes);
+
+ /*
+ ** First create a server port and derive the public port.
+ */
+ var privport = Net.uniqport();
+ var pubport = Net.prv2pub(privport);
+ var checkfield = Net.uniqport();
+ var ncols = cols.length;
+
+
+ Io.out('[DNS] Private Port: ' + Net.Print.port(privport));
+ Io.out('[DNS] Public Port: ' + Net.Print.port(pubport));
+ Io.out('[DNS] Checkfield: ' + Net.Print.port(checkfield));
+
+ Io.out('[DNS] AFS server [1]: '+Net.Print.capability(fs_caps[0]) + ' (default)');
+ Io.out('[DNS] AFS server [2]: '+Net.Print.capability(fs_caps[1]));
+ self.fs_default=0;
+
+ if (!Io.exists(part_inode)) {
+ Io.out('[DNS] Creating directory partition ' + part_inode);
+ } else if (overwrite) {
+ Io.out('[DNS] Overwriting existing directory partition ' + part_inode);
+ } else {
+ Io.err('[DNS] Found existing directory partition ' + part_inode);
+ }
+ var part_fd = Io.open(part_inode, (overwrite ? 'w+' : 'r+'));
+ self.dns_part = part_inode;
+ self.dns_part_fd = part_fd;
+
+ /*
+ ** The partition magic headers. After the magic string,
+ ** the value 0xaa is written.
+ */
+ var magic1 = Buf.Buffer();
+ Buf.buf_put_string(magic1, Dns.MAGIC_STR);
+ Buf.buf_pad(magic1, Dns.DEF_MAGIC_SIZE, 0xaa);
+
+ Io.out('[DNS] Writing partition magic header... ');
+ n = Buf.buf_write(part_fd, magic1);
+ if (n != Dns.DEF_MAGIC_SIZE) throw Status.STD_IOERR;
+
+ /*
+ ** The disk super structure.
+ */
+
+ var super1 = Buf.Buffer();
+ Buf.buf_put_string(super1, label);
+ Buf.buf_pad(super1, 256, 0xaa);
+ Buf.buf_put_int32(super1, ninodes);
+ Buf.buf_put_int32(super1, blocksize);
+ Buf.buf_put_port(super1, privport);
+ Buf.buf_put_port(super1, pubport);
+ Buf.buf_put_port(super1, checkfield);
+ Buf.buf_put_int32(super1, ncols);
+
+ for (i = 0; i < ncols; i++) {
+ Buf.buf_put_string(super1, cols[i]);
+ }
+ for (i = 0; i < ncols; i++) {
+ DnsInt.buf_put_rights(super1, colmasks[i]);
+ }
+ for (i=0;i<2;i++) {
+ Buf.buf_put_cap(super1,fs_caps[i]);
+ }
+
+ Io.out('[DNS] Done. ');
+ Io.out('[DNS] Writing super block ... ');
+ Buf.buf_pad(super1, Dns.DEF_BLOCK_SIZE - Dns.DEF_MAGIC_SIZE, 0xaa);
+ n = Buf.buf_write(part_fd, super1);
+ if (n != (Dns.DEF_BLOCK_SIZE - Dns.DEF_MAGIC_SIZE)) throw Status.STD_IOERR;
+ Io.out('[DNS] Done. ');
+
+
+ Io.out('[DNS] Writing i-nodes... ');
+
+ var buf = Buf.Buffer();
+ var inode = Dns.Dns_inode(0, [Net.nilcap, Net.nilcap], 0, Afs.Afs_file_state.FF_invalid);
+
+ for (i = 0; i < ninodes; i++) {
+ inode.i_objnum = i;
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf, inode);
+ Buf.buf_pad(buf, Dns.DNS_INODE_SIZE);
+ n = Buf.buf_write(part_fd, buf);
+ if (n != Dns.DNS_INODE_SIZE) throw Status.STD_IOERR;
+ }
+ self.dns_super = Dns.Dns_super(label, blocksize, privport, pubport, checkfield, ncols, cols, colmasks,
+ Dns.Fs_server(fs_caps, [Dns.Fs_state.FS_unknown, Dns.Fs_state.FS_unknown],
+ 0, Dns.Dns_mode.DNSMODE_ONECOPY));
+ Io.out('[DNS] Done. ');
+ /*
+ ** The live time table. It's a fixed size bitfield table build with
+ ** a string of sufficient size.
+ **
+ ** Assumption: Maximale Live time value < 128, therefore
+ ** 7 bit are used for each object. The MSB is
+ ** the used flag (=1 -> inode used).
+ **
+ ** The first entry (obj=0) is used for the lock status
+ ** of the live table.
+ **
+ */
+ Io.out('[DNS] Writing live table... ');
+
+ var live_size = ninodes + 1;
+ var live_table = Buf.Buffer();
+ var live_off = Dns.DEF_BLOCK_SIZE + (ninodes * Dns.DNS_INODE_SIZE);
+
+ function live_set(obj, time, flag) {
+ Buf.buf_set(live_table, obj, ((time & 0x7f) | ((flag << 7) & 0x80)));
+ }
+
+ function live_write() {
+ /*
+ ** Unlock the live table
+ */
+ live_set(0, 0, 1);
+ n = Buf.buf_write(part_fd, live_table, live_off);
+ if (n != live_size) throw Status.STD_IOERR;
+ }
+
+ for (i = 0; i <= ninodes; i++) {
+ live_set(i, Dns.DNS_MAX_LIVE, 0);
+ }
+ Buf.buf_pad(live_table, live_size);
+ live_write();
+
+ Io.out('[DNS] Done. ');
+
+ // w/o cache version, used ony to create the root directory...
+ function write_inode(obj, data, size) {
+ /*
+ ** Read and write full blocks!
+ */
+ var buf = Buf.Buffer();
+ var off = 512 + Dns.off_of_inode(obj);
+ var fileoff = div(off,blocksize)*blocksize;
+ var blockoff = off % blocksize;
+ var n = Buf.buf_read(self.dns_part_fd, buf, fileoff,blocksize);
+ if (n != blocksize)
+ return Status.STD_IOERR;
+ Buf.buf_blit(buf,blockoff,data,0,size);
+ n = Buf.buf_write(self.dns_part_fd, buf, fileoff, blocksize);
+ if (self.sync_write_mode) Io.sync(self.dns_part_fd);
+ if (n == blocksize)
+ return Status.STD_OK;
+ else
+ return Status.STD_IOERR;
+ }
+
+ var rootinode;
+ var rootcap;
+
+ function create_dir(dir,callback) {
+ var stat;
+ var cap = undefined;
+ var obj = dir.dd_objnum;
+ var fs = self.dns_super.dns_fs;
+ var fs_default=fs.fs_default;
+ var buf = Buf.Buffer();
+
+ self.stats.op_create++;
+ Sch.ScheduleBlock([
+ function () {
+ self.afs.afs_create(fs.fs_cap[fs_default],buf,0,Afs.Afs_commit_flag.AFS_UNCOMMIT,function (_stat,_cap) {
+ stat=_stat;
+ cap=_cap;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ dir.dd_state=Dns.Dns_dir_state.DD_unlocked;
+ var fs_caps = [Net.nilcap,Net.nilcap];
+ fs_caps[fs_default]=cap;
+ rootinode = Dns.Dns_inode(obj,fs_caps,0,Afs.Afs_file_state.FF_unlocked);
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf,rootinode);
+ stat=write_inode(obj,buf,Dns.DNS_INODE_SIZE);
+ callback(stat);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e); else callback(Status.STD_SYSERR);
+ })
+ }
+
+ function modify_dir(dir,callback) {
+ var stat;
+ var cap = undefined;
+ var obj = dir.dd_objnum;
+ var fs = self.dns_super.dns_fs;
+ var fs_default=fs.fs_default;
+ var dirsize = dir.size();
+ var buf = Buf.Buffer();
+ Dns.buf_put_dir(buf,dir);
+ self.stats.op_create++;
+ Sch.ScheduleBlock([
+ function () {
+ self.afs.afs_modify(rootinode.i_caps[fs_default],buf,dirsize,0,Afs.Afs_commit_flag.AFS_SAFETY,function (_stat,_cap) {
+ stat=_stat;
+ cap=_cap;
+ })
+ },
+ function () {
+ if (stat!=Status.STD_OK) throw stat;
+ dir.dd_state=Dns.Dns_dir_state.DD_locked;
+ rootinode.i_size=dirsize;
+ rootinode.i_state=Afs.Afs_file_state.FF_locked;
+ rootcap=Net.Capability(self.dns_super.dns_putport,
+ Net.prv_encode(rootinode.i_objnum,Rights.PRV_ALL_RIGHTS,dir.dd_random));
+ Buf.buf_init(buf);
+ Dns.buf_put_inode(buf,rootinode);
+ stat=write_inode(obj,buf,Dns.DNS_INODE_SIZE);
+ callback(stat);
+ }
+ ],function (e) {
+ if (typeof e == 'number') callback(e); else callback(Status.STD_SYSERR);
+ })
+ }
+
+ /** Create the root directory (Object number 1) and return it and the status
+ ** of the server create function. All pending writes are already done here.
+ **
+ ** Note: the root directory can't be deleted. Therefore, it's not
+ ** garbage collected.
+ *
+ */
+ function create_root(callback) {
+ var stat;
+ var sup=self.dns_super;
+ self.stats.op_create++;
+ assert(sup.try_lock());
+ if (sup.dns_nextfree!=1) {
+ sup.unlock();
+ callback(Status.STD_EXISTS);
+ return;
+ }
+ sup.dns_nextfree++;
+ var rootdir = Dns.Dns_dir(1,sup.dns_ncols,0,sup.dns_colnames,Net.uniqport(),[],
+ Dns.Dns_dir_state.DD_unlocked,self.time(),Dns.DNS_MAX_LIVE);
+ rootdir.dd_lock.acquire(); // Non-blocking, we're the first one
+ sup.unlock();
+ Sch.ScheduleBlock([
+ function () {
+ create_dir(rootdir,function (_stat) {stat=_stat});
+ },
+ function () {
+ if (stat!=Status.STD_OK) {
+ throw stat;
+ }
+ modify_dir(rootdir,function (_stat) {
+ stat=_stat;
+ });
+ }, function () {
+ rootdir.dd_lock.release();
+ callback(stat,rootdir);
+ }
+ ], function (e) {
+ if (rootdir!=undefined) rootdir.dd_lock.release();
+ if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
+ });
+ }
+
+
+ var dir = undefined;
+ Sch.ScheduleBlock([
+ function () {
+ Io.out('[DNS] Creating the root directory ... ');
+ create_root(function (_stat,_dir) {
+ stat = _stat;
+ dir = _dir;
+ })
+ },
+ function () {
+ if (stat == Status.STD_OK) {
+ Io.out('[DNS] Done.');
+ self.rootdir = dir;
+ }
+ else Io.out('[DNS] Creation of root directory failed: ' + Status.print(stat));
+ Io.close(part_fd);
+ self.dns_part_fd=undefined;
+ Io.out('[DNS] Finished.');
+ callback(stat);
+ }
+ ])
+ }
+ try {
+ create (callback);
+ } catch (e) {
+ if (self.dns_part_fd) Io.close(self.dns_part_fd);
+ self.dns_part_fd=undefined;
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.create_fs');
+ callback(Status.STD_IOERR);
+ }
+ throw Error(e);
+ }
+};
+
+/** Open the DNS system (one inode partition, UNIX file) [BLOCKING]
+ **
+ ** part_inode: Directory Mapping Partition File
+ ** cache: Cache parameters
+ *
+ *
+ * @param {string} part_inode
+ * @param {dns_cache_parameter} cache_params
+ * @param {function(Status.STD_OK|*)} callback
+ */
+dns_server.prototype.open_fs = function (part_inode,cache_params,callback) {
+ /**
+ *
+ * @type {dns_server}
+ */
+ var self = this;
+ self.dns_super=Dns.Dns_super();
+ var sup;
+
+ function open(callback) {
+ var n, i,j;
+ self.dns_super=Dns.Dns_super();
+ sup = self.dns_super;
+
+ var stat = Status.STD_OK;
+ Io.out('[DNS] Opening DNS ...');
+ if (Io.exists(part_inode)) {
+ Io.out('[DNS] Using directory i-node partition ' + part_inode);
+ } else {
+ Io.out('[DNS] No existing directory i-node partition ' + part_inode);
+ throw Status.STD_IOERR;
+ }
+ self.dns_part = part_inode;
+ self.dns_part_fd = Io.open(part_inode, (self.iocache_bypass ? 'rs+' : 'r+'));
+ /*
+ ** Either the filesystem is read only or we have an
+ ** inconsistent filesystem. In that case, only read
+ ** request are allowed.
+ */
+ var readonly = false;
+ Io.out('[DNS] Reading the super block...');
+ var superbuf = Buf.Buffer();
+ n = Buf.buf_read(self.dns_part_fd, superbuf, 0, Dns.DEF_BLOCK_SIZE);
+ if (n != Dns.DEF_BLOCK_SIZE) {
+ Io.out('[DNS] Cannot read super block.');
+ Io.close(self.dns_part_fd);
+ throw Status.STD_IOERR;
+ }
+ var magic1 = Buf.buf_get_string(superbuf);
+ if (!String.equal(magic1, Dns.MAGIC_STR)) {
+ Io.out('[DNS] Invalid i-node partition magic found, got ' + magic1 + ', but exptected ' + Dns.MAGIC_STR);
+ throw Status.STD_IOERR;
+ } else {
+ Io.out('[DNS] Found i-node partition magic ' + magic1);
+ }
+ Buf.buf_set_pos(superbuf, Dns.DEF_MAGIC_SIZE);
+ sup.dns_name = Buf.buf_get_string(superbuf);
+ Buf.buf_set_pos(superbuf, 256 + Dns.DEF_MAGIC_SIZE);
+ sup.dns_ndirs = Buf.buf_get_int32(superbuf);
+ sup.dns_block_size = Buf.buf_get_int32(superbuf);
+ sup.dns_getport = Buf.buf_get_port(superbuf);
+ sup.dns_putport = Buf.buf_get_port(superbuf);
+ sup.dns_checkfield = Buf.buf_get_port(superbuf);
+ sup.dns_ncols = Buf.buf_get_int32(superbuf);
+ var cols=[];
+ var colmasks=[];
+
+ for(i=0;i inode used).
+ **
+ ** The first entry (obj=0) is used for the lock status
+ ** of the live table.
+ */
+ var live_size = sup.dns_ndirs+1;
+ var live_table = Buf.Buffer();
+ var live_off = 512 + (sup.dns_ndirs*Dns.DNS_INODE_SIZE)
+ /**
+ *
+ * @param {number} obj
+ * @param {number} time
+ * @param {number} flag
+ */
+ function live_set(obj, time, flag) {
+ Buf.buf_set(live_table, obj, ((time & 0x7f) | ((flag << 7) & 0x80)));
+ }
+
+ /**
+ *
+ * @param {number} obj
+ * @returns {{time: number, flag: number}}
+ */
+ function live_get(obj) {
+ var v = Buf.buf_get(live_table, obj);
+ return {time: (v & 0x7f), flag: ((v & 0x80) >> 7)}
+ }
+
+ /**
+ *
+ * @returns {number}
+ */
+ function live_read() {
+ var n = Buf.buf_read(self.dns_part_fd, live_table, live_off, live_size);
+ if (n != live_size) {
+ return Status.STD_IOERR;
+ }
+ var live = live_get(0);
+ if (live.time == 0 && live.flag == 1) {
+ /*
+ ** Now lock the live table. After a server crash,
+ ** the restarted server will found the lock and must
+ ** discard the live table. All server objects got
+ ** the maximal live time!
+ */
+ live_set(0, 1, 1);
+ n = Buf.buf_write(self.dns_part_fd, live_table, live_off, 1);
+ if (n != 1) return Status.STD_IOERR;
+ else return Status.STD_OK;
+ } else return Status.STD_ARGBAD;
+ }
+
+ /**
+ *
+ * @returns {number}
+ */
+ function live_write() {
+ /*
+ ** Unlock the live table
+ */
+ live_set(0, 0, 1);
+ n = Buf.buf_write(self.dns_part_fd, live_table, live_off);
+ if (n != live_size) return Status.STD_IOERR;
+ if (self.sync_write_mode) Io.sync(self.dns_part_fd);
+ return Status.STD_OK;
+ }
+
+ self.live_set = live_set;
+ self.live_get = live_get;
+ self.live_read = live_read;
+ self.live_write = live_write;
+
+ /*
+ ** Read the live table
+ */
+ Io.out('[DNS] Reading the live time table...');
+ stat = live_read();
+ if (stat == Status.STD_ARGBAD) {
+ Io.out('[DNS] Found locked live table: Reinitialize...');
+ for (i = 0; i < sup.dns_ndirs - 1; i++) {
+ live_set(i, Dns.DNS_MAX_LIVE, 0);
+ }
+ } else if (stat == Status.STD_IOERR) {
+ Io.out('[DNS] IO Error.');
+ Io.close(self.dns_part_fd);
+ throw Status.STD_IOERR;
+ }
+ Io.out('[DNS] Ok.');
+ stat=Status.STD_OK;
+
+ /**
+ ** Raw disk Read and Write functions for the cache module.
+ **
+ ** Units:
+ ** addr: blocks
+ ** size: bytes
+ **
+ */
+ /**
+ *
+ * @param obj
+ * @param addr
+ * @param data
+ * @param size
+ * @returns {number}
+ */
+ self.read_inode = function (obj, addr, data, size) {
+ var off = addr * sup.dns_block_size;
+ Io.log((log < 10) || ('read_inode obj=' + obj + ' addr=' + addr + ' size=' + size));
+ var n = Buf.buf_read(self.dns_part_fd, data, off, size);
+ if (n == size)
+ return Status.STD_OK;
+ else
+ return Status.STD_SYSERR;
+
+ };
+
+ /**
+ *
+ * @param obj
+ * @param addr
+ * @param data
+ * @param size
+ * @returns {number}
+ */
+ self.write_inode = function (obj, addr, data, size) {
+ var off = addr * sup.dns_block_size;
+ Io.log((log < 10) || ('write_inode obj=' + obj + ' addr=' + addr + ' size=' + size));
+ var n = Buf.buf_write(self.dns_part_fd, data, off, size);
+ if (self.sync_write_mode) Io.sync(self.dns_part_fd);
+ if (n == size)
+ return Status.STD_OK;
+ else
+ return Status.STD_SYSERR;
+ };
+
+ self.sync_inode = function (obj) {
+ };
+
+ Io.out('[DNS] Creating inode cache ' + cache_params.c_inode_size + '[' + cache_params.c_inode_buffers + '] ... ');
+
+ /*
+ ** Note: I-nodes are handled only on buffer block level
+ ** #i-node=cache_params.c_inode_size/DEF_INODE_SIZE
+ */
+ var cache_inode =
+ Fcache.Fcache(
+ 'DNS Inode',
+ cache_params.c_inode_buffers,
+ Dns.DEF_BLOCK_SIZE,
+ cache_params.c_inode_size,
+ self.read_inode,
+ self.write_inode,
+ self.sync_inode,
+ Fcache.Fs_cache_mode.Cache_R);
+ self.cache_inode = cache_inode;
+ if (self.cache_inode == undefined) {
+ Io.out('[DNS] IO Error.');
+ Io.close(self.dns_part_fd);
+ throw Status.STD_IOERR;
+ }
+ /*
+ ** The i-node cache object. One object for all i-nodes!
+ ** Disk address = obj = 1 [blocks]
+ */
+ var res = cache_inode.cache_lookup(1, 1, Dns.off_of_inode(sup.dns_ndirs), Afs.Afs_file_state.FF_unlocked);
+ var inode_fse = res.fse;
+ self.inode_cache_entry = inode_fse;
+
+ /*
+ ** Some i-node utils
+ */
+ self.inode_of_obj = function(obj) {
+ try {
+ var inode = undefined;
+ var buf = Buf.Buffer();
+ var stat = cache_inode.cache_read(inode_fse, buf, Dns.off_of_inode(obj), Dns.DNS_INODE_SIZE);
+ if (stat != Status.STD_OK) return {stat: stat, inode: undefined};
+ inode = Dns.buf_get_inode(buf);
+ return {stat: stat, inode: inode};
+ } catch (e) {
+ return {stat: Status.STD_SYSERR, inode: undefined};
+ }
+ };
+
+ /**
+ *
+ * @param {number} obj
+ * @returns {{stat:(Status.STD_OK|*),cap:(capability|undefined),inode:(dns_inode|undefined)}}
+ */
+ self.cap_of_obj = function(obj) {
+ var stat;
+ var si = self.inode_of_obj(obj);
+ if (si.stat != Status.STD_OK) return {stat:stat,cap:undefined,inode:undefined};
+ var cap = si.inode.i_caps[self.fs_default];
+ if (si.inode.i_state == Afs.Afs_file_state.FF_locked ||
+ si.inode.i_state == Afs.Afs_file_state.FF_unlocked) stat=Status.STD_OK;
+ else stat=Status.STD_SYSERR;
+ return {stat:stat,cap:cap,inode:si.inode};
+ };
+
+ /**
+ *
+ * @param {dns_inode} inode
+ * @returns {Status.STD_OK|*}
+ */
+ self.update_inode = function(inode) {
+ try {
+ var buf = Buf.Buffer();
+ Dns.buf_put_inode(buf, inode);
+ var stat = cache_inode.cache_write(inode_fse, buf, Dns.off_of_inode(inode.i_objnum),Dns.DEF_INODE_SIZE);
+ return stat;
+ } catch (e) {
+ return Status.STD_SYSERR;
+ }
+ };
+
+ var cache_data = Cache.Cache(cache_params.c_dir_buffers);
+
+ self.cache_data = cache_data;
+
+ if (cache_data == undefined) {
+ Io.out('[DNS] IO Error.');
+ Io.close(self.dns_part_fd);
+ throw Status.STD_IOERR;
+ }
+
+ /*
+ ** Round up the value x to block_size
+ */
+
+ function block(x) {
+ return Dns.ceil_block_bytes(x, sup.dns_block_size);
+ }
+
+ function to_block(x) {
+ return div(x, sup.dns_block_size);
+ }
+
+ function of_block(x) {
+ return (x * sup.dns_block_size);
+ }
+
+ self.block = block;
+ self.to_block = to_block;
+ self.of_block = of_block;
+ /*
+ ** Read the i-node table.
+ ** Build a list with free i-node object numbers below the
+ ** nextfree boundary (above nextfree there are all
+ ** free i-nodes, if any).
+ */
+ Io.out('[DNS] reading the i-node table ...');
+ var freeino=[];
+ var firstfree=-1;
+ var nextfree=-1;
+ var nused=0;
+ var inodeb = Buf.Buffer();
+ for(i=1;i 0) {
+
+ /*
+ ** There are free i-nodes at the end of the i-node table.
+ */
+ nextfree = firstfree;
+ }
+
+
+ sup.dns_nused = nused;
+ sup.dns_freeobjnums = freeino;
+ sup.dns_nextfree = nextfree;
+
+ Io.out('[DNS] Found ' + sup.dns_nused + ' used Inode(s)');
+
+ Io.log((log < 0) || (self.dns_super.print()));
+ /*
+ ** Read the root directory...
+ */
+ self.acquire_dir(1,function (_stat,_dir) {
+ stat=_stat;
+ self.rootdir=_dir;
+ callback(stat);
+ });
+
+ }
+ try {
+ open(callback);
+ } catch (e) {
+ if (typeof e == 'number') callback(e); else {
+ Io.printstack(e,'Dns_server.open_fs');
+ callback(Status.STD_IOERR);
+ }
+ // throw Error(e);
+ }
+};
+
+
+/*
+ ** ================
+ ** PUBLIC INTERFACE
+ ** ================
+ */
+
+var DnsRpc = Require('dos/dns_srv_rpc');
+//afs_server.prototype=new AfsRpc.afs_server_rpc();
+dns_server.prototype.dns_lookup=DnsRpc.dns_server_rpc.prototype.dns_lookup;
+dns_server.prototype.dns_create=DnsRpc.dns_server_rpc.prototype.dns_create;
+dns_server.prototype.dns_delete=DnsRpc.dns_server_rpc.prototype.dns_delete;
+dns_server.prototype.dns_delete_dir=DnsRpc.dns_server_rpc.prototype.dns_delete_dir;
+dns_server.prototype.dns_append=DnsRpc.dns_server_rpc.prototype.dns_append;
+dns_server.prototype.dns_rename=DnsRpc.dns_server_rpc.prototype.dns_rename;
+dns_server.prototype.dns_replace=DnsRpc.dns_server_rpc.prototype.dns_replace;
+dns_server.prototype.dns_info=DnsRpc.dns_server_rpc.prototype.dns_info;
+dns_server.prototype.dns_stat=DnsRpc.dns_server_rpc.prototype.dns_stat;
+dns_server.prototype.dns_touch=DnsRpc.dns_server_rpc.prototype.dns_touch;
+dns_server.prototype.dns_age=DnsRpc.dns_server_rpc.prototype.dns_age;
+dns_server.prototype.dns_restrict=DnsRpc.dns_server_rpc.prototype.dns_restrict;
+dns_server.prototype.dns_exit=DnsRpc.dns_server_rpc.prototype.dns_exit;
+dns_server.prototype.dns_list=DnsRpc.dns_server_rpc.prototype.dns_list;
+dns_server.prototype.dns_setlookup=DnsRpc.dns_server_rpc.prototype.dns_setlookup;
+dns_server.prototype.dns_start_server=DnsRpc.dns_server_rpc.prototype.dns_start_server;
+
+module.exports = {
+ /**
+ *
+ * @param rpc
+ * @returns {dns_server}
+ */
+ Server: function(rpc,options) {
+ var obj = new dns_server(rpc,options);
+ Object.preventExtensions(obj);
+ return obj;
+ }
+};