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