· 6 years ago · Nov 26, 2019, 08:02 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 boldBlue();
1243 dprintf("File_Read(%d, buffer, %d):\n", fd, size);
1244 reset();
1245
1246 //check if fd is valid index
1247 if(fd < 0 || fd > MAX_OPEN_FILES){
1248 blue();
1249 dprintf("... fd=%d out of bound", fd);
1250 reset();
1251 osErrno = E_BAD_FD;
1252 return -1;
1253 }
1254
1255 open_file_t file = open_files[fd];
1256
1257 //check if not an open file
1258 if(file.inode <= 0){
1259 blue();
1260 dprintf("... fd=%d not an open file\n", fd);
1261 reset();
1262 osErrno = E_BAD_FD;
1263 return -1;
1264 }
1265
1266
1267 //check if file size is empty
1268 if(file.size == 0)
1269 {
1270 //none to read
1271 blue();
1272 dprintf("... file fd=%d is empty\n", fd);
1273 reset();
1274 return 0;
1275 }
1276
1277 /***determine how many sectors can actually be read***/
1278
1279 //none to read, position at end of file
1280 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) == 0)
1281 {
1282 blue();
1283 dprintf("... file fd=%d is at end of file\n", fd);
1284 reset();
1285 return 0;
1286 }
1287 //something to read
1288 //remaining file size left to read
1289 int remFileSize = file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte));
1290 int sectorsToRead = 0;
1291
1292 int sizeToRead = 0;
1293
1294 /***ask user if to read initial amount or also the remaining***/
1295 /*
1296 if(remFileSize > size && readRemaining(fd) == 2){
1297 sizeToRead = remFileSize;
1298 }
1299 else{*/
1300 sizeToRead = min(remFileSize,size);
1301
1302
1303 //if less than size is remaining, read only remaining
1304 //else pick initial given size to read
1305
1306
1307 //if posByte is at part of current sector
1308 if(file.posByte != 0){
1309 sectorsToRead++;//read only one sector
1310 if(SECTOR_SIZE - file.posByte < sizeToRead){//need to read more sectors
1311 int remSize = sizeToRead - (SECTOR_SIZE - file.posByte);
1312 sectorsToRead += remSize/SECTOR_SIZE;
1313
1314 //check for remainder
1315 if(remSize%SECTOR_SIZE){
1316 sectorsToRead++;
1317 }
1318 }
1319 }
1320 else{//if posByte is at beginning of a sector
1321 sectorsToRead = sizeToRead/SECTOR_SIZE;
1322
1323 //check remainder
1324 if(sizeToRead%SECTOR_SIZE){
1325 sectorsToRead++;
1326 }
1327 }
1328
1329 blue();
1330 dprintf("... sectors to read=%d with size to read=%d of file size=%d at data[%d] at byte position=%d\n",
1331 sectorsToRead, sizeToRead, file.size, file.pos, file.posByte);
1332 reset();
1333
1334 /***get file inode***/
1335
1336 //load file's inode disk sectors
1337 int inode = file.inode;
1338 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1339 char inode_buffer[SECTOR_SIZE];
1340 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1341
1342 blue();
1343 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1344 reset();
1345
1346 //get inode
1347 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1348 int offset = inode-inode_start_entry;
1349 assert(0 <= offset && offset < INODES_PER_SECTOR);
1350 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1351
1352 blue();
1353 dprintf("... inode %d (size=%d, type=%d)\n",
1354 inode, fileInode->size, fileInode->type);
1355 reset();
1356
1357 int position = file.pos;
1358
1359 /***read contents of data blocks***/
1360
1361 //temp buffer to read entire sector in
1362 char tempBuff[SECTOR_SIZE];
1363 memset(buffer,0,sizeToRead);
1364
1365 //will position buffer ptr to next available space to copy data into
1366 int ctrSize = 0;
1367 for(int i = 0; i < sectorsToRead; i++){
1368 bzero(tempBuff, SECTOR_SIZE);
1369
1370 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1371 blue();
1372 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1373 reset();
1374 osErrno=E_GENERAL;
1375 return -1;
1376 }
1377
1378 blue();
1379 dprintf("... Reading data[%d]=%d at positionByte=%d\n",
1380 position, fileInode->data[position], file.posByte);
1381 reset();
1382
1383 int currBytes = 0; //keep track of what's currently read from sector
1384
1385 if(file.posByte != 0){//starting out at arbitrary point in currsector
1386 currBytes = SECTOR_SIZE - file.posByte;
1387 }
1388 else if(i == sectorsToRead - 1){//at last sector to read from
1389 currBytes = sizeToRead - ctrSize;
1390 }
1391 else{//full sectors
1392 currBytes = SECTOR_SIZE;
1393 }
1394
1395 blue();
1396 dprintf("... Copying data of %d bytes at %d into buffer ptr at %d\n",
1397 currBytes, file.posByte, ctrSize);
1398 reset();
1399
1400 //copy what's needed into buffer from buff
1401 memcpy(buffer+ctrSize, tempBuff+file.posByte, currBytes);
1402
1403 ctrSize += currBytes;
1404
1405 //specify new byte position if didn't read the entire last sector
1406 if(i == sectorsToRead - 1 && file.posByte + currBytes < SECTOR_SIZE){
1407 file.posByte = currBytes;
1408 }
1409 else{//new byte position and block position if read entire sectors
1410 position++;
1411 file.posByte = 0;
1412 }
1413
1414
1415 }
1416
1417
1418 //set new read/write position and new posbyte to read at
1419 open_files[fd].pos = position;
1420 open_files[fd].posByte = file.posByte;
1421
1422 blue();
1423 dprintf("... file=%d is now at pos=%d with byte pos=%d\n", fd, open_files[fd].pos, open_files[fd].posByte);
1424
1425 dprintf("... successfully read %d sectors with size=%d\n", sectorsToRead, sizeToRead);
1426 reset();
1427
1428 return sizeToRead;
1429
1430}
1431
1432//helper function
1433//gets user input on what action to take related to overwriting a file
1434//when the file recently opens and is non-empty
1435int toOverwriteOrNot(int fd){
1436 int action = 0;
1437 do{
1438 printf("File of fd=%d is non-empty. Do you\n"
1439 "1. wish to overwrite from position 0 (will delete entire file's contents)?\n"
1440 "2. wish to append data from first empty position?\n"
1441 "3. wish to not overwrite?\n"
1442 "Please input 1, 2 or 3 indicating your desired action.\n", fd);
1443
1444 scanf("%d", &action);
1445 }while(action != 1 && action != 2 && action != 3);
1446
1447 return action;
1448}
1449
1450//case 1: file_write first called on empty file -> no checks, write into file
1451//case 2: file_write first called on a recently opened non-empty file
1452// -> check if user wishes to overwrite
1453// if 1 -> overwrite from given position
1454// if 2 -> write only from the first empty position
1455// if 3 -> do not overwrite
1456int File_Write(int fd, void* buffer, int size)
1457{
1458 boldBlue();
1459 dprintf("File_Write(%d, buffer, %d):\n",fd, size);
1460 reset();
1461
1462 //check if fd is valid index
1463 if(fd < 0 || fd > MAX_OPEN_FILES){
1464 blue();
1465 dprintf("... error: fd=%d out of bound\n", fd);
1466 reset();
1467 osErrno = E_BAD_FD;
1468 return -1;
1469 }
1470
1471 open_file_t file = open_files[fd];
1472
1473 //check if not an open file
1474 if(file.inode <= 0){
1475 blue();
1476 dprintf("... error: fd=%d not an open file\n", fd);
1477 reset();
1478 osErrno = E_BAD_FD;
1479 return -1;
1480 }
1481
1482 //check if file size is full
1483 if(file.size == MAX_SECTORS_PER_FILE*SECTOR_SIZE)
1484 {
1485 blue();
1486 dprintf("... error: file fd=%d is full\n", fd);
1487 reset();
1488 osErrno = E_FILE_TOO_BIG;
1489 return 0;
1490 }
1491
1492 //load file's inode disk sectors
1493 int inode = file.inode;
1494 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1495 char inode_buffer[SECTOR_SIZE];
1496 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
1497
1498 blue();
1499 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
1500 reset();
1501
1502 //get inode
1503 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1504 int offset = inode-inode_start_entry;
1505 assert(0 <= offset && offset < INODES_PER_SECTOR);
1506 inode_t* fileInode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1507 blue();
1508 dprintf("... inode %d (size=%d, type=%d)\n",
1509 inode, fileInode->size, fileInode->type);
1510 reset();
1511
1512 /***check if user wishes to overwrite or not***/
1513
1514 //write data from after the given data pointer
1515 int position = file.pos;
1516 int action = 0;
1517 int positionByte = file.posByte;
1518
1519 //check cases for potential overwriting:
1520 //if recently opened file is non-empty
1521 //if current pointer is at arbitrary point within file contents
1522 if(file.size - ((file.pos+1)*SECTOR_SIZE-(SECTOR_SIZE-file.posByte)) != 0){
1523 //check if user wishes to overwrite
1524 action = toOverwriteOrNot(fd);
1525 if(action == 3){
1526 blue();
1527 printf("... User wishes to not overwrite already existing file. No write done\n");
1528 reset();
1529 return 0;
1530 }
1531 else if(action == 2){ //write only from the first empty position
1532 position = file.size/SECTOR_SIZE;
1533 positionByte = 0;
1534
1535 if(file.size%SECTOR_SIZE){
1536 positionByte = file.size%SECTOR_SIZE;
1537 }
1538 blue();
1539 dprintf("... User wishes to write from first empty byte position=%d in block position=%d with file size=%d\n",
1540 positionByte, position, file.size);
1541 reset();
1542 }
1543 else{//wishes to overwrite. delete file contents
1544 int pos = 0;
1545 char dataBuffer[SECTOR_SIZE];
1546 bzero(dataBuffer, SECTOR_SIZE);
1547 while(fileInode->data[pos] != 0){
1548 // disk sector has to be freed
1549 if(Disk_Write(fileInode->data[pos], dataBuffer) < 0) return -1;
1550 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, fileInode->data[pos]) < 0) {
1551 blue();
1552 dprintf("... error: free sector occupied by file in sector bitmap unsuccessful\n");
1553 reset();
1554 return -1;
1555 }
1556 pos++;
1557 }
1558 position = 0;
1559 positionByte = 0;
1560 file.size = 0;
1561 fileInode->size = 0;
1562 blue();
1563 dprintf("... User wishes to overwrite entire file. Starting at block position=%d with file size=%d\n",
1564 position, file.size);
1565 reset();
1566 }
1567 }
1568
1569 //check if extra data doesn't go over max sectors per file
1570 if(file.size + size > MAX_SECTORS_PER_FILE*SECTOR_SIZE){
1571 blue();
1572 dprintf("... error: fd=%d of size=%d at block position=%d at byte position=%d cannot add size=%d bytes."
1573 "No space for that amount\n", fd, file.size, position, positionByte, size);
1574 reset();
1575
1576 osErrno = E_FILE_TOO_BIG;
1577 return 0;
1578 }
1579
1580 /***determine how many sectors need to be written in***/
1581
1582 int sectorsToWrite = 0;
1583
1584 if(positionByte != 0){//if posByte is at part of current sector
1585 sectorsToWrite++;
1586 if(SECTOR_SIZE - positionByte < size){//need to write more sectors
1587 int remSize = size - (SECTOR_SIZE - positionByte);
1588 sectorsToWrite += remSize/SECTOR_SIZE;
1589
1590 //check for remainder
1591 if(remSize%SECTOR_SIZE){
1592 sectorsToWrite++;
1593 }
1594 }
1595 }
1596 else{//if posByte is at beginning of a sector
1597 sectorsToWrite = size/SECTOR_SIZE;
1598
1599 //check remainder
1600 if(size%SECTOR_SIZE){
1601 sectorsToWrite++;
1602 }
1603 }
1604
1605 blue();
1606 dprintf("... extra sectors needed=%d for fd=%d at data[%d] at byte position=%d\n", sectorsToWrite,
1607 fd, position, positionByte);
1608 reset();
1609
1610 /***write into data blocks***/
1611
1612 char tempBuff[SECTOR_SIZE];
1613 int ctrSize = 0;
1614 for(int i = 0; i < sectorsToWrite; i++){
1615 //find first sector to use if starting at new position
1616 int newsec = 0;
1617 if(positionByte == 0){
1618 newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
1619
1620 //check if space exists on disk for write
1621 if(newsec < 0){
1622 blue();
1623 dprintf("... error: no space on disk, write cannot complete\n");
1624 reset();
1625 osErrno = E_NO_SPACE;
1626 return -1;
1627 }
1628 fileInode->data[position] = newsec;
1629 }
1630
1631 bzero(tempBuff, SECTOR_SIZE);
1632 if(Disk_Read(fileInode->data[position], tempBuff) < 0){
1633 blue();
1634 dprintf("... error: can't read sector %d\n", fileInode->data[position]);
1635 reset();
1636 osErrno=E_GENERAL;
1637 return -1;
1638 }
1639
1640 blue();
1641 dprintf("... going to write into disk in sector %d\n", newsec);
1642 reset();
1643
1644 int currBytes = 0; //keep track of what's currently needed to extract from buffer
1645
1646 if(positionByte != 0){//starting out at arbitrary point in currsector
1647 currBytes = SECTOR_SIZE - positionByte;
1648 }
1649 else if(i == sectorsToWrite - 1){//at last sector to write into
1650 currBytes = size - ctrSize;
1651 }
1652 else{//full sectors
1653 currBytes = SECTOR_SIZE;
1654 }
1655
1656 blue();
1657 dprintf("... copying data %d bytes from ptr position %d in buffer into tempbuff at position %d\n",
1658 currBytes, ctrSize, positionByte);
1659 reset();
1660
1661 //copy what's needed from buffer into tempbuff
1662 memcpy(tempBuff+positionByte, buffer+ctrSize, currBytes);
1663
1664 ctrSize += currBytes;//where to next extract from buffer
1665
1666 if(Disk_Write(fileInode->data[position], tempBuff) < 0) {
1667 blue();
1668 dprintf("... error: failed to write buffer data\n");
1669 reset();
1670 osErrno = E_GENERAL;
1671 return -1;
1672 }
1673 blue();
1674 dprintf("... Writing data[%d]=%d\n", position, fileInode->data[position]);
1675 reset();
1676
1677 //specify new byte position if didn't read the entire last sector
1678 if(i == sectorsToWrite - 1 && positionByte + currBytes < SECTOR_SIZE){
1679 positionByte = currBytes;
1680 }
1681 else{//new byte position and block position if read entire sectors
1682 position++;
1683 positionByte = 0;
1684 }
1685 }
1686
1687 /***update file and file inode***/
1688
1689 //update file and inode size
1690 open_files[fd].size = file.size + size;
1691 open_files[fd].pos = position;
1692 open_files[fd].posByte = positionByte;
1693
1694 //set new inode size
1695 fileInode->size = open_files[fd].size;
1696
1697
1698 //write to disk the updated inode
1699 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
1700 blue();
1701 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
1702 inode, fileInode->size, fileInode->type, inode_sector);
1703
1704 dprintf("... file=%d is now at block pos=%d and byte position=%d with size=%d\n",
1705 fd, open_files[fd].pos, open_files[fd].posByte, open_files[fd].size);
1706 reset();
1707 return size;
1708}
1709
1710int File_Seek(int fd, int offset)
1711{
1712 dprintf("File_Seek(%d, %d):\n", fd, offset);
1713 //check if fd is valid index
1714 if(fd < 0 || fd > MAX_OPEN_FILES){
1715 dprintf("... error: fd=%d out of bound\n", fd);
1716 osErrno = E_BAD_FD;
1717 return -1;
1718 }
1719 //check if open file
1720 if(open_files[fd].inode <= 0) {
1721 dprintf("... error: fd=%d not an open file\n", fd);
1722 osErrno = E_BAD_FD;
1723 return -1;
1724 }
1725
1726 //check if offset is within bounds
1727 if(offset > open_files[fd].size || offset == -1){
1728 dprintf("... error: offset=%d out of bound\n", fd);
1729 osErrno = E_SEEK_OUT_OF_BOUNDS;
1730 return -1;
1731 }
1732
1733 open_files[fd].pos = offset/SECTOR_SIZE + (offset%SECTOR_SIZE != 0 ? 0 : 1);
1734 open_files[fd].posByte = offset%SECTOR_SIZE;
1735
1736 return open_files[fd].pos;
1737}
1738
1739int File_Close(int fd)
1740{
1741 dprintf("File_Close(%d):\n", fd);
1742 if(0 > fd || fd > MAX_OPEN_FILES) {
1743 dprintf("... error: fd=%d out of bound\n", fd);
1744 osErrno = E_BAD_FD;
1745 return -1;
1746 }
1747 if(open_files[fd].inode <= 0) {
1748 dprintf("... error: fd=%d not an open file\n", fd);
1749 osErrno = E_BAD_FD;
1750 return -1;
1751 }
1752
1753
1754
1755 dprintf("... file closed successfully\n");
1756 open_files[fd].inode = 0;
1757 return 0;
1758}
1759
1760int Dir_Create(char* path)
1761{
1762 dprintf("Dir_Create('%s'):\n", path);
1763 return create_file_or_directory(1, path);
1764}
1765
1766int Dir_Unlink(char* path)
1767{
1768 dprintf("Dir_Unlink('%s'):\n", path);
1769 // no empty path and no root directory allowed
1770 if(path==NULL) {
1771 dprintf("... error: empty path (NULL) for directory unlink");
1772 osErrno = E_GENERAL;
1773 return -1;
1774 }
1775
1776 if(strcmp(path, "/")==0) {
1777 dprintf("... error: not allowed to unlink root directory");
1778 osErrno = E_ROOT_DIR;
1779 return -1;
1780 }
1781
1782 // find parent and children (if theres any)
1783 int child_inode;
1784 int parent_inode = follow_path(path, &child_inode, NULL);
1785 if(parent_inode < 0) {
1786 dprintf("... error: directory '%s' not found\n", path);
1787 osErrno = E_NO_SUCH_DIR;
1788 return -1;
1789 }
1790
1791 int remove = remove_inode(1, parent_inode, child_inode);
1792
1793 if (remove==-1) {
1794 dprintf("... error: general error when unlinking directory\n");
1795 osErrno = E_GENERAL;
1796 return -1;
1797 } else if (remove==-3) {
1798 dprintf("... error: no directory, wrong type\n");
1799 osErrno = E_NO_SUCH_DIR;
1800 return -1;
1801 } else if (remove==-2) {
1802 dprintf("... error: directory %s not empty", path);
1803 osErrno = E_DIR_NOT_EMPTY;
1804 return -1;
1805 } else {
1806 return 0;
1807 }
1808
1809
1810}
1811
1812int Dir_Size(char* path)
1813{
1814 dprintf("Dir_Size('%s'):\n", path);
1815 // no empty path allowed
1816 if(path==NULL) {
1817 dprintf("... error: empty path (NULL) given as parameter\n");
1818 osErrno = E_GENERAL;
1819 return -1;
1820 }
1821
1822 // directory has to exist
1823 int child_inode;
1824 int parent_inode = follow_path(path, &child_inode, NULL);
1825 if(parent_inode < 0) {
1826 dprintf("... error: directory '%s' not found\n", path);
1827 osErrno = E_NO_SUCH_DIR;
1828 return -1;
1829 }
1830
1831 // load the disk sector containing the child inode
1832 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
1833 char inode_buffer[SECTOR_SIZE];
1834 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1835 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
1836
1837 // get the child inode
1838 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1839 int offset = child_inode-inode_start_entry;
1840 assert(0 <= offset && offset < INODES_PER_SECTOR);
1841 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1842
1843 // check for type
1844 if (child->type!=1) {
1845 dprintf("... error: wrong type, path leads to file\n");
1846 osErrno = E_GENERAL;
1847 return -1;
1848 }
1849
1850 return child->size*sizeof(dirent_t);
1851}
1852
1853int Dir_Read(char* path, void* buffer, int size)
1854{
1855 dprintf("Dir_Read('%s', buffer, %d):\n", path, size);
1856
1857 // no empty path allowed
1858 if(path==NULL) {
1859 dprintf("... error: empty path (NULL) given as parameter\n");
1860 osErrno = E_GENERAL;
1861 return -1;
1862 }
1863
1864 // directory has to exist
1865 int inode;
1866 int parent_inode = follow_path(path, &inode, NULL);
1867 if(parent_inode < 0) {
1868 dprintf("... error: directory '%s' not found\n", path);
1869 osErrno = E_NO_SUCH_DIR;
1870 return -1;
1871 }
1872
1873 int act_size = (int)malloc_usable_size(buffer);
1874
1875 // check if size of buffer is large enough to hold all elements in the directory
1876 if (size>act_size) {
1877 dprintf("... error: buffer provided has size %d, but %d bytes required\n",
1878 act_size, size);
1879 osErrno=E_BUFFER_TOO_SMALL;
1880 return -1;
1881 }
1882
1883 // initialize buffer
1884 bzero(buffer, size);
1885
1886 // load disk sector from directory
1887 char inode_buffer[SECTOR_SIZE];
1888 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
1889 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
1890 dprintf("... load inode table for parent inode %d from disk sector %d\n",
1891 inode, inode_sector);
1892
1893 // get the directory inode
1894 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
1895 int offset = inode-inode_start_entry;
1896 assert(0 <= offset && offset < INODES_PER_SECTOR);
1897 inode_t* dir_inode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
1898 dprintf("... get inode %d (size=%d, type=%d)\n",
1899 inode, dir_inode->size, dir_inode->type);
1900
1901 // read the directory entries into the buffer
1902 int remainder=dir_inode->size%DIRENTS_PER_SECTOR;
1903 int group=dir_inode->size/DIRENTS_PER_SECTOR;
1904 int buf_offset = (int)(SECTOR_SIZE-SECTOR_SIZE%sizeof(dirent_t));
1905
1906 // a) completely filled sectors
1907 for(int i=0; i<group;i++) {
1908 if(Disk_Read(dir_inode->data[i], buffer+i*buf_offset) < 0) {
1909 dprintf("... error: cant read sector %d\n", dir_inode->data[i]);
1910 osErrno=E_GENERAL;
1911 return -1;
1912 }
1913 }
1914
1915 // b) partly filled sector
1916 if(remainder) {
1917 char buff[SECTOR_SIZE];
1918 if(Disk_Read(dir_inode->data[group], buff) < 0){
1919 dprintf("... error: cant read sector %d\n", dir_inode->data[group]);
1920 osErrno=E_GENERAL;
1921 return -1;
1922 }
1923
1924 memcpy(buffer+group*buf_offset, buff, remainder*sizeof(dirent_t));
1925
1926 }
1927
1928 /*
1929 int bytes = Dir_Size(path);
1930 printf("Number Entries: %d\n", dir_inode->size);
1931 printf("Dirents per sector: %d\n", (int)DIRENTS_PER_SECTOR);
1932 printf("Size of dir in bytes: %d\n", bytes);
1933 printf("Size of buffer in bytes: %d\n", act_size);
1934 printf("Group: %d, Remainder: %d\n", group, remainder);
1935 printf("Sector size: %d\n", (int)SECTOR_SIZE);*/
1936
1937 return dir_inode->size;
1938}