jsos

college code for operating system fundamentals in js

git clone https://9o.is/git/jsos.git

deviceDriverFilesystem.js

(17197B)


      1 /* ----------------------------------
      2    DeviceDriverFilesystem.js
      3    
      4    Requires deviceDriver.js
      5    
      6    The Kernel Filesystem Device Driver.
      7  
      8    ---------------------------------- */
      9 
     10 DeviceDriverFilesystem.prototype = new DeviceDriver;  // "Inherit" from prototype
     11 
     12 function DeviceDriverFilesystem() {
     13   
     14   /* TSB of root directory. */
     15   this.rootDir = undefined;
     16 
     17   /* TSB of current working directory. */
     18   this.cd = undefined;
     19 
     20   this.driverEntry = function() {
     21 
     22     // set all tsb's that dont exist to zeros
     23     //this.init();
     24     var rootTSB = new tsb(1,1,1);
     25     
     26     // init if it hasn't been before
     27     if(_Disk.readByte(rootTSB,1) == undefined)
     28       this.initBlockDirectory(rootTSB);
     29     this.rootDir = new Directory(rootTSB);
     30     this.cd = this.rootDir;
     31 
     32     this.status = "loaded";
     33   };
     34   
     35   /*
     36    * Formats entire filesystem by setting it to intial use.
     37    */
     38   this.format = function() {
     39     for(t=1; t<=_Disk.TRACKS; t++)
     40       for(s=1; s<=_Disk.SECTORS; s++)
     41         for(b=1; b<=_Disk.BLOCKS; b++) {
     42           var ts = new tsb(t,s,b);
     43           if(_Disk.read(ts).length == 0)
     44             _Disk.write(ts,[0], true);
     45           else {
     46             var file = new File(ts);
     47             if(file.type() != file.TYPE_SWAP) 
     48               _Disk.write(ts,[0], true);
     49           }
     50         }
     51 
     52     return true;
     53   };
     54 
     55   /* 
     56    * Initializes a block with type text & as unavailable 
     57    * with next block (0,0,0) & GS=1D 
     58    */
     59   this.initBlockTextFile = function(ts) {
     60     var value = ["1","0","0","0","1d"]; 
     61     _Disk.write(ts,value, true);
     62   };
     63 
     64   /* 
     65    * Initializes a block with type directory & as 
     66    * unavailable with next block (0,0,0) & GS=1D 
     67    */
     68   this.initBlockDirectory = function(ts) {
     69     var value = ["3","0","0","0","1d"]; 
     70     _Disk.write(ts,value, true);
     71   };
     72 
     73   /* 
     74    * Initializes a block with type swap & as unavailable 
     75    * with next block (0,0,0) 
     76    */
     77   this.initBlockSwap = function(ts) {
     78     var value = ["5","0","0","0"]; 
     79     _Disk.write(ts,value, true);
     80   };
     81 
     82  /*
     83   * Finds an available block in disk. 
     84   */
     85   this.getAvailableBlock = function() {
     86     for(t=1; t<=_Disk.TRACKS; t++)
     87       for(s=1; s<=_Disk.SECTORS; s++)
     88         for(b=1; b<=_Disk.BLOCKS; b++) {
     89           var ts = new tsb(t,s,b);
     90           var f = new File(ts);
     91           if(f.available() || _Disk.readByte(ts,1) == undefined)
     92             return ts;
     93         }
     94     return undefined;
     95   };
     96 
     97   /*
     98    * Creates a text file with a name.
     99    */
    100   this.createTextFile = function(fn) {
    101     fn = fn.trim();
    102     fn = this.rename(fn);
    103     var tsb = this.getAvailableBlock();
    104     if(tsb == undefined) return false;
    105 
    106     this.initBlockTextFile(tsb);
    107     var textFile = new TextFile(tsb);
    108     textFile.setName(fn);
    109     return this.cd.addTextFile(textFile);
    110   };
    111 
    112   /*
    113    * Creates a directory file with a name.
    114    */
    115   this.createDirectory = function(fn) {
    116     fn = fn.trim();
    117     fn = this.rename(fn);
    118     var tsb = this.getAvailableBlock();
    119     if(tsb == undefined) return false;
    120 
    121     this.initBlockDirectory(tsb);
    122     var dir = new Directory(tsb);
    123     dir.setName(fn);
    124     return this.cd.addDirectory(dir);
    125   };
    126 
    127   /*
    128    * Writes text to a text file under a name.
    129    */
    130   this.writeTextFile = function(fn, text) {
    131     fn = fn.trim();
    132     var file = this.cd.getTextFile(fn);
    133     if(file == undefined) return false;
    134     return file.setText(text);
    135   };
    136 
    137   /*
    138    * Reads text to a text file under a name.
    139    */
    140   this.readTextFile = function(fn) {
    141     fn = fn.trim();
    142     var file = this.cd.getTextFile(fn);
    143     if(file == undefined)
    144       return undefined;
    145     return file.text();
    146   };
    147 
    148   /*
    149    * Deletes a file with a name.
    150    */
    151   this.deleteFile = function(fn) {
    152     fn = fn.trim();
    153     return this.cd.deleteFile(fn);
    154   };
    155 
    156   /*
    157    * List working directory contents.
    158    */
    159   this.ls = function() {
    160     return this.cd.directories().concat(this.cd.textFiles());
    161   };
    162 
    163   /*
    164    * Changes working directory.
    165    */
    166   this.changeDirectory = function(fn) {
    167     fn = fn.trim();
    168 
    169     if(fn=="..") {
    170       if(this.cd.parentDirectory() != undefined) {
    171         this.cd = this.cd.parentDirectory();
    172         return true;
    173       } else 
    174         return false;
    175     }
    176     if(fn==".") {
    177       return true;
    178     }
    179 
    180     var dir = this.cd.getDirectory(fn);
    181     if(dir != undefined) {
    182       this.cd = dir;
    183       return true;
    184     }
    185     return false;
    186   };
    187 
    188   /*
    189    * Rename file name if existing file name is in this 
    190    * working directory. Pads it with zero (exponentially).
    191    */
    192   this.rename = function(name,suffix) {
    193     if(suffix==undefined) suffix=0;
    194 
    195     var dirs = this.cd.directories();
    196     var txtFiles = this.cd.textFiles();
    197 
    198     for(var i = 0; i < dirs.length; i++) {
    199       var name1 = dirs[i].name();
    200       if(name1 == name)
    201         return this.rename(name+pad("",suffix+1), suffix+1);
    202     }
    203     for(var i = 0; i < txtFiles.length; i++) {
    204       if(txtFiles[i].name() == name)
    205         return this.rename(name+pad("",suffix+1), suffix+1);
    206     }
    207 
    208     return name;
    209   };
    210 };
    211 
    212 
    213 /*----------------------------------------------------------
    214 
    215    Anything saved in disk is a file. 
    216    One file can take multiple blocks in disk.
    217 
    218    Header format for each block:
    219    status , track , sector , block (1 byte each)
    220 
    221    header: 4 bytes
    222    payload: 60 bytes
    223 
    224    Status represents type and availability flag bits:
    225 
    226    ----------------------------------------
    227    |X|X|X|X|X| type | type | availability |
    228    ----------------------------------------
    229 
    230    file types: 0=text, 1=directory, 2=swap
    231    availability: 0=available 1=unavailable
    232 
    233 
    234    Header(4 bytes)
    235    -----------------------------------
    236    | Status | Track | Sector | Block |
    237    -----------------------------------
    238 
    239    Track, Sector, Block points to the next block in disk if 
    240    the payload is greater than 60 bytes. It's set to zero 
    241    if it's the last block in the file.
    242 
    243 ----------------------------------------------------------*/
    244 function File(TSB) {
    245 
    246   this.TYPE_TEXT = 0;
    247   this.TYPE_DIR  = 1;
    248   this.TYPE_SWAP = 2;
    249 
    250   /* Header size of file. */
    251   this.HEADER_SIZE = 4;
    252   
    253   this.tsb = TSB;
    254   this.all_tsb = [TSB];
    255 
    256   /*
    257    * Gets the data of this file from disk.
    258    */
    259   this.data = function() {
    260     var data = _Disk.read(this.tsb);
    261     var header = data.slice(0, this.HEADER_SIZE);
    262     while(this.next(header) != undefined) {
    263       var tsb = this.next(header);
    264       var extData = _Disk.read(tsb);
    265       header = extData.slice(0, this.HEADER_SIZE);
    266       extData = extData.slice(this.HEADER_SIZE, extData.length);
    267       data = data.concat(extData);
    268     }
    269     return data;
    270   };
    271 
    272   /*
    273    * Write data to this file.
    274    * @param clear erase existing data
    275    */
    276   this.write = function(data, clear) {
    277     if(clear) {
    278       // set all TSB's to available
    279       for(i=1; i<this.all_tsb.length; i++)
    280         _Disk.write(this.all_tsb[i], [0], true);
    281       this.all_tsb = this.all_tsb.slice(0,1);
    282 
    283       // clear data in all tsb's
    284       var header = this.data().slice(0, this.HEADER_SIZE);
    285       while(this.next(header) != undefined) {
    286         var tsb = this.next(header);
    287         var extData = _Disk.read(tsb);
    288         header = extData.slice(0, this.HEADER_SIZE);
    289         _Disk.write(tsb, [0], clear);
    290       }
    291 
    292     } else {
    293       data = this.data().concat(data);   
    294     }
    295 
    296     return this.writeData(data, clear);
    297   }
    298 
    299   /*
    300    * Called by this write function.
    301    */
    302   this.writeData = function(data,clear) {
    303     var ts = this.tsb;
    304     while(data.length > 0) {
    305         data = _Disk.write(ts, data, clear);
    306 
    307         // get extra block if data does not fit
    308         if(data.length > 0) {
    309           var ts1 = krnFilesystemDriver.getAvailableBlock();
    310           if(ts1 == undefined) return false;
    311           this.all_tsb.push(ts1);
    312           this.setNext(ts, ts1);
    313           ts = ts1;
    314           data = [this.data()[0],"0","0","0"].concat(data);
    315         }      
    316       }
    317     return true;
    318   }
    319 
    320   /*
    321    * Get the next block if the data in this block 
    322    * continues to another one.
    323    */
    324   this.next = function(data) {
    325     var track = data[1];
    326     var sector = data[2];
    327     var block = data[3];
    328 
    329     if(track==0 || sector==0 || block==0 ||
    330        track==undefined || sector==undefined || block==undefined)
    331       return undefined;
    332     return new tsb(track, sector, block);
    333   };
    334 
    335   /*
    336    * Sets the next, contuining block so data can grow over 
    337    * fixed block size.
    338    */
    339   this.setNext = function(tsb, tsbNext) {
    340     return _Disk.writeByte(tsb,2, tsbNext.track) &&
    341            _Disk.writeByte(tsb,3, tsbNext.sector) &&
    342            _Disk.writeByte(tsb,4, tsbNext.block);
    343   };
    344 
    345   /*
    346    * Get the availability status (to read and/or 
    347    * write) of the block.
    348    * If true, block is available, else it's occupied.
    349    */
    350   this.available = function() {
    351     var byte = parseInt(this.data()[0]);
    352     var bit = pad(byte.toString(2),8).substring(7,8);
    353     return bit == 0 ? true : false;
    354   };
    355 
    356   /*
    357    * Get the type of the block.
    358    * If true, it's a file, else it's a directory.
    359    */
    360   this.type = function() {
    361     var data = this.data();
    362     var byte = parseInt(data.slice(0,1)[0]);
    363     var typeBinary = pad(byte.toString(2),8).substring(5,7);
    364     var type = parseInt(typeBinary, 2);
    365     return type;
    366   };
    367 
    368   /*
    369    * Returns the data excluding the header.
    370    */
    371   this.payload = function() {
    372     var data = this.data();
    373     return data.slice(this.HEADER_SIZE, data.length);
    374   };
    375 };
    376 
    377 
    378 
    379 /*----------------------------------------------------------
    380 
    381    A directory is a type of file with a file header and payload.
    382 
    383 
    384    Format of the payload:
    385    -----------------------------
    386    | File Name | GS | Data.... |
    387    -----------------------------
    388 
    389    The first thing in the payload is the file name that can 
    390    be any length. The file name is separated by a 
    391    "Group Separator" (hex value: 0x1D). 
    392 
    393    The rest of the payload is a listing of all files' 
    394    track-sector-block location.
    395    ---------------------------------------------------------
    396    | track | sector | block | track | sector | block | etc |
    397    --------------------------------------------------------- 
    398 
    399    The first TSB is the parent directory.
    400 
    401 ----------------------------------------------------------*/
    402 Directory.prototype = new File();
    403 function Directory(TSB) {
    404   File.call(this, TSB);
    405 
    406   /*
    407    * Returns a directory of this directory's parent directory.
    408    */
    409   this.parentDirectory = function() {
    410     if(this.tsb.toString() != "111") { // not root directory
    411       var ts = this.data().slice(this.gs()+1, this.gs()+4);
    412       return new Directory(new tsb(ts[0],ts[1],ts[2]));
    413     } else
    414       return undefined;
    415   };
    416 
    417   /*
    418    * Returns the location of the group separator.
    419    * @param Including only the payload?
    420    */
    421   this.gs = function(payload) {
    422     var data = payload ? this.payload() : this.data();
    423 
    424     for(i=0; i<data.length; i++)
    425       if(data[i] == "1d")
    426         return i;
    427   };
    428 
    429   /*
    430    * Returns the name of this file.
    431    */
    432   this.name = function() {
    433     var hex_name = this.data().slice(this.HEADER_SIZE, this.gs(false));
    434     var name = "";
    435 
    436     for(i=0; i<hex_name.length; i++) 
    437       name += hex2ASCII(hex_name[i]);
    438 
    439     return name;
    440   };
    441 
    442   /*
    443    * Returns all files in this directory, 
    444    * including text files and directories.
    445    */
    446   this.files = function() {
    447     var data = this.payload();
    448     data = data.slice(this.gs(true)+1, data.length);
    449 
    450     var files = [];
    451     for(i=0; i<data.length; i+=3) {
    452       var ts = new tsb(data[i], data[i+1], data[i+2]);
    453       if(ts != undefined)
    454         files.push(new File(ts));
    455     }
    456       
    457     return files;
    458   };
    459 
    460   /*
    461    * Returns all directories in this directory.
    462    */
    463   this.directories = function() {
    464     var dirs = [];
    465     var files = this.files();
    466     for(var i = 0; i < files.length; i++) {
    467       if(files[i].type() == this.TYPE_DIR) 
    468         dirs.push(new Directory(files[i].tsb));
    469     }
    470 
    471     if(this.tsb.toString() != "111") { // not root directory
    472       return dirs.slice(1, dirs.length);
    473     } else
    474       return dirs;
    475   };
    476 
    477   /*
    478    * Returns the directory that matches the specified name.
    479    */
    480   this.getDirectory = function(fn) {
    481     var directories = this.directories();
    482     for(var i=0; i<directories.length; i++)
    483       if(directories[i].name() == fn)
    484         return directories[i];
    485     return undefined;
    486   };
    487 
    488   /*
    489    * Returns all text files in this directory.
    490    */
    491   this.textFiles = function() {
    492     var textFiles = [];
    493     var files = this.files();
    494     for(i=0; i<files.length; i++)
    495       if(files[i].type() == this.TYPE_TEXT) 
    496         textFiles.push(new TextFile(files[i].tsb));
    497 
    498     return textFiles;
    499   };
    500 
    501   /*
    502    * Returns the text file that matches the specified name.
    503    */
    504   this.getTextFile = function(fn) {
    505     var textFiles = this.textFiles();
    506     for(var i=0; i < textFiles.length; i++)
    507       if(textFiles[i].name() == fn)
    508         return textFiles[i];
    509     return undefined;
    510   };
    511 
    512   /*
    513    * Adds a directory in this directory.
    514    */
    515   this.addDirectory = function(dir) {
    516     dir.write(dir.data().concat([this.tsb.track,this.tsb.sector,this.tsb.block]), true);
    517     var tsb = dir.tsb;
    518     var data = this.data().concat([tsb.track, tsb.sector, tsb.block]);
    519     return this.write(data, true);
    520   };
    521 
    522   /*
    523    * Adds a text file in this directory.
    524    */
    525   this.addTextFile = function(textFile) {
    526     var tsb = textFile.tsb;
    527     var data = this.data().concat([tsb.track, tsb.sector, tsb.block]);
    528     return this.write(data, true);
    529   };
    530 
    531   /*
    532    * Deletes a file in this directory matching name.
    533    */
    534   this.deleteFile = function(name) {
    535     var file = undefined;
    536     var directories = this.directories();
    537     var textFiles = this.textFiles();
    538 
    539     // find from directories
    540     for(var i = 0; i < directories.length; i++) {
    541       if(directories[i].name() == name) {
    542         file = directories[i];
    543         directories.splice[i,1];
    544       }
    545     }
    546 
    547     // find from text files
    548     for(var i = 0; i < textFiles.length; i++) {
    549       if(textFiles[i].name() == name) {
    550         file = textFiles[i];
    551         textFiles.splice[i,1];
    552       }
    553     }
    554 
    555     if(file == undefined)
    556       return false;
    557     else { 
    558       _Disk.write(file.tsb,[0], true);
    559       return this.deleteTSB(file.tsb);
    560     }
    561   };
    562 
    563   /*
    564    * Deletes a track-sector-block in this directory listing.
    565    */
    566   this.deleteTSB = function(tsb) {
    567     var data = this.data();
    568     var header = data.slice(0, this.gs(false)+1);
    569     var payload = data.slice(this.gs(false)+1, data.length);
    570 
    571     for(var i = 0; i < payload.length; i+=3) {
    572       if(tsb.track == payload[i] &&
    573          tsb.sector == payload[i+1] &&
    574          tsb.block == payload[i+2]) {
    575         payload[i]   = 0;
    576         payload[i+1] = 0;
    577         payload[i+2] = 0;
    578         return this.write(header.concat(payload), true);
    579       }
    580     }
    581     return false;
    582   };
    583 
    584   /*
    585    * Sets the name of this file.
    586    */
    587   this.setName = function(name) {
    588     var data = this.data();
    589     data = data.slice(0, this.HEADER_SIZE).concat(
    590            ascii2hex(name)).concat( 
    591            data.slice(this.gs(false), data.length));
    592 
    593     return this.write(data, true);
    594   };
    595 };
    596 
    597 
    598 
    599 /*----------------------------------------------------------
    600 
    601    A text file is a type of file with a file header and payload.
    602 
    603 
    604    Format of the payload:
    605    -----------------------------
    606    | File Name | GS | Data.... |
    607    -----------------------------
    608 
    609    The first thing in the payload is the file name that can 
    610    be any length. The file name is separated by a 
    611    "Group Separator" (hex value: 0x1D). 
    612 
    613    The rest of the payload is just ASCII bytes (in hex format).
    614 
    615 ----------------------------------------------------------*/
    616 TextFile.prototype = new File();
    617 function TextFile(TSB) {
    618   File.call(this, TSB);
    619 
    620   /*
    621    * Returns the location of the group separator.
    622    * @param Including only the payload?
    623    */
    624   this.gs = function(payload) {
    625     var data = payload ? this.payload() : this.data();
    626 
    627     for(i=0; i<data.length; i++)
    628       if(data[i] == "1d")
    629         return i;
    630   };
    631 
    632   /*
    633    * Gets the name of this file.
    634    */
    635   this.name = function() {
    636     var hex_name = this.data().slice(this.HEADER_SIZE, this.gs(false));
    637     var name = "";
    638 
    639     for(i=0; i<hex_name.length; i++) 
    640       name += hex2ASCII(hex_name[i]);
    641 
    642     return name;
    643   };
    644 
    645   /*
    646    * Gets the text of this file.
    647    */
    648   this.text = function() {
    649     var data = this.payload();
    650     data = data.slice(this.gs(true)+1, data.length);
    651 
    652     var text = "";
    653     for(var i=0; i < data.length; i++) {
    654       text += hex2ASCII(data[i]);
    655     }
    656     return text;
    657   };
    658 
    659   /*
    660    * Sets the name of this file.
    661    */
    662   this.setName = function(name) {
    663     var data = this.data();
    664     data = data.slice(0, this.HEADER_SIZE).concat(
    665            ascii2hex(name)).concat( 
    666            data.slice(this.gs(false), data.length));
    667 
    668     return this.write(data, true);
    669   };
    670 
    671   /*
    672    * Sets the text of this file.
    673    */
    674   this.setText = function(text) {
    675     var data = this.data().slice(0, this.gs(false)+1).concat(ascii2hex(text)); 
    676     return this.write(data,true);
    677   };
    678  };
    679 
    680 
    681 /*----------------------------------------------------------
    682 
    683    A swap is a type of file with a file header and payload.
    684 
    685 
    686    Format of the payload:
    687    ------------
    688    | Data.... |
    689    ------------
    690 
    691    Nothing special.
    692 
    693 ----------------------------------------------------------*/
    694 Swap.prototype = new File();
    695 function Swap(TSB) {
    696   File.call(this, TSB);
    697 };