· 6 years ago · Nov 27, 2019, 03:50 PM
1#include <assert.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include <ctype.h>
7#include <malloc.h>
8#include "LibDisk.h"
9#include "LibFS.h"
10
11// set to 1 to have detailed debug print-outs and 0 to have none
12#define FSDEBUG 1
13
14#if FSDEBUG
15#define dprintf printf
16#else
17#define dprintf noprintf
18void noprintf(char* str, ...) {}
19#endif
20
21// the file system partitions the disk into five parts:
22
23// 1. the superblock (one sector), which contains a magic number at
24// its first four bytes (integer)
25#define SUPERBLOCK_START_SECTOR 0
26
27// the magic number chosen for our file system
28#define OS_MAGIC 0xdeadbeef
29
30// 2. the inode bitmap (one or more sectors), which indicates whether
31// the particular entry in the inode table (#4) is currently in use
32#define INODE_BITMAP_START_SECTOR 1
33
34// the total number of bytes and sectors needed for the inode bitmap;
35// we use one bit for each inode (whether it's a file or directory) to
36// indicate whether the particular inode in the inode table is in use
37#define INODE_BITMAP_SIZE ((MAX_FILES+7)/8)
38#define INODE_BITMAP_SECTORS ((INODE_BITMAP_SIZE+SECTOR_SIZE-1)/SECTOR_SIZE)
39
40// 3. the sector bitmap (one or more sectors), which indicates whether
41// the particular sector in the disk is currently in use
42#define SECTOR_BITMAP_START_SECTOR (INODE_BITMAP_START_SECTOR+INODE_BITMAP_SECTORS)
43
44// the total number of bytes and sectors needed for the data block
45// bitmap (we call it the sector bitmap); we use one bit for each
46// sector of the disk to indicate whether the sector is in use or not
47#define SECTOR_BITMAP_SIZE ((TOTAL_SECTORS+7)/8)
48#define SECTOR_BITMAP_SECTORS ((SECTOR_BITMAP_SIZE+SECTOR_SIZE-1)/SECTOR_SIZE)
49
50// 4. the inode table (one or more sectors), which contains the inodes
51// stored consecutively
52#define INODE_TABLE_START_SECTOR (SECTOR_BITMAP_START_SECTOR+SECTOR_BITMAP_SECTORS)
53
54// an inode is used to represent each file or directory; the data
55// structure supposedly contains all necessary information about the
56// corresponding file or directory
57typedef struct _inode {
58 int size; // the size of the file or number of directory entries
59 int type; // 0 means regular file; 1 means directory
60 int data[MAX_SECTORS_PER_FILE]; // indices to sectors containing data blocks
61} inode_t;
62
63// the inode structures are stored consecutively and yet they don't
64// straddle accross the sector boundaries; that is, there may be
65// fragmentation towards the end of each sector used by the inode
66// table; each entry of the inode table is an inode structure; there
67// are as many entries in the table as the number of files allowed in
68// the system; the inode bitmap (#2) indicates whether the entries are
69// current in use or not
70#define INODES_PER_SECTOR (SECTOR_SIZE/sizeof(inode_t))
71#define INODE_TABLE_SECTORS ((MAX_FILES+INODES_PER_SECTOR-1)/INODES_PER_SECTOR)
72
73// 5. the data blocks; all the rest sectors are reserved for data
74// blocks for the content of files and directories
75#define DATABLOCK_START_SECTOR (INODE_TABLE_START_SECTOR+INODE_TABLE_SECTORS)
76
77// other file related definitions
78
79// max length of a path is 256 bytes (including the ending null)
80#define MAX_PATH 256
81
82// max length of a filename is 16 bytes (including the ending null)
83#define MAX_NAME 16
84
85// max number of open files is 256
86#define MAX_OPEN_FILES 256
87
88// each directory entry represents a file/directory in the parent
89// directory, and consists of a file/directory name (less than 16
90// bytes) and an integer inode number
91typedef struct _dirent {
92 char fname[MAX_NAME]; // name of the file
93 int inode; // inode of the file
94} dirent_t;
95
96// the number of directory entries that can be contained in a sector
97#define DIRENTS_PER_SECTOR (SECTOR_SIZE/sizeof(dirent_t))
98
99// global errno value here
100int osErrno;
101
102// the name of the disk backstore file (with which the file system is booted)
103static char bs_filename[1024];
104
105/* the following functions are internal helper functions */
106//find min
107int min(int num1, int num2){
108 return (num1 > num2) ? num2 : num1;
109}
110
111// check magic number in the superblock; return 1 if OK, and 0 if not
112static int check_magic()
113{
114 char buf[SECTOR_SIZE];
115 if(Disk_Read(SUPERBLOCK_START_SECTOR, buf) < 0)
116 return 0;
117 if(*(int*)buf == OS_MAGIC) return 1;
118 else return 0;
119}
120
121#define CHARBITS (8) //number of bits in a char
122
123/*
124 The following functions were created by ehenl001@fiu to for bit manipulation
125*/
126static void set_bit(char *bitmap, int index)
127{
128 ( bitmap[(index/CHARBITS)] |= (1UL << (index % CHARBITS)));
129}
130
131static void clear_bit(char *bitmap, int index)
132{
133 ( bitmap[(index/CHARBITS)] &= ~(1UL << (index % CHARBITS)));
134}
135
136static int test_bit(char *bitmap, int index)
137{
138 return ( bitmap[(index/CHARBITS)] & (1UL << (index % CHARBITS))) > 0;
139}
140
141static int array_size(int numBits)
142{
143 return (numBits/CHARBITS+(!!(numBits%CHARBITS)));
144}
145
146void yellow(){printf("\033[0;33m");};
147void boldYellow(){printf("\033[1m\033[33m");};
148void blue(){printf("\033[0;34m");};
149void boldBlue(){printf("\033[1m\033[34m");};
150void green(){printf("\033[0;32m");};
151void boldGreen(){printf("\033[1m\033[32m");};
152void red(){printf("\033[0;31m");}
153void boldRed(){printf("\033[1m\033[31m");};
154void reset(){printf("\033[0m");};
155// initialize a bitmap with 'num' sectors starting from 'start'
156// sector; all bits should be set to zero except that the first
157// 'nbits' number of bits are set to one
158// type for inode bitmap sector = 0
159// type for sector bitmap sector = 1
160static void bitmap_init(int start, int num, int nbits, int type)
161{
162 /* YOUR CODE */
163 green();
164 dprintf("bitmap_init(%d, %d, %d)\n",start, num, nbits);
165 reset();
166
167 //create bitmap with size of arry of chars neccessary to support num bits
168 char *bitmap;
169
170 //if type then bitmap sector, else inode sector, assign size appropriately
171 int size = -1;
172 if(type){
173 size = SECTOR_BITMAP_SIZE;
174 }else{
175 size = INODE_BITMAP_SIZE;
176 }
177 //allocate the neccessary bytes for the num size bitmap
178 bitmap = (char *)calloc(size, sizeof(char));
179
180 //initialize all bits to 0 (clear all bits)
181 for (int i = 0; i < num; i++)
182 {
183 clear_bit(bitmap,i);
184 }
185
186 //for nbits set the bit (bit = 1)
187 for (int i = 0; i < nbits; i++)
188 {
189 set_bit(bitmap,i);
190 }
191
192 //set the bits of the sector that thec current bitmap occupies
193 for(int i = start; i < start + num; i++) set_bit(bitmap, i);
194
195 // check if bitmap will fit in one sector (SECTOR_SIZE > size(bitmap))
196 if(SECTOR_SIZE >= size)
197 {
198
199 if(Disk_Write(start, bitmap)<0)
200 {
201 green();
202 dprintf("---> Error initializing bitmap, func=bitmap_init\n");
203 reset();
204 }
205 else{
206 green();
207 dprintf("---> bitmap wrote to disk successfully using 1 sector to store, func=bitmap_init\n");
208 reset();
209 }
210
211
212 }
213 else
214 {
215 int toXfr = size;//track total bytes to write to disk
216 int numBytes = 0;
217 int i = -1;
218 int numSectorUsed = 0;
219
220 for (i = 0; i<= size; i+=SECTOR_SIZE)
221 {
222 char buff[SECTOR_SIZE];
223 if(toXfr>SECTOR_SIZE)
224 {
225 numBytes = SECTOR_SIZE;
226 }
227 else
228 {
229 numBytes = toXfr;
230 }
231 //copy to buff numBytes of bitmap
232 if( memcpy(buff, &bitmap[i], numBytes)==NULL){dprintf("error with bitmap copy\n");}
233
234 if(Disk_Write(start++, buff)<0)
235 {
236 green();
237 dprintf("---> Error initializing bitmap\n");
238 reset();
239 }
240 numSectorUsed +=1;
241 bzero(buff, SECTOR_SIZE);
242 toXfr = toXfr - numBytes;//update number of bytes remaining to write to disk
243
244 }
245 green();
246 dprintf("---> bitmap written to disk using %d sectors to store on disk, func=bitmap_init\n", numSectorUsed);
247 reset();
248 }
249 free(bitmap);
250 green();
251 dprintf("---> mem freed for bitmap, func=bitmap_init\n");
252 reset();
253}
254
255// set the first unused bit from a bitmap of 'nbits' bits (flip the
256// first zero appeared in the bitmap to one) and return its location;
257// return -1 if the bitmap is already full (no more zeros)
258static int bitmap_first_unused(int start, int num, int nbits)
259{
260 /* YOUR CODE */
261
262 green();
263 dprintf("bitmap_first_unused(%d, %d, %d)\n", start, num, nbits);
264 reset();
265 int numSec = num;
266 int found = 0;
267 int firstUnused = -1;
268
269 //determine number of bytes needed to represent bitmap with nbits
270 int bmpARRLen = nbits;
271 int currSec = start;
272
273 //get number of bits represented by bmp
274 int bits;
275 if (start == INODE_BITMAP_START_SECTOR){
276 bits = 1000;
277 }else{
278 bits = 10000;
279 }
280
281 //prep bitmap array
282 char *bitmap;
283 bitmap = (char*)calloc(bmpARRLen, sizeof(char));
284 bzero(bitmap,bmpARRLen);
285
286 int index = 0; //track index in bmp where next chunk from buff will be stored
287 int numBytes = bmpARRLen; //track remaining bytes to be copied to form complete bitmap
288 int bytesToCopy = -1;
289 int indexCount = 0;
290
291 //read bitmap from disk
292 for(int i = 0; i < numSec; i++)
293 {
294 char buff[SECTOR_SIZE];
295 //read each sector and build full bitmap
296 if(Disk_Read(currSec, buff) < 0) return -1;
297 //copy buff to bitmap
298 index = SECTOR_SIZE * i;
299
300 if(numBytes<=SECTOR_SIZE)
301 {
302 bytesToCopy = numBytes;
303 }
304 else
305 {
306 bytesToCopy = SECTOR_SIZE;
307 numBytes -= SECTOR_SIZE;
308 }
309
310 for(int j = 0; j<numBytes; j++){
311 bitmap[indexCount++] = buff[j];
312 }
313 bzero(buff, SECTOR_SIZE);
314 currSec++;
315 }
316
317 //search for first bit equal to 0
318 for(int i =0; i < bits; i++)
319 {
320 //if equal to 0 set bit and return i
321 if (test_bit(bitmap, i) == 0)
322 {
323 //set ith bit to 1
324 green();
325 dprintf("---> attempting to set bit %d\n", i);
326 reset();
327 set_bit(bitmap, i);
328 found = 1;
329 firstUnused = i;
330 }
331 if(found) break;
332 }
333 //write new bit map to disk
334 numBytes = bmpARRLen;
335 currSec = start;
336 index = 0;
337 for(int i = 0; i < numSec; i++)
338 {
339 char buff[SECTOR_SIZE];
340 //update index, beginning of next section of bitmap to copy
341 index = SECTOR_SIZE * i;
342 //check if remaining bytes to be copied (numBytes) is less than sector size
343 if(numBytes <= SECTOR_SIZE)
344 {
345 bytesToCopy = numBytes;
346 }
347 else
348 {
349 bytesToCopy = SECTOR_SIZE;
350 }
351 //copy from bitmap to buff
352 memcpy(buff, &bitmap[index], bytesToCopy);
353 //write to currSec full bitmap or part of full bitmap
354 if(Disk_Write(currSec, buff) < 0) return -1;
355
356 currSec++;
357
358 bzero(buff, SECTOR_SIZE);
359 //update remaining number of bytes needing to be written to disk
360 numBytes -= bytesToCopy;
361 }
362 //free allocated memory of bitmap
363 free(bitmap);
364 //check if bitmap corrupted
365 if(found ==0 && start==SECTOR_BITMAP_START_SECTOR){
366 green();
367 dprintf("---> error bitmap corrupted superblock sector found as first unused\n");
368 reset();
369 return -1;
370 }
371
372 //if unused is found return its index, else return -1
373 if(found)
374 {
375 green();
376 dprintf("---> found first unused bit in nbytes=%d bmp at index=%d\n", nbits, firstUnused);
377 reset();
378 return firstUnused;
379 }
380 else
381 {
382 green();
383 dprintf("---> unused bit NOT FOUND in nbytes=%d bmp\n", nbits);
384 reset();
385 return -1;
386 }
387}
388
389// reset the i-th bit of a bitmap with 'num' sectors starting from
390// 'start' sector; return 0 if successful, -1 otherwise
391// reset the i-th bit of a bitmap with 'num' sectors starting from
392// 'start' sector; return 0 if successful, -1 otherwise
393static int bitmap_reset(int start, int num, int ibit)
394{
395 /* YOUR CODE */
396 green();
397 dprintf("bitmap_reset(%d, %d, %d)\n", start, num, ibit);
398 reset();
399 if((start==SECTOR_BITMAP_START_SECTOR) && ((ibit == 0)||(ibit == 1) ||(ibit == 2) || (ibit == 3)||(ibit == 4))){
400 green();
401 dprintf("---> error attempting to over critical sector=%d\n", ibit);
402 dprintf("---> exiting bitmap_reset UNSUCCESSFULLY\n");
403 reset();
404 return -1;
405 }
406 int numSec = num;
407 //determine number of bytes needed to represent bitmap with nbits
408 int bmpARRLen = -1;
409 if (start == INODE_BITMAP_START_SECTOR){
410 bmpARRLen = INODE_BITMAP_SIZE;
411 }else{
412 bmpARRLen = SECTOR_BITMAP_SIZE;
413 }
414 int currSec = start;
415
416 //prep bitmap array
417 char *bitmap;
418 bitmap = (char*)calloc(bmpARRLen, sizeof(char));
419 bzero(bitmap,bmpARRLen);
420
421 int index = 0; //track index in bmp where next chunk from buff will be stored
422 int numBytes = bmpARRLen; //track remaining bytes to be copied to form complete bitmap
423 int bytesToCopy = -1;
424 int indexCount = 0;
425
426 //read bitmap from disk
427 for(int i = 0; i < numSec; i++)
428 {
429 char buff[SECTOR_SIZE];
430 //read each sector and build full bitmap
431 if(Disk_Read(currSec, buff) < 0) return -1;
432 //copy buff to bitmap
433 index = SECTOR_SIZE * i;
434 if(numBytes<=SECTOR_SIZE)
435 {
436 bytesToCopy = numBytes;
437 }
438 else
439 {
440 bytesToCopy = SECTOR_SIZE;
441 numBytes -= SECTOR_SIZE;
442 }
443
444 for(int j = 0; j<numBytes; j++){
445 bitmap[indexCount++] = buff[j];
446 }
447 bzero(buff, SECTOR_SIZE);
448 currSec++;
449 }
450 //checkt if ibit already clear if not clear
451 if(test_bit(bitmap, ibit) == 0){
452 green();
453 dprintf("---> error bit already clear\n");
454 reset();
455 return -1;
456 }else{
457 clear_bit(bitmap, ibit);
458 }
459 //write new bit map to disk
460 numBytes = bmpARRLen;
461 currSec = start;
462 index = 0;
463 for(int i = 0; i < numSec; i++)
464 {
465 char buff[SECTOR_SIZE];
466 //update index, beginning of next section of bitmap to copy
467 index = SECTOR_SIZE * i;
468 //check if remaining bytes to be copied (numBytes) is less than sector size
469 if(numBytes <= SECTOR_SIZE)
470 {
471 bytesToCopy = numBytes;
472 }
473 else
474 {
475 bytesToCopy = SECTOR_SIZE;
476 }
477 //copy from bitmap to buff
478 memcpy(buff, &bitmap[index], bytesToCopy);
479 //write to currSec full bitmap or part of full bitmap
480 if(Disk_Write(currSec, buff) < 0) return -1;
481 currSec++;
482
483 bzero(buff, SECTOR_SIZE);
484 //update remaining number of bytes needing to be written to disk
485 numBytes -= bytesToCopy;
486 }
487 //free allocated memory of bitmap
488 free(bitmap);
489
490 green();
491 dprintf("---> bitmap_reset COMPLETED SUCCESSFULLY\n");
492 reset();
493 return 0;
494}
495// return 1 if the file name is illegal; otherwise, return 0; legal
496// characters for a file name include letters (case sensitive),
497// numbers, dots, dashes, and underscores; and a legal file name
498// should not be more than MAX_NAME-1 in length
499static int illegal_filename(char* name)
500{
501 // Check if name is non empty or too long
502 if(name==NULL || strlen(name)==0 || strlen(name)>=MAX_NAME)
503 {
504 dprintf("... Filename is null or too long: %s\n", name);
505 return 1;
506 }
507
508 // Check if entries only contain allowed elements
509 for(int i=0; i < strlen(name);i++){
510 if(!(isalpha(name[i]) || isdigit(name[i]) ||
511 name[i] == '.' || name[i] == '_' || name[i] == '-' || name[i] == '/'))
512 {
513 dprintf("... Invalid characters in filename: %s\n", name);
514 return 1; }
515 }
516
517 return 0;
518}
519
520// return the child inode of the given file name 'fname' from the
521// parent inode; the parent inode is currently stored in the segment
522// of inode table in the cache (we cache only one disk sector for
523// this); once found, both cached_inode_sector and cached_inode_buffer
524// may be updated to point to the segment of inode table containing
525// the child inode; the function returns -1 if no such file is found;
526// it returns -2 is something else is wrong (such as parent is not
527// directory, or there's read error, etc.)
528static int find_child_inode(int parent_inode, char* fname,
529 int *cached_inode_sector, char* cached_inode_buffer)
530{
531 int cached_start_entry = ((*cached_inode_sector)-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
532 int offset = parent_inode-cached_start_entry;
533 assert(0 <= offset && offset < INODES_PER_SECTOR);
534 inode_t* parent = (inode_t*)(cached_inode_buffer+offset*sizeof(inode_t));
535 dprintf("... load parent inode: %d (size=%d, type=%d)\n",
536 parent_inode, parent->size, parent->type);
537 if(parent->type != 1) {
538 dprintf("... parent not a directory\n");
539 return -2;
540 }
541
542 int nentries = parent->size; // remaining number of directory entries
543 int idx = 0;
544 while(nentries > 0) {
545 char buf[SECTOR_SIZE]; // cached content of directory entries
546 if(Disk_Read(parent->data[idx], buf) < 0) return -2;
547 for(int i=0; i<DIRENTS_PER_SECTOR; i++) {
548 if(i>nentries) break;
549 if(!strcmp(((dirent_t*)buf)[i].fname, fname)) {
550 // found the file/directory; update inode cache
551 int child_inode = ((dirent_t*)buf)[i].inode;
552 dprintf("... found child_inode=%d\n", child_inode);
553 int sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
554 if(sector != (*cached_inode_sector)) {
555 *cached_inode_sector = sector;
556 if(Disk_Read(sector, cached_inode_buffer) < 0) return -2;
557 dprintf("... load inode table for child\n");
558 }
559 return child_inode;
560 }
561 }
562 idx++; nentries -= DIRENTS_PER_SECTOR;
563 }
564 dprintf("... could not find child inode\n");
565 return -1; // not found
566}
567
568// follow the absolute path; if successful, return the inode of the
569// parent directory immediately before the last file/directory in the
570// path; for example, for '/a/b/c/d.txt', the parent is '/a/b/c' and
571// the child is 'd.txt'; the child's inode is returned through the
572// parameter 'last_inode' and its file name is returned through the
573// parameter 'last_fname' (both are references); it's possible that
574// the last file/directory is not in its parent directory, in which
575// case, 'last_inode' points to -1; if the function returns -1, it
576// means that we cannot follow the path
577static int follow_path(char* path, int* last_inode, char* last_fname)
578{
579 if(!path) {
580 dprintf("... invalid path\n");
581 return -1;
582 }
583 if(path[0] != '/') {
584 dprintf("... '%s' not absolute path\n", path);
585 return -1;
586 }
587
588 // make a copy of the path (skip leading '/'); this is necessary
589 // since the path is going to be modified by strsep()
590 char pathstore[MAX_PATH];
591 strncpy(pathstore, path+1, MAX_PATH-1);
592 pathstore[MAX_PATH-1] = '\0'; // for safety
593 char* lpath = pathstore;
594
595 int parent_inode = -1, child_inode = 0; // start from root
596 // cache the disk sector containing the root inode
597 int cached_sector = INODE_TABLE_START_SECTOR;
598 char cached_buffer[SECTOR_SIZE];
599 if(Disk_Read(cached_sector, cached_buffer) < 0) return -1;
600 dprintf("... load inode table for root from disk sector %d\n", cached_sector);
601
602 // for each file/directory name separated by '/'
603 char* token;
604 while((token = strsep(&lpath, "/")) != NULL) {
605 dprintf("... process token: '%s'\n", token);
606 if(*token == '\0') continue; // multiple '/' ignored
607 if(illegal_filename(token)) {
608 dprintf("... illegal file name: '%s'\n", token);
609 return -1;
610 }
611 if(child_inode < 0) {
612 // regardless whether child_inode was not found previously, or
613 // there was issues related to the parent (say, not a
614 // directory), or there was a read error, we abort
615 dprintf("... parent inode can't be established\n");
616 return -1;
617 }
618 parent_inode = child_inode;
619 child_inode = find_child_inode(parent_inode, token,
620 &cached_sector, cached_buffer);
621 if(last_fname) strcpy(last_fname, token);
622 }
623 if(child_inode < -1) return -1; // if there was error, abort
624 else {
625 // there was no error, several possibilities:
626 // 1) '/': parent = -1, child = 0
627 // 2) '/valid-dirs.../last-valid-dir/not-found': parent=last-valid-dir, child=-1
628 // 3) '/valid-dirs.../last-valid-dir/found: parent=last-valid-dir, child=found
629 // in the first case, we set parent=child=0 as special case
630 if(parent_inode==-1 && child_inode==0) parent_inode = 0;
631 dprintf("... found parent_inode=%d, child_inode=%d\n", parent_inode, child_inode);
632 *last_inode = child_inode;
633 return parent_inode;
634 }
635}
636
637// add a new file or directory (determined by 'type') of given name
638// 'file' under parent directory represented by 'parent_inode'
639int add_inode(int type, int parent_inode, char* file)
640{
641 // get a new inode for child
642 int child_inode = bitmap_first_unused(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, INODE_BITMAP_SIZE);
643 if(child_inode < 0) {
644 dprintf("... error: inode table is full\n");
645 return -1;
646 }
647 dprintf("... new child inode %d\n", child_inode);
648
649 // load the disk sector containing the child inode
650 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
651 char inode_buffer[SECTOR_SIZE];
652 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
653 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
654
655 // get the child inode
656 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
657 int offset = child_inode-inode_start_entry;
658 assert(0 <= offset && offset < INODES_PER_SECTOR);
659 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
660
661 // update the new child inode and write to disk
662 memset(child, 0, sizeof(inode_t));
663 child->type = type;
664 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
665 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
666 child_inode, child->size, child->type, inode_sector);
667
668 // get the disk sector containing the parent inode
669 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
670 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
671 dprintf("... load inode table for parent inode %d from disk sector %d\n",
672 parent_inode, inode_sector);
673
674 // get the parent inode
675 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
676 offset = parent_inode-inode_start_entry;
677 assert(0 <= offset && offset < INODES_PER_SECTOR);
678 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
679 dprintf("... get parent inode %d (size=%d, type=%d)\n",
680 parent_inode, parent->size, parent->type);
681
682 // get the dirent sector
683 if(parent->type != 1) {
684 dprintf("... error: parent inode is not directory\n");
685 return -2; // parent not directory
686 }
687 int group = parent->size/DIRENTS_PER_SECTOR;
688 //check if group has reach max sectors per directory and abort if true
689 if (group >= MAX_SECTORS_PER_FILE){
690 printf("... all sectors of parent director is filled\n");
691 return -1;
692 }
693 char dirent_buffer[SECTOR_SIZE];
694 if(group*DIRENTS_PER_SECTOR == parent->size) {
695 // new disk sector is needed
696 int newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
697 if(group == MAX_SECTORS_PER_FILE-1){
698 dprintf(".... error: all sectors referenced in data attribute of parent inode fully occupied. Parent size: %d\n", parent->size);
699 return -1;
700 }
701 if(newsec < 0) {
702 dprintf("... error: disk is full\n");
703 return -1;
704 }
705 parent->data[group] = newsec;
706 memset(dirent_buffer, 0, SECTOR_SIZE);
707 dprintf("... new disk sector %d for dirent group %d\n", newsec, group);
708 } else {
709 if(Disk_Read(parent->data[group], dirent_buffer) < 0)
710 return -1;
711 dprintf("... load disk sector %d for dirent group %d\n", parent->data[group], group);
712 }
713
714 // add the dirent and write to disk
715 int start_entry = group*DIRENTS_PER_SECTOR;
716 offset = parent->size-start_entry;
717 dirent_t* dirent = (dirent_t*)(dirent_buffer+offset*sizeof(dirent_t));
718 strncpy(dirent->fname, file, MAX_NAME);
719 dirent->inode = child_inode;
720 if(Disk_Write(parent->data[group], dirent_buffer) < 0) return -1;
721 dprintf("... append dirent %d (name='%s', inode=%d) to group %d, update disk sector %d\n",
722 parent->size, dirent->fname, dirent->inode, group, parent->data[group]);
723
724 // update parent inode and write to disk
725 parent->size++;
726 dprintf("... updating parent inode size: %d", parent->size);
727 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
728 dprintf("... update parent inode on disk sector %d\n", inode_sector);
729
730 return 0;
731}
732
733// used by both File_Create() and Dir_Create(); type=0 is file, type=1
734// is directory
735int create_file_or_directory(int type, char* pathname)
736{
737 int child_inode;
738 char last_fname[MAX_NAME];
739 int parent_inode = follow_path(pathname, &child_inode, last_fname);
740 if(parent_inode >= 0)
741 {
742 if(child_inode >= 0)
743 {
744 dprintf("... file/directory '%s' already exists, failed to create\n", pathname);
745 osErrno = E_CREATE;
746 return -1;
747 } else
748 {
749 if(add_inode(type, parent_inode, last_fname) >= 0)
750 {
751 dprintf("... successfully created file/directory: '%s'\n", pathname);
752 return 0;
753 }
754 else {
755 dprintf("... error: something wrong with adding child inode\n");
756 osErrno = E_CREATE;
757 return -1;
758 }
759 }
760 } else {
761 dprintf("... error: something wrong with the file/path: '%s'\n", pathname);
762 osErrno = E_CREATE;
763 return -1;
764 }
765}
766
767// remove the child from parent; the function is called by both
768// File_Unlink() and Dir_Unlink(); the function returns 0 if success,
769// -1 if general error, -2 if directory not empty, -3 if wrong type
770int remove_inode(int type, int parent_inode, int child_inode)
771{
772
773 dprintf("... remove inode %d\n", child_inode);
774
775 // load the disk sector containing the child inode
776 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
777 char inode_buffer[SECTOR_SIZE];
778 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
779 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
780
781 // get the child inode
782 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
783 int offset = child_inode-inode_start_entry;
784 assert(0 <= offset && offset < INODES_PER_SECTOR);
785 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
786
787 // check for right type
788 if(child->type!=type){
789 dprintf("... error: the type parameter does not match the actual inode type\n");
790 return -3;
791 }
792
793 // check if child is non-empty directory
794 if(child->type==1 && child->size>0){
795 dprintf("... error: inode is non-empty directory\n");
796 return -2;
797 }
798
799 // reset data blocks of file
800 if(type==0){
801
802 /**
803 * DEBUGGING PRINT
804 */
805 dprintf("Inode size: %d, data sector 0: %d\n", child->size, child->data[0]);
806
807 char data_buffer[SECTOR_SIZE];
808
809 int sectors = child->size/SECTOR_SIZE+(child->size%SECTOR_SIZE != 0 ? 1 : 0);
810 for(int i=0; i<sectors; i++){
811 if(Disk_Read(child->data[i], data_buffer) < 0) return -1;
812 dprintf("... delete contents of file with inode %d from sector %d\n",
813 child_inode, child->data[i]);
814 bzero(data_buffer, SECTOR_SIZE);
815 if(Disk_Write(child->data[i], data_buffer) < 0) return -1;
816 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, child->data[i]) < 0) {
817 dprintf("... error: free sector occupied by file in sector bitmap unsuccessful\n");
818 return -1;
819 }
820 }
821
822 }
823
824 // delete the child inode
825 memset(child, 0, sizeof(inode_t));
826 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
827 dprintf("... delete inode %d, update disk sector %d\n",
828 child_inode, inode_sector);
829
830 // get the disk sector containing the parent inode
831 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
832 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
833 dprintf("... load inode table for parent inode %d from disk sector %d\n",
834 parent_inode, inode_sector);
835
836 // get the parent inode
837 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
838 offset = parent_inode-inode_start_entry;
839 assert(0 <= offset && offset < INODES_PER_SECTOR);
840 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
841 dprintf("... get parent inode %d (size=%d, type=%d)\n",
842 parent_inode, parent->size, parent->type);
843
844 // check if parent is directory
845 if(parent->type != 1) {
846 dprintf("... error: parent inode is not directory\n");
847 return -3;
848 }
849
850 // check if parent is non-empty
851 if(parent->size < 1) {
852 dprintf("... error: parent directory has no entries\n");
853 return -1;
854 }
855
856 // reset bit of child inode in bitmap
857 if (bitmap_reset(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, child_inode) < 0) {
858 dprintf("... error: reset inode in bitmap unsuccessful\n");
859 return -1;
860 }
861
862 int remainder = parent->size % DIRENTS_PER_SECTOR;
863 int group = (parent->size+DIRENTS_PER_SECTOR-1)/DIRENTS_PER_SECTOR;
864 char dirent_buffer[SECTOR_SIZE];
865 dirent_t* dirent;
866 int counter = 0;
867 int found = 0;
868
869 // search for the dirent in every group
870 for(int i = 0; i<group; i++){
871
872 if(Disk_Read(parent->data[i], dirent_buffer) < 0) return -1;
873 dprintf("... search for child in disk sector %d for dirent group %d\n", parent->data[i], i);
874
875 for(int j=0; j<DIRENTS_PER_SECTOR; j++){
876 counter ++;
877 dirent = (dirent_t*)(dirent_buffer+j*sizeof(dirent_t));
878
879 if(counter < parent->size && (!found)) {
880
881 // Case 1: Child node found and last dirent in the same sector
882 if(dirent->inode == child_inode && i == group-1) {
883 dirent_t* last_dirent = (dirent_t*)(dirent_buffer+(remainder-1)*sizeof(dirent_t));
884 memcpy(dirent, last_dirent, sizeof(dirent_t));
885 memset(last_dirent, 0, sizeof(dirent_t));
886 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
887 dprintf("... delete dirent (inode=%d) from group %d, update disk sector %d\n",
888 child_inode, i, parent->data[i]);
889 found=1;
890
891 // Case 2: Child node found, but last dirent in other sector
892 } else if(dirent->inode == child_inode) {
893 char last_dirent_buffer[SECTOR_SIZE];
894 if(Disk_Read(parent->data[group-1], last_dirent_buffer) < 0) return -1;
895 dprintf("... load last dirent from parent %d in disk sector %d for dirent group %d\n",
896 parent_inode, parent->data[group-1], group-1);
897 dirent_t* last_dirent = (dirent_t*)(last_dirent_buffer+(remainder-1)*sizeof(dirent_t));
898 memcpy(dirent, last_dirent, sizeof(dirent_t));
899 memset(last_dirent, 0, sizeof(dirent_t));
900 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
901 dprintf("...delete dirent (inode=%d) from group %d, update disk sector %d\n",
902 child_inode, i, parent->data[i]);
903 if(Disk_Write(parent->data[group-1], last_dirent_buffer) < 0) return -1;
904 dprintf("...copy last dirent with inode %d into group %d, update disk sector %d\n",
905 dirent->inode, group-1, parent->data[group-1]);
906 found=1;
907 }
908
909 } else if(!found) {
910
911 // Case 3: Child found and last dirent from parent
912 if(dirent->inode == child_inode) {
913 memset(dirent, 0, sizeof(dirent_t));
914 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
915 dprintf("... delete dirent (inode=%d) from group %d, update disk sector %d\n",
916 child_inode, group-1, parent->data[i]);
917 found=1;
918
919 // Case 4: Child node not found, and reached last dirent from parent
920 } else if(counter >= parent->size) {
921 dprintf("Counter: %d, Parent size: %d\n", counter, parent->size);
922 dprintf("... error: child inode could not be found in parent directory\n");
923 return -1;
924 }
925 }
926 }
927 }
928
929 // check for remaining dirents from parent in that sector (otherwise reset sector bitmap)
930 if (remainder == 1) {
931 // disk sector has to be freed
932 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, parent->data[group]) < 0) {
933 dprintf("... error: free sector in bitmap unsuccessful\n");
934 return -1;
935 }
936 parent->data[group-1] = 0;
937 }
938
939 // update parent inode and write to disk
940 parent->size--;
941 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
942 dprintf("... update parent inode on disk sector %d\n", inode_sector);
943
944 return 0;
945}
946
947// representing an open file
948typedef struct _open_file {
949 int inode; // pointing to the inode of the file (0 means entry not used)
950 int size; // file size cached here for convenience
951 int pos; // read/write position within the data array
952 int posByte; //starting byte to read from
953} open_file_t;
954static open_file_t open_files[MAX_OPEN_FILES];
955
956// return true if the file pointed to by inode has already been open
957int is_file_open(int inode)
958{
959 for(int i=0; i<MAX_OPEN_FILES; i++) {
960 if(open_files[i].inode == inode)
961 return 1;
962 }
963 return 0;
964}
965
966// return a new file descriptor not used; -1 if full
967int new_file_fd()
968{
969 for(int i=0; i<MAX_OPEN_FILES; i++) {
970 if(open_files[i].inode <= 0)
971 return i;
972 }
973 return -1;
974}
975
976/* end of internal helper functions, start of API functions */
977
978int FS_Boot(char* backstore_fname)
979{
980 dprintf("FS_Boot('%s'):\n", backstore_fname);
981 // initialize a new disk (this is a simulated disk)
982 if(Disk_Init() < 0) {
983 dprintf("... disk init failed\n");
984 osErrno = E_GENERAL;
985 return -1;
986 }
987 dprintf("... disk initialized\n");
988
989 // we should copy the filename down; if not, the user may change the
990 // content pointed to by 'backstore_fname' after calling this function
991 strncpy(bs_filename, backstore_fname, 1024);
992 bs_filename[1023] = '\0'; // for safety
993
994 // we first try to load disk from this file
995 if(Disk_Load(bs_filename) < 0) {
996 dprintf("... load disk from file '%s' failed\n", bs_filename);
997
998 // if we can't open the file; it means the file does not exist, we
999 // need to create a new file system on disk
1000 if(diskErrno == E_OPENING_FILE) {
1001 dprintf("... couldn't open file, create new file system\n");
1002
1003 // format superblock
1004 char buf[SECTOR_SIZE];
1005 memset(buf, 0, SECTOR_SIZE);
1006 *(int*)buf = OS_MAGIC;
1007 if(Disk_Write(SUPERBLOCK_START_SECTOR, buf) < 0) {
1008 dprintf("... failed to format superblock\n");
1009 osErrno = E_GENERAL;
1010 return -1;
1011 }
1012 dprintf("... formatted superblock (sector %d)\n", SUPERBLOCK_START_SECTOR);
1013
1014 // format inode bitmap (reserve the first inode to root)
1015 //type for inode bitmap sector = 0
1016 dprintf("... calling bitmap int for inode bitmap with start=%d, num=%d, nbits=1\n",
1017 INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS);
1018 bitmap_init(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, 1, 0);
1019 dprintf("... formatted inode bitmap (start=%d, num=%d)\n",
1020 (int)INODE_BITMAP_START_SECTOR, (int)INODE_BITMAP_SECTORS);
1021
1022 // format sector bitmap (reserve the first few sectors to
1023 // superblock, inode bitmap, sector bitmap, and inode table)
1024 //type for sector bitmap = 1
1025 dprintf("... calling bitmap int for sector bitmap with start=%d, num=%d, nbits=%ld\n",
1026 SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, DATABLOCK_START_SECTOR);
1027 bitmap_init(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS,
1028 DATABLOCK_START_SECTOR, 1);
1029 dprintf("... formatted sector bitmap (start=%d, num=%d)\n",
1030 (int)SECTOR_BITMAP_START_SECTOR, (int)SECTOR_BITMAP_SECTORS);
1031
1032 // format inode tables
1033 for(int i=0; i<INODE_TABLE_SECTORS; i++) {
1034 memset(buf, 0, SECTOR_SIZE);
1035 if(i==0) {
1036 // the first inode table entry is the root directory
1037 ((inode_t*)buf)->size = 0;
1038 ((inode_t*)buf)->type = 1;
1039 }
1040 if(Disk_Write(INODE_TABLE_START_SECTOR+i, buf) < 0) {
1041 dprintf("... failed to format inode table\n");
1042 osErrno = E_GENERAL;
1043 return -1;
1044 }
1045 }
1046 dprintf("... formatted inode table (start=%d, num=%d)\n",
1047 (int)INODE_TABLE_START_SECTOR, (int)INODE_TABLE_SECTORS);
1048
1049 // we need to synchronize the disk to the backstore file (so
1050 // that we don't lose the formatted disk)
1051 if(Disk_Save(bs_filename) < 0) {
1052 // if can't write to file, something's wrong with the backstore
1053 dprintf("... failed to save disk to file '%s'\n", bs_filename);
1054 osErrno = E_GENERAL;
1055 return -1;
1056 } else {
1057 // everything's good now, boot is successful
1058 dprintf("... successfully formatted disk, boot successful\n");
1059 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
1060 return 0;
1061 }
1062 } else {
1063 // something wrong loading the file: invalid param or error reading
1064 dprintf("... couldn't read file '%s', boot failed\n", bs_filename);
1065 osErrno = E_GENERAL;
1066 return -1;
1067 }
1068 } else {
1069 dprintf("... load disk from file '%s' successful\n", bs_filename);
1070
1071 // we successfully loaded the disk, we need to do two more checks,
1072 // first the file size must be exactly the size as expected (thiis
1073 // supposedly should be folded in Disk_Load(); and it's not)
1074 int sz = 0;
1075 FILE* f = fopen(bs_filename, "r");
1076 if(f) {
1077 fseek(f, 0, SEEK_END);
1078 sz = ftell(f);
1079 fclose(f);
1080 }
1081 if(sz != SECTOR_SIZE*TOTAL_SECTORS) {
1082 dprintf("... check size of file '%s' failed\n", bs_filename);
1083 osErrno = E_GENERAL;
1084 return -1;
1085 }
1086 dprintf("... check size of file '%s' successful\n", bs_filename);
1087
1088 // check magic
1089 if(check_magic()) {
1090 // everything's good by now, boot is successful
1091 dprintf("... check magic successful\n");
1092 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
1093 return 0;
1094 } else {
1095 // mismatched magic number
1096 dprintf("... check magic failed, boot failed\n");
1097 osErrno = E_GENERAL;
1098 return -1;
1099 }
1100 }
1101}
1102
1103int FS_Sync()
1104{
1105 dprintf("FS_Sync():\n");
1106 if(Disk_Save(bs_filename) < 0) {
1107 // if can't write to file, something's wrong with the backstore
1108 dprintf("FS_Sync():\n... failed to save disk to file '%s'\n", bs_filename);
1109 osErrno = E_GENERAL;
1110 return -1;
1111 } else {
1112 // everything's good now, sync is successful
1113 dprintf("FS_Sync():\n... successfully saved disk to file '%s'\n", bs_filename);
1114 return 0;
1115 }
1116}
1117
1118int File_Create(char* file)
1119{
1120 dprintf("File_Create('%s'):\n", file);
1121 return create_file_or_directory(0, file);
1122}
1123
1124//TODO: check if really remove the data blocks
1125int File_Unlink(char* file)
1126{
1127 dprintf("File_Unlink('%s'):\n", file);
1128 int child_inode;
1129
1130 int parent_inode = follow_path(file, &child_inode, NULL);
1131
1132
1133 if(child_inode >= 0) //file exists
1134 {
1135 //check if file is open
1136 if(is_file_open(child_inode)){
1137 dprintf("... %s is an open file. Cannot unlink\n", file);
1138 osErrno = E_FILE_IN_USE;
1139 return -1;
1140 }
1141
1142
1143 int remove = remove_inode(0, parent_inode, child_inode);
1144 if(remove == -1){
1145 dprintf("... error: general error when unlinking file\n");
1146 osErrno = E_GENERAL;
1147 return -1;
1148 }
1149 else if(remove == -3){
1150 dprintf("... error: no file, incorrect type\n");
1151 osErrno = E_NO_SUCH_FILE;
1152 return -1;
1153 }
1154 else{
1155 return 0;
1156 }
1157
1158 }
1159
1160 else{ //file does not exist
1161 dprintf("... %s file does not exist\n", file);
1162 osErrno = E_NO_SUCH_FILE;
1163 return -1;
1164
1165 }
1166}
1167
1168int File_Open(char* file)
1169{
1170 dprintf("File_Open('%s'):\n", file);
1171
1172 int fd = new_file_fd();
1173
1174 if(fd < 0) {
1175 dprintf("... max open files reached\n");
1176 osErrno = E_TOO_MANY_OPEN_FILES;
1177 return -1;
1178 }
1179
1180 int child_inode;
1181 follow_path(file, &child_inode, NULL);
1182
1183 if(child_inode >= 0) { // child is the one, file exists
1184 //check if file is already open
1185 if(is_file_open(child_inode)){
1186 dprintf("... error: %s is an open file already.\n", file);
1187 osErrno = E_FILE_IN_USE;
1188 return -1;
1189 }
1190 // load the disk sector containing the inode
1191 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
1192 char inode_buffer[SECTOR_SIZE];
1193 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1194 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1195
1196 // get the inode
1197 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1198 int offset = child_inode-inode_start_entry;
1199 assert(0 <= offset && offset < INODES_PER_SECTOR);
1200 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1201 dprintf("... inode %d (size=%d, type=%d)\n",
1202 child_inode, child->size, child->type);
1203
1204 if(child->type != 0) {
1205 dprintf("... error: '%s' is not a file\n", file);
1206 osErrno = E_GENERAL;
1207 return -1;
1208 }
1209
1210 // initialize open file entry and return its index
1211 open_files[fd].inode = child_inode;
1212 open_files[fd].size = child->size;
1213 open_files[fd].pos = 0;
1214 open_files[fd].posByte = 0;
1215
1216
1217
1218
1219 return fd;
1220 } else {
1221 dprintf("... file '%s' is not found\n", file);
1222 osErrno = E_NO_SUCH_FILE;
1223 return -1;
1224 }
1225}
1226
1227//helper function
1228//ask user if they wish to read remaining data in file
1229//Action 1: Do not read remaining data
1230//Action 2: Read initial requested and remaining data
1231int readRemaining(int fd){
1232 int action = 0;
1233 do{
1234 printf("File of fd=%d still has remaining data left to read.\n"
1235 "Do you wish to:\n"
1236 "1. not read the remaining data?\n"
1237 "2. read the initial requested and remaining data?\n"
1238 "Please input 1 or 2 as your desired action.\n", fd);
1239
1240 scanf("%d", &action);
1241 }while(action != 1 && action != 2);
1242
1243 return action;
1244}
1245
1246//Case 1: Size to read is bigger than actual size -> read only the total amount
1247//Case 2: Size to read is less than actual size ->
1248// ask if user wants to read the size given or
1249// if user wants to read the remaining as well
1250//Case 3: Size to read is equal to actual size -> read the given size
1251int File_Read(int fd, void* buffer, int size)
1252{
1253 boldBlue();
1254 dprintf("File_Read(%d, buffer, %d):\n", fd, size);
1255 reset();
1256
1257 //check if fd is valid index
1258 if(fd < 0 || fd > MAX_OPEN_FILES){
1259 blue();
1260 dprintf("... fd=%d out of bound", fd);
1261 reset();
1262 osErrno = E_BAD_FD;
1263 return -1;
1264 }
1265
1266 open_file_t file = open_files[fd];
1267
1268 //check if not an open file
1269 if(file.inode <= 0){
1270 blue();
1271 dprintf("... fd=%d not an open file\n", fd);
1272 reset();
1273 osErrno = E_BAD_FD;
1274 return -1;
1275 }
1276
1277
1278 //check if file size is empty
1279 if(file.size == 0)
1280 {
1281 //none to read
1282 blue();
1283 dprintf("... file fd=%d is empty\n", fd);
1284 reset();
1285 return 0;
1286 }
1287
1288 /***determine how many sectors can actually be read***/
1289
1290 //none to read, position at end of file
1291 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) == 0)
1292 {
1293 blue();
1294 dprintf("... file fd=%d is at end of file\n", fd);
1295 reset();
1296 return 0;
1297 }
1298 //something to read
1299 //remaining file size left to read
1300 int remFileSize = file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte));
1301 int sectorsToRead = 0;
1302
1303 int sizeToRead = 0;
1304
1305 /***ask user if to read initial amount or also the remaining***/
1306 /*
1307 if(remFileSize > size && readRemaining(fd) == 2){
1308 sizeToRead = remFileSize;
1309 }
1310 else{*/
1311 sizeToRead = min(remFileSize,size);
1312
1313
1314 //if less than size is remaining, read only remaining
1315 //else pick initial given size to read
1316
1317
1318 //if posByte is at part of current sector
1319 if(file.posByte != 0){
1320 sectorsToRead++;//read only one sector
1321 if(SECTOR_SIZE - file.posByte < sizeToRead){//need to read more sectors
1322 int remSize = sizeToRead - (SECTOR_SIZE - file.posByte);
1323 sectorsToRead += remSize/SECTOR_SIZE;
1324
1325 //check for remainder
1326 if(remSize%SECTOR_SIZE){
1327 sectorsToRead++;
1328 }
1329 }
1330 }
1331 else{//if posByte is at beginning of a sector
1332 sectorsToRead = sizeToRead/SECTOR_SIZE;
1333
1334 //check remainder
1335 if(sizeToRead%SECTOR_SIZE){
1336 sectorsToRead++;
1337 }
1338 }
1339
1340 blue();
1341 dprintf("... sectors to read=%d with size to read=%d of file size=%d at data[%d] at byte position=%d\n",
1342 sectorsToRead, sizeToRead, file.size, file.pos, file.posByte);
1343 reset();
1344
1345 /***get file inode***/
1346
1347 //load file's inode disk sectors
1348 int inode = file.inode;
1349 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1350 char inode_buffer[SECTOR_SIZE];
1351 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1352
1353 blue();
1354 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1355 reset();
1356
1357 //get inode
1358 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1359 int offset = inode-inode_start_entry;
1360 assert(0 <= offset && offset < INODES_PER_SECTOR);
1361 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1362
1363 blue();
1364 dprintf("... inode %d (size=%d, type=%d)\n",
1365 inode, fileInode->size, fileInode->type);
1366 reset();
1367
1368 int position = file.pos;
1369
1370 /***read contents of data blocks***/
1371
1372 //temp buffer to read entire sector in
1373 char tempBuff[SECTOR_SIZE];
1374 memset(buffer,0,sizeToRead);
1375
1376 //will position buffer ptr to next available space to copy data into
1377 int ctrSize = 0;
1378 for(int i = 0; i < sectorsToRead; i++){
1379 bzero(tempBuff, SECTOR_SIZE);
1380
1381 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1382 blue();
1383 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1384 reset();
1385 osErrno=E_GENERAL;
1386 return -1;
1387 }
1388
1389 blue();
1390 dprintf("... Reading data[%d]=%d at positionByte=%d\n",
1391 position, fileInode->data[position], file.posByte);
1392 reset();
1393
1394 int currBytes = 0; //keep track of what's currently read from sector
1395
1396 if(file.posByte != 0){//starting out at arbitrary point in currsector
1397 currBytes = SECTOR_SIZE - file.posByte;
1398 }
1399 else if(i == sectorsToRead - 1){//at last sector to read from
1400 currBytes = sizeToRead - ctrSize;
1401 }
1402 else{//full sectors
1403 currBytes = SECTOR_SIZE;
1404 }
1405
1406 blue();
1407 dprintf("... Copying data of %d bytes at %d into buffer ptr at %d\n",
1408 currBytes, file.posByte, ctrSize);
1409 reset();
1410
1411 //copy what's needed into buffer from buff
1412 memcpy(buffer+ctrSize, tempBuff+file.posByte, currBytes);
1413
1414 ctrSize += currBytes;
1415
1416 //specify new byte position if didn't read the entire last sector
1417 if(i == sectorsToRead - 1 && file.posByte + currBytes < SECTOR_SIZE){
1418 file.posByte = currBytes;
1419 }
1420 else{//new byte position and block position if read entire sectors
1421 position++;
1422 file.posByte = 0;
1423 }
1424
1425
1426 }
1427
1428
1429 //set new read/write position and new posbyte to read at
1430 open_files[fd].pos = position;
1431 open_files[fd].posByte = file.posByte;
1432
1433 blue();
1434 dprintf("... file=%d is now at pos=%d with byte pos=%d\n", fd, open_files[fd].pos, open_files[fd].posByte);
1435
1436 dprintf("... successfully read %d sectors with size=%d\n", sectorsToRead, sizeToRead);
1437 reset();
1438
1439 return sizeToRead;
1440
1441}
1442
1443//helper function
1444//gets user input on what action to take related to overwriting a file
1445//when the file recently opens and is non-empty
1446int toOverwriteOrNot(int fd){
1447 int action = 0;
1448 do{
1449 printf("File of fd=%d is non-empty. Do you\n"
1450 "1. wish to overwrite from position 0 (will delete entire file's contents)?\n"
1451 "2. wish to append data from first empty position?\n"
1452 "3. wish to not overwrite?\n"
1453 "Please input 1, 2 or 3 indicating your desired action.\n", fd);
1454
1455 scanf("%d", &action);
1456 }while(action != 1 && action != 2 && action != 3);
1457
1458 return action;
1459}
1460
1461//case 1: file_write first called on empty file -> no checks, write into file
1462//case 2: file_write first called on a recently opened non-empty file
1463// -> check if user wishes to overwrite
1464// if 1 -> overwrite from given position
1465// if 2 -> write only from the first empty position
1466// if 3 -> do not overwrite
1467int File_Write(int fd, void* buffer, int size)
1468{
1469 boldBlue();
1470 dprintf("File_Write(%d, buffer, %d):\n",fd, size);
1471 reset();
1472
1473 //check if fd is valid index
1474 if(fd < 0 || fd > MAX_OPEN_FILES){
1475 blue();
1476 dprintf("... error: fd=%d out of bound\n", fd);
1477 reset();
1478 osErrno = E_BAD_FD;
1479 return -1;
1480 }
1481
1482 open_file_t file = open_files[fd];
1483
1484 //check if not an open file
1485 if(file.inode <= 0){
1486 blue();
1487 dprintf("... error: fd=%d not an open file\n", fd);
1488 reset();
1489 osErrno = E_BAD_FD;
1490 return -1;
1491 }
1492
1493 //check if file size is full
1494 if(file.size == MAX_SECTORS_PER_FILE*SECTOR_SIZE)
1495 {
1496 blue();
1497 dprintf("... error: file fd=%d is full\n", fd);
1498 reset();
1499 osErrno = E_FILE_TOO_BIG;
1500 return 0;
1501 }
1502
1503 //load file's inode disk sectors
1504 int inode = file.inode;
1505 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1506 char inode_buffer[SECTOR_SIZE];
1507 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1508
1509 blue();
1510 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1511 reset();
1512
1513 //get inode
1514 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1515 int offset = inode-inode_start_entry;
1516 assert(0 <= offset && offset < INODES_PER_SECTOR);
1517 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1518 blue();
1519 dprintf("... inode %d (size=%d, type=%d)\n",
1520 inode, fileInode->size, fileInode->type);
1521 reset();
1522
1523 /***check if user wishes to overwrite or not***/
1524
1525 //write data from after the given data pointer
1526 int position = file.pos;
1527 int action = 0;
1528 int positionByte = file.posByte;
1529
1530 //check cases for potential overwriting:
1531 //if recently opened file is non-empty
1532 //if current pointer is at arbitrary point within file contents
1533 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) != 0){
1534 //check if user wishes to overwrite
1535 action = toOverwriteOrNot(fd);
1536 if(action == 3){
1537 blue();
1538 printf("... User wishes to not overwrite already existing file. No write done\n");
1539 reset();
1540 return 0;
1541 }
1542 else if(action == 2){ //write only from the first empty position
1543 position = file.size/SECTOR_SIZE;
1544 positionByte = 0;
1545
1546 if(file.size%SECTOR_SIZE){
1547 positionByte = file.size%SECTOR_SIZE;
1548 }
1549 blue();
1550 dprintf("... User wishes to write from first empty byte position=%d in block position=%d with file size=%d\n",
1551 positionByte, position, file.size);
1552 reset();
1553 }
1554 else{//wishes to overwrite. delete file contents
1555 int pos = 0;
1556 char dataBuffer[SECTOR_SIZE];
1557 bzero(dataBuffer, SECTOR_SIZE);
1558 while(fileInode->data[pos] != 0){
1559 // disk sector has to be freed
1560 if(Disk_Write(fileInode->data[pos], dataBuffer) < 0) return -1;
1561 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, fileInode->data[pos]) < 0) {
1562 blue();
1563 dprintf("... error: free sector occupied by file in sector bitmap unsuccessful\n");
1564 reset();
1565 return -1;
1566 }
1567 pos++;
1568 }
1569 position = 0;
1570 positionByte = 0;
1571 file.size = 0;
1572 fileInode->size = 0;
1573 blue();
1574 dprintf("... User wishes to overwrite entire file. Starting at block position=%d with file size=%d\n",
1575 position, file.size);
1576 reset();
1577 }
1578 }
1579
1580 //check if extra data doesn't go over max sectors per file
1581 if(file.size + size > MAX_SECTORS_PER_FILE*SECTOR_SIZE){
1582 blue();
1583 dprintf("... error: fd=%d of size=%d at block position=%d at byte position=%d cannot add size=%d bytes."
1584 "No space for that amount\n", fd, file.size, position, positionByte, size);
1585 reset();
1586
1587 osErrno = E_FILE_TOO_BIG;
1588 return 0;
1589 }
1590
1591 /***determine how many sectors need to be written in***/
1592
1593 int sectorsToWrite = 0;
1594
1595 if(positionByte != 0){//if posByte is at part of current sector
1596 sectorsToWrite++;
1597 if(SECTOR_SIZE - positionByte < size){//need to write more sectors
1598 int remSize = size - (SECTOR_SIZE - positionByte);
1599 sectorsToWrite += remSize/SECTOR_SIZE;
1600
1601 //check for remainder
1602 if(remSize%SECTOR_SIZE){
1603 sectorsToWrite++;
1604 }
1605 }
1606 }
1607 else{//if posByte is at beginning of a sector
1608 sectorsToWrite = size/SECTOR_SIZE;
1609
1610 //check remainder
1611 if(size%SECTOR_SIZE){
1612 sectorsToWrite++;
1613 }
1614 }
1615
1616 blue();
1617 dprintf("... extra sectors needed=%d for fd=%d at data[%d] at byte position=%d\n", sectorsToWrite,
1618 fd, position, positionByte);
1619 reset();
1620
1621 /***write into data blocks***/
1622
1623 char tempBuff[SECTOR_SIZE];
1624 int ctrSize = 0;
1625 for(int i = 0; i < sectorsToWrite; i++){
1626 //find first sector to use if starting at new position
1627 int newsec = 0;
1628 if(positionByte == 0){
1629 newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
1630
1631 //check if space exists on disk for write
1632 if(newsec < 0){
1633 blue();
1634 dprintf("... error: no space on disk, write cannot complete\n");
1635 reset();
1636 osErrno = E_NO_SPACE;
1637 return -1;
1638 }
1639 fileInode->data[position] = newsec;
1640 }
1641
1642 bzero(tempBuff, SECTOR_SIZE);
1643 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1644 blue();
1645 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1646 reset();
1647 osErrno=E_GENERAL;
1648 return -1;
1649 }
1650
1651 blue();
1652 dprintf("... going to write into disk in sector %d\n", newsec);
1653 reset();
1654
1655 int currBytes = 0; //keep track of what's currently needed to extract from buffer
1656
1657 if(positionByte != 0){//starting out at arbitrary point in currsector
1658 currBytes = SECTOR_SIZE - positionByte;
1659 }
1660 else if(i == sectorsToWrite - 1){//at last sector to write into
1661 currBytes = size - ctrSize;
1662 }
1663 else{//full sectors
1664 currBytes = SECTOR_SIZE;
1665 }
1666
1667 blue();
1668 dprintf("... copying data %d bytes from ptr position %d in buffer into tempbuff at position %d\n",
1669 currBytes, ctrSize, positionByte);
1670 reset();
1671
1672 //copy what's needed from buffer into tempbuff
1673 memcpy(tempBuff+positionByte, buffer+ctrSize, currBytes);
1674
1675 ctrSize += currBytes;//where to next extract from buffer
1676
1677 if(Disk_Write(fileInode->data[position], tempBuff) < 0) {
1678 blue();
1679 dprintf("... error: failed to write buffer data\n");
1680 reset();
1681 osErrno = E_GENERAL;
1682 return -1;
1683 }
1684 blue();
1685 dprintf("... Writing data[%d]=%d\n", position, fileInode->data[position]);
1686 reset();
1687
1688 //specify new byte position if didn't read the entire last sector
1689 if(i == sectorsToWrite - 1 && positionByte + currBytes < SECTOR_SIZE){
1690 positionByte = currBytes;
1691 }
1692 else{//new byte position and block position if read entire sectors
1693 position++;
1694 positionByte = 0;
1695 }
1696 }
1697
1698 /***update file and file inode***/
1699
1700 //update file and inode size
1701 open_files[fd].size = file.size + size;
1702 open_files[fd].pos = position;
1703 open_files[fd].posByte = positionByte;
1704
1705 //set new inode size
1706 fileInode->size = open_files[fd].size;
1707
1708
1709 //write to disk the updated inode
1710 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
1711 blue();
1712 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
1713 inode, fileInode->size, fileInode->type, inode_sector);
1714
1715 dprintf("... file=%d is now at block pos=%d and byte position=%d with size=%d\n",
1716 fd, open_files[fd].pos, open_files[fd].posByte, open_files[fd].size);
1717 reset();
1718 return size;
1719}
1720
1721int File_Seek(int fd, int offset)
1722{
1723 dprintf("File_Seek(%d, %d):\n", fd, offset);
1724 //check if fd is valid index
1725 if(fd < 0 || fd > MAX_OPEN_FILES){
1726 dprintf("... error: fd=%d out of bound\n", fd);
1727 osErrno = E_BAD_FD;
1728 return -1;
1729 }
1730 //check if open file
1731 if(open_files[fd].inode <= 0) {
1732 dprintf("... error: fd=%d not an open file\n", fd);
1733 osErrno = E_BAD_FD;
1734 return -1;
1735 }
1736
1737 //check if offset is within bounds
1738 if(offset > open_files[fd].size || offset == -1){
1739 dprintf("... error: offset=%d out of bound\n", fd);
1740 osErrno = E_SEEK_OUT_OF_BOUNDS;
1741 return -1;
1742 }
1743
1744 open_files[fd].pos = offset/SECTOR_SIZE + (offset%SECTOR_SIZE != 0 ? 0 : 1);
1745 open_files[fd].posByte = offset%SECTOR_SIZE;
1746
1747 return open_files[fd].pos;
1748}
1749
1750int File_Close(int fd)
1751{
1752 dprintf("File_Close(%d):\n", fd);
1753 if(0 > fd || fd > MAX_OPEN_FILES) {
1754 dprintf("... error: fd=%d out of bound\n", fd);
1755 osErrno = E_BAD_FD;
1756 return -1;
1757 }
1758 if(open_files[fd].inode <= 0) {
1759 dprintf("... error: fd=%d not an open file\n", fd);
1760 osErrno = E_BAD_FD;
1761 return -1;
1762 }
1763
1764
1765
1766 dprintf("... file closed successfully\n");
1767 open_files[fd].inode = 0;
1768 return 0;
1769}
1770
1771int Dir_Create(char* path)
1772{
1773 dprintf("Dir_Create('%s'):\n", path);
1774 return create_file_or_directory(1, path);
1775}
1776
1777int Dir_Unlink(char* path)
1778{
1779 dprintf("Dir_Unlink('%s'):\n", path);
1780 // no empty path and no root directory allowed
1781 if(path==NULL) {
1782 dprintf("... error: empty path (NULL) for directory unlink");
1783 osErrno = E_GENERAL;
1784 return -1;
1785 }
1786
1787 if(strcmp(path, "/")==0) {
1788 dprintf("... error: not allowed to unlink root directory");
1789 osErrno = E_ROOT_DIR;
1790 return -1;
1791 }
1792
1793 // find parent and children (if theres any)
1794 int child_inode;
1795 int parent_inode = follow_path(path, &child_inode, NULL);
1796 if(parent_inode < 0) {
1797 dprintf("... error: directory '%s' not found\n", path);
1798 osErrno = E_NO_SUCH_DIR;
1799 return -1;
1800 }
1801
1802 int remove = remove_inode(1, parent_inode, child_inode);
1803
1804 if (remove==-1) {
1805 dprintf("... error: general error when unlinking directory\n");
1806 osErrno = E_GENERAL;
1807 return -1;
1808 } else if (remove==-3) {
1809 dprintf("... error: no directory, wrong type\n");
1810 osErrno = E_NO_SUCH_DIR;
1811 return -1;
1812 } else if (remove==-2) {
1813 dprintf("... error: directory %s not empty", path);
1814 osErrno = E_DIR_NOT_EMPTY;
1815 return -1;
1816 } else {
1817 return 0;
1818 }
1819
1820
1821}
1822
1823int Dir_Size(char* path)
1824{
1825 dprintf("Dir_Size('%s'):\n", path);
1826 // no empty path allowed
1827 if(path==NULL) {
1828 dprintf("... error: empty path (NULL) given as parameter\n");
1829 osErrno = E_GENERAL;
1830 return -1;
1831 }
1832
1833 // directory has to exist
1834 int child_inode;
1835 int parent_inode = follow_path(path, &child_inode, NULL);
1836 if(parent_inode < 0) {
1837 dprintf("... error: directory '%s' not found\n", path);
1838 osErrno = E_NO_SUCH_DIR;
1839 return -1;
1840 }
1841
1842 // load the disk sector containing the child inode
1843 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
1844 char inode_buffer[SECTOR_SIZE];
1845 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1846 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
1847
1848 // get the child inode
1849 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1850 int offset = child_inode-inode_start_entry;
1851 assert(0 <= offset && offset < INODES_PER_SECTOR);
1852 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1853
1854 // check for type
1855 if (child->type!=1) {
1856 dprintf("... error: wrong type, path leads to file\n");
1857 osErrno = E_GENERAL;
1858 return -1;
1859 }
1860
1861 return child->size*sizeof(dirent_t);
1862}
1863
1864int Dir_Read(char* path, void* buffer, int size)
1865{
1866 dprintf("Dir_Read('%s', buffer, %d):\n", path, size);
1867
1868 // no empty path allowed
1869 if(path==NULL) {
1870 dprintf("... error: empty path (NULL) given as parameter\n");
1871 osErrno = E_GENERAL;
1872 return -1;
1873 }
1874
1875 // directory has to exist
1876 int inode;
1877 int parent_inode = follow_path(path, &inode, NULL);
1878 if(parent_inode < 0) {
1879 dprintf("... error: directory '%s' not found\n", path);
1880 osErrno = E_NO_SUCH_DIR;
1881 return -1;
1882 }
1883
1884 // check if size parameter matches the actual directory size
1885 int act_size = Dir_Size(path);
1886 if (size>buf_size) {
1887 dprintf("... error: size parameter %d does not match actual directory size of %d bytes.\n",
1888 size, act_size);
1889 osErrno=E_GENERAL;
1890 return -1;
1891 }
1892
1893 // check if size of buffer is large enough to hold all elements in the directory
1894 int buf_size = (int)malloc_usable_size(buffer);
1895 if (size>buf_size) {
1896 dprintf("... error: buffer provided has size %d, but %d bytes required\n",
1897 buf_size, size);
1898 osErrno=E_BUFFER_TOO_SMALL;
1899 return -1;
1900 }
1901
1902 // initialize buffer
1903 bzero(buffer, size);
1904
1905 // load disk sector from directory
1906 char inode_buffer[SECTOR_SIZE];
1907 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1908 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1909 dprintf("... load inode table for parent inode %d from disk sector %d\n",
1910 inode, inode_sector);
1911
1912 // get the directory inode
1913 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1914 int offset = inode-inode_start_entry;
1915 assert(0 <= offset && offset < INODES_PER_SECTOR);
1916 inode_t* dir_inode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1917 dprintf("... get inode %d (size=%d, type=%d)\n",
1918 inode, dir_inode->size, dir_inode->type);
1919
1920 // read the directory entries into the buffer
1921 int remainder=dir_inode->size%DIRENTS_PER_SECTOR;
1922 int group=dir_inode->size/DIRENTS_PER_SECTOR;
1923 int buf_offset = (int)(SECTOR_SIZE-SECTOR_SIZE%sizeof(dirent_t));
1924
1925 // a) completely filled sectors
1926 for(int i=0; i<group;i++) {
1927 if(Disk_Read(dir_inode->data[i], buffer+i*buf_offset) < 0) {
1928 dprintf("... error: cant read sector %d\n", dir_inode->data[i]);
1929 osErrno=E_GENERAL;
1930 return -1;
1931 }
1932 }
1933
1934 // b) partly filled sector
1935 if(remainder) {
1936 char buff[SECTOR_SIZE];
1937 if(Disk_Read(dir_inode->data[group], buff) < 0){
1938 dprintf("... error: cant read sector %d\n", dir_inode->data[group]);
1939 osErrno=E_GENERAL;
1940 return -1;
1941 }
1942
1943 memcpy(buffer+group*buf_offset, buff, remainder*sizeof(dirent_t));
1944
1945 }
1946
1947 return dir_inode->size;
1948}