From 9e7eebfea0f4dbea0114da1aa9c0d605a72d9e72 Mon Sep 17 00:00:00 2001 From: sbosse Date: Mon, 21 Jul 2025 22:47:22 +0200 Subject: [PATCH] Mon 21 Jul 22:43:21 CEST 2025 --- js/dos/afs_srv_common.js | 597 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 597 insertions(+) create mode 100644 js/dos/afs_srv_common.js diff --git a/js/dos/afs_srv_common.js b/js/dos/afs_srv_common.js new file mode 100644 index 0000000..3a63bfc --- /dev/null +++ b/js/dos/afs_srv_common.js @@ -0,0 +1,597 @@ +/** + ** ================================== + ** OOOO OOOO OOOO O O OOOO + ** O O O O O O O O O + ** O O O O O O O O O + ** OOOO OOOO OOOO O OOO OOOO + ** O O O O O O O O O + ** O O O O O O O O O + ** OOOO OOOO 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. + ** 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) 2006-2016 BSSLAB + ** $CREATED: 7/1/15 by sbosse. + ** $VERSION: 1.1.3 + ** + ** $INFO: + ** + ** DOS: AFS Server Common Interface + ** + ** $ENDOFINFO + */ + +var util = Require('util'); +var Io = Require('com/io'); +var Net = Require('dos/network'); +var Status = Net.Status; +var Command = Net.Command; +var Rights = Net.Rights; +var Std = Require('dos/std'); +var Sch = Require('dos/scheduler'); +var Buf = Require('dos/buf'); +var Rpc = Require('dos/rpc'); +var Fcache = Require('dos/fcache'); +var Flist = Require('dos/freelist'); +var Comp = Require('com/compat'); +var String = Comp.string; +var Array = Comp.array; +var Perv = Comp.pervasives; +var Hashtbl = Comp.hashtbl; +var assert = Comp.assert; +var Fs = Require('fs'); +var div = Comp.div; + +/** AFS Commit flags + * + * @enum + * @type {{AFS_UNCOMMIT: number, AFS_COMMIT: number, AFS_SAFETY: number}} + */ +var Afs_commit_flag = { + AFS_UNCOMMIT: 0x0, + AFS_COMMIT: 0x1, + AFS_SAFETY: 0x2, + print: function(flag) { + switch (flag) { + case Afs_commit_flag.AFS_UNCOMMIT: return 'AFS_UNCOMMIT'; + case Afs_commit_flag.AFS_COMMIT: return 'AFS_COMMIT'; + case Afs_commit_flag.AFS_SAFETY: return 'AFS_SAFETY'; + default: return 'Afs_commit_flag?' + } + } +}; + + +/* + ** Default filesystem (data) block size. This should be + ** the smallest value supported by the underlying OS hardware + ** layer! This block size is always used for the i-node partition. + */ + +var DEF_BLOCK_SIZE = 512; +var DEF_INODE_SIZE = 32; +var DEF_MAGIC_SIZE = 32; + +/* + ** Default Number of cache entries for the inode cache + */ +var DEF_INODE_ENTRIES = 500; + +/* + ** Default Number of cache entries for the data cache + */ +var DEF_DATA_ENTRIES = 200; + +/* + ** Default Data buffer size (multiple of block_size) + */ +var DEF_DATA_BUFSIZE = 8 * DEF_BLOCK_SIZE; + +/* + ** Age periodically the data cache and remove timed out + ** cache objects [sec]. + */ +var CACHE_GC_TIME = 10; + +/* + ** Because on file creation and modification, the total size + ** of a file is not know in advance, it's necessary to reserve + ** disk space in multiples of the def_res_SIZE. After + ** the file was committed, the not used part of the last reserved + ** disk cluster is returned to the free cluster list. [bytes] + */ +var DEF_RES_SIZE = DEF_BLOCK_SIZE * 100; + + +var magic_str1 = 'AMOEBA::AFS::INODE::032'; +var magic_str2 = 'AMOEBA::AFS::DATAX::032'; + +/* + ** Files have live time between MAXLIVE and 0. + ** Zero live time means: destroy the file after an age request. + */ +var AFS_MAXLIVE=7; + + +/** AFS File State + * @enum + * @type {{FF_invalid: number, FF_unlocked: number, FF_commit: number, FF_locked: number, print: Function}} + */ +var Afs_file_state = { + FF_invalid:0, // I-node not used *) + FF_unlocked:0x10, // New file created *) + FF_commit:0x40, // File committed, but not synced /AFS_COMMIT/ *) + FF_locked:0x80, // File committed, and synced /AFS_SAFETY/ *) + print: function(state) { + switch (state) { + case Afs_file_state.FF_invalid: return 'FF_invalid'; + case Afs_file_state.FF_unlocked: return 'FF_unlocked'; + case Afs_file_state.FF_commit: return 'FF_commit'; + case Afs_file_state.FF_locked: return 'FF_locked'; + default: return 'Afs_file_state?' + } + } +}; + +/** AFS Disk i-node Class + * + * @param disk_addr + * @param disk_ioff + * @param disk_res + * @constructor + * @typedef {{disk_addr:number,disk_ioff:number,disk_res:number}} disk~obj + * @see disk~obj + */ +var disk = function (disk_addr,disk_ioff,disk_res) { + this.disk_addr=disk_addr||-1; + this.disk_ioff=disk_ioff||-1; + this.disk_res=disk_res||0; +}; + +/** A Disk I-Node Object + * + * @param disk_addr + * @param disk_ioff + * @param disk_res + * @returns {disk} + */ +var Disk = function (disk_addr,disk_ioff,disk_res) { + var obj = new disk(disk_addr,disk_ioff,disk_res); + Object.preventExtensions(obj); + return obj; +}; + +/** AFS I-node Class + ** Internal node information (Physical disk offset of file and + ** the i-node itself, and perhaps reserved space for unlocked + ** files). + * + * @param i_file_num + * @param i_disk_addr + * @param i_disk_size in bytes + * @param i_disk_res + * @param {(Afs_file_state.FF_invalid|*)} i_state + * @param i_time + * @param {port} i_random + * @constructor + * @typedef {{i_file_num:number,i_disk_addr:number,i_disk_size:number,i_disk_res:number, + * i_state:Afs_file_state,i_time:number,i_random:port}} afs_inode~obj + * @see afs_inode~obj + */ +var afs_inode = function (i_file_num,i_disk_addr,i_disk_size,i_disk_res,i_state,i_time,i_random) { + this.i_file_num= i_file_num||0; // all integers + this.i_disk_addr= i_disk_addr||0; // + this.i_disk_size= i_disk_size||0; + this.i_disk_res= i_disk_res||0; // + this.i_state= i_state; // Afs_file_state + this.i_time= i_time||0; + this.i_random= i_random; // port +}; + +/** AFS Inode Object + * + * @param i_file_num + * @param i_disk_addr + * @param i_disk_size + * @param i_disk_res + * @param {(Afs_file_state.FF_invalid|*)} i_state + * @param i_time + * @param {port} i_random + * @returns {afs_inode} + */ +function Afs_inode(i_file_num,i_disk_addr,i_disk_size,i_disk_res,i_state,i_time,i_random) { + var obj = new afs_inode(i_file_num,i_disk_addr,i_disk_size,i_disk_res,i_state,i_time,i_random); + Object.preventExtensions(obj); + return obj; +} + +afs_inode.prototype.print = function () { + var str=''; + str=str+'#'+this.i_file_num+' ['+Net.Print.port(this.i_random)+'] @'+this.i_time; + str=str+' daddr='+this.i_disk_addr+' ['+this.i_disk_size+' bytes]'; + str=str+' state='+Afs_file_state.print(this.i_state); + return str; +}; + +/** + ** File handle + * + * @param {number} [ff_objnum] + * @param {port} [ff_random] + * @param {number} [ff_size] + * @param {number} [ff_time] + * @param {number} [ff_live] + * @param {(Afs_file_state.FF_invalid|*)} [ff_state] + * @param {(disk|buffer)} [ff_disk] + * @param {boolean} [ff_modified] + * @constructor + * @typedef {{ff_objnum,ff_random,ff_size,ff_time,ff_live,ff_state,ff_disk,ff_modified,ff_lock:Lock}} afs_file~obj + * @see afs_file~obj + * @see afs_file~meth + */ +var afs_file = function (ff_objnum,ff_random,ff_size,ff_time,ff_live,ff_state,ff_disk,ff_modified) { + this.ff_objnum = ff_objnum||0; // int; The directory index number + this.ff_random = ff_random; // port; Random check number + this.ff_time = ff_time||0; // int; Time stamp in 10s units + this.ff_live = ff_live||AFS_MAXLIVE; // int; Live time [0..MAXLIVE] + this.ff_state = ff_state||Afs_file_state.FF_unlocked; // afs_file_state; The status of a file + this.ff_size = ff_size||0; // int; Size of the file [bytes] + this.ff_disk = ff_disk||Disk(); // disk; Disk I-node or data buffer (emb.) + this.ff_modified = ff_modified||true; // bool; modified afs_file ? + this.ff_lock = Sch.Lock(); // Mutex.t; +}; + +/** + * @typedef {{lock:afs_file.lock,unlock:afs_file.unlock,print:afs_file.print}} afs_file~meth + */ + +/** Lock a file. [BLOCKING] + * + */ +afs_file.prototype.lock = function () { + this.ff_lock.acquire(); +}; + +/** + * @returns {boolean} + */ +afs_file.prototype.try_lock = function () { + return this.ff_lock.try_acquire(); +}; +/** Unlock a file. + * + */ +afs_file.prototype.unlock = function () { + this.ff_lock.release(); +}; +/** + * + * @returns {string} + */ +afs_file.prototype.print = function () { + var str=''; + str='#'+this.ff_objnum+' ['+Net.Print.port(this.ff_random)+'] @'+this.ff_time+' '+Afs_file_state.print(this.ff_state); + str=str+' ('+this.ff_size+' bytes) '+util.inspect(this.ff_disk)+(this.ff_modified?' is modified':''); + return str; +}; + +/** AFS File handle + * + * @param {number} [ff_objnum] + * @param {port} [ff_random] + * @param {number} [ff_size] + * @param {number} [ff_time] + * @param {number} [ff_live] + * @param {(Afs_file_state.FF_invalid|*)} [ff_state] + * @param {disk} [ff_disk] + * @param {boolean} [ff_modified] + * @returns {afs_file} + */ +function Afs_file(ff_objnum,ff_random,ff_size,ff_time,ff_live,ff_state,ff_disk,ff_modified) { + var obj = new afs_file(ff_objnum,ff_random,ff_size,ff_time,ff_live,ff_state,ff_disk,ff_modified); + Object.preventExtensions(obj); + return obj; +} + +/** + * + * @param {buffer} buf + * @param {afs_inode} inode + */ +function buf_put_inode(buf,inode) { + Buf.buf_put_int32(buf,inode.i_file_num); + Buf.buf_put_int32(buf,inode.i_disk_addr); + Buf.buf_put_int32(buf,inode.i_disk_size); + Buf.buf_put_int32(buf,inode.i_disk_res); + Buf.buf_put_int16(buf,inode.i_state); + Buf.buf_put_int32(buf,inode.i_time); + Buf.buf_put_port(buf,inode.i_random); +} + +/** Store i-node structure created from afs_file object + * + * @param {buffer} buf + * @param {afs_file} file + */ +function buf_put_ff_inode(buf,file) { + Buf.buf_put_int32(buf,file.ff_objnum); + Buf.buf_put_int32(buf,file.ff_disk.disk_addr); + Buf.buf_put_int32(buf,file.ff_size); + Buf.buf_put_int32(buf,file.ff_disk.disk_res); + Buf.buf_put_int16(buf,file.ff_state); + Buf.buf_put_int32(buf,file.ff_time); + Buf.buf_put_port(buf,file.ff_random); +} + +/** + * + * @param {buffer} buf + * @param {afs_inode} [inode] + * @returns {*} + */ +function buf_get_inode(buf,inode) { + if (inode==undefined) inode=Afs_inode(); + inode.i_file_num=Buf.buf_get_int32(buf); + inode.i_disk_addr=Buf.buf_get_int32(buf); + inode.i_disk_size=Buf.buf_get_int32(buf); + inode.i_disk_res=Buf.buf_get_int32(buf); + inode.i_state=Buf.buf_get_int16(buf); + inode.i_time=Buf.buf_get_int32(buf); + inode.i_random=Buf.buf_get_port(buf); + return inode; +} + +/** Get afs_file object from i-node structure + * + * @param {buffer} buf + * @param {afs_file} [file] + * @returns {afs_file} + */ +function buf_get_ff_inode(buf,file) { + if (file==undefined) file=Afs_file(); + file.ff_objnum=Buf.buf_get_int32(buf); + file.ff_disk.disk_addr=Buf.buf_get_int32(buf); + file.ff_size=Buf.buf_get_int32(buf); + file.ff_disk.disk_res=Buf.buf_get_int32(buf); + file.ff_state=Buf.buf_get_int16(buf); + file.ff_time=Buf.buf_get_int32(buf); + file.ff_random=Buf.buf_get_port(buf); + return file; +} + +/** AFS Super Class + ** The main AFS structure: the all known super structure with basic + ** information about the file system. This structure is generated + ** by the server with fixed information from the super block + ** of the filesystem (name, nfiles, ports, size), and dynamically + ** from the inode table (nfiles, nused, freeobj, nextfree). + * + * + * @param {string} afs_name + * @param {number} afs_nfiles + * @param {number} afs_nused + * @param {number []} afs_freeobjnums + * @param {number} afs_nextfree + * @param {port} afs_getport + * @param {port} afs_putport + * @param {port} afs_checkfield + * @param {number} afs_block_size + * @param {number} afs_nblocks + * @constructor + * @typedef {{afs_lock:lock,afs_name:string,afs_nfiles:number,afs_nused:number,afs_freeobjnums:number [], + * afs_nextfree:number,afs_getport:port,afs_putport:port,afs_checkfield:port, + * afs_block_size:number,afs_nblocks:number}} afs_super~obj + * @see afs_super~obj + * @see afs_super~meth + */ +var afs_super = function (afs_name,afs_nfiles,afs_nused,afs_freeobjnums, + afs_nextfree,afs_getport,afs_putport,afs_checkfield,afs_block_size,afs_nblocks) { + this.afs_name = afs_name; // string: The server name + + this.afs_nfiles = afs_nfiles; // int: Number of total table entries + this.afs_nused = afs_nused; // int: Number of used table entries + this.afs_freeobjnums = afs_freeobjnums; // int list: Free slots list + this.afs_nextfree = afs_nextfree; // int: Next free slot + + + this.afs_getport = afs_getport; // port: Private getport + this.afs_putport = afs_putport; // port: Public putport + this.afs_checkfield = afs_checkfield; // port: Private checkfield + + /* + ** Not used by this module + */ + + this.afs_block_size = afs_block_size; // int: Data Blocksize [bytes] + this.afs_nblocks = afs_nblocks; // int: Number of total data blocks + this.afs_lock = Sch.Lock(); // Mutex.t: Super protection lock +}; + +/** + * @typedef {{lock,unlock,try_lock,print}} afs_super~meth + */ + +/** Lock super structure [Non Blocking with invariant exception] + * + */ +afs_super.prototype.lock = function () { + // assert((!this.afs_lock.is_locked())||('afs_super is already locked!')); + this.afs_lock.acquire(); +}; + +afs_super.prototype.try_lock = function () { + return this.afs_lock.try_acquire(); +}; + +/** + * + */ +afs_super.prototype.unlock = function () { + this.afs_lock.release(); +}; + +/** + * + * @returns {string} + */ +afs_super.prototype.print = function () { + var str=''; + str='SUPER BLOCK:\n'; + str=str+' LABEL = '+this.afs_name+'\n'; + str=str+' BLOCKS = '+this.afs_nblocks+'\n'; + str=str+' BLSIZE = '+this.afs_block_size+'\n'; + str=str+' FILES = '+this.afs_nfiles+'\n'; + str=str+' USED = '+this.afs_nused+'\n'; + str=str+' FREE = '+this.afs_freeobjnums.length+'\n'; + str=str+' NEXTFREE = '+this.afs_nextfree+'\n'; + str=str+' GETPORT = '+Net.Print.port(this.afs_getport)+'\n'; + str=str+' PUTPORT = '+Net.Print.port(this.afs_putport)+'\n'; + str=str+' CHECKFI = '+Net.Print.port(this.afs_checkfield)+'\n'; + return str; +}; + + +/** AFS Super Object + * + * @param {string} afs_name + * @param {number} afs_nfiles + * @param {number} afs_nused + * @param {number []} afs_freeobjnums + * @param {number} afs_nextfree + * @param {port} afs_getport + * @param {port} afs_putport + * @param {port} afs_checkfield + * @param {number} afs_block_size + * @param {number} afs_nblocks + * @returns {afs_super} + */ + +function Afs_super(afs_name,afs_nfiles,afs_nused,afs_freeobjnums, + afs_nextfree,afs_getport,afs_putport,afs_checkfield,afs_block_size,afs_nblocks) { + var obj = new afs_super(afs_name,afs_nfiles,afs_nused,afs_freeobjnums, + afs_nextfree,afs_getport,afs_putport,afs_checkfield,afs_block_size,afs_nblocks); + Object.preventExtensions(obj); + return obj; +} + +/** AFS Cache Parameter Class + * + * @param c_inode_buffers Number of inode cache buffers + * @param c_inode_size Size of one inode cache buffer [blocks] + * @param c_data_buffers Number of data cache buffers + * @param c_data_size Size of one data buffer [blocks] + * @constructor + */ +var afs_cache_parameter = function (c_inode_buffers,c_inode_size,c_data_buffers,c_data_size) { + this.c_inode_buffers=c_inode_buffers; + this.c_inode_size=c_inode_size; + this.c_data_buffers=c_data_buffers; + this.c_data_size=c_data_size; +}; +/** AFS Cache Parameter Object + * + * @param c_inode_buffers Number of inode cache buffers + * @param c_inode_size Size of one inode cache buffer [blocks] + * @param c_data_buffers Number of data cache buffers + * @param c_data_size Size of one data buffer [blocks] + * @returns {afs_cache_parameter} + */ +function Afs_cache_parameter(c_inode_buffers,c_inode_size,c_data_buffers,c_data_size) { + var obj = new afs_cache_parameter(c_inode_buffers,c_inode_size,c_data_buffers,c_data_size); + Object.preventExtensions(obj); + return obj; +} + +/* + ** Calculate the logical partition offset for an inode + */ + +function off_of_inode(inode) { + return (inode * DEF_INODE_SIZE); +} + +/* + ** Convert block to byte units and vice versa + */ +function to_block (x,block_size) { + return div(x,block_size); +} + +function of_block (x,block_size) { + return x * block_size; +} + + +/* + ** Round up the value x to nearest block_size -> block & byte units + */ + +function ceil_block (x,block_size) { + return div((x + block_size - 1),block_size); +} + +/* + ** Round up the value x to block_size + */ + +function ceil_block_bytes (x,block_size) { + return div((x + block_size - 1),block_size) * block_size; +} + +/* + ** Round down the value x to nearest block_size -> block & byte units + */ + +function floor_block (x,block_size) { + return div(x,block_size); +} + +function floor_block_bytes (x,block_size) { + return div(x,block_size) * block_size; +} +/** + * + * @type {{magic_str1: string, magic_str2: string, AFS_MAXLIVE: number, DEF_BLOCK_SIZE: number, DEF_INODE_SIZE: number, DEF_MAGIC_SIZE: number, buf_get_inode: buf_get_inode, buf_get_ff_inode: buf_get_ff_inode, buf_put_inode: buf_put_inode, buf_put_ff_inode: buf_put_ff_inode, ceil_block: ceil_block, ceil_block_bytes: ceil_block_bytes, floor_block: floor_block, floor_block_bytes: floor_block_bytes, of_block: of_block, to_block: to_block, off_of_inode: off_of_inode, Afs_commit_flag: {AFS_UNCOMMIT: number, AFS_COMMIT: number, AFS_SAFETY: number}, Afs_file: Afs_file, Afs_file_state: {FF_invalid: number, FF_unlocked: number, FF_commit: number, FF_locked: number, print: Function}, Afs_inode: Afs_inode, Afs_super: Afs_super, Cache_parameter: Cache_parameter, Disk: Function}} + */ +module.exports = { + magic_str1:magic_str1, + magic_str2:magic_str2, + AFS_MAXLIVE:AFS_MAXLIVE, + DEF_BLOCK_SIZE:DEF_BLOCK_SIZE, + DEF_INODE_SIZE:DEF_INODE_SIZE, + DEF_MAGIC_SIZE:DEF_MAGIC_SIZE, + DEF_INODE_ENTRIES:DEF_INODE_ENTRIES, + DEF_DATA_ENTRIES:DEF_DATA_ENTRIES, + DEF_DATA_BUFSIZE:DEF_DATA_BUFSIZE, + CACHE_GC_TIME:CACHE_GC_TIME, + DEF_RES_SIZE:DEF_RES_SIZE, + + buf_get_inode:buf_get_inode, + buf_get_ff_inode:buf_get_ff_inode, + buf_put_inode:buf_put_inode, + buf_put_ff_inode:buf_put_ff_inode, + + ceil_block:ceil_block, + ceil_block_bytes:ceil_block_bytes, + floor_block:floor_block, + floor_block_bytes:floor_block_bytes, + of_block:of_block, + to_block:to_block, + off_of_inode:off_of_inode, + + Afs_commit_flag:Afs_commit_flag, + Afs_file:Afs_file, + Afs_file_state:Afs_file_state, + Afs_inode:Afs_inode, + Afs_super:Afs_super, + + Afs_cache_parameter:Afs_cache_parameter, + Disk:Disk +};