/** ** ============================== ** 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; } };