2170 lines
69 KiB
JavaScript
2170 lines
69 KiB
JavaScript
/**
|
|
** ==============================
|
|
** O O O OOOO
|
|
** O O O O O O
|
|
** O O O O O O
|
|
** OOOO OOOO O OOO OOOO
|
|
** O O O O O O O
|
|
** O O O O O O O
|
|
** OOOO OOOO O O OOOO
|
|
** ==============================
|
|
** BSSLAB, Dr. Stefan Bosse http://www.bsslab.de
|
|
**
|
|
** COPYRIGHT: THIS SOFTWARE, EXECUTABLE AND SOURCE CODE IS OWNED
|
|
** BY THE AUTHOR(S).
|
|
** THIS SOURCE CODE MAY NOT BE COPIED, EXTRACTED,
|
|
** MODIFIED, OR OTHERWISE USED IN A CONTEXT
|
|
** OUTSIDE OF THE SOFTWARE SYSTEM.
|
|
**
|
|
** $AUTHORS: Stefan Bosse
|
|
** $INITIAL: (C) 2015-2017 bLAB
|
|
** $CREATED: 23-4-15 by sbosse.
|
|
** $VERSION: 1.3.8
|
|
**
|
|
** $INFO:
|
|
** DNS Server with local file system storage and row capability cache.
|
|
*
|
|
* $ENDINFO
|
|
*/
|
|
"use strict";
|
|
|
|
var util = Require('util');
|
|
var Io = Require('com/io');
|
|
var Net = Require('dos/network');
|
|
var Std = Require('dos/std');
|
|
var Sch = Require('dos/scheduler');
|
|
var Buf = Require('dos/buf');
|
|
var Rpc = Require('dos/rpc');
|
|
var Cs = Require('dos/capset');
|
|
var Comp = Require('com/compat');
|
|
var String = Comp.string;
|
|
var Array = Comp.array;
|
|
var Perv = Comp.pervasives;
|
|
var Printf = Comp.printf;
|
|
var div = Perv.div;
|
|
var assert = Comp.assert;
|
|
var Rand = Comp.random;
|
|
var Status = Net.Status;
|
|
var Command = Net.Command;
|
|
var Rights = Net.Rights;
|
|
var AfsInt = Require('dos/afs');
|
|
var Afs = Require('dos/afs_srv_common');
|
|
var Dns = Require('dos/dns_srv_common');
|
|
var DnsInt = Require('dos/dns');
|
|
var Fs = Require('fs');
|
|
var Fcache = Require('dos/fcache');
|
|
var Cache = Require('dos/cache');
|
|
var Afs_file_state = Afs.Afs_file_state;
|
|
|
|
var log = 0;
|
|
var CACHETMO = 30; // seconds
|
|
|
|
/** DNS Dns_server
|
|
*
|
|
* @param {rpcint} rpc
|
|
* @constructor
|
|
* @typedef {{rpc,afs,std,cs,dns_super:dns_super,dns_part:string,dns_part_fd,cache_inode,cache_data,inode_cache_entry,
|
|
* rootdir:dns_dir,fs_default,
|
|
* block,to_block,of_block,
|
|
* live_set,live_get,live_read,live_write,
|
|
* read_inode,write_inode,sync_inode,
|
|
* inode_of_obj,cap_of_obj,update_inode,
|
|
* iocachebypass,sync_write_mode,
|
|
* stats}} dns_server~obj
|
|
* @see dns_server~obj
|
|
* @see dns_server~meth
|
|
*/
|
|
var dns_server = function (rpc,options) {
|
|
this.verbose=options.verbose||0;
|
|
this.rpc=rpc;
|
|
this.afs=AfsInt.AfsInt(rpc);
|
|
this.std=Std.StdInt(rpc);
|
|
this.cs=Cs.CsInt(rpc);
|
|
/**
|
|
*
|
|
* @type {dns_super|undefined}
|
|
*/
|
|
this.dns_super=undefined;
|
|
this.dns_part=''; // DNS inode partition file path
|
|
this.dns_part_fd=undefined;
|
|
|
|
/**
|
|
*
|
|
* @type {fsc_cache|undefined}
|
|
*/
|
|
this.cache_inode = undefined;
|
|
/**
|
|
*
|
|
* @type {cache|undefined}
|
|
*/
|
|
this.cache_data = undefined;
|
|
this.inode_cache_entry=undefined;
|
|
|
|
|
|
/** Capability cache to speed-up row
|
|
* capability restriction using a column mask.
|
|
* Key: cap-key+mask
|
|
* Value: Restricted cap
|
|
*
|
|
*/
|
|
this.cache_cap = Cache.Cache(1000);
|
|
|
|
|
|
this.rootdir=undefined;
|
|
this.fs_default=0;
|
|
|
|
this.block=function() {};
|
|
this.to_block=function() {};
|
|
this.of_block=function() {};
|
|
|
|
/**
|
|
*
|
|
* @param {number} obj
|
|
* @param {number} time
|
|
* @param {number} flag
|
|
*/
|
|
this.live_set=function (obj,time,flag) {};
|
|
/**
|
|
*
|
|
* @param {number} obj
|
|
* @returns {{time: number, flag: number}}
|
|
*/
|
|
this.live_get=function (obj) {return {time:0,flag:0}};
|
|
/**
|
|
*
|
|
* @returns {(Status.STD_OK|*)}
|
|
*/
|
|
this.live_read=function () {return 0;};
|
|
/**
|
|
*
|
|
* @returns {(Status.STD_OK|*)}
|
|
*/
|
|
this.live_write=function () {return 0;};
|
|
|
|
/**
|
|
*
|
|
* @param obj
|
|
* @param addr
|
|
* @param data
|
|
* @param size
|
|
* @returns {number}
|
|
*/
|
|
this.read_inode=function(obj,addr,data,size) {return 0};
|
|
/**
|
|
*
|
|
* @param obj
|
|
* @param addr
|
|
* @param data
|
|
* @param size
|
|
* @returns {number}
|
|
*/
|
|
this.write_inode=function(obj,addr,data,size) {return 0};
|
|
this.sync_inode=function() {};
|
|
|
|
/**
|
|
*
|
|
* @param {number} obj
|
|
* @returns {{stat:(Status.STD_OK|*),inode:(dns_inode|undefined)}}
|
|
*/
|
|
this.inode_of_obj=function(obj){return {stat:0,inode:undefined}};
|
|
/**
|
|
*
|
|
* @param {number} obj
|
|
* @returns {{stat:(Status.STD_OK|*),cap:(capability|undefined),inode:(dns_inode|undefined)}}
|
|
*/
|
|
this.cap_of_obj=function(obj){return {stat:0,cap:undefined,inode:undefined}};
|
|
this.update_inode=function(){};
|
|
|
|
this.iocache_bypass=false;
|
|
this.sync_write_mode=false;
|
|
|
|
this.stats = {
|
|
op_read:0,
|
|
op_modify:0,
|
|
op_create:0,
|
|
op_destroy:0,
|
|
op_touch:0,
|
|
op_age:0
|
|
}
|
|
};
|
|
|
|
/** Initialite and start up services
|
|
*
|
|
*/
|
|
|
|
dns_server.prototype.init = function () {
|
|
var self=this;
|
|
Sch.AddTimer(1000,'DNS Server Garbage Collector',function () {
|
|
self.cache_cap.refresh(function (key,data) {
|
|
if (data.tmo) { data.tmo--; return data.tmo>0}
|
|
else return true;
|
|
});
|
|
});
|
|
}
|
|
|
|
/** Get the current system time in 10s units
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
dns_server.prototype.time =function () {
|
|
var self=this;
|
|
return div(Perv.time(),10);
|
|
};
|
|
|
|
|
|
/** Acquire and lock a directory [BLOCKING]
|
|
*
|
|
* @param {number} obj
|
|
* @param {function((Status.STD_OK|*),(dns_dir|undefined))} callback
|
|
*/
|
|
dns_server.prototype.acquire_dir = function (obj,callback) {
|
|
var self=this;
|
|
var dir=undefined;
|
|
var stat;
|
|
Sch.ScheduleBlock([
|
|
function () {self.dns_super.lock();},
|
|
function () {
|
|
var res;
|
|
res=self.read_dir(obj,function (_stat,_dir) {
|
|
stat=_stat;
|
|
dir=_dir;
|
|
});
|
|
if (stat==Status.STD_OK) {
|
|
dir.lock();
|
|
}
|
|
},
|
|
function () {
|
|
self.dns_super.unlock();
|
|
Io.log((log < 1) || ('Dns_srv.acquire_dir: done ' + obj));
|
|
callback(stat,dir);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.acquire_dir');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
});
|
|
};
|
|
|
|
/** Release an acquired directory. Flush all pending writes - the
|
|
** super structure and the i-node table, and the directory itself
|
|
** if modified. Return the status of the operation. [BLOCKING]
|
|
*
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.release_dir = function (dir,callback) {
|
|
var stat=Status.STD_OK;
|
|
Io.log((log < 1) || ('dns_srv.release_dir: ' + util.inspect(dir)));
|
|
if (dir != undefined) {
|
|
if ((dir.dd_state == Dns.Dns_dir_state.DD_unlocked ||
|
|
dir.dd_state == Dns.Dns_dir_state.DD_modified)) {
|
|
this.modify_dir(dir, function (_stat) {
|
|
callback(_stat);
|
|
});
|
|
}
|
|
else {
|
|
stat = Status.STD_OK;
|
|
dir.unlock();
|
|
if (callback) callback(stat);
|
|
}
|
|
} else {
|
|
stat=Status.STD_ARGBAD;
|
|
if (callback!=undefined) callback(stat);
|
|
}
|
|
};
|
|
/** Release an acquired but unmodified directory. Flush all pending writes - the
|
|
** super structure and the i-node table.
|
|
** Return the status of the operation.
|
|
*
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @returns {(Status.STD_OK|*)}
|
|
*
|
|
* @param dir
|
|
*/
|
|
dns_server.prototype.release_unmodified_dir = function (dir) {
|
|
var stat=Status.STD_OK;
|
|
Io.log((log < 1) || ('dns_srv.release_unmodified_dir: ' + util.inspect(dir)));
|
|
if (dir != undefined) {
|
|
if ((dir.dd_state == Dns.Dns_dir_state.DD_unlocked ||
|
|
dir.dd_state == Dns.Dns_dir_state.DD_modified)) {
|
|
Io.out('Dns_srv.release_unmodified_dir: Directory '+dir.dd_objnum+' was modified!');
|
|
return Status.STD_SYSERR;
|
|
}
|
|
else {
|
|
stat = Status.STD_OK;
|
|
dir.unlock();
|
|
return stat;
|
|
}
|
|
} else {
|
|
return Status.STD_ARGBAD;
|
|
}
|
|
};
|
|
|
|
|
|
/** Lookup a directory object (priv.prv_obj) and check the required rights.
|
|
* The directory is returned unlocked!
|
|
*
|
|
* @param {privat} priv
|
|
* @param {number} req
|
|
* @param {function((Status.STD_OK|*),(dns_dir|undefined))} callback
|
|
*/
|
|
dns_server.prototype.lookup_dir = function(priv,req,callback) {
|
|
var self = this;
|
|
var obj = Net.prv_number(priv);
|
|
var stat;
|
|
var dir;
|
|
|
|
if (obj < 1 || obj >= this.dirs_top) {
|
|
callback(Status.STD_ARGBAD,undefined);
|
|
} else Sch.ScheduleBlock([
|
|
function () {
|
|
Io.log((log<1)||('Dns_server.lookup_dir: read_dir('+obj+')'));
|
|
self.read_dir(obj,function (_stat,_dir) {
|
|
stat=_stat;
|
|
dir=_dir;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
if (!Net.prv_decode(priv, dir.dd_random)) {
|
|
throw Status.STD_DENIED;
|
|
}
|
|
/*
|
|
** When checking the presence of column rights, only take the
|
|
** *actual*
|
|
** columns present into acount (i.e., do not use dns_COLMASK here)
|
|
*/
|
|
var ncols=dir.dd_ncols;
|
|
var rights = Net.prv_rights(priv);
|
|
var colbits = Dns.dns_col_bits[ncols];
|
|
var colmask = colbits-1;
|
|
|
|
Io.log((log<1)||('Dns_server.lookup_dir: '+Net.Print.private(priv))+' req='+req+' colmask='+colmask+' rights='+rights);
|
|
|
|
if ((rights & colmask) == 0 ||
|
|
(rights & req) != req) {
|
|
throw Status.STD_DENIED;
|
|
}
|
|
callback(Status.STD_OK,dir);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.lookup_dir');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
/** A client request arrived. Enter the critical section. Check the capability.
|
|
** If it is correct, check whether the required rights are present or not.
|
|
** Return the directory structure, but only if the required rights are present.
|
|
*
|
|
* @param priv
|
|
* @param req
|
|
* @param callback
|
|
*/
|
|
dns_server.prototype.request_dir = function(priv,req,callback) {
|
|
var self = this;
|
|
var obj = Net.prv_number(priv);
|
|
var stat;
|
|
var dir;
|
|
|
|
if (obj < 1 || obj >= this.dirs_top) {
|
|
callback(Status.STD_ARGBAD,undefined);
|
|
} else Sch.ScheduleBlock([
|
|
function () {
|
|
Io.log((log<1)||('Dns_server.request_dir: acquire_dir('+obj+')'));
|
|
self.acquire_dir(obj,function (_stat,_dir) {
|
|
stat=_stat;
|
|
dir=_dir;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
if (!Net.prv_decode(priv, dir.dd_random)) {
|
|
self.release_unmodified_dir(dir);
|
|
throw Status.STD_DENIED;
|
|
}
|
|
/*
|
|
** When checking the presence of column rights, only take the
|
|
** *actual*
|
|
** columns present into acount (i.e., do not use dns_COLMASK here)
|
|
*/
|
|
var ncols=dir.dd_ncols;
|
|
var rights = Net.prv_rights(priv);
|
|
var colbits = Dns.dns_col_bits[ncols];
|
|
var colmask = colbits-1;
|
|
|
|
Io.log((log<1)||('Dns_server.request_dir: '+Net.Print.private(priv))+' req='+req+' colmask='+colmask+' rights='+rights);
|
|
|
|
if ((rights & colmask) == 0 ||
|
|
(rights & req) != req) {
|
|
self.release_unmodified_dir(dir);
|
|
throw Status.STD_DENIED;
|
|
}
|
|
callback(Status.STD_OK,dir);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.request_dir');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
** With a given directory capability set check if this directory belongs
|
|
** to this server.
|
|
*
|
|
*
|
|
* @param {capset} dir
|
|
* @returns {private|undefined}
|
|
*/
|
|
dns_server.prototype.check_dir = function(dir) {
|
|
var self=this;
|
|
Io.log((log<1)||('Dns_server.check_dir: '+Cs.Print.capset(dir)));
|
|
if (dir==undefined) return undefined;
|
|
for (var i = 0; i<dir.cs_final;i++) {
|
|
var s = dir.cs_suite[i];
|
|
if (s.s_current == true) {
|
|
var cap = s.s_object;
|
|
if (Net.Equal.port(cap.cap_port, self.dns_super.dns_putport) == true &&
|
|
Net.prv_number(cap.cap_priv) != 0) {
|
|
/*
|
|
** A capability for me. Check it.
|
|
*/
|
|
return cap.cap_priv;
|
|
}
|
|
}
|
|
}
|
|
Io.log((log<1)||'Dns_server.check_dir: not for me');
|
|
return undefined;
|
|
};
|
|
|
|
|
|
/** Calculate the size of a directory structure.
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @returns {number}
|
|
*/
|
|
dns_server.prototype.dir_size = function (dir) {
|
|
return dir.size();
|
|
};
|
|
|
|
/** Return a free objnum in the directory table (index).
|
|
** Note: protect this function with the server lock untill
|
|
** the directory creation is finished.
|
|
*
|
|
* @returns {number}
|
|
*/
|
|
dns_server.prototype.get_freeobjnum = function () {
|
|
var self=this;
|
|
var sup=this.dns_super;
|
|
var obj=-1;
|
|
Array.match(sup.dns_freeobjnums,
|
|
function(hd,tl){
|
|
sup.dns_freeobjnums=tl;
|
|
obj=hd;
|
|
},
|
|
function () {
|
|
obj=sup.dns_nextfree;
|
|
if((obj+1)==sup.dns_ndirs) {
|
|
Io.out('[DNS] Out of directory slots.');
|
|
throw Status.STD_NOSPACE;
|
|
}
|
|
sup.dns_nextfree=obj+1;
|
|
});
|
|
return obj;
|
|
};
|
|
|
|
/** Convert a directory structure into a capset with maybe
|
|
** restricted rights.
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @returns {capset}
|
|
*/
|
|
dns_server.prototype.capset_of_dir = function(dir,rights) {
|
|
var self=this;
|
|
var sup=this.dns_super;
|
|
var cs;
|
|
var cap = Net.Capability(
|
|
sup.dns_putport,
|
|
Net.prv_encode(dir.dd_objnum,rights,dir.dd_random)
|
|
);
|
|
cs=this.cs.cs_singleton(cap);
|
|
return cs;
|
|
};
|
|
|
|
/** Restrict a directory capability set.
|
|
* The directoty structure from capability set 'dir' must be unlocked to avoid.
|
|
*
|
|
* @param {capset} cs
|
|
* @param mask
|
|
* @param {function((Status.STD_OK|*),capset|undefined)} callback
|
|
*/
|
|
dns_server.prototype.restrict = function(cs,mask,callback) {
|
|
var self=this,
|
|
cs=Cs.Copy.capset(cs),
|
|
stat=Status.STD_CAPBAD,
|
|
index=0;
|
|
Sch.ScheduleLoop(function() {
|
|
return (stat!=Status.STD_OK && index<cs.cs_final);
|
|
},[
|
|
function () {
|
|
var s = cs.cs_suite[index],
|
|
cap,key,cache;
|
|
index++;
|
|
if (s.s_current == true) {
|
|
cap = s.s_object;
|
|
|
|
if (Net.Equal.port(cap.cap_port, self.dns_super.dns_putport) == true &&
|
|
Net.prv_number(cap.cap_priv) != 0) {
|
|
/*
|
|
** A capability for me. Restrict it.
|
|
*/
|
|
self.lookup_dir(cap.cap_priv,0,function (_stat,_dir) {
|
|
if (_stat==Status.STD_OK) {
|
|
cap.cap_priv=Net.prv_encode(cap.cap_priv.prv_obj,
|
|
cap.cap_priv.prv_rights & mask,
|
|
_dir.dd_random);
|
|
}
|
|
stat=_stat;
|
|
}); // dir is unlocked, no release!
|
|
} else {
|
|
Io.log((log<1)||('Dns_server.restrict: doing std_restrict for '+Net.Print.capability(cap)+' with mask='+mask));
|
|
// Try to use capability cache - but can be outdated
|
|
key=Net.key(cap);
|
|
cache=self.cache_cap.lookup(key);
|
|
|
|
if (cache && cache.restrict && cache.restrict[mask]) {
|
|
Net.Copy.private(cache.restrict[mask].cap_priv,cap.cap_priv);
|
|
stat=cache.stat;
|
|
} else self.std.std_restrict(cap,mask,function (_stat,_cap) {
|
|
Io.log((log<1)||('Dns_server.restrict: std_restrict returned: '+Status.print(_stat)));
|
|
stat=_stat;
|
|
if (stat==Status.STD_OK) {
|
|
if (cache && cache.restrict) {
|
|
cache.stat=stat;
|
|
cache.restrict[mask]=_cap;
|
|
cache.tmo=CACHETMO;
|
|
} else
|
|
self.cache_cap.add(key,{restrict:self.cache_cap.map(mask,_cap),
|
|
tmo:CACHETMO,stat:stat});
|
|
Net.Copy.private(_cap.cap_priv,cap.cap_priv);
|
|
} else {
|
|
if (cache && cache.restrict) {
|
|
cache.stat=stat;
|
|
cache.restrict[mask]=Net.nilcap;
|
|
cache.cap=cap
|
|
} else
|
|
self.cache_cap.add(key,{restrict:self.cache_cap.map(mask,Net.nilcap),
|
|
tmo:CACHETMO,stat:stat,cap:cap});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
],[
|
|
function () {
|
|
callback(stat,cs);
|
|
}
|
|
],function (e) {
|
|
if (typeof e != 'number') {Io.printstack(e,'Dns_server.restrict');}
|
|
if (typeof e == 'number') callback(e,undefined); else callback(Status.STD_SYSERR,undefined);
|
|
});
|
|
};
|
|
/** Get defualt file server.
|
|
*
|
|
*
|
|
*
|
|
* @param {function((Status.STD_OK|*),(Dns_mode.DNSMODE_ONECOPY|*))} callback
|
|
*/
|
|
dns_server.prototype.get_def_fs = function (callback) {
|
|
var self=this;
|
|
var sup = this.dns_super;
|
|
var fs = sup.dns_fs;
|
|
var cap = undefined;
|
|
var stat;
|
|
var goods = 0;
|
|
var goodcap = undefined;
|
|
var mode;
|
|
|
|
var index = 0;
|
|
Sch.ScheduleLoop(
|
|
function() {
|
|
return index<2;
|
|
}, [
|
|
function () {
|
|
cap = fs.fs_cap[index];
|
|
if (cap != undefined) {
|
|
self.std.std_info(cap,function (_stat,_info) {
|
|
stat=_stat;
|
|
})
|
|
} else stat=Status.STD_CAPBAD;
|
|
},
|
|
function () {
|
|
if (cap!=undefined) {
|
|
if (stat == Status.STD_OK) {
|
|
fs.fs_state[index] = Dns.Fs_state.FS_up;
|
|
goods++;
|
|
if (goodcap==undefined) {
|
|
goodcap=cap;
|
|
fs.fs_default=index;
|
|
}
|
|
}
|
|
else
|
|
fs.fs_state[index] = Dns.Fs_state.FS_down;
|
|
}
|
|
}
|
|
],[
|
|
function () {
|
|
if (goodcap==undefined) stat=Status.STD_NOTFOUND;
|
|
if (goods==1) mode = Dns.Dns_mode.DNSMODE_ONECOPY;
|
|
if (goods==2) mode = Dns.Dns_mode.DNSMODE_TWOCOPY;
|
|
callback(stat,mode);
|
|
}
|
|
],
|
|
function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.get_def_fs');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
|
|
})
|
|
};
|
|
|
|
|
|
/**
|
|
** The create, read, modify and utility functions needed
|
|
** for the Dns_server module.
|
|
**
|
|
** Note:
|
|
** After a directory is locked, any modification of this
|
|
** directory leads to a new AFS file object and a new
|
|
** directory capability!
|
|
** In one copy mode, the old cap will be destroyed, after
|
|
** the new one is committed and the i-node mapping was
|
|
** updated.
|
|
*/
|
|
|
|
|
|
/** Read the directory content from an AFS file. [BLOCKING]
|
|
*
|
|
*/
|
|
dns_server.prototype.read_dir_file = function (inode,callback) {
|
|
var self=this;
|
|
var stat,dir;
|
|
var fs_default = self.dns_super.dns_fs.fs_default;
|
|
var buf = Buf.Buffer();
|
|
|
|
Sch.ScheduleBlock([
|
|
function () {
|
|
assert((inode.i_size>0)||('Dns_srv.read_dir_file: inode.i_size is zero'));
|
|
Io.log((log<1)||('Dns_server.read_dir_file cap='+Net.Print.capability(inode.i_caps[fs_default])+' '+util.inspect(inode)));
|
|
self.afs.afs_read(inode.i_caps[fs_default],buf,0,inode.i_size, function (_stat) {
|
|
stat=_stat;
|
|
if (stat==Status.STD_OK) dir=Dns.buf_get_dir(buf); else dir=undefined;
|
|
})
|
|
},
|
|
function () {
|
|
callback(stat,dir);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.read_dir_file');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
/** Read a directory specified with his object number (index) number.
|
|
* Either the directory is currently cached, or the directory is read from an AFS file.
|
|
*
|
|
* @param {number} obj
|
|
* @param {function((Status.STD_OK|*), dns_dir)} callback
|
|
*/
|
|
dns_server.prototype.read_dir = function (obj,callback) {
|
|
var self=this;
|
|
var stat,dir,inode;
|
|
this.stats.op_read++;
|
|
var si=this.inode_of_obj(obj);
|
|
stat=si.stat;inode=si.inode;
|
|
Io.log((log<1)||('Dns_server.read_dir obj='+obj));
|
|
if (stat!=Status.STD_OK) callback(stat,undefined);
|
|
else Sch.ScheduleBlock([
|
|
function () {
|
|
dir=self.cache_data.lookup(inode.i_objnum);
|
|
if(dir==undefined) stat=Status.STD_NOTFOUND; else stat=Status.STD_OK;
|
|
},
|
|
function () {
|
|
if (stat==Status.STD_NOTFOUND) self.read_dir_file(inode,function (_stat,_dir) {
|
|
stat = _stat;
|
|
dir = _dir;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat==Status.STD_OK) {
|
|
var tf=self.live_get(dir.dd_objnum);
|
|
dir.dd_live=tf.time;
|
|
}
|
|
callback(stat,dir);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e,undefined); else {
|
|
Io.printstack(e,'Dns_server.read_dir');
|
|
callback(Status.STD_SYSERR,undefined);
|
|
}
|
|
});
|
|
|
|
};
|
|
|
|
/**
|
|
* Write an existing directory (only if modified).
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.modify_dir = function (dir,callback) {
|
|
var i;
|
|
var stat;
|
|
var self = this;
|
|
var oldcaps;
|
|
var cap = undefined;
|
|
var obj = dir.dd_objnum;
|
|
var inode,inode2,dirsize;
|
|
var fs = this.dns_super.dns_fs;
|
|
var fs_default=fs.fs_default;
|
|
var buf = Buf.Buffer();
|
|
var si=this.inode_of_obj(obj);
|
|
stat=si.stat;inode=si.inode;
|
|
Io.log((log<1)||('Dns_server.modify_dir dir='+util.inspect(dir)));
|
|
this.stats.op_modify++;
|
|
/*
|
|
** Distinguish these two cases:
|
|
** dd_state = DD_modified -> a new AFS object must be created
|
|
** dd_state = DD_unlocked -> commit the AFS object
|
|
*/
|
|
if (stat!=Status.STD_OK) callback(stat);
|
|
else
|
|
{
|
|
if (dir.dd_state==Dns.Dns_dir_state.DD_modified)
|
|
Sch.ScheduleBlock([
|
|
/*
|
|
** Create a new AFS object. If in two copy mode,
|
|
** the copy will be duplicated later.
|
|
** Destroy the old capabilities after this oepration
|
|
** succeeded.
|
|
*/
|
|
function () {
|
|
oldcaps = Array.map(inode.i_caps,function (cap) {return Net.Copy.capability(cap)});
|
|
dirsize=dir.size();
|
|
Dns.buf_put_dir(buf,dir);
|
|
self.afs.afs_create(fs.fs_cap[fs_default],buf,dirsize,Afs.Afs_commit_flag.AFS_SAFETY,function (_stat,_cap) {
|
|
stat=_stat;
|
|
cap=_cap;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
dir.dd_state=Dns.Dns_dir_state.DD_locked;
|
|
var caps=[Net.nilcap,Net.nilcap];
|
|
caps[fs_default]=cap;
|
|
inode2=Dns.Dns_inode(obj,caps,dirsize,Afs.Afs_file_state.FF_locked);
|
|
Buf.buf_init(buf);
|
|
Dns.buf_put_inode(buf,inode2);
|
|
/*
|
|
** Write the inode data through the cache to disk ...
|
|
*/
|
|
stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
i=0;
|
|
Sch.ScheduleLoop(function(index) {
|
|
i=index;
|
|
return index<2;
|
|
},[
|
|
/*
|
|
** Destroy the old caps.
|
|
*/
|
|
function () {
|
|
if(!Net.Equal.capability(oldcaps[i],Net.nilcap)) {
|
|
self.std.std_destroy(oldcaps[i],function (_stat) {
|
|
stat=_stat;
|
|
})
|
|
}
|
|
}
|
|
],[
|
|
function () {
|
|
stat=Status.STD_OK;
|
|
}
|
|
])
|
|
},
|
|
function () {
|
|
callback(stat)
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e); else {
|
|
Io.printstack(e,'Dns_server.modify_dir');
|
|
callback(Status.STD_SYSERR);
|
|
}
|
|
});
|
|
else // dir.dd_state = DD_unlocked
|
|
Sch.ScheduleBlock([
|
|
function () {
|
|
dirsize=self.dir_size(dir);
|
|
Dns.buf_put_dir(buf,dir);
|
|
self.afs.afs_modify(inode.i_caps[fs_default],buf,dirsize,0,Afs.Afs_commit_flag.AFS_SAFETY,function (_stat,_cap) {
|
|
stat=_stat;
|
|
cap=_cap;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
dir.dd_state=Dns.Dns_dir_state.DD_locked;
|
|
inode.i_size=dirsize;
|
|
inode.i_state=Afs.Afs_file_state.FF_locked;
|
|
Buf.buf_init(buf);
|
|
Dns.buf_put_inode(buf,inode);
|
|
stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
|
|
},
|
|
function () {
|
|
callback(stat)
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e); else {
|
|
Io.printstack(e,'Dns_server.modify_dir');
|
|
callback(Status.STD_SYSERR);
|
|
}
|
|
});
|
|
}
|
|
};
|
|
|
|
/** Insert a new created directory into the DNS table.
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @param {function((Status.STD_OK|*))} callback
|
|
*/
|
|
dns_server.prototype.create_dir = function (dir,callback) {
|
|
var stat;
|
|
var self = this;
|
|
var cap = undefined;
|
|
var obj = dir.dd_objnum;
|
|
var fs = this.dns_super.dns_fs;
|
|
var fs_default=fs.fs_default;
|
|
var buf = Buf.Buffer();
|
|
|
|
this.stats.op_create++;
|
|
Sch.ScheduleBlock([
|
|
function () {
|
|
/*
|
|
** Create a new AFS object. If in two copy mode,
|
|
** the copy will be duplicated later.
|
|
*/
|
|
self.afs.afs_create(fs.fs_cap[fs_default],buf,0,Afs.Afs_commit_flag.AFS_UNCOMMIT,function (_stat,_cap) {
|
|
stat=_stat;
|
|
cap=_cap;
|
|
})
|
|
},
|
|
function () {
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
dir.dd_state=Dns.Dns_dir_state.DD_unlocked;
|
|
self.live_set(obj,Dns.DNS_MAX_LIVE,1);
|
|
/*
|
|
** First read the inode from disk to update
|
|
** the inode cache (reads/writes always full blocks!).
|
|
*/
|
|
stat=self.cache_inode.cache_read(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DEF_INODE_SIZE);
|
|
|
|
if (stat!=Status.STD_OK) throw stat;
|
|
|
|
var fs_caps = [Net.nilcap,Net.nilcap];
|
|
fs_caps[fs_default]=cap;
|
|
var inode = Dns.Dns_inode(obj,fs_caps,0,Afs.Afs_file_state.FF_unlocked);
|
|
Buf.buf_init(buf);
|
|
Dns.buf_put_inode(buf,inode);
|
|
stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DEF_INODE_SIZE);
|
|
self.cache_data.add(obj,dir);
|
|
|
|
callback(stat);
|
|
}
|
|
],function (e) {
|
|
if (typeof e == 'number') callback(e); else {
|
|
Io.printstack(e,'Dns_server.create_dir');
|
|
callback(Status.STD_SYSERR);
|
|
}
|
|
})
|
|
};
|
|
|
|
/** Create a new directory. Return the directory structure and
|
|
** the status returned by the server create function. The super structure
|
|
** is already modified by this function. The new directory remains
|
|
** locked.
|
|
*
|
|
* @param colnames
|
|
* @param {function ((Status.STD_OK|*),(dns_dir|undefined))} callback
|
|
*/
|
|
dns_server.prototype.alloc_dir = function (colnames,callback) {
|
|
var self=this;
|
|
var sup=this.dns_super;
|
|
Sch.ScheduleBlock([
|
|
function () {
|
|
sup.lock();
|
|
},
|
|
function () {
|
|
var obj=self.get_freeobjnum();
|
|
var dir = Dns.Dns_dir(obj,colnames.length,0,colnames,Net.uniqport(),[],
|
|
Dns.Dns_dir_state.DD_unlocked,self.time(),Dns.DNS_MAX_LIVE);
|
|
assert(dir.try_lock());
|
|
sup.unlock();
|
|
self.create_dir(dir,function (_stat) {
|
|
if (_stat==Status.STD_OK) callback(_stat,dir);
|
|
else callback(_stat,undefined);
|
|
})
|
|
}
|
|
]);
|
|
};
|
|
|
|
|
|
/** Delete a directory. Destroy associated AFS objects.
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.delete_dir = function (dir,callback) {
|
|
var stat;
|
|
var i;
|
|
var self = this;
|
|
var cap = undefined;
|
|
var obj = dir.dd_objnum;
|
|
var fs = this.dns_super.dns_fs;
|
|
var fs_default=fs.fs_default;
|
|
var buf = Buf.Buffer();
|
|
var oldcaps;
|
|
/*
|
|
** Get the i-node mapping
|
|
*/
|
|
var inode;
|
|
this.stats.op_destroy++;
|
|
var si=this.inode_of_obj(obj);
|
|
stat=si.stat;inode=si.inode;
|
|
if (stat!=Status.STD_OK) callback(stat);
|
|
else {
|
|
self.live_set(obj,0,0);
|
|
oldcaps = Array.map(inode.i_caps,function (cap) {return Net.Copy.capability(cap)});
|
|
/*
|
|
** Destroy first the old caps
|
|
*/
|
|
i=0;
|
|
Sch.ScheduleLoop(function(index) {
|
|
i=index;
|
|
return index<2;
|
|
},[
|
|
/*
|
|
** Destroy the old caps.
|
|
*/
|
|
function () {
|
|
if(!Net.Equal.capability(oldcaps[i],Net.nilcap)) {
|
|
self.std.std_destroy(oldcaps[i],function (_stat) {
|
|
stat=_stat;
|
|
})
|
|
}
|
|
}
|
|
],[
|
|
function () {
|
|
stat=Status.STD_OK;
|
|
/*
|
|
** Now destroy ourself!
|
|
** Write the i-node data through the cache to disk ...
|
|
*/
|
|
inode.i_state=Afs_file_state.FF_invalid;
|
|
inode.i_size=0;
|
|
inode.i_caps=[Net.nilcap,Net.nilcap];
|
|
Dns.buf_put_inode(buf,inode);
|
|
stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
|
|
/*
|
|
** Invalidate cache entry.
|
|
*/
|
|
self.cache_data.invalidate(inode.i_objnum);
|
|
callback(stat);
|
|
}
|
|
], function(e) {
|
|
if (typeof e == 'number') callback(e); else {
|
|
Io.printstack(e,'Dns_server.delete_dir');
|
|
callback(Status.STD_SYSERR);
|
|
}
|
|
|
|
})
|
|
}
|
|
};
|
|
dns_server.prototype.dir_modified = function (dir) {
|
|
if (dir.dd_state == Dns.Dns_dir_state.DD_locked)
|
|
dir.dd_state = Dns.Dns_dir_state.DD_modified;
|
|
};
|
|
|
|
/**
|
|
* Delete an i-node without destroying AFS objects.
|
|
*
|
|
* @param {number} obj
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.delete_inode = function (obj,callback) {
|
|
var stat;
|
|
var self = this;
|
|
var buf = Buf.Buffer();
|
|
/*
|
|
** Get the inode mapping
|
|
*/
|
|
var inode;
|
|
this.stats.op_destroy++;
|
|
var si=this.inode_of_obj(obj);
|
|
stat=si.stat;inode=si.inode;
|
|
if (stat!=Status.STD_OK) callback(stat);
|
|
else {
|
|
/*
|
|
** Destroy ourself without destroying AFS objects!
|
|
** Write the i-node data through the cache to disk ...
|
|
*/
|
|
inode.i_state=Afs_file_state.FF_invalid;
|
|
inode.i_size=0;
|
|
inode.i_caps=[Net.nilcap,Net.nilcap];
|
|
Dns.buf_put_inode(buf,inode);
|
|
stat=self.cache_inode.cache_write(self.inode_cache_entry,buf,Dns.off_of_inode(obj),Dns.DNS_INODE_SIZE);
|
|
/*
|
|
** Invalidate cache entry.
|
|
*/
|
|
self.cache_data.invalidate(inode.i_objnum);
|
|
callback(stat);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Create generic server statistics informations.
|
|
*
|
|
* @returns {string}
|
|
*/
|
|
dns_server.prototype.stat = function () {
|
|
var self=this;
|
|
var stats,res;
|
|
stats=self.stats;
|
|
res = 'DNS server status\n';
|
|
res = res + 'Inode cache statistics:\n' +
|
|
(self.cache_inode.cache_stat()) +
|
|
'\nServer statistics\n\n'+
|
|
'Read: '+Printf.sprintf2([['%8d',stats.op_read]])+
|
|
' Modify: '+Printf.sprintf2([['%8d',stats.op_modify]])+
|
|
' Create: '+Printf.sprintf2([['%8d',stats.op_create]])+'\n'+
|
|
'Touch: '+Printf.sprintf2([['%8d',stats.op_touch]])+
|
|
' Age: '+Printf.sprintf2([['%8d',stats.op_age]])+
|
|
' Destroy:'+Printf.sprintf2([['%8d',stats.op_destroy]])+'\n';
|
|
return res;
|
|
};
|
|
|
|
/**
|
|
* Touch directory and AFS content table objects
|
|
*
|
|
* @param {dns_dir} dir
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.touch = function (dir,callback) {
|
|
var self=this,
|
|
stat,
|
|
cap,inode,
|
|
i;
|
|
this.stats.op_touch++;
|
|
/*
|
|
** Keep object internally alive.
|
|
*/
|
|
dir.dd_live=Dns.DNS_MAX_LIVE;
|
|
self.live_set(dir.dd_objnum,Dns.DNS_MAX_LIVE,1);
|
|
/*
|
|
** Keep object(s) externally alive.
|
|
*/
|
|
var sci = self.cap_of_obj(dir.dd_objnum);
|
|
stat=sci.stat;inode=sci.inode;
|
|
i=0;
|
|
if (stat!=Status.STD_OK) callback(stat);
|
|
else Sch.ScheduleLoop(function(index) {
|
|
i=index;
|
|
return index < 2;
|
|
},[
|
|
function () {
|
|
cap=inode.i_caps[i];
|
|
if (!Net.Equal.capability(cap,Net.nilcap)) {
|
|
self.std.std_touch(cap,function (_stat) {
|
|
stat=_stat;
|
|
})
|
|
}
|
|
}
|
|
],[
|
|
function () {
|
|
callback(Status.STD_OK);
|
|
}
|
|
],function(e) {
|
|
if (typeof e == 'number') callback(e); else {
|
|
Io.printstack(e,'Dns_server.touch');
|
|
callback(Status.STD_SYSERR);
|
|
}
|
|
});
|
|
};
|
|
|
|
/** Age all objects (used and unused inodes). Destroy directories
|
|
** with live time equal zero (only valid files). Remember that
|
|
** the root dirctory (obj=1) can't be aged or deleted.
|
|
*
|
|
* @param {function(Status.STD_OK|*)} callback
|
|
*/
|
|
dns_server.prototype.age = function (callback) {
|
|
var self=this,
|
|
sup = this.dns_super,
|
|
stat=Status.STD_OK,
|
|
i=2,
|
|
lt,
|
|
dir,
|
|
gone=0,
|
|
aged=0,
|
|
scavenge=[];
|
|
this.stats.op_age++;
|
|
if (self.verbose) Io.out('[DNS] Aging ..');
|
|
for(i=2;i<sup.dns_ndirs;i++) {
|
|
lt = self.live_get(i);
|
|
if (lt.flag == 1 && lt.time > 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<sup.dns_ncols;i++) {
|
|
cols.push(Buf.buf_get_string(superbuf));
|
|
}
|
|
for(i=0;i<sup.dns_ncols;i++) {
|
|
colmasks.push(DnsInt.buf_get_rights(superbuf));
|
|
}
|
|
var fs_caps=[];
|
|
for(i=0;i<2;i++) {
|
|
var cap=Buf.buf_get_cap(superbuf);
|
|
if (!Net.Equal.capability(cap,Net.nilcap)) Io.out('[DNS] Using AFS ' + Net.Print.capability(cap));
|
|
fs_caps.push(cap);
|
|
}
|
|
|
|
sup.dns_colnames=cols;
|
|
sup.dns_generic_colmask=colmasks;
|
|
sup.dns_fs=Dns.Fs_server(fs_caps, [Dns.Fs_state.FS_unknown, Dns.Fs_state.FS_unknown],
|
|
0, Dns.Dns_mode.DNSMODE_ONECOPY);
|
|
/*
|
|
** Defaulf file server and supported mode must be located later (get_def_fs)
|
|
*/
|
|
Io.out(' DNS: Label: '+sup.dns_name);
|
|
Io.out(' DNS: Maximal number of directories: '+sup.dns_ndirs);
|
|
Io.out(' DNS: Blocksize: '+sup.dns_block_size);
|
|
/*
|
|
** The livtime 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.
|
|
*/
|
|
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<sup.dns_ndirs;i++) {
|
|
stat = cache_inode.cache_read(inode_fse, inodeb, Dns.off_of_inode(i), Dns.DEF_INODE_SIZE);
|
|
if (stat != Status.STD_OK) {
|
|
Io.out('[DNS] Failed to read i-node ' + i + ': ' + Status.print(stat));
|
|
throw stat;
|
|
}
|
|
inodeb.pos = 0;
|
|
var inode = Dns.buf_get_inode(inodeb);
|
|
/*
|
|
** Some sanity checks first
|
|
*/
|
|
|
|
if (inode.i_objnum != i) {
|
|
Fs.closeSync(self.dns_part_fd);
|
|
Io.out('[DNS] got invalid i-node ' + i + ': ' + util.inspect(inode));
|
|
Io.out('[DNS] Abort.');
|
|
throw Status.STD_SYSERR;
|
|
}
|
|
if (inode.i_state == Afs_file_state.FF_invalid ||
|
|
inode.i_state == Afs_file_state.FF_unlocked) {
|
|
|
|
live_set(i, 0, 0);
|
|
|
|
if (nextfree != -1 && nextfree != (i - 1)) {
|
|
|
|
for (j = firstfree; j <= nextfree; j++) {
|
|
freeino.push(j);
|
|
}
|
|
firstfree = i;
|
|
nextfree = i;
|
|
} else {
|
|
nextfree = i;
|
|
if (firstfree == -1)
|
|
firstfree = i;
|
|
}
|
|
|
|
if (inode.i_state == Afs_file_state.FF_unlocked) {
|
|
Io.out('[DNS] Unlocked/uncommitted directory found. Destroy it: ' + inode.print());
|
|
inode.i_state = Afs_file_state.FF_invalid;
|
|
inodeb.pos = 0;
|
|
Dns.buf_put_inode(inodeb, inode);
|
|
stat = cache_inode.cache_write(inode_fse, inodeb, Dns.off_of_inode(i), Dns.DEF_INODE_SIZE);
|
|
|
|
if (stat != Status.STD_OK)
|
|
throw stat;
|
|
|
|
}
|
|
} else {
|
|
var tf = live_get(i);
|
|
live_set(i, tf.time, 1);
|
|
nused++;
|
|
}
|
|
}
|
|
Io.out('[DNS] Ok.');
|
|
|
|
if (nextfree != -1 && nextfree < (sup.dns_ndirs - 1)) {
|
|
|
|
/*
|
|
** There are only used i-nodes at the end of the i-node table.
|
|
*/
|
|
for (j = firstfree; j <= nextfree; j++) {
|
|
freeino.push(j);
|
|
}
|
|
nextfree = -1;
|
|
} else if (firstfree == 0 && nextfree == (sup.dns_ndirs - 1)) {
|
|
|
|
/*
|
|
** Empty filesystem.
|
|
*/
|
|
nextfree = 1;
|
|
} else if (firstfree > 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;
|
|
}
|
|
};
|