Advertisement
guitarplayer616

MyFilesystem

Jul 29th, 2020 (edited)
2,618
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 46.37 KB | None | 0 0
  1. /*
  2.  
  3.   MyFS: a tiny file-system written for educational purposes
  4.  
  5.   MyFS is
  6.  
  7.   Copyright 2018-20 by
  8.  
  9.   University of Alaska Anchorage, College of Engineering.
  10.  
  11.   Contributors: Christoph Lauter
  12.                 Austin Williams
  13.                 ...
  14.  
  15.   and based on
  16.  
  17.   FUSE: Filesystem in Userspace
  18.   Copyright (C) 2001-2007  Miklos Szeredi <miklos@szeredi.hu>
  19.  
  20.   This program can be distributed under the terms of the GNU GPL.
  21.   See the file COPYING.
  22.  
  23.   gcc -Wall myfs.c implementation.c `pkg-config fuse --cflags --libs` -o myfs
  24.  
  25. */
  26.  
  27. #include <stddef.h>
  28. #include <sys/stat.h>
  29. #include <sys/statvfs.h>
  30. #include <stdint.h>
  31. #include <string.h>
  32. #include <time.h>
  33. #include <stdlib.h>
  34. #include <sys/types.h>
  35. #include <unistd.h>
  36. #include <fcntl.h>
  37. #include <errno.h>
  38. #include <stdio.h>
  39.  
  40.  
  41. /* The filesystem you implement must support all the 13 operations
  42.    stubbed out below. There need not be support for access rights,
  43.    links, symbolic links. There needs to be support for access and
  44.    modification times and information for statfs.
  45.  
  46.    The filesystem must run in memory, using the memory of size
  47.    fssize pointed to by fsptr. The memory comes from mmap and
  48.    is backed with a file if a backup-file is indicated. When
  49.    the filesystem is unmounted, the memory is written back to
  50.    that backup-file. When the filesystem is mounted again from
  51.    the backup-file, the same memory appears at the newly mapped
  52.    in virtual address. The filesystem datastructures hence must not
  53.    store any pointer directly to the memory pointed to by fsptr; it
  54.    must rather store offsets from the beginning of the memory region.
  55.  
  56.    When a filesystem is mounted for the first time, the whole memory
  57.    region of size fssize pointed to by fsptr reads as zero-bytes. When
  58.    a backup-file is used and the filesystem is mounted again, certain
  59.    parts of the memory, which have previously been written, may read
  60.    as non-zero bytes. The size of the memory region is at least 2048
  61.    bytes.
  62.  
  63.    CAUTION:
  64.  
  65.    * You MUST NOT use any global variables in your program for reasons
  66.    due to the way FUSE is designed.
  67.  
  68.    You can find ways to store a structure containing all "global" data
  69.    at the start of the memory region representing the filesystem.
  70.  
  71.    * You MUST NOT store (the value of) pointers into the memory region
  72.    that represents the filesystem. Pointers are virtual memory
  73.    addresses and these addresses are ephemeral. Everything will seem
  74.    okay UNTIL you remount the filesystem again.
  75.  
  76.    You may store offsets/indices (of type size_t) into the
  77.    filesystem. These offsets/indices are like pointers: instead of
  78.    storing the pointer, you store how far it is away from the start of
  79.    the memory region. You may want to define a type for your offsets
  80.    and to write two functions that can convert from pointers to
  81.    offsets and vice versa.
  82.  
  83.    * You may use any function out of libc for your filesystem,
  84.    including (but not limited to) malloc, calloc, free, strdup,
  85.    strlen, strncpy, strchr, strrchr, memset, memcpy. However, your
  86.    filesystem MUST NOT depend on memory outside of the filesystem
  87.    memory region. Only this part of the virtual memory address space
  88.    gets saved into the backup-file. As a matter of course, your FUSE
  89.    process, which implements the filesystem, MUST NOT leak memory: be
  90.    careful in particular not to leak tiny amounts of memory that
  91.    accumulate over time. In a working setup, a FUSE process is
  92.    supposed to run for a long time!
  93.  
  94.    It is possible to check for memory leaks by running the FUSE
  95.    process inside valgrind:
  96.  
  97.    valgrind --leak-check=full ./myfs --backupfile=test.myfs ~/fuse-mnt/ -f
  98.  
  99.    However, the analysis of the leak indications displayed by valgrind
  100.    is difficult as libfuse contains some small memory leaks (which do
  101.    not accumulate over time). We cannot (easily) fix these memory
  102.    leaks inside libfuse.
  103.  
  104.    * Avoid putting debug messages into the code. You may use fprintf
  105.    for debugging purposes but they should all go away in the final
  106.    version of the code. Using gdb is more professional, though.
  107.  
  108.    * You MUST NOT fail with exit(1) in case of an error. All the
  109.    functions you have to implement have ways to indicate failure
  110.    cases. Use these, mapping your internal errors intelligently onto
  111.    the POSIX error conditions.
  112.  
  113.    * And of course: your code MUST NOT SEGFAULT!
  114.  
  115.    It is reasonable to proceed in the following order:
  116.  
  117.    (1)   Design and implement a mechanism that initializes a filesystem
  118.          whenever the memory space is fresh. That mechanism can be
  119.          implemented in the form of a filesystem handle into which the
  120.          filesystem raw memory pointer and sizes are translated.
  121.          Check that the filesystem does not get reinitialized at mount
  122.          time if you initialized it once and unmounted it but that all
  123.          pieces of information (in the handle) get read back correctly
  124.          from the backup-file.
  125.  
  126.    (2)   Design and implement functions to find and allocate free memory
  127.          regions inside the filesystem memory space. There need to be
  128.          functions to free these regions again, too. Any "global" variable
  129.          goes into the handle structure the mechanism designed at step (1)
  130.          provides.
  131.  
  132.    (3)   Carefully design a data structure able to represent all the
  133.          pieces of information that are needed for files and
  134.          (sub-)directories.  You need to store the location of the
  135.          root directory in a "global" variable that, again, goes into the
  136.          handle designed at step (1).
  137.  
  138.    (4)   Write __myfs_getattr_implem and debug it thoroughly, as best as
  139.          you can with a filesystem that is reduced to one
  140.          function. Writing this function will make you write helper
  141.          functions to traverse paths, following the appropriate
  142.          subdirectories inside the file system. Strive for modularity for
  143.          these filesystem traversal functions.
  144.  
  145.    (5)   Design and implement __myfs_readdir_implem. You cannot test it
  146.          besides by listing your root directory with ls -la and looking
  147.          at the date of last access/modification of the directory (.).
  148.          Be sure to understand the signature of that function and use
  149.          caution not to provoke segfaults nor to leak memory.
  150.  
  151.    (6)   Design and implement __myfs_mknod_implem. You can now touch files
  152.          with
  153.  
  154.          touch foo
  155.  
  156.          and check that they start to exist (with the appropriate
  157.          access/modification times) with ls -la.
  158.  
  159.    (7)   Design and implement __myfs_mkdir_implem. Test as above.
  160.  
  161.    (8)   Design and implement __myfs_truncate_implem. You can now
  162.          create files filled with zeros:
  163.  
  164.          truncate -s 1024 foo
  165.  
  166.    (9)   Design and implement __myfs_statfs_implem. Test by running
  167.          df before and after the truncation of a file to various lengths.
  168.          The free "disk" space must change accordingly.
  169.  
  170.    (10)  Design, implement and test __myfs_utimens_implem. You can now
  171.          touch files at different dates (in the past, in the future).
  172.  
  173.    (11)  Design and implement __myfs_open_implem. The function can
  174.          only be tested once __myfs_read_implem and __myfs_write_implem are
  175.          implemented.
  176.  
  177.    (12)  Design, implement and test __myfs_read_implem and
  178.          __myfs_write_implem. You can now write to files and read the data
  179.          back:
  180.  
  181.          echo "Hello world" > foo
  182.          echo "Hallo ihr da" >> foo
  183.          cat foo
  184.  
  185.          Be sure to test the case when you unmount and remount the
  186.          filesystem: the files must still be there, contain the same
  187.          information and have the same access and/or modification
  188.          times.
  189.  
  190.    (13)  Design, implement and test __myfs_unlink_implem. You can now
  191.          remove files.
  192.  
  193.    (14)  Design, implement and test __myfs_unlink_implem. You can now
  194.          remove directories.
  195.  
  196.    (15)  Design, implement and test __myfs_rename_implem. This function
  197.          is extremely complicated to implement. Be sure to cover all
  198.          cases that are documented in man 2 rename. The case when the
  199.          new path exists already is really hard to implement. Be sure to
  200.          never leave the filessystem in a bad state! Test thoroughly
  201.          using mv on (filled and empty) directories and files onto
  202.          inexistant and already existing directories and files.
  203.  
  204.    (16)  Design, implement and test any function that your instructor
  205.          might have left out from this list. There are 13 functions
  206.          __myfs_XXX_implem you have to write.
  207.  
  208.    (17)  Go over all functions again, testing them one-by-one, trying
  209.          to exercise all special conditions (error conditions): set
  210.          breakpoints in gdb and use a sequence of bash commands inside
  211.          your mounted filesystem to trigger these special cases. Be
  212.          sure to cover all funny cases that arise when the filesystem
  213.          is full but files are supposed to get written to or truncated
  214.          to longer length. There must not be any segfault; the user
  215.          space program using your filesystem just has to report an
  216.          error. Also be sure to unmount and remount your filesystem,
  217.          in order to be sure that it contents do not change by
  218.          unmounting and remounting. Try to mount two of your
  219.          filesystems at different places and copy and move (rename!)
  220.          (heavy) files (your favorite movie or song, an image of a cat
  221.          etc.) from one mount-point to the other. None of the two FUSE
  222.          processes must provoke errors. Find ways to test the case
  223.          when files have holes as the process that wrote them seeked
  224.          beyond the end of the file several times. Your filesystem must
  225.          support these operations at least by making the holes explicit
  226.          zeros (use dd to test this aspect).
  227.  
  228.    (18)  Run some heavy testing: copy your favorite movie into your
  229.          filesystem and try to watch it out of the filesystem.
  230.  
  231. */
  232.  
  233. /* Helper types and functions */
  234. typedef enum{
  235.   Empty,
  236.   File,
  237.   Directory,
  238.   Stockpile,
  239. } blockTypes;
  240.  
  241. typedef uint32_t offset_t;
  242.  
  243. typedef struct FS_header_t {
  244.   offset_t freeblock_tail; // points to the tail block of the linked list of available freeblock numbers (stored in blocks)
  245.   int freeblock_count; // might need later
  246.   _Bool is_setup;
  247.   int total_blocks;
  248.   struct timespec tempTime;
  249.   int root_dir;
  250. } FS_header_t;
  251.  
  252. #define TRUE 1
  253. #define FALSE 0
  254. #define BLOCK_SIZE 4096
  255. #define CHAR_LIMIT_INCLUDE_NULL 256
  256. #define START_OFFSET sizeof(FS_header_t)
  257. #define PATH_SEPARATOR ("/")
  258. #define PATH_CHAR ('/')
  259.  
  260. typedef struct entry_t {
  261.   char file_name[CHAR_LIMIT_INCLUDE_NULL];
  262.   int block_num;
  263. } entry_t;
  264.  
  265. typedef struct block_t {
  266.   int block_num;
  267.   blockTypes content_type;
  268.   size_t bytes_occupied;
  269. } block_t;
  270.  
  271. typedef struct file_t {
  272.   int next_block;
  273.   struct timespec atim;
  274.   struct timespec mtim;
  275. } file_t;
  276.  
  277. typedef struct directory_t {
  278.   int next_block;
  279.   struct timespec atim;
  280.   struct timespec mtim;
  281.   int entry_count;
  282.   int furthest_index;
  283.   // entries written under it (up to 15)
  284. } directory_t;
  285.  
  286. // a linked list of blocks holding a stack of empty block numbers
  287. typedef struct freeblock_t {
  288.   int next_block;
  289.   int prev_block;
  290.   int freeblock_count;
  291. } freeblock_t;
  292.  
  293. #define FREE_BLOCK_NUMBERS_PER_BLOCK (BLOCK_SIZE - sizeof(block_t) - sizeof(freeblock_t))/sizeof(int)
  294. #define ENTRIES_PER_BLOCK (BLOCK_SIZE-sizeof(block_t)-sizeof(directory_t))/sizeof(entry_t)
  295.  
  296. // typedef struct file_header_t {
  297. //
  298. // } file_header_t;
  299. //
  300. // typedef struct block_header_t {
  301. //
  302. // } block_header_t;
  303.  
  304. /* YOUR HELPER FUNCTIONS GO HERE */
  305. // FS_header_t* initialize_FS(void* fsptr, size_t fssize) {
  306. //   return (FS_header_t*) fsptr;
  307. // }
  308. // start_fs()
  309. // get_free_block()
  310. // return_free_block()
  311. // typedef struct freeblock {
  312. //
  313. // }
  314.  
  315.  
  316.  
  317.  
  318. // block goes from 0 to total_blocks-1
  319. block_t* retrieve_block(void *fsptr, int n) {
  320.   offset_t offset = n*BLOCK_SIZE;
  321.   block_t *block = (block_t*) (fsptr+START_OFFSET+offset);
  322.   //sizeof(block_t)
  323.   return block;
  324. }
  325.  
  326. // initializes each block with its block number, needed for pointing to next/prev, (could also give them number in retrieve block function if takes too long)
  327. int initialize_blocks(FS_header_t *handle) {
  328.   for (int i=0; i<handle->total_blocks; i++) {
  329.     block_t *block = retrieve_block((void*)handle, i);
  330.     block->block_num = i;
  331.     block->bytes_occupied = sizeof(block_t);
  332.   }
  333.   return 0;
  334. }
  335.  
  336. freeblock_t* get_stockpile_header(block_t*);
  337. int* get_stockpile_num_array(block_t*);
  338.  
  339.  
  340. // returns pointer under the block header
  341. void* __get_header(block_t* block) {
  342.   void *ptr = (void*)block;
  343.   return ptr+sizeof(block_t);
  344. }
  345.  
  346. file_t* get_file_header(block_t *file_block) {
  347.   if (file_block->content_type != File) {
  348.     fprintf(stderr, "file_block is not a file block\n");
  349.     return NULL;
  350.   }
  351.   return (file_t*)__get_header(file_block);
  352. }
  353.  
  354. void* get_file_content(block_t *file_block) {
  355.   if (file_block->content_type != File) {
  356.     fprintf(stderr, "file_block is not a file block\n");
  357.     return NULL;
  358.   }
  359.   return ((void*)file_block)+sizeof(file_t)+sizeof(block_t);
  360. }
  361.  
  362. directory_t* get_directory_header(block_t *dir_block) {
  363.   if (dir_block->content_type != Directory) {
  364.     fprintf(stderr, "dir_block is not a directory block\n");
  365.     return NULL;
  366.   }
  367.   return (directory_t*)__get_header(dir_block);
  368. }
  369.  
  370. entry_t* get_directory_entries(block_t *dir_block) {
  371.   if (dir_block->content_type != Directory) {
  372.     fprintf(stderr, "dir_block is not a directory block\n");
  373.     return NULL;
  374.   }
  375.   return (entry_t*)(((void*)dir_block)+sizeof(directory_t)+sizeof(block_t));
  376. }
  377.  
  378. freeblock_t* get_stockpile_header(block_t *stockpile_block) {
  379.   if (stockpile_block->content_type != Stockpile) {
  380.     fprintf(stderr, "stockpile_block is not a stockpile block\n");
  381.     return NULL;
  382.   }
  383.   return (freeblock_t*)__get_header(stockpile_block);
  384. }
  385.  
  386. int* get_stockpile_num_array(block_t *stockpile_block) {
  387.   if (stockpile_block->content_type != Stockpile) {
  388.     fprintf(stderr, "stockpile_block is not a stockpile block\n");
  389.     return NULL;
  390.   }
  391.   return (int*)(((void*)stockpile_block)+sizeof(freeblock_t)+sizeof(block_t));
  392. }
  393.  
  394. // only called by setup_filesystem
  395. // initializes the root directory
  396. // store_file(int startingBlock)
  397. struct timespec get_time(FS_header_t* globals) {
  398.   // are we allowed to use the stack or no?
  399.   // get deleted after moving out of stack
  400.   clock_gettime(CLOCK_REALTIME, &(globals->tempTime));
  401.   return globals->tempTime;
  402. }
  403.  
  404. int set_dir_time(directory_t *header, _Bool has_been_modified) {
  405.   struct timespec temp_time;
  406.   clock_gettime(CLOCK_REALTIME, &(temp_time));
  407.   header->atim = temp_time;
  408.   if (has_been_modified) {
  409.     header->mtim = temp_time;
  410.   }
  411.   return 0;
  412. }
  413.  
  414. int set_file_time(file_t *header, _Bool has_been_modified) {
  415.   struct timespec temp_time;
  416.   clock_gettime(CLOCK_REALTIME, &(temp_time));
  417.   header->atim = temp_time;
  418.   if (has_been_modified) {
  419.     header->mtim = temp_time;
  420.   }
  421.   return 0;
  422. }
  423.  
  424. int set_block_time(block_t *block, _Bool has_been_modified) {
  425.   int return_value;
  426.   if (block->content_type == Directory) {
  427.     return_value = set_dir_time(get_directory_header(block), has_been_modified);
  428.   } else if (block->content_type == File) {
  429.     return_value = set_file_time(get_file_header(block), has_been_modified);
  430.   }
  431.   return return_value;
  432. }
  433.  
  434. block_t* clean_block(FS_header_t *handle, block_t *block) {
  435.   if (block->content_type == Empty) {
  436.     fprintf(stderr, "block is already clean\n");
  437.     return NULL;
  438.   }
  439.   else if (block->content_type == File){
  440.     /*STUB*/
  441.   }
  442.   else if (block->content_type == Directory){
  443.     /*STUB*/
  444.   }
  445.   else if (block->content_type == Stockpile){
  446.     freeblock_t *header = get_stockpile_header(block);
  447.     // make sure stockpile is tail end of linked list;
  448.     if (header->prev_block == -1 || header->next_block != -1) {
  449.       fprintf(stderr, "only tail end of stockpile block may be deleted\n");
  450.       return NULL;
  451.     }
  452.     // make sure there are no entries
  453.     if (header->freeblock_count > 0) {
  454.       fprintf(stderr, "must store no freeblocks to format\n");
  455.       return NULL;
  456.     }
  457.     // make sure it is pointed to by handle / if block num is accurate
  458.     if (handle->freeblock_tail != block->block_num) {
  459.       fprintf(stderr, "handle->freeblock_tail is not storing this block's number\n");
  460.       return NULL;
  461.     }
  462.     // finally do stuff
  463.     // make previous block's next to -1
  464.     block_t* prev_block = retrieve_block((void*)handle, header->prev_block);
  465.     freeblock_t* prev_block_header = get_stockpile_header(prev_block);
  466.     prev_block_header->next_block = -1;
  467.  
  468.     // make handle->tail point to prev block
  469.     handle->freeblock_tail = header->prev_block;
  470.  
  471.     //format block
  472.     char *zeroes = (char*)header;
  473.     for (int i=0; i<BLOCK_SIZE-sizeof(block_t); i++) {
  474.       zeroes[i] = 0;
  475.     }
  476.  
  477.     // convert block type to Empty
  478.     block->content_type = Empty;
  479.  
  480.     // put size to initial
  481.     block->bytes_occupied = sizeof(block_t);
  482.  
  483.     return block;
  484.   }
  485.   fprintf(stderr, "invalid block content type\n");
  486.   return NULL;
  487. }
  488.  
  489. block_t* get_stockpile_block(void *fsptr, int block_num) {
  490.   block_t *empty_block = retrieve_block(fsptr, block_num);
  491.   // error if block specified is not empty
  492.   if (empty_block->content_type != Empty || empty_block->bytes_occupied > sizeof(block_t)) {
  493.     fprintf(stderr, "ERROR: cannot convert non-empty block\n");
  494.     return NULL;
  495.   }
  496.   // initialize default stockpile values
  497.   empty_block->content_type = Stockpile;
  498.  
  499.   freeblock_t *freeblock_header = (freeblock_t*)(((void*)empty_block)+sizeof(block_t));
  500.   freeblock_header->next_block = -1;
  501.   freeblock_header->prev_block = -1;
  502.   empty_block->bytes_occupied = sizeof(block_t)+sizeof(freeblock_t);
  503.  
  504.   return empty_block;
  505. }
  506.  
  507. // internal function to push the number into the stockpile block, does not check if full or if it will fit
  508. int __push_free_block_num(FS_header_t *handle, block_t *stockpile_block, int block_num) {
  509.   freeblock_t *header = (freeblock_t*)(((void*)stockpile_block)+sizeof(block_t));
  510.   int *numbers = (int*)(((void*)header)+sizeof(freeblock_t));
  511.  
  512.   int count = header->freeblock_count;
  513.   numbers[count] = block_num;
  514.   stockpile_block->bytes_occupied += sizeof(int);
  515.   header->freeblock_count++;
  516.   handle->freeblock_count++;
  517.   return 0;
  518. }
  519.  
  520. // used to push the cleared block back on the stack for others to use
  521. int push_free_block_num(FS_header_t *handle, int block_num) {
  522.   _Bool is_empty = retrieve_block((void*)handle, block_num)->content_type == Empty;
  523.   if (!is_empty) {
  524.     fprintf(stderr, "block_num describes non-empty block\n");
  525.     return -1;
  526.   }
  527.  
  528.   block_t* stockpile = retrieve_block((void*)handle, handle->freeblock_tail);
  529.  
  530.   _Bool array_is_full = BLOCK_SIZE-stockpile->bytes_occupied < sizeof(int);
  531.   if (array_is_full) {
  532.     block_t* new_stockpile = get_stockpile_block((void*)handle, block_num);
  533.     freeblock_t* new_header = get_stockpile_header(new_stockpile);
  534.     new_header->prev_block = handle->freeblock_tail;
  535.     handle->freeblock_tail = block_num;
  536.   }
  537.   else {
  538.     __push_free_block_num(handle, stockpile, block_num);
  539.   }
  540.   return 0;
  541. }
  542.  
  543. int pop_free_block_num(FS_header_t *handle) {
  544.   // if there is no freeblocks in the stockpile, empty stockpile and offer it as freeblock, change handle tail to prev block
  545.   if (handle->freeblock_tail == -1) {
  546.     fprintf(stderr, "ERROR: Out of space\n");
  547.     return -1;
  548.   }
  549.   block_t* stockpile = retrieve_block((void*)handle, handle->freeblock_tail);
  550.   freeblock_t* header = get_stockpile_header(stockpile);
  551.   int* array = get_stockpile_num_array(stockpile);
  552.  
  553.   int last_element = header->freeblock_count-1;
  554.   if (header->freeblock_count > 0) {
  555.     int block_num = array[last_element];
  556.     // delete the remnants
  557.     array[last_element] = 0;
  558.     header->freeblock_count--;
  559.     handle->freeblock_count--;
  560.     return block_num;
  561.   } else if (header->freeblock_count == 0){
  562.     // convert block into free block
  563.     block_t* cleared_block = clean_block(handle, stockpile);
  564.     return cleared_block->block_num;
  565.   } else {
  566.     fprintf(stderr, "ERROR: freeblock count is off\n");
  567.     return -1;
  568.   }
  569.   return 0;
  570. }
  571.  
  572. int setup_free_blocks(FS_header_t *handle, int starting_block) {
  573.   block_t *stockpile_block = get_stockpile_block((void*)handle, starting_block);
  574.   freeblock_t *header = (freeblock_t*)(((void*)stockpile_block)+sizeof(block_t));
  575.  
  576.   _Bool has_enough_space = 1;
  577.   // goes from n-2 to 0, uinitialized points to 0 (reserved for root directory "/")
  578.   for (int i=handle->total_blocks-2; i>=0; i--) {
  579.     has_enough_space = BLOCK_SIZE - stockpile_block->bytes_occupied >= 4;
  580.     if (has_enough_space) {
  581.       __push_free_block_num(handle, stockpile_block, i);
  582.     } else {
  583.       // append to linked list, update handle->tail
  584.       int prev_num = stockpile_block->block_num;
  585.       header->next_block = i;
  586.       handle->freeblock_tail = i;
  587.       stockpile_block = get_stockpile_block((void*)handle, i);
  588.       header = get_stockpile_header(stockpile_block);
  589.       header->prev_block = prev_num;
  590.     }
  591.   }
  592.   return 0;
  593. }
  594.  
  595. block_t* get_free_block(FS_header_t *handle) {
  596.   return retrieve_block((void*)handle, pop_free_block_num(handle));
  597. }
  598.  
  599. int find_empty_entry(block_t *dir_block) {
  600.   // assume push and pop for now
  601.   directory_t *dir_header = get_directory_header(dir_block);
  602.   entry_t *dir_entries = get_directory_entries(dir_block);
  603.   int search_this_many = dir_header->entry_count+1;
  604.   for (int i = 0; i<search_this_many; i++) {
  605.     if (strncmp(dir_entries[i].file_name,"\0\0\0\0\0",5)) {
  606.       return i;
  607.     }
  608.   }
  609.   return -1;
  610. }
  611.  
  612. block_t* get_file_block(FS_header_t *handle) {
  613.   // creates an inode referring to all the files as well as the file_t
  614.   block_t *file_block = get_free_block(handle);
  615.   file_block->content_type = File;
  616.   file_t *file_header = get_file_header(file_block);
  617.  
  618.   // set the time
  619.   set_file_time(file_header, TRUE);
  620.  
  621.   return file_block;
  622. }
  623.  
  624. block_t* get_dir_block(FS_header_t *handle) {
  625.   block_t *dir_block = get_free_block(handle);
  626.   dir_block->content_type = Directory;
  627.   directory_t *dir_header = get_directory_header(dir_block);
  628.   dir_header->next_block = -1;
  629.   dir_header->entry_count = 0;
  630.  
  631.   // set the time
  632.   set_dir_time(dir_header, TRUE);
  633.  
  634.   return dir_block;
  635. }
  636.  
  637. int __update_furthest_index(int furthest, entry_t *entries) {
  638.   for (int i = furthest-1; i>=0; i--) {
  639.     if (strncmp(entries[i].file_name, "\0", 1) != 0) {
  640.       return i;
  641.     }
  642.   }
  643.   return 0;
  644. }
  645.  
  646. int __clear_entry(entry_t *entries, int i) {
  647.   // only have to clear to the first null
  648.   entries[i].block_num = 0;
  649.   char *fill_null = (char*)(&entries[i]);
  650.   for (int i=0; i<CHAR_LIMIT_INCLUDE_NULL; i++) {
  651.     if (fill_null[i] == '\0') {
  652.       break;
  653.     }
  654.     fill_null[i] = '\0';
  655.   }
  656.   return 0;
  657. }
  658.  
  659. // removes file/ directory entry from directory entry array
  660. int __remove_entry(block_t *dir_block, int block_num) {
  661.   directory_t *dir_header = get_directory_header(dir_block);
  662.   entry_t *dir_entries = get_directory_entries(dir_block);
  663.   // find if there's any entries not in use
  664.   for (int i=0; i<=dir_header->furthest_index; i++) {
  665.     if (strncmp(dir_entries[i].file_name, "\0", 1) != 0) {
  666.       if (dir_entries[i].block_num == block_num) {
  667.         __clear_entry(dir_entries, i);
  668.         dir_header->entry_count--;
  669.         dir_block->bytes_occupied -= sizeof(entry_t);
  670.         if (i >= dir_header->furthest_index-1) {
  671.           dir_header->furthest_index -= 1; // __update_furthest_index(dir_header->furthest_index, dir_entries);
  672.         }
  673.         set_dir_time(dir_header, TRUE);
  674.         return 0;
  675.       }
  676.     }
  677.   }
  678.   return -1;
  679. }
  680.  
  681. // inserts the file/directory content into the directory's entry array
  682. int __insert_entry(block_t *dir_block, char *name, int block_num) {
  683.   directory_t *dir_header = get_directory_header(dir_block);
  684.   entry_t *dir_entries = get_directory_entries(dir_block);
  685.   // check if there's space
  686.   _Bool has_space = BLOCK_SIZE - dir_block->bytes_occupied > sizeof(int);
  687.   if (!has_space) {
  688.     // todo: add new page to directory
  689.     fprintf(stderr, "directory is out of space!\n");
  690.     return -1;
  691.   }
  692.   // find if there's any entries not in use
  693.   for (int i=0; i<=dir_header->furthest_index; i++) {
  694.     if (strncmp(dir_entries[i].file_name, "\0", 1) == 0) {
  695.       // add entry HERE
  696.       strcpy(dir_entries[i].file_name, name);
  697.       dir_entries[i].block_num = block_num;
  698.       dir_header->entry_count++;
  699.       if (i >= dir_header->furthest_index-1) {
  700.         dir_header->furthest_index = i+1;
  701.       }
  702.       dir_block->bytes_occupied += sizeof(entry_t);
  703.       // modify time
  704.       set_dir_time(dir_header, TRUE);
  705.       return 0;
  706.     }
  707.   }
  708.   return -1;
  709. }
  710.  
  711. int new_entry(block_t *parent, block_t *content_block, char *name) {
  712.   // check if parent is directory_t
  713.   if (parent->content_type != Directory) {
  714.     fprintf(stderr, "parent must be a directory\n");
  715.     return -1;
  716.   }
  717.   if (!(content_block->content_type == File || content_block->content_type == Directory)) {
  718.     fprintf(stderr, "can only insert new directories and files\n");
  719.     return -1;
  720.   }
  721.   return __insert_entry(parent, name, content_block->block_num);
  722. }
  723.  
  724. int remove_entry(block_t *parent, block_t *content_block) {
  725.   // check if parent is directory_t
  726.   if (parent->content_type != Directory) {
  727.     fprintf(stderr, "parent must be a directory\n");
  728.     return -1;
  729.   }
  730.   if (!(content_block->content_type == File || content_block->content_type == Directory)) {
  731.     fprintf(stderr, "can only remove directories and files\n");
  732.     return -1;
  733.   }
  734.   if (content_block->content_type == Directory) {
  735.     directory_t *temp_header = get_directory_header(content_block);
  736.     if (temp_header->entry_count > 0) {
  737.       fprintf(stderr, "directory must be empty\n");
  738.       return -1;
  739.     }
  740.   }
  741.   return __remove_entry(parent, content_block->block_num);
  742. }
  743.  
  744. // int create_directory(FS_header_t *handle, block_t *parent) {
  745. //   block_t *dir_block = get_dir_block(handle);
  746. //   block_t
  747. // }
  748.  
  749. // int create_file(FS_header_t *handle, block_t *dir_block, const char *name) {
  750. //   // check if dir_block is directory_t
  751. //   directory_t *dir_header = get_directory_header(dir_block);
  752. //   entry_t *dir_entries = get_directory_entries(dir_block);
  753. //
  754. //   int count = dir_header->entry_count;
  755. //   strcpy(dir_entries[count].file_name, name);
  756. //   int file_start = get_file_block(handle)->block_num;
  757. //   dir_entries[count].block_num = file_start;
  758. //   dir_header->entry_count++;
  759. //   return 0;
  760. // }
  761.  
  762. int create_root_directory(FS_header_t *handle) {
  763.   block_t *dir_block = get_dir_block(handle);
  764.   handle->root_dir = dir_block->block_num;
  765.   return 0;
  766. }
  767.  
  768. // allocates blocks for filesystem and sets up the globals_handle
  769. int setup_filesystem(FS_header_t *handle, size_t fssize, int *errnoptr) {
  770.   size_t total_storage_bytes = fssize - START_OFFSET;
  771.   int total_blocks = total_storage_bytes/BLOCK_SIZE;
  772.   handle->total_blocks = total_blocks;
  773.   handle->is_setup = TRUE;
  774.   handle->freeblock_tail = START_OFFSET;
  775.   handle->freeblock_count = total_blocks;
  776.   // setup up freeblock system (stockpiles grow from the bottom up)
  777.   initialize_blocks(handle);
  778.   setup_free_blocks(handle, total_blocks-1);
  779.   // setup the root directory
  780.   create_root_directory(handle);
  781.   // add entry to directory
  782.   return 0;
  783. }
  784.  
  785.  
  786.  
  787.  
  788. size_t str_len(char *str) {
  789.   size_t length = 0;
  790.   for (char *c = str; *c; c++) {
  791.     length++;
  792.   }
  793.   return length;
  794. }
  795.  
  796. _Bool is_dir(block_t *block) {
  797.   if (block->content_type == Directory) {
  798.     return 1;
  799.   }
  800.   return 0;
  801. }
  802.  
  803. int search_directory(block_t *dir_block, char* file_name) {
  804.   directory_t *dir_header;
  805.   entry_t *dir_entries;
  806.  
  807.   if (!is_dir(dir_block)) {
  808.     fprintf(stderr, "cannot search into non directory block\n");
  809.     return -1;
  810.   }
  811.  
  812.   dir_header = get_directory_header(dir_block);
  813.   dir_entries = get_directory_entries(dir_block);
  814.   // assumes push and pop directories for now
  815.   int count = dir_header->entry_count;
  816.   for (int i = 0; i < count; i++) {
  817.     if (strncmp(dir_entries[i].file_name,"\0", 1) != 0) {
  818.       if (strcmp(dir_entries[i].file_name, file_name) == 0) {
  819.         return dir_entries[i].block_num;
  820.       }
  821.     }
  822.   }
  823.   return -1;
  824. }
  825.  
  826. block_t* find_root (FS_header_t *handle) {
  827.   return retrieve_block((void*)handle, handle->root_dir);
  828. }
  829.  
  830. block_t* resolve_path(FS_header_t *handle, const char *path) {
  831.   // working dir support?
  832.   // if path begins with /
  833.   char *temp,*found;
  834.   block_t *root_dir = find_root(handle);
  835.   block_t  *prev_dir;
  836.   int result;
  837.  
  838.   if (strncmp(path, PATH_SEPARATOR, 1) != 0) {
  839.     fprintf(stderr, "path must start with /\n");
  840.     return NULL;
  841.   }
  842.  
  843.   if (strcmp(path, PATH_SEPARATOR)==0) {
  844.     return root_dir;
  845.   }
  846.  
  847.   temp = strdup(path);
  848.   prev_dir = root_dir;
  849.   while ((found = strsep(&temp, PATH_SEPARATOR)) != NULL ) {
  850.       if ((result=search_directory(prev_dir, found)) != -1) {
  851.         prev_dir = retrieve_block((void*)handle, result);
  852.       }
  853.   }
  854.   free(temp);
  855.   return prev_dir;
  856.   // split path into tokens / / /
  857.   // get index of each / token
  858.   // read between the slashes
  859.   // find /, find root, find dir, find file.txt
  860. }
  861.  
  862. block_t* resolve_parent(FS_header_t *handle, const char *path) {
  863.   // working dir support?
  864.   // if path begins with /
  865.   char *temp,*found;
  866.   block_t *root_dir = find_root(handle);
  867.   block_t  *prev_dir, *parent;
  868.   int result;
  869.  
  870.   if (strncmp(path, PATH_SEPARATOR, 1) != 0) {
  871.     fprintf(stderr, "path must start with /\n");
  872.     return NULL;
  873.   }
  874.  
  875.   if (strcmp(path, PATH_SEPARATOR)==0) {
  876.     return root_dir;
  877.   }
  878.   temp = strdup(path);
  879.   prev_dir = root_dir;
  880.   while ((found = strsep(&temp, PATH_SEPARATOR)) != NULL ) {
  881.       if ((result=search_directory(prev_dir, found)) != -1) {
  882.         parent = prev_dir;
  883.         prev_dir = retrieve_block((void*)handle, result);
  884.       }
  885.   }
  886.   free(temp);
  887.   return parent;
  888.   // split path into tokens / / /
  889.   // get index of each / token
  890.   // read between the slashes
  891.   // find /, find root, find dir, find file.txt
  892. }
  893.  
  894. void test_program(FS_header_t *handle, size_t fssize, int *errnoptr) {
  895.   block_t *root = get_dir_block(handle);
  896.   block_t *dir = get_dir_block(handle);
  897.   block_t *file = get_file_block(handle);
  898.   block_t *file2 = get_file_block(handle);
  899.  
  900.   new_entry(find_root(handle), root, "root");
  901.   new_entry(root, dir, "dir");
  902.   new_entry(dir, file, "file.txt");
  903.   new_entry(dir, file2, "file2.txt");
  904.  
  905.   block_t *result_block = resolve_path(handle, "/root/dir/file.txt");
  906.  
  907.   remove_entry(dir, result_block);
  908.   remove_entry(root, dir);
  909.   remove_entry(dir, file2);
  910.   block_t *new = get_dir_block(handle);
  911.   new_entry(dir, new, "new");
  912.   remove_entry(dir, file);
  913.   remove_entry(root, dir);
  914.  
  915.   block_t *text = get_file_block(handle);
  916.   new_entry(new, text, "text.txt");
  917. }
  918.  
  919. FS_header_t* get_handle(void *fsptr, size_t fssize, int *errnoptr) {
  920.   // write some stuff at 0
  921.   FS_header_t *handle = (FS_header_t *) fsptr;
  922.   if (!handle->is_setup) {
  923.     setup_filesystem(handle, fssize, errnoptr);
  924.   }
  925.   // get a block, cast it into a file struct, write a long string into the file, read the string
  926.   // test_program(handle, fssize, errnoptr);
  927.   return handle;
  928.  
  929. }
  930.  
  931.  
  932.  
  933.  
  934.  
  935. /* End of helper functions */
  936.  
  937. /* Implemegetnts an emulation of the stat system call on the filesystem
  938.    of size fssize pointed to by fsptr.
  939.  
  940.    If path can be followed and describes a file or directory
  941.    that exists and is accessable, the access information is
  942.    put into stbuf.
  943.  
  944.    On success, 0 is returned. On failure, -1 is returned and
  945.    the appropriate error code is put into *errnoptr.
  946.  
  947.    man 2 stat documents all possible error codes and gives more detail
  948.    on what fields of stbuf need to be filled in. Essentially, only the
  949.    following fields need to be supported:
  950.  
  951.    st_uid      the value passed in argument
  952.    st_gid      the value passed in argument
  953.    st_mode     (as fixed values S_IFDIR | 0755 for directories,
  954.                                 S_IFREG | 0755 for files)
  955.    st_nlink    (as many as there are subdirectories (not files) for directories
  956.                 (including . and ..),
  957.                 1 for files)
  958.    st_size     (supported only for files, where it is the real file size)
  959.    st_atim
  960.    st_mtim
  961.  
  962. */
  963. int __myfs_getattr_implem(void *fsptr, size_t fssize, int *errnoptr,
  964.                           uid_t uid, gid_t gid,
  965.                           const char *path, struct stat *stbuf) {
  966.   // get the handle / table of everything
  967.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  968.   if (handle == NULL) {
  969.     *errnoptr = EFAULT;
  970.     return -1;
  971.   }
  972.   block_t* block = resolve_path(handle, path);
  973.   if (block == NULL) {
  974.     *errnoptr = ENOENT;
  975.     return -1;
  976.   }
  977.   // flush buffer
  978.   memset(stbuf, 0, sizeof(struct stat));
  979.  
  980.   stbuf->st_uid = uid;
  981.   stbuf->st_gid = gid;
  982.   if (block->content_type == Directory) {
  983.     directory_t *dir_header = get_directory_header(block);
  984.     stbuf->st_mode = S_IFDIR | 0755;
  985.     stbuf->st_nlink = get_directory_header(block)->entry_count+2; //+2 counts . and ..
  986.     stbuf->st_atim = dir_header->atim;
  987.     stbuf->st_mtim = dir_header->mtim;
  988.   }
  989.   else if (block->content_type == File) {
  990.     file_t *file_header = get_file_header(block);
  991.     stbuf->st_mode = S_IFREG | 0755;
  992.     stbuf->st_nlink = 1;
  993.     stbuf->st_size = block->bytes_occupied;
  994.     stbuf->st_atim = file_header->atim;
  995.     stbuf->st_mtim = file_header->mtim;
  996.   }
  997.  
  998.   // figure out if it's a directory or file
  999.   //struct timespec currTime; // this variable is on the stack so is that ok?
  1000.   //clock_gettime(CLOCK_REALTIME, &currTime);
  1001.   return 0;
  1002. }
  1003.  
  1004. /* Implements an emulation of the readdir system call on the filesystem
  1005.    of size fssize pointed to by fsptr.
  1006.  
  1007.    If path can be followed and describes a directory that exists and
  1008.    is accessable, the names of the subdirectories and files
  1009.    contained in that directory are output into *namesptr. The . and ..
  1010.    directories must not be included in that listing.
  1011.  
  1012.    If it needs to output file and subdirectory names, the function
  1013.    starts by allocating (with calloc) an array of pointers to
  1014.    characters of the right size (n entries for n names). Sets
  1015.    *namesptr to that pointer. It then goes over all entries
  1016.    in that array and allocates, for each of them an array of
  1017.    characters of the right size (to hold the i-th name, together
  1018.    with the appropriate '\0' terminator). It puts the pointer
  1019.    into that i-th array entry and fills the allocated array
  1020.    of characters with the appropriate name. The calling function
  1021.    will call free on each of the entries of *namesptr and
  1022.    on *namesptr.
  1023.  
  1024.    The function returns the number of names that have been
  1025.    put into namesptr.
  1026.  
  1027.    If no name needs to be reported because the directory does
  1028.    not contain any file or subdirectory besides . and .., 0 is
  1029.    returned and no allocation takes place.
  1030.  
  1031.    On failure, -1 is returned and the *errnoptr is set to
  1032.    the appropriate error code.
  1033.  
  1034.    The error codes are documented in man 2 readdir.
  1035.  
  1036.    In the case memory allocation with malloc/calloc fails, failure is
  1037.    indicated by returning -1 and setting *errnoptr to EINVAL.
  1038.  
  1039. */
  1040.  
  1041. void free_string_array(char **names, int entries) {
  1042.   for (int i=0; i<entries; i++) {
  1043.     free(names[i]);
  1044.   }
  1045.   free(names);
  1046. }
  1047.  
  1048. char** get_names_array(block_t *block, int entries, int *errnoptr) {
  1049.   entry_t *dir_entries = get_directory_entries(block);
  1050.   char **names = calloc(entries, sizeof(char *));
  1051.   if (names == NULL) {
  1052.     *errnoptr = EINVAL;
  1053.     return NULL;
  1054.   }
  1055.   for (int i = 0; i < entries; i++) {
  1056.     // duplicate file name to name
  1057.     char *name = strdup(dir_entries[i].file_name);
  1058.     // fail if memory alloc didnt work
  1059.     if (name == NULL) {
  1060.       free_string_array(names, i);
  1061.       *errnoptr = EINVAL;
  1062.       return NULL;
  1063.     }
  1064.     // add to names
  1065.     names[i] = name;
  1066.   }
  1067.   return names;
  1068. }
  1069.  
  1070.  
  1071. int __myfs_readdir_implem(void *fsptr, size_t fssize, int *errnoptr,
  1072.                           const char *path, char ***namesptr) {
  1073.  
  1074.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1075.   if (handle == NULL) {
  1076.     *errnoptr = EFAULT;
  1077.     return -1;
  1078.   }
  1079.  
  1080.   block_t* block = resolve_path(handle, path);
  1081.   if (block == NULL) {
  1082.     *errnoptr = ENOENT;
  1083.     return -1;
  1084.   }
  1085.   // check that its a dir
  1086.   if (!(block->content_type == Directory)) {
  1087.     *errnoptr = ENOTDIR;
  1088.     return -1;
  1089.   }
  1090.   directory_t *dir_header = get_directory_header(block);
  1091.   int entries = dir_header->entry_count;
  1092.   // set the time
  1093.   set_dir_time(dir_header, FALSE);
  1094.   // check that it has children
  1095.   if (entries <= 0) {
  1096.     return 0;
  1097.   }
  1098.   // point the array of strings pointer to an array of the file names
  1099.   *namesptr = get_names_array(block, entries, errnoptr);
  1100.   if (*namesptr == NULL) {
  1101.     return -1;
  1102.   }
  1103.   return entries;
  1104. }
  1105.  
  1106. /* Implements an emulation of the mknod system call for regular files
  1107.    on the filesystem of size fssize pointed to by fsptr.
  1108.  
  1109.    This function is called only for the creation of regular files.
  1110.  
  1111.    If a file gets created, it is of size zero and has default
  1112.    ownership and mode bits.
  1113.  
  1114.    The call creates the file indicated by path.
  1115.  
  1116.    On success, 0 is returned.
  1117.  
  1118.    On failure, -1 is returned and *errnoptr is set appropriately.
  1119.  
  1120.    The error codes are documented in man 2 mknod.
  1121.  
  1122. */
  1123. void get_new_name(const char *path, char **new_name) {
  1124.   *new_name = strrchr(path, PATH_CHAR)+1;
  1125. }
  1126.  
  1127. int __myfs_mknod_implem(void *fsptr, size_t fssize, int *errnoptr,
  1128.                         const char *path) {
  1129.   if (path == NULL) {
  1130.     *errnoptr = ENOENT;
  1131.     return -1;
  1132.   }
  1133.  
  1134.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1135.   if (handle == NULL) {
  1136.     *errnoptr = EFAULT;
  1137.     return -1;
  1138.   }
  1139.  
  1140.   block_t *parent = resolve_parent(handle, path);
  1141.   block_t *file = get_file_block(handle);
  1142.   char *new_name;
  1143.   get_new_name(path, &new_name);
  1144.  
  1145.   new_entry(parent, file, new_name);
  1146.   // block_t* block = resolve_path(handle, path);
  1147.   // if (block == NULL) {
  1148.   //   *errnoptr = ENOENT;
  1149.   //   return -1;
  1150.   // }
  1151.  
  1152.   return 0;
  1153. }
  1154.  
  1155. /* Implements an emulation of the unlink system call for regular files
  1156.    on the filesystem of size fssize pointed to by fsptr.
  1157.  
  1158.    This function is called only for the deletion of regular files.
  1159.  
  1160.    On success, 0 is returned.
  1161.  
  1162.    On failure, -1 is returned and *errnoptr is set appropriately.
  1163.  
  1164.    The error codes are documented in man 2 unlink.
  1165.  
  1166. */
  1167. int __myfs_unlink_implem(void *fsptr, size_t fssize, int *errnoptr,
  1168.                         const char *path) {
  1169.   /* STUB */
  1170.   return -1;
  1171. }
  1172.  
  1173. /* Implements an emulation of the rmdir system call on the filesystem
  1174.    of size fssize pointed to by fsptr.
  1175.  
  1176.    The call deletes the directory indicated by path.
  1177.  
  1178.    On success, 0 is returned.
  1179.  
  1180.    On failure, -1 is returned and *errnoptr is set appropriately.
  1181.  
  1182.    The function call must fail when the directory indicated by path is
  1183.    not empty (if there are files or subdirectories other than . and ..).
  1184.  
  1185.    The error codes are documented in man 2 rmdir.
  1186.  
  1187. */
  1188. int __myfs_rmdir_implem(void *fsptr, size_t fssize, int *errnoptr,
  1189.                         const char *path) {
  1190.   /* STUB */
  1191.   return -1;
  1192. }
  1193.  
  1194. /* Implements an emulation of the mkdir system call on the filesystem
  1195.    of size fssize pointed to by fsptr.
  1196.  
  1197.    The call creates the directory indicated by path.
  1198.  
  1199.    On success, 0 is returned.
  1200.  
  1201.    On failure, -1 is returned and *errnoptr is set appropriately.
  1202.  
  1203.    The error codes are documented in man 2 mkdir.
  1204.  
  1205. */
  1206. int __myfs_mkdir_implem(void *fsptr, size_t fssize, int *errnoptr,
  1207.                         const char *path) {
  1208.   char *new_name;
  1209.   block_t *parent, *directory;
  1210.  
  1211.   if (path == NULL) {
  1212.     *errnoptr = ENOENT;
  1213.     return -1;
  1214.   }
  1215.  
  1216.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1217.   if (handle == NULL) {
  1218.     *errnoptr = EFAULT;
  1219.     return -1;
  1220.   }
  1221.  
  1222.   parent = resolve_parent(handle, path);
  1223.   directory = get_dir_block(handle);
  1224.  
  1225.   get_new_name(path, &new_name);
  1226.  
  1227.   new_entry(parent, directory, new_name);
  1228.   return 0;
  1229. }
  1230.  
  1231. /* Implements an emulation of the rename system call on the filesystem
  1232.    of size fssize pointed to by fsptr.
  1233.  
  1234.    The call moves the file or directory indicated by from to to.
  1235.  
  1236.    On success, 0 is returned.
  1237.  
  1238.    On failure, -1 is returned and *errnoptr is set appropriately.
  1239.  
  1240.    Caution: the function does more than what is hinted to by its name.
  1241.    In cases the from and to paths differ, the file is moved out of
  1242.    the from path and added to the to path.
  1243.  
  1244.    The error codes are documented in man 2 rename.
  1245.  
  1246. */
  1247. int __myfs_rename_implem(void *fsptr, size_t fssize, int *errnoptr,
  1248.                          const char *from, const char *to) {
  1249.   /* STUB */
  1250.   return -1;
  1251. }
  1252.  
  1253. /* Implements an emulation of the truncate system call on the filesystem
  1254.    of size fssize pointed to by fsptr.
  1255.  
  1256.    The call changes the size of the file indicated by path to offset
  1257.    bytes.
  1258.  
  1259.    When the file becomes smaller due to the call, the extending bytes are
  1260.    removed. When it becomes larger, zeros are appended.
  1261.  
  1262.    On success, 0 is returned.
  1263.  
  1264.    On failure, -1 is returned and *errnoptr is set appropriately.
  1265.  
  1266.    The error codes are documented in man 2 truncate.
  1267.  
  1268. */
  1269. int __myfs_truncate_implem(void *fsptr, size_t fssize, int *errnoptr,
  1270.                            const char *path, off_t offset) {
  1271.   /* STUB */
  1272.   return -1;
  1273. }
  1274.  
  1275. /* Implements an emulation of the open system call on the filesystem
  1276.    of size fssize pointed to by fsptr, without actually performing the opening
  1277.    of the file (no file descriptor is returned).
  1278.  
  1279.    The call just checks if the file (or directory) indicated by path
  1280.    can be accessed, i.e. if the path can be followed to an existing
  1281.    object for which the access rights are granted.
  1282.  
  1283.    On success, 0 is returned.
  1284.  
  1285.    On failure, -1 is returned and *errnoptr is set appropriately.
  1286.  
  1287.    The two only interesting error codes are
  1288.  
  1289.    * EFAULT: the filesystem is in a bad state, we can't do anything
  1290.  
  1291.    * ENOENT: the file that we are supposed to open doesn't exist (or a
  1292.              subpath).
  1293.  
  1294.    It is possible to restrict ourselves to only these two error
  1295.    conditions. It is also possible to implement more detailed error
  1296.    condition answers.
  1297.  
  1298.    The error codes are documented in man 2 open.
  1299.  
  1300. */
  1301. int __myfs_open_implem(void *fsptr, size_t fssize, int *errnoptr,
  1302.                        const char *path) {
  1303.  
  1304.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1305.   if (handle == NULL) {
  1306.    *errnoptr = EFAULT;
  1307.    return -1;
  1308.   }
  1309.  
  1310.   block_t* block = resolve_path(handle, path);
  1311.   if (block == NULL) {
  1312.     *errnoptr = ENOENT;
  1313.     return -1;
  1314.   }
  1315.  
  1316.   return 0;
  1317. }
  1318.  
  1319. /* Implements an emulation of the read system call on the filesystem
  1320.    of size fssize pointed to by fsptr.
  1321.  
  1322.    The call copies up to size bytes from the file indicated by
  1323.    path into the buffer, starting to read at offset. See the man page
  1324.    for read for the details when offset is beyond the end of the file etc.
  1325.  
  1326.    On success, the appropriate number of bytes read into the buffer is
  1327.    returned. The value zero is returned on an end-of-file condition.
  1328.  
  1329.    On failure, -1 is returned and *errnoptr is set appropriately.
  1330.  
  1331.    The error codes are documented in man 2 read.
  1332.  
  1333. */
  1334. int __myfs_read_implem(void *fsptr, size_t fssize, int *errnoptr,
  1335.                        const char *path, char *buf, size_t size, off_t offset) {
  1336.   /* STUB */
  1337.   return -1;
  1338. }
  1339.  
  1340. /* Implements an emulation of the write system call on the filesystem
  1341.    of size fssize pointed to by fsptr.
  1342.  
  1343.    The call copies up to size bytes to the file indicated by
  1344.    path into the buffer, starting to write at offset. See the man page
  1345.    for write for the details when offset is beyond the end of the file etc.
  1346.  
  1347.    On success, the appropriate number of bytes written into the file is
  1348.    returned. The value zero is returned on an end-of-file condition.
  1349.  
  1350.    On failure, -1 is returned and *errnoptr is set appropriately.
  1351.  
  1352.    The error codes are documented in man 2 write.
  1353.  
  1354. */
  1355. int __myfs_write_implem(void *fsptr, size_t fssize, int *errnoptr,
  1356.                         const char *path, const char *buf, size_t size, off_t offset) {
  1357.   /* STUB */
  1358.   return -1;
  1359. }
  1360.  
  1361. /* Implements an emulation of the utimensat system call on the filesystem
  1362.    of size fssize pointed to by fsptr.
  1363.  
  1364.    The call changes the access and modification times of the file
  1365.    or directory indicated by path to the values in ts.
  1366.  
  1367.    On success, 0 is returned.
  1368.  
  1369.    On failure, -1 is returned and *errnoptr is set appropriately.
  1370.  
  1371.    The error codes are documented in man 2 utimensat.
  1372.  
  1373. */
  1374. int __myfs_utimens_implem(void *fsptr, size_t fssize, int *errnoptr,
  1375.                           const char *path, const struct timespec ts[2]) {
  1376.  
  1377.   FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1378.   if (handle == NULL) {
  1379.     *errnoptr = EFAULT;
  1380.     return -1;
  1381.   }
  1382.  
  1383.   block_t* block = resolve_path(handle, path);
  1384.   if (block == NULL) {
  1385.     *errnoptr = ENOENT;
  1386.     return -1;
  1387.   }
  1388.  
  1389.   if (block->content_type == Directory) {
  1390.     directory_t *dir_header = get_directory_header(block);
  1391.     dir_header->atim = ts[0];
  1392.     dir_header->mtim = ts[1];
  1393.   }
  1394.   else if (block->content_type == File) {
  1395.     file_t *file_header = get_file_header(block);
  1396.     file_header->atim = ts[0];
  1397.     file_header->mtim = ts[1];
  1398.   }
  1399.   return 0;
  1400. }
  1401.  
  1402. /* Implements an emulation of the statfs system call on the filesystem
  1403.    of size fssize pointed to by fsptr.
  1404.  
  1405.    The call gets information of the filesystem usage and puts in
  1406.    into stbuf.
  1407.  
  1408.    On success, 0 is returned.
  1409.  
  1410.    On failure, -1 is returned and *errnoptr is set appropriately.
  1411.  
  1412.    The error codes are documented in man 2 statfs.
  1413.  
  1414.    Essentially, only the following fields of struct statvfs need to be
  1415.    supported:
  1416.  
  1417.    f_bsize   fill with what you call a block (typically 1024 bytes)
  1418.    f_blocks  fill with the total number of blocks in the filesystem
  1419.    f_bfree   fill with the free number of blocks in the filesystem
  1420.    f_bavail  fill with same value as f_bfree
  1421.    f_namemax fill with your maximum file/directory name, if your
  1422.              filesystem has such a maximum
  1423.  
  1424. */
  1425. int __myfs_statfs_implem(void *fsptr, size_t fssize, int *errnoptr,
  1426.                          struct statvfs* stbuf) {
  1427.  FS_header_t* handle = get_handle(fsptr, fssize, errnoptr);
  1428.  if (handle == NULL) {
  1429.    *errnoptr = EFAULT;
  1430.    return -1;
  1431.  }
  1432.  
  1433.  stbuf->f_bsize = BLOCK_SIZE;
  1434.  stbuf->f_blocks = handle->total_blocks;
  1435.  stbuf->f_bfree = handle->freeblock_count;
  1436.  stbuf->f_bavail = handle->freeblock_count;
  1437.  stbuf->f_namemax = CHAR_LIMIT_INCLUDE_NULL;
  1438.  return 0;
  1439. }
  1440.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement