· 6 years ago · Nov 06, 2019, 07:28 PM
1#include <assert.h>
2#include <stdio.h>
3#include <stdlib.h>
4#include <string.h>
5#include <unistd.h>
6#include "LibDisk.h"
7#include "LibFS.h"
8
9// set to 1 to have detailed debug print-outs and 0 to have none
10#define FSDEBUG 0
11
12#if FSDEBUG
13#define dprintf printf
14#else
15#define dprintf noprintf
16void noprintf(char* str, ...) {}
17#endif
18
19// the file system partitions the disk into five parts:
20
21// 1. the superblock (one sector), which contains a magic number at
22// its first four bytes (integer)
23#define SUPERBLOCK_START_SECTOR 0
24
25// the magic number chosen for our file system
26#define OS_MAGIC 0xdeadbeef
27
28// 2. the inode bitmap (one or more sectors), which indicates whether
29// the particular entry in the inode table (#4) is currently in use
30#define INODE_BITMAP_START_SECTOR 1
31
32// the total number of bytes and sectors needed for the inode bitmap;
33// we use one bit for each inode (whether it's a file or directory) to
34// indicate whether the particular inode in the inode table is in use
35#define INODE_BITMAP_SIZE ((MAX_FILES+7)/8)
36#define INODE_BITMAP_SECTORS ((INODE_BITMAP_SIZE+SECTOR_SIZE-1)/SECTOR_SIZE)
37
38// 3. the sector bitmap (one or more sectors), which indicates whether
39// the particular sector in the disk is currently in use
40#define SECTOR_BITMAP_START_SECTOR (INODE_BITMAP_START_SECTOR+INODE_BITMAP_SECTORS)
41
42// the total number of bytes and sectors needed for the data block
43// bitmap (we call it the sector bitmap); we use one bit for each
44// sector of the disk to indicate whether the sector is in use or not
45#define SECTOR_BITMAP_SIZE ((TOTAL_SECTORS+7)/8)
46#define SECTOR_BITMAP_SECTORS ((SECTOR_BITMAP_SIZE+SECTOR_SIZE-1)/SECTOR_SIZE)
47
48// 4. the inode table (one or more sectors), which contains the inodes
49// stored consecutively
50#define INODE_TABLE_START_SECTOR (SECTOR_BITMAP_START_SECTOR+SECTOR_BITMAP_SECTORS)
51
52// an inode is used to represent each file or directory; the data
53// structure supposedly contains all necessary information about the
54// corresponding file or directory
55typedef struct _inode {
56 int size; // the size of the file or number of directory entries
57 int type; // 0 means regular file; 1 means directory
58 int data[MAX_SECTORS_PER_FILE]; // indices to sectors containing data blocks
59} inode_t;
60
61// the inode structures are stored consecutively and yet they don't
62// straddle accross the sector boundaries; that is, there may be
63// fragmentation towards the end of each sector used by the inode
64// table; each entry of the inode table is an inode structure; there
65// are as many entries in the table as the number of files allowed in
66// the system; the inode bitmap (#2) indicates whether the entries are
67// current in use or not
68#define INODES_PER_SECTOR (SECTOR_SIZE/sizeof(inode_t))
69#define INODE_TABLE_SECTORS ((MAX_FILES+INODES_PER_SECTOR-1)/INODES_PER_SECTOR)
70
71// 5. the data blocks; all the rest sectors are reserved for data
72// blocks for the content of files and directories
73#define DATABLOCK_START_SECTOR (INODE_TABLE_START_SECTOR+INODE_TABLE_SECTORS)
74
75// other file related definitions
76
77// max length of a path is 256 bytes (including the ending null)
78#define MAX_PATH 256
79
80// max length of a filename is 16 bytes (including the ending null)
81#define MAX_NAME 16
82
83// max number of open files is 256
84#define MAX_OPEN_FILES 256
85
86// each directory entry represents a file/directory in the parent
87// directory, and consists of a file/directory name (less than 16
88// bytes) and an integer inode number
89typedef struct _dirent {
90 char fname[MAX_NAME]; // name of the file
91 int inode; // inode of the file
92} dirent_t;
93
94// the number of directory entries that can be contained in a sector
95#define DIRENTS_PER_SECTOR (SECTOR_SIZE/sizeof(dirent_t))
96
97// global errno value here
98int osErrno;
99
100// the name of the disk backstore file (with which the file system is booted)
101static char bs_filename[1024];
102
103/* the following functions are internal helper functions */
104
105// check magic number in the superblock; return 1 if OK, and 0 if not
106static int check_magic()
107{
108 char buf[SECTOR_SIZE];
109 if(Disk_Read(SUPERBLOCK_START_SECTOR, buf) < 0)
110 return 0;
111 if(*(int*)buf == OS_MAGIC) return 1;
112 else return 0;
113}
114
115// initialize a bitmap with 'num' sectors starting from 'start'
116// sector; all bits should be set to zero except that the first
117// 'nbits' number of bits are set to one
118static void bitmap_init(int start, int num, int nbits)
119{
120 /* YOUR CODE */
121}
122
123// set the first unused bit from a bitmap of 'nbits' bits (flip the
124// first zero appeared in the bitmap to one) and return its location;
125// return -1 if the bitmap is already full (no more zeros)
126static int bitmap_first_unused(int start, int num, int nbits)
127{
128 /* YOUR CODE */
129 return -1;
130}
131
132// reset the i-th bit of a bitmap with 'num' sectors starting from
133// 'start' sector; return 0 if successful, -1 otherwise
134static int bitmap_reset(int start, int num, int ibit)
135{
136 /* YOUR CODE */
137 return -1;
138}
139
140// return 1 if the file name is illegal; otherwise, return 0; legal
141// characters for a file name include letters (case sensitive),
142// numbers, dots, dashes, and underscores; and a legal file name
143// should not be more than MAX_NAME-1 in length
144static int illegal_filename(char* name)
145{
146 /* YOUR CODE */
147 return 1;
148}
149
150// return the child inode of the given file name 'fname' from the
151// parent inode; the parent inode is currently stored in the segment
152// of inode table in the cache (we cache only one disk sector for
153// this); once found, both cached_inode_sector and cached_inode_buffer
154// may be updated to point to the segment of inode table containing
155// the child inode; the function returns -1 if no such file is found;
156// it returns -2 is something else is wrong (such as parent is not
157// directory, or there's read error, etc.)
158static int find_child_inode(int parent_inode, char* fname,
159 int *cached_inode_sector, char* cached_inode_buffer)
160{
161 int cached_start_entry = ((*cached_inode_sector)-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
162 int offset = parent_inode-cached_start_entry;
163 assert(0 <= offset && offset < INODES_PER_SECTOR);
164 inode_t* parent = (inode_t*)(cached_inode_buffer+offset*sizeof(inode_t));
165 dprintf("... load parent inode: %d (size=%d, type=%d)\n",
166 parent_inode, parent->size, parent->type);
167 if(parent->type != 1) {
168 dprintf("... parent not a directory\n");
169 return -2;
170 }
171
172 int nentries = parent->size; // remaining number of directory entries
173 int idx = 0;
174 while(nentries > 0) {
175 char buf[SECTOR_SIZE]; // cached content of directory entries
176 if(Disk_Read(parent->data[idx], buf) < 0) return -2;
177 for(int i=0; i<DIRENTS_PER_SECTOR; i++) {
178 if(i>nentries) break;
179 if(!strcmp(((dirent_t*)buf)[i].fname, fname)) {
180 // found the file/directory; update inode cache
181 int child_inode = ((dirent_t*)buf)[i].inode;
182 dprintf("... found child_inode=%d\n", child_inode);
183 int sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
184 if(sector != (*cached_inode_sector)) {
185 *cached_inode_sector = sector;
186 if(Disk_Read(sector, cached_inode_buffer) < 0) return -2;
187 dprintf("... load inode table for child\n");
188 }
189 return child_inode;
190 }
191 }
192 idx++; nentries -= DIRENTS_PER_SECTOR;
193 }
194 dprintf("... could not find child inode\n");
195 return -1; // not found
196}
197
198// follow the absolute path; if successful, return the inode of the
199// parent directory immediately before the last file/directory in the
200// path; for example, for '/a/b/c/d.txt', the parent is '/a/b/c' and
201// the child is 'd.txt'; the child's inode is returned through the
202// parameter 'last_inode' and its file name is returned through the
203// parameter 'last_fname' (both are references); it's possible that
204// the last file/directory is not in its parent directory, in which
205// case, 'last_inode' points to -1; if the function returns -1, it
206// means that we cannot follow the path
207static int follow_path(char* path, int* last_inode, char* last_fname)
208{
209 if(!path) {
210 dprintf("... invalid path\n");
211 return -1;
212 }
213 if(path[0] != '/') {
214 dprintf("... '%s' not absolute path\n", path);
215 return -1;
216 }
217
218 // make a copy of the path (skip leading '/'); this is necessary
219 // since the path is going to be modified by strsep()
220 char pathstore[MAX_PATH];
221 strncpy(pathstore, path+1, MAX_PATH-1);
222 pathstore[MAX_PATH-1] = '\0'; // for safety
223 char* lpath = pathstore;
224
225 int parent_inode = -1, child_inode = 0; // start from root
226 // cache the disk sector containing the root inode
227 int cached_sector = INODE_TABLE_START_SECTOR;
228 char cached_buffer[SECTOR_SIZE];
229 if(Disk_Read(cached_sector, cached_buffer) < 0) return -1;
230 dprintf("... load inode table for root from disk sector %d\n", cached_sector);
231
232 // for each file/directory name separated by '/'
233 char* token;
234 while((token = strsep(&lpath, "/")) != NULL) {
235 dprintf("... process token: '%s'\n", token);
236 if(*token == '\0') continue; // multiple '/' ignored
237 if(illegal_filename(token)) {
238 dprintf("... illegal file name: '%s'\n", token);
239 return -1;
240 }
241 if(child_inode < 0) {
242 // regardless whether child_inode was not found previously, or
243 // there was issues related to the parent (say, not a
244 // directory), or there was a read error, we abort
245 dprintf("... parent inode can't be established\n");
246 return -1;
247 }
248 parent_inode = child_inode;
249 child_inode = find_child_inode(parent_inode, token,
250 &cached_sector, cached_buffer);
251 if(last_fname) strcpy(last_fname, token);
252 }
253 if(child_inode < -1) return -1; // if there was error, abort
254 else {
255 // there was no error, several possibilities:
256 // 1) '/': parent = -1, child = 0
257 // 2) '/valid-dirs.../last-valid-dir/not-found': parent=last-valid-dir, child=-1
258 // 3) '/valid-dirs.../last-valid-dir/found: parent=last-valid-dir, child=found
259 // in the first case, we set parent=child=0 as special case
260 if(parent_inode==-1 && child_inode==0) parent_inode = 0;
261 dprintf("... found parent_inode=%d, child_inode=%d\n", parent_inode, child_inode);
262 *last_inode = child_inode;
263 return parent_inode;
264 }
265}
266
267// add a new file or directory (determined by 'type') of given name
268// 'file' under parent directory represented by 'parent_inode'
269int add_inode(int type, int parent_inode, char* file)
270{
271 // get a new inode for child
272 int child_inode = bitmap_first_unused(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, INODE_BITMAP_SIZE);
273 if(child_inode < 0) {
274 dprintf("... error: inode table is full\n");
275 return -1;
276 }
277 dprintf("... new child inode %d\n", child_inode);
278
279 // load the disk sector containing the child inode
280 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
281 char inode_buffer[SECTOR_SIZE];
282 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
283 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
284
285 // get the child inode
286 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
287 int offset = child_inode-inode_start_entry;
288 assert(0 <= offset && offset < INODES_PER_SECTOR);
289 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
290
291 // update the new child inode and write to disk
292 memset(child, 0, sizeof(inode_t));
293 child->type = type;
294 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
295 dprintf("... update child inode %d (size=%d, type=%d), update disk sector %d\n",
296 child_inode, child->size, child->type, inode_sector);
297
298 // get the disk sector containing the parent inode
299 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
300 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
301 dprintf("... load inode table for parent inode %d from disk sector %d\n",
302 parent_inode, inode_sector);
303
304 // get the parent inode
305 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
306 offset = parent_inode-inode_start_entry;
307 assert(0 <= offset && offset < INODES_PER_SECTOR);
308 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
309 dprintf("... get parent inode %d (size=%d, type=%d)\n",
310 parent_inode, parent->size, parent->type);
311
312 // get the dirent sector
313 if(parent->type != 1) {
314 dprintf("... error: parent inode is not directory\n");
315 return -2; // parent not directory
316 }
317 int group = parent->size/DIRENTS_PER_SECTOR;
318 char dirent_buffer[SECTOR_SIZE];
319 if(group*DIRENTS_PER_SECTOR == parent->size) {
320 // new disk sector is needed
321 int newsec = bitmap_first_unused(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, SECTOR_BITMAP_SIZE);
322 if(newsec < 0) {
323 dprintf("... error: disk is full\n");
324 return -1;
325 }
326 parent->data[group] = newsec;
327 memset(dirent_buffer, 0, SECTOR_SIZE);
328 dprintf("... new disk sector %d for dirent group %d\n", newsec, group);
329 } else {
330 if(Disk_Read(parent->data[group], dirent_buffer) < 0) return -1;
331 dprintf("... load disk sector %d for dirent group %d\n", parent->data[group], group);
332 }
333
334 // add the dirent and write to disk
335 int start_entry = group*DIRENTS_PER_SECTOR;
336 offset = parent->size-start_entry;
337 dirent_t* dirent = (dirent_t*)(dirent_buffer+offset*sizeof(dirent_t));
338 strncpy(dirent->fname, file, MAX_NAME);
339 dirent->inode = child_inode;
340 if(Disk_Write(parent->data[group], dirent_buffer) < 0) return -1;
341 dprintf("... append dirent %d (name='%s', inode=%d) to group %d, update disk sector %d\n",
342 parent->size, dirent->fname, dirent->inode, group, parent->data[group]);
343
344 // update parent inode and write to disk
345 parent->size++;
346 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
347 dprintf("... update parent inode on disk sector %d\n", inode_sector);
348
349 return 0;
350}
351
352// used by both File_Create() and Dir_Create(); type=0 is file, type=1
353// is directory
354int create_file_or_directory(int type, char* pathname)
355{
356 int child_inode;
357 char last_fname[MAX_NAME];
358 int parent_inode = follow_path(pathname, &child_inode, last_fname);
359 if(parent_inode >= 0) {
360 if(child_inode >= 0) {
361 dprintf("... file/directory '%s' already exists, failed to create\n", pathname);
362 osErrno = E_CREATE;
363 return -1;
364 } else {
365 if(add_inode(type, parent_inode, last_fname) >= 0) {
366 dprintf("... successfully created file/directory: '%s'\n", pathname);
367 return 0;
368 } else {
369 dprintf("... error: something wrong with adding child inode\n");
370 osErrno = E_CREATE;
371 return -1;
372 }
373 }
374 } else {
375 dprintf("... error: something wrong with the file/path: '%s'\n", pathname);
376 osErrno = E_CREATE;
377 return -1;
378 }
379}
380
381// remove the child from parent; the function is called by both
382// File_Unlink() and Dir_Unlink(); the function returns 0 if success,
383// -1 if general error, -2 if directory not empty, -3 if wrong type
384int remove_inode(int type, int parent_inode, int child_inode)
385{
386
387 dprintf("... remove inode %d\n", child_inode);
388
389 // load the disk sector containing the child inode
390 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
391 char inode_buffer[SECTOR_SIZE];
392 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
393 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
394
395 // get the child inode
396 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
397 int offset = child_inode-inode_start_entry;
398 assert(0 <= offset && offset < INODES_PER_SECTOR);
399 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
400
401 // check for right type
402 if(child->type!=type){
403 dprintf("... error: the type parameter does not match the actual inode type\n");
404 return -3;
405 }
406
407 // check if child is non-empty directory
408 if(child->type==1 && child->size>0){
409 dprintf("... error: inode is non-empty directory\n");
410 return -2;
411 }
412
413 // get the disk sector containing the parent inode
414 inode_sector = INODE_TABLE_START_SECTOR+parent_inode/INODES_PER_SECTOR;
415 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
416 dprintf("... load inode table for parent inode %d from disk sector %d\n",
417 parent_inode, inode_sector);
418
419 // get the parent inode
420 inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
421 offset = parent_inode-inode_start_entry;
422 assert(0 <= offset && offset < INODES_PER_SECTOR);
423 inode_t* parent = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
424 dprintf("... get parent inode %d (size=%d, type=%d)\n",
425 parent_inode, parent->size, parent->type);
426
427 // check if parent is directory
428 if(parent->type != 1) {
429 dprintf("... error: parent inode is not directory\n");
430 return -3;
431 }
432
433 // check if parent is non-empty
434 if(parent->size < 1) {
435 dprintf("... error: parent directory has no entries\n");
436 return -1;
437 }
438
439 // reset bit of child inode in bitmap
440 if (bitmap_reset(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, child_inode) < 0) {
441 dprintf("... error: reset inode in bitmap unsuccessful\n");
442 return -1;
443 }
444
445 // check for remaining dirents from parent in that sector (otherwise reset sector bitmap)
446 int remainder = parent->size % DIRENTS_PER_SECTOR;
447 int group = parent->size/DIRENTS_PER_SECTOR;
448 if (remainder == 1) {
449 // disk sector has to be freed
450 if (bitmap_reset(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS, parent->data[group]) < 0) {
451 dprintf("... error: free sector in bitmap unsuccessful\n");
452 return -1;
453 }
454 parent->data[group] = 0;
455 }
456
457 // delete the dirent and write to disk
458 char dirent_buffer[SECTOR_SIZE];
459 if(Disk_Read(parent->data[group], dirent_buffer) < 0) return -1;
460 dprintf("... load disk sector %d for dirent group %d\n", parent->data[group], group);
461 int start_entry = group*DIRENTS_PER_SECTOR;
462 offset = (parent->size-1)-start_entry;
463 dirent_t* dirent = (dirent_t*)(dirent_buffer+offset*sizeof(dirent_t));
464 memset(dirent->fname, 0, sizeof(dirent->fname));
465 dirent->inode = -1;
466 if(Disk_Write(parent->data[group], dirent_buffer) < 0) return -1;
467 dprintf("... delete dirent %d (inode=%d) from group %d, update disk sector %d\n",
468 parent->size, child_inode, group, parent->data[group]);
469
470 // update parent inode and write to disk
471 parent->size--;
472 if(Disk_Write(inode_sector, inode_buffer) < 0) return -1;
473 dprintf("... update parent inode on disk sector %d\n", inode_sector);
474
475 return 0;
476}
477
478// representing an open file
479typedef struct _open_file {
480 int inode; // pointing to the inode of the file (0 means entry not used)
481 int size; // file size cached here for convenience
482 int pos; // read/write position
483} open_file_t;
484static open_file_t open_files[MAX_OPEN_FILES];
485
486// return true if the file pointed to by inode has already been open
487int is_file_open(int inode)
488{
489 for(int i=0; i<MAX_OPEN_FILES; i++) {
490 if(open_files[i].inode == inode)
491 return 1;
492 }
493 return 0;
494}
495
496// return a new file descriptor not used; -1 if full
497int new_file_fd()
498{
499 for(int i=0; i<MAX_OPEN_FILES; i++) {
500 if(open_files[i].inode <= 0)
501 return i;
502 }
503 return -1;
504}
505
506/* end of internal helper functions, start of API functions */
507
508int FS_Boot(char* backstore_fname)
509{
510 dprintf("FS_Boot('%s'):\n", backstore_fname);
511 // initialize a new disk (this is a simulated disk)
512 if(Disk_Init() < 0) {
513 dprintf("... disk init failed\n");
514 osErrno = E_GENERAL;
515 return -1;
516 }
517 dprintf("... disk initialized\n");
518
519 // we should copy the filename down; if not, the user may change the
520 // content pointed to by 'backstore_fname' after calling this function
521 strncpy(bs_filename, backstore_fname, 1024);
522 bs_filename[1023] = '\0'; // for safety
523
524 // we first try to load disk from this file
525 if(Disk_Load(bs_filename) < 0) {
526 dprintf("... load disk from file '%s' failed\n", bs_filename);
527
528 // if we can't open the file; it means the file does not exist, we
529 // need to create a new file system on disk
530 if(diskErrno == E_OPENING_FILE) {
531 dprintf("... couldn't open file, create new file system\n");
532
533 // format superblock
534 char buf[SECTOR_SIZE];
535 memset(buf, 0, SECTOR_SIZE);
536 *(int*)buf = OS_MAGIC;
537 if(Disk_Write(SUPERBLOCK_START_SECTOR, buf) < 0) {
538 dprintf("... failed to format superblock\n");
539 osErrno = E_GENERAL;
540 return -1;
541 }
542 dprintf("... formatted superblock (sector %d)\n", SUPERBLOCK_START_SECTOR);
543
544 // format inode bitmap (reserve the first inode to root)
545 bitmap_init(INODE_BITMAP_START_SECTOR, INODE_BITMAP_SECTORS, 1);
546 dprintf("... formatted inode bitmap (start=%d, num=%d)\n",
547 (int)INODE_BITMAP_START_SECTOR, (int)INODE_BITMAP_SECTORS);
548
549 // format sector bitmap (reserve the first few sectors to
550 // superblock, inode bitmap, sector bitmap, and inode table)
551 bitmap_init(SECTOR_BITMAP_START_SECTOR, SECTOR_BITMAP_SECTORS,
552 DATABLOCK_START_SECTOR);
553 dprintf("... formatted sector bitmap (start=%d, num=%d)\n",
554 (int)SECTOR_BITMAP_START_SECTOR, (int)SECTOR_BITMAP_SECTORS);
555
556 // format inode tables
557 for(int i=0; i<INODE_TABLE_SECTORS; i++) {
558 memset(buf, 0, SECTOR_SIZE);
559 if(i==0) {
560 // the first inode table entry is the root directory
561 ((inode_t*)buf)->size = 0;
562 ((inode_t*)buf)->type = 1;
563 }
564 if(Disk_Write(INODE_TABLE_START_SECTOR+i, buf) < 0) {
565 dprintf("... failed to format inode table\n");
566 osErrno = E_GENERAL;
567 return -1;
568 }
569 }
570 dprintf("... formatted inode table (start=%d, num=%d)\n",
571 (int)INODE_TABLE_START_SECTOR, (int)INODE_TABLE_SECTORS);
572
573 // we need to synchronize the disk to the backstore file (so
574 // that we don't lose the formatted disk)
575 if(Disk_Save(bs_filename) < 0) {
576 // if can't write to file, something's wrong with the backstore
577 dprintf("... failed to save disk to file '%s'\n", bs_filename);
578 osErrno = E_GENERAL;
579 return -1;
580 } else {
581 // everything's good now, boot is successful
582 dprintf("... successfully formatted disk, boot successful\n");
583 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
584 return 0;
585 }
586 } else {
587 // something wrong loading the file: invalid param or error reading
588 dprintf("... couldn't read file '%s', boot failed\n", bs_filename);
589 osErrno = E_GENERAL;
590 return -1;
591 }
592 } else {
593 dprintf("... load disk from file '%s' successful\n", bs_filename);
594
595 // we successfully loaded the disk, we need to do two more checks,
596 // first the file size must be exactly the size as expected (thiis
597 // supposedly should be folded in Disk_Load(); and it's not)
598 int sz = 0;
599 FILE* f = fopen(bs_filename, "r");
600 if(f) {
601 fseek(f, 0, SEEK_END);
602 sz = ftell(f);
603 fclose(f);
604 }
605 if(sz != SECTOR_SIZE*TOTAL_SECTORS) {
606 dprintf("... check size of file '%s' failed\n", bs_filename);
607 osErrno = E_GENERAL;
608 return -1;
609 }
610 dprintf("... check size of file '%s' successful\n", bs_filename);
611
612 // check magic
613 if(check_magic()) {
614 // everything's good by now, boot is successful
615 dprintf("... check magic successful\n");
616 memset(open_files, 0, MAX_OPEN_FILES*sizeof(open_file_t));
617 return 0;
618 } else {
619 // mismatched magic number
620 dprintf("... check magic failed, boot failed\n");
621 osErrno = E_GENERAL;
622 return -1;
623 }
624 }
625}
626
627int FS_Sync()
628{
629 if(Disk_Save(bs_filename) < 0) {
630 // if can't write to file, something's wrong with the backstore
631 dprintf("FS_Sync():\n... failed to save disk to file '%s'\n", bs_filename);
632 osErrno = E_GENERAL;
633 return -1;
634 } else {
635 // everything's good now, sync is successful
636 dprintf("FS_Sync():\n... successfully saved disk to file '%s'\n", bs_filename);
637 return 0;
638 }
639}
640
641int File_Create(char* file)
642{
643 dprintf("File_Create('%s'):\n", file);
644 return create_file_or_directory(0, file);
645}
646
647int File_Unlink(char* file)
648{
649 /* YOUR CODE */
650 return -1;
651}
652
653int File_Open(char* file)
654{
655 dprintf("File_Open('%s'):\n", file);
656 int fd = new_file_fd();
657 if(fd < 0) {
658 dprintf("... max open files reached\n");
659 osErrno = E_TOO_MANY_OPEN_FILES;
660 return -1;
661 }
662
663 int child_inode;
664 follow_path(file, &child_inode, NULL);
665 if(child_inode >= 0) { // child is the one
666 // load the disk sector containing the inode
667 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
668 char inode_buffer[SECTOR_SIZE];
669 if(Disk_Read(inode_sector, inode_buffer) < 0) { osErrno = E_GENERAL; return -1; }
670 dprintf("... load inode table for inode from disk sector %d\n", inode_sector);
671
672 // get the inode
673 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
674 int offset = child_inode-inode_start_entry;
675 assert(0 <= offset && offset < INODES_PER_SECTOR);
676 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
677 dprintf("... inode %d (size=%d, type=%d)\n",
678 child_inode, child->size, child->type);
679
680 if(child->type != 0) {
681 dprintf("... error: '%s' is not a file\n", file);
682 osErrno = E_GENERAL;
683 return -1;
684 }
685
686 // initialize open file entry and return its index
687 open_files[fd].inode = child_inode;
688 open_files[fd].size = child->size;
689 open_files[fd].pos = 0;
690 return fd;
691 } else {
692 dprintf("... file '%s' is not found\n", file);
693 osErrno = E_NO_SUCH_FILE;
694 return -1;
695 }
696}
697
698int File_Read(int fd, void* buffer, int size)
699{
700 /* YOUR CODE */
701 return -1;
702}
703
704int File_Write(int fd, void* buffer, int size)
705{
706 /* YOUR CODE */
707 return -1;
708}
709
710int File_Seek(int fd, int offset)
711{
712 /* YOUR CODE */
713 return 0;
714}
715
716int File_Close(int fd)
717{
718 dprintf("File_Close(%d):\n", fd);
719 if(0 > fd || fd > MAX_OPEN_FILES) {
720 dprintf("... fd=%d out of bound\n", fd);
721 osErrno = E_BAD_FD;
722 return -1;
723 }
724 if(open_files[fd].inode <= 0) {
725 dprintf("... fd=%d not an open file\n", fd);
726 osErrno = E_BAD_FD;
727 return -1;
728 }
729
730 dprintf("... file closed successfully\n");
731 open_files[fd].inode = 0;
732 return 0;
733}
734
735int Dir_Create(char* path)
736{
737 dprintf("Dir_Create('%s'):\n", path);
738 return create_file_or_directory(1, path);
739}
740
741int Dir_Unlink(char* path)
742{
743
744 // no empty path and no root directory allowed
745 if(path==NULL) {
746 dprintf("... error: empty path (NULL) for directory unlink");
747 osErrno = E_GENERAL;
748 return -1;
749 }
750
751 if(strcmp(path, "/")==0) {
752 dprintf("... error: not allowed to unlink root directory");
753 osErrno = E_ROOT_DIR;
754 return -1;
755 }
756
757 // find parent and children (if theres any)
758 int child_inode;
759 int parent_inode = follow_path(path, &child_inode, NULL);
760 if(parent_inode < 0) {
761 dprintf("... error: directory '%s' not found\n", path);
762 osErrno = E_NO_SUCH_DIR;
763 return -1;
764 }
765
766 int remove = remove_inode(1, parent_inode, child_inode);
767
768 if (remove==-1) {
769 dprintf("... error: general error when unlinking directory\n");
770 osErrno = E_GENERAL;
771 return -1;
772 } else if (remove==-3) {
773 dprintf("... error: no directory, wrong type\n");
774 osErrno = E_NO_SUCH_DIR;
775 return -1;
776 } else if (remove==-2) {
777 dprintf("... error: directory %s not empty", path);
778 osErrno = E_DIR_NOT_EMPTY;
779 return -1;
780 } else {
781 return 0;
782 }
783}
784
785int Dir_Size(char* path)
786{
787 /* YOUR CODE */
788
789 // no empty path allowed
790 if(path==NULL) {
791 dprintf("... error: empty path (NULL) given as parameter\n");
792 osErrno = E_GENERAL;
793 return -1;
794 }
795
796 // directory has to exist
797 int child_inode;
798 int parent_inode = follow_path(path, &child_inode, NULL);
799 if(parent_inode < 0) {
800 dprintf("... error: directory '%s' not found\n", path);
801 osErrno = E_NO_SUCH_DIR;
802 return -1;
803 }
804
805 // load the disk sector containing the child inode
806 int inode_sector = INODE_TABLE_START_SECTOR+child_inode/INODES_PER_SECTOR;
807 char inode_buffer[SECTOR_SIZE];
808 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
809 dprintf("... load inode table for child inode from disk sector %d\n", inode_sector);
810
811 // get the child inode
812 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
813 int offset = child_inode-inode_start_entry;
814 assert(0 <= offset && offset < INODES_PER_SECTOR);
815 inode_t* child = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
816
817 // check for type
818 if (child->type!=1) {
819 dprintf("... error: wrong type, path leads to file\n");
820 osErrno = E_GENERAL;
821 return -1;
822 }
823
824 return child->size*sizeof(dirent_t);
825}
826
827int Dir_Read(char* path, void* buffer, int size)
828{
829 /* YOUR CODE */
830
831 int real_size = Dir_Size(path);
832 if(real_size<0) return -1;
833 if(real_size==0) return 0;
834
835 // check if size of buffer is large enough to hold all elements in the directory
836 if (size<real_size) {
837 osErrno=E_BUFFER_TOO_SMALL;
838 return -1;
839 }
840
841 // initialize buffer
842 memset(buffer, 0, size);
843
844 // load disk sector from directory
845 int inode;
846 char inode_buffer[SECTOR_SIZE];
847 int parent_inode=follow_path(path, &inode, NULL);
848 if(parent_inode<0) return -1;
849 int inode_sector = INODE_TABLE_START_SECTOR+inode/INODES_PER_SECTOR;
850 if(Disk_Read(inode_sector, inode_buffer) < 0) return -1;
851 dprintf("... load inode table for parent inode %d from disk sector %d\n",
852 inode, inode_sector);
853
854 // get the parent inode
855 int inode_start_entry = (inode_sector-INODE_TABLE_START_SECTOR)*INODES_PER_SECTOR;
856 int offset = inode-inode_start_entry;
857 assert(0 <= offset && offset < INODES_PER_SECTOR);
858 inode_t* dir_inode = (inode_t*)(inode_buffer+offset*sizeof(inode_t));
859 dprintf("... get inode %d (size=%d, type=%d)\n",
860 inode, dir_inode->size, dir_inode->type);
861
862 // read the directory entries into the buffer
863 int remainder=dir_inode->size%DIRENTS_PER_SECTOR;
864 int group=dir_inode->size/DIRENTS_PER_SECTOR;
865
866 // a) completely filled sectors
867 for(int i=0; i<group;i++) {
868 if(Disk_Read(dir_inode->data[group], (char*)buffer+i*SECTOR_SIZE) < 0) {
869 dprintf("... error: cant read sector %d\n", dir_inode->data[i]);
870 osErrno=E_GENERAL;
871 return -1;
872 }
873 }
874
875 // b) partly filled sector
876 if(remainder>0) {
877 if(Disk_Read(dir_inode->data[group], inode_buffer) < 0){
878 dprintf("... error: cant read sector %d\n", dir_inode->data[group]);
879 osErrno=E_GENERAL;
880 return -1;
881 }
882 strncpy((char*)buffer+group*SECTOR_SIZE, inode_buffer, remainder*sizeof(dirent_t));
883 }
884
885 return dir_inode->size;
886}