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 };