From b74affcd518fb3a4c18797c37e1bf0864b36c284 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 22:48:04 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/dos/dns_srv.js | 2169 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 2169 insertions(+) create mode 100644 js/dos/dns_srv.js 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; i0)||('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; + } +};