· 6 years ago · Nov 25, 2019, 04:14 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();
491dprintf("---> bitmap_reset COMPLETED SUCCESSFULLY\n");
492reset();
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// return the child inode of the given file name 'fname' from the
520// parent inode; the parent inode is currently stored in the segment
521// of inode table in the cache (we cache only one disk sector for
522// this); once found, both cached_inode_sector and cached_inode_buffer
523// may be updated to point to the segment of inode table containing
524// the child inode; the function returns -1 if no such file is found;
525// it returns -2 is something else is wrong (such as parent is not
526// directory, or there's read error, etc.)
527static int find_child_inode(int parent_inode, char* fname,
528 int *cached_inode_sector, char* cached_inode_buffer)
529{
530 int cached_start_entry = ((*cached_inode_sector)-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
531 int offset = parent_inode-cached_start_entry;
532 assert(0 <= offset && offset < INODES_PER_SECTOR);
533 inode_t* parent = (inode_t*)(cached_inode_buffer+offset*sizeof(inode_t));
534 dprintf("... load parent inode: %d (size=%d, type=%d)\n",
535 parent_inode, parent->size, parent->type);
536 if(parent->type != 1) {
537 dprintf("... parent not a directory\n");
538 return -2;
539 }
540
541 int nentries = parent->size; // remaining number of directory entries
542 int idx = 0;
543 while(nentries > 0) {
544 char buf[SECTOR_SIZE]; // cached content of directory entries
545 if(Disk_Read(parent->data[idx], buf) < 0) return -2;
546 for(int i=0; i<DIRENTS_PER_SECTOR; i++) {
547 if(i>nentries) break;
548 if(!strcmp(((dirent_t*)buf)[i].fname, fname)) {
549 // found the file/directory; update inode cache
550 int child_inode = ((dirent_t*)buf)[i].inode;
551 dprintf("... found child_inode=%d\n", child_inode);
552 int sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
553 if(sector != (*cached_inode_sector)) {
554 *cached_inode_sector = sector;
555 if(Disk_Read(sector, cached_inode_buffer) < 0) return -2;
556 dprintf("... load inode table for child\n");
557 }
558 return child_inode;
559 }
560 }
561 idx++; nentries -= DIRENTS_PER_SECTOR;
562 }
563 dprintf("... could not find child inode\n");
564 return -1; // not found
565}
566
567// follow the absolute path; if successful, return the inode of the
568// parent directory immediately before the last file/directory in the
569// path; for example, for '/a/b/c/d.txt', the parent is '/a/b/c' and
570// the child is 'd.txt'; the child's inode is returned through the
571// parameter 'last_inode' and its file name is returned through the
572// parameter 'last_fname' (both are references); it's possible that
573// the last file/directory is not in its parent directory, in which
574// case, 'last_inode' points to -1; if the function returns -1, it
575// means that we cannot follow the path
576static int follow_path(char* path, int* last_inode, char* last_fname)
577{
578 if(!path) {
579 dprintf("... invalid path\n");
580 return -1;
581 }
582 if(path[0] != '/') {
583 dprintf("... '%s' not absolute path\n", path);
584 return -1;
585 }
586
587 // make a copy of the path (skip leading '/'); this is necessary
588 // since the path is going to be modified by strsep()
589 char pathstore[MAX_PATH];
590 strncpy(pathstore, path+1, MAX_PATH-1);
591 pathstore[MAX_PATH-1] = '\0'; // for safety
592 char* lpath = pathstore;
593
594 int parent_inode = -1, child_inode = 0; // start from root
595 // cache the disk sector containing the root inode
596 int cached_sector = INODE_TABLE_START_SECTOR;
597 char cached_buffer[SECTOR_SIZE];
598 if(Disk_Read(cached_sector, cached_buffer) < 0) return -1;
599 dprintf("... load inode table for root from disk sector %d\n", cached_sector);
600
601 // for each file/directory name separated by '/'
602 char* token;
603 while((token = strsep(&lpath, "/")) != NULL) {
604 dprintf("... process token: '%s'\n", token);
605 if(*token == '\0') continue; // multiple '/' ignored
606 if(illegal_filename(token)) {
607 dprintf("... illegal file name: '%s'\n", token);
608 return -1;
609 }
610 if(child_inode < 0) {
611 // regardless whether child_inode was not found previously, or
612 // there was issues related to the parent (say, not a
613 // directory), or there was a read error, we abort
614 dprintf("... parent inode can't be established\n");
615 return -1;
616 }
617 parent_inode = child_inode;
618 child_inode = find_child_inode(parent_inode, token,
619 &cached_sector, cached_buffer);
620 if(last_fname) strcpy(last_fname, token);
621 }
622 if(child_inode < -1) return -1; // if there was error, abort
623 else {
624 // there was no error, several possibilities:
625 // 1) '/': parent = -1, child = 0
626 // 2) '/valid-dirs.../last-valid-dir/not-found': parent=last-valid-dir, child=-1
627 // 3) '/valid-dirs.../last-valid-dir/found: parent=last-valid-dir, child=found
628 // in the first case, we set parent=child=0 as special case
629 if(parent_inode==-1 && child_inode==0) parent_inode = 0;
630 dprintf("... found parent_inode=%d, child_inode=%d\n", parent_inode, child_inode);
631 *last_inode = child_inode;
632 return parent_inode;
633 }
634}
635
636// add a new file or directory (determined by 'type') of given name
637// 'file' under parent directory represented by 'parent_inode'
638int add_inode(int type, int parent_inode, char* file)
639{
640 // get a new inode for child
641 int child_inode = bitmap_first_unused(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, INODE_BITMAP_SIZE);
642 if(child_inode < 0) {
643 dprintf("... error: inode table is full\n");
644 return -1;
645 }
646 dprintf("... new child inode %d\n", child_inode);
647
648 // load the disk sector containing the child inode
649 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
650 char inode_buffer[SECTOR_SIZE];
651 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
652 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
653
654 // get the child inode
655 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
656 int offset = child_inode-inode_start_entry;
657 assert(0 <= offset && offset < INODES_PER_SECTOR);
658 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
659
660 // update the new child inode and write to disk
661 memset(child, 0, sizeof(inode_t));
662 child->type = type;
663 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
664 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
665 child_inode, child->size, child->type, inode_sector);
666
667 // get the disk sector containing the parent inode
668 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
669 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
670 dprintf("... load inode table for parent inode %d from disk sector %d\n",
671 parent_inode, inode_sector);
672
673 // get the parent inode
674 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
675 offset = parent_inode-inode_start_entry;
676 assert(0 <= offset && offset < INODES_PER_SECTOR);
677 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
678 dprintf("... get parent inode %d (size=%d, type=%d)\n",
679 parent_inode, parent->size, parent->type);
680
681 // get the dirent sector
682 if(parent->type != 1) {
683 dprintf("... error: parent inode is not directory\n");
684 return -2; // parent not directory
685 }
686 int group = parent->size/DIRENTS_PER_SECTOR;
687 char dirent_buffer[SECTOR_SIZE];
688 if(group*DIRENTS_PER_SECTOR == parent->size) {
689 // new disk sector is needed
690 int newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
691 if(newsec < 0) {
692 dprintf("... error: disk is full\n");
693 return -1;
694 }
695 parent->data[group] = newsec;
696 memset(dirent_buffer, 0, SECTOR_SIZE);
697 dprintf("... new disk sector %d for dirent group %d\n", newsec, group);
698 } else {
699 if(Disk_Read(parent->data[group], dirent_buffer) < 0)
700 return -1;
701 dprintf("... load disk sector %d for dirent group %d\n", parent->data[group], group);
702 }
703
704 // add the dirent and write to disk
705 int start_entry = group*DIRENTS_PER_SECTOR;
706 offset = parent->size-start_entry;
707 dirent_t* dirent = (dirent_t*)(dirent_buffer+offset*sizeof(dirent_t));
708 strncpy(dirent->fname, file, MAX_NAME);
709 dirent->inode = child_inode;
710 if(Disk_Write(parent->data[group], dirent_buffer) < 0) return -1;
711 dprintf("... append dirent %d (name='%s', inode=%d) to group %d, update disk sector %d\n",
712 parent->size, dirent->fname, dirent->inode, group, parent->data[group]);
713
714 // update parent inode and write to disk
715 parent->size++;
716 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
717 dprintf("... update parent inode on disk sector %d\n", inode_sector);
718
719 return 0;
720}
721
722// used by both File_Create() and Dir_Create(); type=0 is file, type=1
723// is directory
724int create_file_or_directory(int type, char* pathname)
725{
726 int child_inode;
727 char last_fname[MAX_NAME];
728 int parent_inode = follow_path(pathname, &child_inode, last_fname);
729 if(parent_inode >= 0)
730 {
731 if(child_inode >= 0)
732 {
733 dprintf("... file/directory '%s' already exists, failed to create\n", pathname);
734 osErrno = E_CREATE;
735 return -1;
736 } else
737 {
738 if(add_inode(type, parent_inode, last_fname) >= 0)
739 {
740 dprintf("... successfully created file/directory: '%s'\n", pathname);
741 return 0;
742 }
743 else {
744 dprintf("... error: something wrong with adding child inode\n");
745 osErrno = E_CREATE;
746 return -1;
747 }
748 }
749 } else {
750 dprintf("... error: something wrong with the file/path: '%s'\n", pathname);
751 osErrno = E_CREATE;
752 return -1;
753 }
754}
755
756// remove the child from parent; the function is called by both
757// File_Unlink() and Dir_Unlink(); the function returns 0 if success,
758// -1 if general error, -2 if directory not empty, -3 if wrong type
759int remove_inode(int type, int parent_inode, int child_inode)
760{
761
762 dprintf("... remove inode %d\n", child_inode);
763
764 // load the disk sector containing the child inode
765 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
766 char inode_buffer[SECTOR_SIZE];
767 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
768 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
769
770 // get the child inode
771 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
772 int offset = child_inode-inode_start_entry;
773 assert(0 <= offset && offset < INODES_PER_SECTOR);
774 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
775
776 // check for right type
777 if(child->type!=type){
778 dprintf("... error: the type parameter does not match the actual inode type\n");
779 return -3;
780 }
781
782 // check if child is non-empty directory
783 if(child->type==1 && child->size>0){
784 dprintf("... error: inode is non-empty directory\n");
785 return -2;
786 }
787
788 // reset data blocks of file
789 if(type==0){
790
791 /**
792 * DEBUGGING PRINT
793 */
794 dprintf("Inode size: %d, data sector 0: %d\n", child->size, child->data[0]);
795
796 char data_buffer[SECTOR_SIZE];
797
798 int sectors = child->size/SECTOR_SIZE+(child->size%SECTOR_SIZE != 0 ? 1 : 0);
799 for(int i=0; i<sectors; i++){
800 if(Disk_Read(child->data[i], data_buffer) < 0) return -1;
801 dprintf("... delete contents of file with inode %d from sector %d\n",
802 child_inode, child->data[i]);
803 bzero(data_buffer, SECTOR_SIZE);
804 if(Disk_Write(child->data[i], data_buffer) < 0) return -1;
805 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, child->data[i]) < 0) {
806 dprintf("... error: free sector occupied by file in sector bitmap unsuccessful\n");
807 return -1;
808 }
809 }
810
811 }
812
813 // delete the child inode
814 memset(child, 0, sizeof(inode_t));
815 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
816 dprintf("... delete inode %d, update disk sector %d\n",
817 child_inode, inode_sector);
818
819 // get the disk sector containing the parent inode
820 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
821 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
822 dprintf("... load inode table for parent inode %d from disk sector %d\n",
823 parent_inode, inode_sector);
824
825 // get the parent inode
826 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
827 offset = parent_inode-inode_start_entry;
828 assert(0 <= offset && offset < INODES_PER_SECTOR);
829 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
830 dprintf("... get parent inode %d (size=%d, type=%d)\n",
831 parent_inode, parent->size, parent->type);
832
833 // check if parent is directory
834 if(parent->type != 1) {
835 dprintf("... error: parent inode is not directory\n");
836 return -3;
837 }
838
839 // check if parent is non-empty
840 if(parent->size < 1) {
841 dprintf("... error: parent directory has no entries\n");
842 return -1;
843 }
844
845 // reset bit of child inode in bitmap
846 if (bitmap_reset(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, child_inode) < 0) {
847 dprintf("... error: reset inode in bitmap unsuccessful\n");
848 return -1;
849 }
850
851 int remainder = parent->size % DIRENTS_PER_SECTOR;
852 int group = (parent->size+DIRENTS_PER_SECTOR-1)/DIRENTS_PER_SECTOR;
853 char dirent_buffer[SECTOR_SIZE];
854 dirent_t* dirent;
855 int counter = 0;
856 int found = 0;
857
858 // search for the dirent in every group
859 for(int i = 0; i<group; i++){
860
861 if(Disk_Read(parent->data[i], dirent_buffer) < 0) return -1;
862 dprintf("... search for child in disk sector %d for dirent group %d\n", parent->data[i], i);
863
864 for(int j=0; j<DIRENTS_PER_SECTOR; j++){
865 counter ++;
866 dirent = (dirent_t*)(dirent_buffer+j*sizeof(dirent_t));
867
868 if(counter < parent->size && (!found)) {
869
870 // Case 1: Child node found and last dirent in the same sector
871 if(dirent->inode == child_inode && i == group-1) {
872 dirent_t* last_dirent = (dirent_t*)(dirent_buffer+(remainder-1)*sizeof(dirent_t));
873 memcpy(dirent, last_dirent, sizeof(dirent_t));
874 memset(last_dirent, 0, sizeof(dirent_t));
875 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
876 dprintf("... delete dirent (inode=%d) from group %d, update disk sector %d\n",
877 child_inode, i, parent->data[i]);
878 found=1;
879
880 // Case 2: Child node found, but last dirent in other sector
881 } else if(dirent->inode == child_inode) {
882 char last_dirent_buffer[SECTOR_SIZE];
883 if(Disk_Read(parent->data[group-1], last_dirent_buffer) < 0) return -1;
884 dprintf("... load last dirent from parent %d in disk sector %d for dirent group %d\n",
885 parent_inode, parent->data[group-1], group-1);
886 dirent_t* last_dirent = (dirent_t*)(last_dirent_buffer+(remainder-1)*sizeof(dirent_t));
887 memcpy(dirent, last_dirent, sizeof(dirent_t));
888 memset(last_dirent, 0, sizeof(dirent_t));
889 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
890 dprintf("...delete dirent (inode=%d) from group %d, update disk sector %d\n",
891 child_inode, i, parent->data[i]);
892 if(Disk_Write(parent->data[group-1], last_dirent_buffer) < 0) return -1;
893 dprintf("...copy last dirent with inode %d into group %d, update disk sector %d\n",
894 dirent->inode, group-1, parent->data[group-1]);
895 found=1;
896 }
897
898 } else if(!found) {
899
900 // Case 3: Child found and last dirent from parent
901 if(dirent->inode == child_inode) {
902 memset(dirent, 0, sizeof(dirent_t));
903 if(Disk_Write(parent->data[i], dirent_buffer) < 0) return -1;
904 dprintf("... delete dirent (inode=%d) from group %d, update disk sector %d\n",
905 child_inode, group-1, parent->data[i]);
906 found=1;
907
908 // Case 4: Child node not found, and reached last dirent from parent
909 } else if(counter >= parent->size) {
910 dprintf("Counter: %d, Parent size: %d\n", counter, parent->size);
911 dprintf("... error: child inode could not be found in parent directory\n");
912 return -1;
913 }
914 }
915 }
916 }
917
918 // check for remaining dirents from parent in that sector (otherwise reset sector bitmap)
919 if (remainder == 1) {
920 // disk sector has to be freed
921 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, parent->data[group]) < 0) {
922 dprintf("... error: free sector in bitmap unsuccessful\n");
923 return -1;
924 }
925 parent->data[group-1] = 0;
926 }
927
928 // update parent inode and write to disk
929 parent->size--;
930 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
931 dprintf("... update parent inode on disk sector %d\n", inode_sector);
932
933 return 0;
934}
935
936// representing an open file
937typedef struct _open_file {
938 int inode; // pointing to the inode of the file (0 means entry not used)
939 int size; // file size cached here for convenience
940 int pos; // read/write position within the data array
941 int posByte; //starting byte to read from
942} open_file_t;
943static open_file_t open_files[MAX_OPEN_FILES];
944
945// return true if the file pointed to by inode has already been open
946int is_file_open(int inode)
947{
948 for(int i=0; i<MAX_OPEN_FILES; i++) {
949 if(open_files[i].inode == inode)
950 return 1;
951 }
952 return 0;
953}
954
955// return a new file descriptor not used; -1 if full
956int new_file_fd()
957{
958 for(int i=0; i<MAX_OPEN_FILES; i++) {
959 if(open_files[i].inode <= 0)
960 return i;
961 }
962 return -1;
963}
964
965/* end of internal helper functions, start of API functions */
966
967int FS_Boot(char* backstore_fname)
968{
969 dprintf("FS_Boot('%s'):\n", backstore_fname);
970 // initialize a new disk (this is a simulated disk)
971 if(Disk_Init() < 0) {
972 dprintf("... disk init failed\n");
973 osErrno = E_GENERAL;
974 return -1;
975 }
976 dprintf("... disk initialized\n");
977
978 // we should copy the filename down; if not, the user may change the
979 // content pointed to by 'backstore_fname' after calling this function
980 strncpy(bs_filename, backstore_fname, 1024);
981 bs_filename[1023] = '\0'; // for safety
982
983 // we first try to load disk from this file
984 if(Disk_Load(bs_filename) < 0) {
985 dprintf("... load disk from file '%s' failed\n", bs_filename);
986
987 // if we can't open the file; it means the file does not exist, we
988 // need to create a new file system on disk
989 if(diskErrno == E_OPENING_FILE) {
990 dprintf("... couldn't open file, create new file system\n");
991
992 // format superblock
993 char buf[SECTOR_SIZE];
994 memset(buf, 0, SECTOR_SIZE);
995 *(int*)buf = OS_MAGIC;
996 if(Disk_Write(SUPERBLOCK_START_SECTOR, buf) < 0) {
997 dprintf("... failed to format superblock\n");
998 osErrno = E_GENERAL;
999 return -1;
1000 }
1001 dprintf("... formatted superblock (sector %d)\n", SUPERBLOCK_START_SECTOR);
1002
1003 // format inode bitmap (reserve the first inode to root)
1004 //type for inode bitmap sector = 0
1005 dprintf("... calling bitmap int for inode bitmap with start=%d, num=%d, nbits=1\n",
1006 INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS);
1007 bitmap_init(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, 1, 0);
1008 dprintf("... formatted inode bitmap (start=%d, num=%d)\n",
1009 (int)INODE_BITMAP_START_SECTOR, (int)INODE_BITMAP_SECTORS);
1010
1011 // format sector bitmap (reserve the first few sectors to
1012 // superblock, inode bitmap, sector bitmap, and inode table)
1013 //type for sector bitmap = 1
1014 dprintf("... calling bitmap int for sector bitmap with start=%d, num=%d, nbits=%ld\n",
1015 SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, DATABLOCK_START_SECTOR);
1016 bitmap_init(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS,
1017 DATABLOCK_START_SECTOR, 1);
1018 dprintf("... formatted sector bitmap (start=%d, num=%d)\n",
1019 (int)SECTOR_BITMAP_START_SECTOR, (int)SECTOR_BITMAP_SECTORS);
1020
1021 // format inode tables
1022 for(int i=0; i<INODE_TABLE_SECTORS; i++) {
1023 memset(buf, 0, SECTOR_SIZE);
1024 if(i==0) {
1025 // the first inode table entry is the root directory
1026 ((inode_t*)buf)->size = 0;
1027 ((inode_t*)buf)->type = 1;
1028 }
1029 if(Disk_Write(INODE_TABLE_START_SECTOR+i, buf) < 0) {
1030 dprintf("... failed to format inode table\n");
1031 osErrno = E_GENERAL;
1032 return -1;
1033 }
1034 }
1035 dprintf("... formatted inode table (start=%d, num=%d)\n",
1036 (int)INODE_TABLE_START_SECTOR, (int)INODE_TABLE_SECTORS);
1037
1038 // we need to synchronize the disk to the backstore file (so
1039 // that we don't lose the formatted disk)
1040 if(Disk_Save(bs_filename) < 0) {
1041 // if can't write to file, something's wrong with the backstore
1042 dprintf("... failed to save disk to file '%s'\n", bs_filename);
1043 osErrno = E_GENERAL;
1044 return -1;
1045 } else {
1046 // everything's good now, boot is successful
1047 dprintf("... successfully formatted disk, boot successful\n");
1048 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
1049 return 0;
1050 }
1051 } else {
1052 // something wrong loading the file: invalid param or error reading
1053 dprintf("... couldn't read file '%s', boot failed\n", bs_filename);
1054 osErrno = E_GENERAL;
1055 return -1;
1056 }
1057 } else {
1058 dprintf("... load disk from file '%s' successful\n", bs_filename);
1059
1060 // we successfully loaded the disk, we need to do two more checks,
1061 // first the file size must be exactly the size as expected (thiis
1062 // supposedly should be folded in Disk_Load(); and it's not)
1063 int sz = 0;
1064 FILE* f = fopen(bs_filename, "r");
1065 if(f) {
1066 fseek(f, 0, SEEK_END);
1067 sz = ftell(f);
1068 fclose(f);
1069 }
1070 if(sz != SECTOR_SIZE*TOTAL_SECTORS) {
1071 dprintf("... check size of file '%s' failed\n", bs_filename);
1072 osErrno = E_GENERAL;
1073 return -1;
1074 }
1075 dprintf("... check size of file '%s' successful\n", bs_filename);
1076
1077 // check magic
1078 if(check_magic()) {
1079 // everything's good by now, boot is successful
1080 dprintf("... check magic successful\n");
1081 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
1082 return 0;
1083 } else {
1084 // mismatched magic number
1085 dprintf("... check magic failed, boot failed\n");
1086 osErrno = E_GENERAL;
1087 return -1;
1088 }
1089 }
1090}
1091
1092int FS_Sync()
1093{
1094 dprintf("FS_Sync():\n");
1095 if(Disk_Save(bs_filename) < 0) {
1096 // if can't write to file, something's wrong with the backstore
1097 dprintf("FS_Sync():\n... failed to save disk to file '%s'\n", bs_filename);
1098 osErrno = E_GENERAL;
1099 return -1;
1100 } else {
1101 // everything's good now, sync is successful
1102 dprintf("FS_Sync():\n... successfully saved disk to file '%s'\n", bs_filename);
1103 return 0;
1104 }
1105}
1106
1107int File_Create(char* file)
1108{
1109 dprintf("File_Create('%s'):\n", file);
1110 return create_file_or_directory(0, file);
1111}
1112
1113//TODO: check if really remove the data blocks
1114int File_Unlink(char* file)
1115{
1116 dprintf("File_Unlink('%s'):\n", file);
1117 int child_inode;
1118
1119 int parent_inode = follow_path(file, &child_inode, NULL);
1120
1121
1122 if(child_inode >= 0) //file exists
1123 {
1124 //check if file is open
1125 if(is_file_open(child_inode)){
1126 dprintf("... %s is an open file. Cannot unlink\n", file);
1127 osErrno = E_FILE_IN_USE;
1128 return -1;
1129 }
1130
1131
1132 int remove = remove_inode(0, parent_inode, child_inode);
1133 if(remove == -1){
1134 dprintf("... error: general error when unlinking file\n");
1135 osErrno = E_GENERAL;
1136 return -1;
1137 }
1138 else if(remove == -3){
1139 dprintf("... error: no file, incorrect type\n");
1140 osErrno = E_NO_SUCH_FILE;
1141 return -1;
1142 }
1143 else{
1144 return 0;
1145 }
1146
1147 }
1148
1149 else{ //file does not exist
1150 dprintf("... %s file does not exist\n", file);
1151 osErrno = E_NO_SUCH_FILE;
1152 return -1;
1153
1154 }
1155}
1156
1157int File_Open(char* file)
1158{
1159 dprintf("File_Open('%s'):\n", file);
1160
1161 int fd = new_file_fd();
1162
1163 if(fd < 0) {
1164 dprintf("... max open files reached\n");
1165 osErrno = E_TOO_MANY_OPEN_FILES;
1166 return -1;
1167 }
1168
1169 int child_inode;
1170 follow_path(file, &child_inode, NULL);
1171
1172 if(child_inode >= 0) { // child is the one, file exists
1173 //check if file is already open
1174 if(is_file_open(child_inode)){
1175 dprintf("... error: %s is an open file already.\n", file);
1176 osErrno = E_FILE_IN_USE;
1177 return -1;
1178 }
1179 // load the disk sector containing the inode
1180 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
1181 char inode_buffer[SECTOR_SIZE];
1182 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1183 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1184
1185 // get the inode
1186 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1187 int offset = child_inode-inode_start_entry;
1188 assert(0 <= offset && offset < INODES_PER_SECTOR);
1189 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1190 dprintf("... inode %d (size=%d, type=%d)\n",
1191 child_inode, child->size, child->type);
1192
1193 if(child->type != 0) {
1194 dprintf("... error: '%s' is not a file\n", file);
1195 osErrno = E_GENERAL;
1196 return -1;
1197 }
1198
1199 // initialize open file entry and return its index
1200 open_files[fd].inode = child_inode;
1201 open_files[fd].size = child->size;
1202 open_files[fd].pos = 0;
1203 open_files[fd].posByte = 0;
1204
1205
1206
1207
1208 return fd;
1209 } else {
1210 dprintf("... file '%s' is not found\n", file);
1211 osErrno = E_NO_SUCH_FILE;
1212 return -1;
1213 }
1214}
1215
1216//helper function
1217//ask user if they wish to read remaining data in file
1218//Action 1: Do not read remaining data
1219//Action 2: Read initial requested and remaining data
1220int readRemaining(int fd){
1221 int action = 0;
1222 do{
1223 printf("File of fd=%d still has remaining data left to read.\n"
1224 "Do you wish to:\n"
1225 "1. not read the remaining data?\n"
1226 "2. read the initial requested and remaining data?\n"
1227 "Please input 1 or 2 as your desired action.\n", fd);
1228
1229 scanf("%d", &action);
1230 }while(action != 1 && action != 2);
1231
1232 return action;
1233}
1234
1235//Case 1: Size to read is bigger than actual size -> read only the total amount
1236//Case 2: Size to read is less than actual size ->
1237// ask if user wants to read the size given or
1238// if user wants to read the remaining as well
1239//Case 3: Size to read is equal to actual size -> read the given size
1240int File_Read(int fd, void* buffer, int size)
1241{
1242 blue();
1243 dprintf("File_Read(%d, buffer, %d):\n", fd, size);
1244 //check if fd is valid index
1245 if(fd < 0 || fd > MAX_OPEN_FILES){
1246 blue();
1247 dprintf("... fd=%d out of bound", fd);
1248 osErrno = E_BAD_FD;
1249 return -1;
1250 }
1251
1252 open_file_t file = open_files[fd];
1253
1254 //check if not an open file
1255 if(file.inode <= 0){
1256 blue();
1257 dprintf("... fd=%d not an open file\n", fd);
1258 osErrno = E_BAD_FD;
1259 return -1;
1260 }
1261
1262
1263 //check if file size is empty
1264 if(file.size == 0)
1265 {
1266 //none to read
1267 blue();
1268 dprintf("... file fd=%d is empty\n", fd);
1269 return 0;
1270 }
1271
1272 /***determine how many sectors can actually be read***/
1273
1274 //none to read, position at end of file
1275 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) == 0)
1276 {
1277 blue();
1278 dprintf("... file fd=%d is at end of file\n", fd);
1279 return 0;
1280 }
1281 //something to read
1282 //remaining file size left to read
1283 int remFileSize = file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte));
1284 int sectorsToRead = 0;
1285
1286 int sizeToRead = 0;
1287
1288 /***ask user if to read initial amount or also the remaining***/
1289
1290 if(remFileSize > size && readRemaining(fd) == 2){
1291 sizeToRead = remFileSize;
1292 }
1293 else{
1294 sizeToRead = min(remFileSize,size);
1295 }
1296
1297 //if less than size is remaining, read only remaining
1298 //else pick initial given size to read
1299
1300
1301 //if posByte is at part of current sector
1302 if(file.posByte != 0){
1303 sectorsToRead++;//read only one sector
1304 if(SECTOR_SIZE - file.posByte < sizeToRead){//need to read more sectors
1305 int remSize = sizeToRead - (SECTOR_SIZE - file.posByte);
1306 sectorsToRead += remSize/SECTOR_SIZE;
1307
1308 //check for remainder
1309 if(remSize%SECTOR_SIZE){
1310 sectorsToRead++;
1311 }
1312 }
1313 }
1314 else{//if posByte is at beginning of a sector
1315 sectorsToRead = sizeToRead/SECTOR_SIZE;
1316
1317 //check remainder
1318 if(sizeToRead%SECTOR_SIZE){
1319 sectorsToRead++;
1320 }
1321 }
1322
1323 blue();
1324 dprintf("... sectors to read=%d with size to read=%d of file size=%d at data[%d] at byte position=%d\n",
1325 sectorsToRead, sizeToRead, file.size, file.pos, file.posByte);
1326
1327 /***get file inode***/
1328
1329 //load file's inode disk sectors
1330 int inode = file.inode;
1331 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1332 char inode_buffer[SECTOR_SIZE];
1333 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1334
1335 blue();
1336 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1337
1338 //get inode
1339 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1340 int offset = inode-inode_start_entry;
1341 assert(0 <= offset && offset < INODES_PER_SECTOR);
1342 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1343
1344 blue();
1345 dprintf("... inode %d (size=%d, type=%d)\n",
1346 inode, fileInode->size, fileInode->type);
1347
1348 int position = file.pos;
1349
1350 /***read contents of data blocks***/
1351
1352 //temp buffer to read entire sector in
1353 char tempBuff[SECTOR_SIZE];
1354 bzero(buffer,sizeToRead);
1355
1356 //will position buffer ptr to next available space to copy data into
1357 int ctrSize = 0;
1358 for(int i = 0; i < sectorsToRead; i++){
1359 bzero(tempBuff, SECTOR_SIZE);
1360
1361 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1362 blue();
1363 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1364 osErrno=E_GENERAL;
1365 return -1;
1366 }
1367
1368 blue();
1369 dprintf("... Reading data[%d]=%d\n", position, fileInode->data[position]);
1370
1371 int currBytes = 0; //keep track of what's currently read from sector
1372
1373 if(file.posByte != 0){//starting out at arbitrary point in currsector
1374 currBytes = SECTOR_SIZE - file.posByte;
1375 }
1376 else if(i == sectorsToRead - 1){//at last sector to read from
1377 currBytes = sizeToRead - ctrSize;
1378 }
1379 else{//full sectors
1380 currBytes = SECTOR_SIZE;
1381 }
1382
1383
1384 //copy what's needed into buffer form buff
1385 memcpy(buffer+ctrSize, tempBuff+file.posByte, currBytes);
1386
1387 ctrSize += currBytes;
1388
1389 //specify new byte position if didn't read the entire last sector
1390 if(i == sectorsToRead - 1 && currBytes < SECTOR_SIZE){
1391 file.posByte = currBytes;
1392 }
1393 else{//new byte position and block position if read entire sectors
1394 position++;
1395 file.posByte = 0;
1396 }
1397
1398
1399 }
1400
1401
1402 //set new read/write position and new posbyte to read at
1403 open_files[fd].pos = position;
1404 open_files[fd].posByte = file.posByte;
1405
1406 blue();
1407 dprintf("... file=%d is now at pos=%d with byte pos=%d\n", fd, open_files[fd].pos, open_files[fd].posByte);
1408 blue();
1409 dprintf("... successfully read %d sectors with size=%d\n", sectorsToRead, sizeToRead);
1410
1411 return sizeToRead;
1412
1413}
1414
1415//helper function
1416//gets user input on what action to take related to overwriting a file
1417//when the file recently opens and is non-empty
1418int toOverwriteOrNot(int fd){
1419 int action = 0;
1420 do{
1421 printf("File of fd=%d is non-empty. Do you\n"
1422 "1. wish to overwrite from position 0 (will delete entire file's contents)?\n"
1423 "2. wish to append data from first empty position?\n"
1424 "3. wish to not overwrite?\n"
1425 "Please input 1, 2 or 3 indicating your desired action.\n", fd);
1426
1427 scanf("%d", &action);
1428 }while(action != 1 && action != 2 && action != 3);
1429
1430 return action;
1431}
1432
1433//case 1: file_write first called on empty file -> no checks, write into file
1434//case 2: file_write first called on a recently opened non-empty file
1435// -> check if user wishes to overwrite
1436// if 1 -> overwrite from given position
1437// if 2 -> write only from the first empty position
1438// if 3 -> do not overwrite
1439int File_Write(int fd, void* buffer, int size)
1440{
1441 boldBlue();
1442 dprintf("File_Write(%d, buffer, %d):\n",fd, size);
1443
1444 //check if fd is valid index
1445 if(fd < 0 || fd > MAX_OPEN_FILES){
1446 boldBlue();
1447 dprintf("... error: fd=%d out of bound\n", fd);
1448 osErrno = E_BAD_FD;
1449 return -1;
1450 }
1451
1452 open_file_t file = open_files[fd];
1453
1454 //check if not an open file
1455 if(file.inode <= 0){
1456 boldBlue();
1457 dprintf("... error: fd=%d not an open file\n", fd);
1458 osErrno = E_BAD_FD;
1459 return -1;
1460 }
1461
1462 //check if file size is full
1463 if(file.size == MAX_SECTORS_PER_FILE*SECTOR_SIZE)
1464 {
1465 boldBlue();
1466 dprintf("... error: file fd=%d is full\n", fd);
1467 osErrno = E_FILE_TOO_BIG;
1468 return -1;
1469 }
1470
1471 //load file's inode disk sectors
1472 int inode = file.inode;
1473 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1474 char inode_buffer[SECTOR_SIZE];
1475 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1476
1477 boldBlue();
1478 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1479
1480 //get inode
1481 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1482 int offset = inode-inode_start_entry;
1483 assert(0 <= offset && offset < INODES_PER_SECTOR);
1484 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1485 boldBlue();
1486 dprintf("... inode %d (size=%d, type=%d)\n",
1487 inode, fileInode->size, fileInode->type);
1488
1489 /***check if user wishes to overwrite or not***/
1490
1491 //write data from after the given data pointer
1492 int position = file.pos;
1493 int action = 0;
1494 int positionByte = file.posByte;
1495
1496 //check cases for potential overwriting:
1497 //if recently opened file is non-empty
1498 //if current pointer is at arbitrary point within file contents
1499 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) != 0){
1500 //check if user wishes to overwrite
1501 reset();
1502 action = toOverwriteOrNot(fd);
1503 if(action == 3){
1504 boldBlue();
1505 printf("... User wishes to not overwrite already existing file. No write done\n");
1506 return 0;
1507 }
1508 else if(action == 2){ //write only from the first empty position
1509 position = file.size/SECTOR_SIZE;
1510 positionByte = 0;
1511
1512 if(file.size%SECTOR_SIZE){
1513 positionByte = file.size%SECTOR_SIZE;
1514 }
1515 boldBlue();
1516 dprintf("... User wishes to write from first empty byte position=%d in block position=%d with file size=%d\n",
1517 positionByte, position, file.size);
1518 }
1519 else{//wishes to overwrite. delete file contents
1520 int pos = 0;
1521 char dataBuffer[SECTOR_SIZE];
1522 bzero(dataBuffer, SECTOR_SIZE);
1523 while(fileInode->data[pos] != 0){
1524 // disk sector has to be freed
1525 if(Disk_Write(fileInode->data[pos], dataBuffer) < 0) return -1;
1526 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, fileInode->data[pos]) < 0) {
1527 boldBlue();
1528 dprintf("... error: free sector occupied by file in sector bitmap unsuccessful\n");
1529 return -1;
1530 }
1531 pos++;
1532 }
1533 position = 0;
1534 positionByte = 0;
1535 file.size = 0;
1536 fileInode->size = 0;
1537 boldBlue();
1538 dprintf("... User wishes to overwrite entire file. Starting at block position=%d with file size=%d\n",
1539 position, file.size);
1540 }
1541 }
1542
1543 //check if extra data doesn't go over max sectors per file
1544 if(file.size + size > MAX_SECTORS_PER_FILE*SECTOR_SIZE){
1545 boldBlue();
1546 dprintf("... error: fd=%d of size=%d at block position=%d at byte position=%d cannot add size=%d bytes."
1547 "No space for that amount\n", fd, file.size, position, positionByte, size);
1548
1549 osErrno = E_FILE_TOO_BIG;
1550 return -1;
1551 }
1552
1553 /***determine how many sectors need to be written in***/
1554
1555 int sectorsToWrite = 0;
1556
1557 if(positionByte != 0){//if posByte is at part of current sector
1558 sectorsToWrite++;
1559 if(SECTOR_SIZE - positionByte < size){//need to write more sectors
1560 int remSize = size - (SECTOR_SIZE - positionByte);
1561 sectorsToWrite += remSize/SECTOR_SIZE;
1562
1563 //check for remainder
1564 if(remSize%SECTOR_SIZE){
1565 sectorsToWrite++;
1566 }
1567 }
1568 }
1569 else{//if posByte is at beginning of a sector
1570 sectorsToWrite = size/SECTOR_SIZE;
1571
1572 //check remainder
1573 if(size%SECTOR_SIZE){
1574 sectorsToWrite++;
1575 }
1576 }
1577
1578 boldBlue();
1579 dprintf("... extra sectors needed=%d for fd=%d at position=%d\n", sectorsToWrite,
1580 fd, position);
1581
1582 char tempBuff[SECTOR_SIZE];
1583 int ctrSize = 0;
1584 for(int i = 0; i < sectorsToWrite; i++){
1585 //find first sector to use
1586 int newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
1587
1588 //check if space exists on disk for write
1589 if(newsec < 0){
1590 boldBlue();
1591 dprintf("... error: no space on disk, write cannot complete\n");
1592 osErrno = E_NO_SPACE;
1593 return -1;
1594 }
1595
1596 fileInode->data[position] = newsec;
1597 bzero(tempBuff, SECTOR_SIZE);
1598 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1599 boldBlue();
1600 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1601 osErrno=E_GENERAL;
1602 return -1;
1603 }
1604
1605 boldBlue();
1606 dprintf("... going to write into disk in sector %d\n", newsec);
1607
1608 int currBytes = 0; //keep track of what's currently needed to extract from buffer
1609
1610 if(positionByte != 0){//starting out at arbitrary point in currsector
1611 currBytes = SECTOR_SIZE - positionByte;
1612 }
1613 else if(i == sectorsToWrite - 1){//at last sector to write into
1614 currBytes = size - ctrSize;
1615 }
1616 else{//full sectors
1617 currBytes = SECTOR_SIZE;
1618 }
1619
1620 ctrSize += currBytes;//where to next extract from buffer
1621
1622 //copy what's needed from buffer into tempbuff
1623 memcpy(tempBuff+positionByte, buffer+ctrSize, currBytes);
1624
1625 if(Disk_Write(fileInode->data[position], tempBuff) < 0) {
1626 boldBlue();
1627 dprintf("... error: failed to write buffer data\n");
1628 osErrno = E_GENERAL;
1629 return -1;
1630 }
1631 boldBlue();
1632 dprintf("... Writing data[%d]=%d\n", position, fileInode->data[position]);
1633
1634 //specify new byte position if didn't read the entire last sector
1635 if(i == sectorsToWrite - 1 && currBytes < SECTOR_SIZE){
1636 positionByte = currBytes;
1637 }
1638 else{//new byte position and block position if read entire sectors
1639 position++;
1640 positionByte = 0;
1641 }
1642 }
1643
1644 //update file and inode size
1645 open_files[fd].size = file.size + size;
1646 open_files[fd].pos = position;
1647 open_files[fd].posByte = positionByte;
1648
1649 //set new inode size
1650 fileInode->size = open_files[fd].size;
1651
1652
1653 //write to disk the updated inode
1654 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
1655 boldBlue();
1656 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
1657 inode, fileInode->size, fileInode->type, inode_sector);
1658
1659 boldBlue();
1660 dprintf("... file=%d is now at block pos=%d and byte position=%d with size=%d\n",
1661 fd, open_files[fd].pos, open_files[fd].posByte, open_files[fd].size);
1662 reset();
1663 return size;
1664}
1665
1666int File_Seek(int fd, int offset)
1667{
1668 dprintf("File_Seek(%d, %d):\n", fd, offset);
1669 //check if fd is valid index
1670 if(fd < 0 || fd > MAX_OPEN_FILES){
1671 dprintf("... error: fd=%d out of bound\n", fd);
1672 osErrno = E_BAD_FD;
1673 return -1;
1674 }
1675 //check if open file
1676 if(open_files[fd].inode <= 0) {
1677 dprintf("... error: fd=%d not an open file\n", fd);
1678 osErrno = E_BAD_FD;
1679 return -1;
1680 }
1681
1682 //check if offset is within bounds
1683 if(offset > open_files[fd].size || offset == -1){
1684 dprintf("... error: offset=%d out of bound\n", fd);
1685 osErrno = E_SEEK_OUT_OF_BOUNDS;
1686 return -1;
1687 }
1688
1689 open_files[fd].pos = offset/SECTOR_SIZE + (offset%SECTOR_SIZE != 0 ? 0 : 1);
1690 open_files[fd].posByte = offset%SECTOR_SIZE;
1691
1692 return open_files[fd].pos;
1693}
1694
1695int File_Close(int fd)
1696{
1697 dprintf("File_Close(%d):\n", fd);
1698 if(0 > fd || fd > MAX_OPEN_FILES) {
1699 dprintf("... error: fd=%d out of bound\n", fd);
1700 osErrno = E_BAD_FD;
1701 return -1;
1702 }
1703 if(open_files[fd].inode <= 0) {
1704 dprintf("... error: fd=%d not an open file\n", fd);
1705 osErrno = E_BAD_FD;
1706 return -1;
1707 }
1708
1709
1710
1711 dprintf("... file closed successfully\n");
1712 open_files[fd].inode = 0;
1713 return 0;
1714}
1715
1716int Dir_Create(char* path)
1717{
1718 dprintf("Dir_Create('%s'):\n", path);
1719 return create_file_or_directory(1, path);
1720}
1721
1722int Dir_Unlink(char* path)
1723{
1724 dprintf("Dir_Unlink('%s'):\n", path);
1725 // no empty path and no root directory allowed
1726 if(path==NULL) {
1727 dprintf("... error: empty path (NULL) for directory unlink");
1728 osErrno = E_GENERAL;
1729 return -1;
1730 }
1731
1732 if(strcmp(path, "/")==0) {
1733 dprintf("... error: not allowed to unlink root directory");
1734 osErrno = E_ROOT_DIR;
1735 return -1;
1736 }
1737
1738 // find parent and children (if theres any)
1739 int child_inode;
1740 int parent_inode = follow_path(path, &child_inode, NULL);
1741 if(parent_inode < 0) {
1742 dprintf("... error: directory '%s' not found\n", path);
1743 osErrno = E_NO_SUCH_DIR;
1744 return -1;
1745 }
1746
1747 int remove = remove_inode(1, parent_inode, child_inode);
1748
1749 if (remove==-1) {
1750 dprintf("... error: general error when unlinking directory\n");
1751 osErrno = E_GENERAL;
1752 return -1;
1753 } else if (remove==-3) {
1754 dprintf("... error: no directory, wrong type\n");
1755 osErrno = E_NO_SUCH_DIR;
1756 return -1;
1757 } else if (remove==-2) {
1758 dprintf("... error: directory %s not empty", path);
1759 osErrno = E_DIR_NOT_EMPTY;
1760 return -1;
1761 } else {
1762 return 0;
1763 }
1764
1765
1766}
1767
1768int Dir_Size(char* path)
1769{
1770 dprintf("Dir_Size('%s'):\n", path);
1771 // no empty path allowed
1772 if(path==NULL) {
1773 dprintf("... error: empty path (NULL) given as parameter\n");
1774 osErrno = E_GENERAL;
1775 return -1;
1776 }
1777
1778 // directory has to exist
1779 int child_inode;
1780 int parent_inode = follow_path(path, &child_inode, NULL);
1781 if(parent_inode < 0) {
1782 dprintf("... error: directory '%s' not found\n", path);
1783 osErrno = E_NO_SUCH_DIR;
1784 return -1;
1785 }
1786
1787 // load the disk sector containing the child inode
1788 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
1789 char inode_buffer[SECTOR_SIZE];
1790 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1791 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
1792
1793 // get the child inode
1794 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1795 int offset = child_inode-inode_start_entry;
1796 assert(0 <= offset && offset < INODES_PER_SECTOR);
1797 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1798
1799 // check for type
1800 if (child->type!=1) {
1801 dprintf("... error: wrong type, path leads to file\n");
1802 osErrno = E_GENERAL;
1803 return -1;
1804 }
1805
1806 return child->size*sizeof(dirent_t);
1807}
1808
1809int Dir_Read(char* path, void* buffer, int size)
1810{
1811 dprintf("Dir_Read('%s', buffer, %d):\n", path, size);
1812
1813 // no empty path allowed
1814 if(path==NULL) {
1815 dprintf("... error: empty path (NULL) given as parameter\n");
1816 osErrno = E_GENERAL;
1817 return -1;
1818 }
1819
1820 // directory has to exist
1821 int inode;
1822 int parent_inode = follow_path(path, &inode, NULL);
1823 if(parent_inode < 0) {
1824 dprintf("... error: directory '%s' not found\n", path);
1825 osErrno = E_NO_SUCH_DIR;
1826 return -1;
1827 }
1828
1829 int act_size = (int)malloc_usable_size(buffer);
1830
1831 // check if size of buffer is large enough to hold all elements in the directory
1832 if (size>act_size) {
1833 dprintf("... error: buffer provided has size %d, but %d bytes required\n",
1834 act_size, size);
1835 osErrno=E_BUFFER_TOO_SMALL;
1836 return -1;
1837 }
1838
1839 // initialize buffer
1840 bzero(buffer, size);
1841
1842 // load disk sector from directory
1843 char inode_buffer[SECTOR_SIZE];
1844 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1845 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1846 dprintf("... load inode table for parent inode %d from disk sector %d\n",
1847 inode, inode_sector);
1848
1849 // get the directory inode
1850 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1851 int offset = inode-inode_start_entry;
1852 assert(0 <= offset && offset < INODES_PER_SECTOR);
1853 inode_t* dir_inode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1854 dprintf("... get inode %d (size=%d, type=%d)\n",
1855 inode, dir_inode->size, dir_inode->type);
1856
1857 // read the directory entries into the buffer
1858 int remainder=dir_inode->size%DIRENTS_PER_SECTOR;
1859 int group=dir_inode->size/DIRENTS_PER_SECTOR;
1860 int buf_offset = (int)(SECTOR_SIZE-SECTOR_SIZE%sizeof(dirent_t));
1861
1862 // a) completely filled sectors
1863 for(int i=0; i<group;i++) {
1864 if(Disk_Read(dir_inode->data[i], buffer+i*buf_offset) < 0) {
1865 dprintf("... error: cant read sector %d\n", dir_inode->data[i]);
1866 osErrno=E_GENERAL;
1867 return -1;
1868 }
1869 }
1870
1871 // b) partly filled sector
1872 if(remainder) {
1873 char buff[SECTOR_SIZE];
1874 if(Disk_Read(dir_inode->data[group], buff) < 0){
1875 dprintf("... error: cant read sector %d\n", dir_inode->data[group]);
1876 osErrno=E_GENERAL;
1877 return -1;
1878 }
1879
1880 memcpy(buffer+group*buf_offset, buff, remainder*sizeof(dirent_t));
1881
1882 }
1883
1884 /*
1885 int bytes = Dir_Size(path);
1886 printf("Number Entries: %d\n", dir_inode->size);
1887 printf("Dirents per sector: %d\n", (int)DIRENTS_PER_SECTOR);
1888 printf("Size of dir in bytes: %d\n", bytes);
1889 printf("Size of buffer in bytes: %d\n", act_size);
1890 printf("Group: %d, Remainder: %d\n", group, remainder);
1891 printf("Sector size: %d\n", (int)SECTOR_SIZE);*/
1892
1893 return dir_inode->size;
1894}