Advertisement
guitarplayer616

FilesystemTEMP

Jul 28th, 2020 (edited)
1,392
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C 76.55 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.                 ... and
  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 <errno.h>
  28. #include <fcntl.h>
  29. #include <fuse.h>
  30. #include <stddef.h>
  31. #include <stdint.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <sys/stat.h>
  36. #include <sys/statvfs.h>
  37. #include <sys/types.h>
  38. #include <time.h>
  39. #include <unistd.h>
  40.  
  41. #ifdef DEBUG
  42. #   include <assert.h>
  43. #endif
  44.  
  45.  
  46. /* The filesystem you implement must support all the 13 operations
  47.    stubbed out below. There need not be support for access rights,
  48.    links, symbolic links. There needs to be support for access and
  49.    modification times and information for statfs.
  50.  
  51.    The filesystem must run in memory, using the memory of size
  52.    fssize pointed to by fsptr. The memory comes from mmap and
  53.    is backed with a file if a backup-file is indicated. When
  54.    the filesystem is unmounted, the memory is written back to
  55.    that backup-file. When the filesystem is mounted again from
  56.    the backup-file, the same memory appears at the newly mapped
  57.    in virtual address. The filesystem datastructures hence must not
  58.    store any pointer directly to the memory pointed to by fsptr; it
  59.    must rather store offsets from the beginning of the memory region.
  60.  
  61.    When a filesystem is mounted for the first time, the whole memory
  62.    region of size fssize pointed to by fsptr reads as zero-bytes. When
  63.    a backup-file is used and the filesystem is mounted again, certain
  64.    parts of the memory, which have previously been written, may read
  65.    as non-zero bytes. The size of the memory region is at least 2048
  66.    bytes.
  67.  
  68.    CAUTION:
  69.  
  70.    * You MUST NOT use any global variables in your program for reasons
  71.    due to the way FUSE is designed.
  72.  
  73.    You can find ways to store a structure containing all "global" data
  74.    at the start of the memory region representing the filesystem.
  75.  
  76.    * You MUST NOT store (the value of) pointers into the memory region
  77.    that represents the filesystem. Pointers are virtual memory
  78.    addresses and these addresses are ephemeral. Everything will seem
  79.    okay UNTIL you remount the filesystem again.
  80.  
  81.    You may store offsets/indices (of type size_t) into the
  82.    filesystem. These offsets/indices are like pointers: instead of
  83.    storing the pointer, you store how far it is away from the start of
  84.    the memory region. You may want to define a type for your offsets
  85.    and to write two functions that can convert from pointers to
  86.    offsets and vice versa.
  87.  
  88.    * You may use any function out of libc for your filesystem,
  89.    including (but not limited to) malloc, calloc, free, strdup,
  90.    strlen, strncpy, strchr, strrchr, memset, memcpy. However, your
  91.    filesystem MUST NOT depend on memory outside of the filesystem
  92.    memory region. Only this part of the virtual memory address space
  93.    gets saved into the backup-file. As a matter of course, your FUSE
  94.    process, which implements the filesystem, MUST NOT leak memory: be
  95.    careful in particular not to leak tiny amounts of memory that
  96.    accumulate over time. In a working setup, a FUSE process is
  97.    supposed to run for a long time!
  98.  
  99.    It is possible to check for memory leaks by running the FUSE
  100.    process inside valgrind:
  101.  
  102.    valgrind --leak-check=full ./myfs --backupfile=test.myfs ~/fuse-mnt/ -f
  103.  
  104.    However, the analysis of the leak indications displayed by valgrind
  105.    is difficult as libfuse contains some small memory leaks (which do
  106.    not accumulate over time). We cannot (easily) fix these memory
  107.    leaks inside libfuse.
  108.  
  109.    * Avoid putting debug messages into the code. You may use fprintf
  110.    for debugging purposes but they should all go away in the final
  111.    version of the code. Using gdb is more professional, though.
  112.  
  113.    * You MUST NOT fail with exit(1) in case of an error. All the
  114.    functions you have to implement have ways to indicate failure
  115.    cases. Use these, mapping your internal errors intelligently onto
  116.    the POSIX error conditions.
  117.  
  118.    * And of course: your code MUST NOT SEGFAULT!
  119.  
  120.    It is reasonable to proceed in the following order:
  121.  
  122.    (1)   Design and implement a mechanism that initializes a filesystem
  123.          whenever the memory space is fresh. That mechanism can be
  124.          implemented in the form of a filesystem handle into which the
  125.          filesystem raw memory pointer and sizes are translated.
  126.          Check that the filesystem does not get reinitialized at mount
  127.          time if you initialized it once and unmounted it but that all
  128.          pieces of information (in the handle) get read back correctly
  129.          from the backup-file.
  130.  
  131.    (2)   Design and implement functions to find and allocate free memory
  132.          regions inside the filesystem memory space. There need to be
  133.          functions to free these regions again, too. Any "global" variable
  134.          goes into the handle structure the mechanism designed at step (1)
  135.          provides.
  136.  
  137.    (3)   Carefully design a data structure able to represent all the
  138.          pieces of information that are needed for files and
  139.          (sub-)directories.  You need to store the location of the
  140.          root directory in a "global" variable that, again, goes into the
  141.          handle designed at step (1).
  142.  
  143.    (4)   Write __myfs_getattr_implem and debug it thoroughly, as best as
  144.          you can with a filesystem that is reduced to one
  145.          function. Writing this function will make you write helper
  146.          functions to traverse paths, following the appropriate
  147.          subdirectories inside the file system. Strive for modularity for
  148.          these filesystem traversal functions.
  149.  
  150.    (5)   Design and implement __myfs_readdir_implem. You cannot test it
  151.          besides by listing your root directory with ls -la and looking
  152.          at the date of last access/modification of the directory (.).
  153.          Be sure to understand the signature of that function and use
  154.          caution not to provoke segfaults nor to leak memory.
  155.  
  156.    (6)   Design and implement __myfs_mknod_implem. You can now touch files
  157.          with
  158.  
  159.          touch foo
  160.  
  161.          and check that they start to exist (with the appropriate
  162.          access/modification times) with ls -la.
  163.  
  164.    (7)   Design and implement __myfs_mkdir_implem. Test as above.
  165.  
  166.    (8)   Design and implement __myfs_truncate_implem. You can now
  167.          create files filled with zeros:
  168.  
  169.          truncate -s 1024 foo
  170.  
  171.    (9)   Design and implement __myfs_statfs_implem. Test by running
  172.          df before and after the truncation of a file to various lengths.
  173.          The free "disk" space must change accordingly.
  174.  
  175.    (10)  Design, implement and test __myfs_utimens_implem. You can now
  176.          touch files at different dates (in the past, in the future).
  177.  
  178.    (11)  Design and implement __myfs_open_implem. The function can
  179.          only be tested once __myfs_read_implem and __myfs_write_implem are
  180.          implemented.
  181.  
  182.    (12)  Design, implement and test __myfs_read_implem and
  183.          __myfs_write_implem. You can now write to files and read the data
  184.          back:
  185.  
  186.          echo "Hello world" > foo
  187.          echo "Hallo ihr da" >> foo
  188.          cat foo
  189.  
  190.          Be sure to test the case when you unmount and remount the
  191.          filesystem: the files must still be there, contain the same
  192.          information and have the same access and/or modification
  193.          times.
  194.  
  195.    (13)  Design, implement and test __myfs_unlink_implem. You can now
  196.          remove files.
  197.  
  198.    (14)  Design, implement and test __myfs_unlink_implem. You can now
  199.          remove directories.
  200.  
  201.    (15)  Design, implement and test __myfs_rename_implem. This function
  202.          is extremely complicated to implement. Be sure to cover all
  203.          cases that are documented in man 2 rename. The case when the
  204.          new path exists already is really hard to implement. Be sure to
  205.          never leave the filessystem in a bad state! Test thoroughly
  206.          using mv on (filled and empty) directories and files onto
  207.          inexistant and already existing directories and files.
  208.  
  209.    (16)  Design, implement and test any function that your instructor
  210.          might have left out from this list. There are 13 functions
  211.          __myfs_XXX_implem you have to write.
  212.  
  213.    (17)  Go over all functions again, testing them one-by-one, trying
  214.          to exercise all special conditions (error conditions): set
  215.          breakpoints in gdb and use a sequence of bash commands inside
  216.          your mounted filesystem to trigger these special cases. Be
  217.          sure to cover all funny cases that arise when the filesystem
  218.          is full but files are supposed to get written to or truncated
  219.          to longer length. There must not be any segfault; the user
  220.          space program using your filesystem just has to report an
  221.          error. Also be sure to unmount and remount your filesystem,
  222.          in order to be sure that it contents do not change by
  223.          unmounting and remounting. Try to mount two of your
  224.          filesystems at different places and copy and move (rename!)
  225.          (heavy) files (your favorite movie or song, an image of a cat
  226.          etc.) from one mount-point to the other. None of the two FUSE
  227.          processes must provoke errors. Find ways to test the case
  228.          when files have holes as the process that wrote them seeked
  229.          beyond the end of the file several times. Your filesystem must
  230.          support these operations at least by making the holes explicit
  231.          zeros (use dd to test this aspect).
  232.  
  233.    (18)  Run some heavy testing: copy your favorite movie into your
  234.          filesystem and try to watch it out of the filesystem.
  235.  
  236. */
  237.  
  238. /* Helper types */
  239. typedef size_t offset_t;
  240. typedef uint32_t inodenum_t; // 2 billion files seems enough?
  241. typedef nlink_t link_t;     // For hardlinks and entries in directory.
  242.  
  243. #ifndef MAGIC_NUM
  244. #   define MAGIC_NUM (0xf54c1a55 /*fs for class*/)
  245. #endif
  246. #ifndef CHAR_LIMIT_INCLUDE_NULL
  247. #   define CHAR_LIMIT_INCLUDE_NULL (256)
  248. #endif
  249. #ifndef BLOCKSIZE
  250. #   define BLOCKSIZE (4096)
  251. #endif
  252. #ifndef MINIMUM_INODE_COUNT
  253. #   define MINIMUM_INODE_COUNT                                                  \
  254.         ((BLOCKSIZE - START_SIZE)                                                  \
  255.          / sizeof(struct inode_t) /* For small filesystems */)
  256. #endif
  257. #ifndef SIZE_PER_INODE
  258. #   define SIZE_PER_INODE (16384 /* For small filesystems */)
  259. #endif
  260. #ifndef INODE_INLINE_COUNT
  261. #   define INODE_INLINE_COUNT                                                   \
  262.         (8 /* Number of block_t to include inline before ptr_block */)
  263. #endif
  264.  
  265. // TODO: move most of these types to different ones
  266.  
  267. struct start_t {
  268.     uint32_t magic;
  269.     uint32_t version;  //!< Version, in case we change it.
  270.     offset_t size;     //!< Space in the filesystem
  271.     inodenum_t inodes; //!< Number of inodes
  272.     offset_t
  273.       firstfree; //!< Offset from beginning of the linked list of free space
  274.  
  275.     unsigned clean_umount : 1; //!< Did we unmount cleanly?
  276. };
  277.  
  278. struct block_t {
  279.     offset_t location; //!< Offset from start to an array of struct direntry_t
  280.     offset_t num;      //!< Number of adjacent blocks that have been combined
  281.     union {
  282.         struct {
  283.             offset_t byteindex; //!< Index of first entry in block, byte
  284.             link_t bytecount;   //!< Number of bytes in this block.
  285.         };
  286.         struct {
  287.             link_t dirindex; //!< Index of first entry in block, directory entries
  288.             link_t dircount; //!< Number of directory entries in this block.
  289.         };
  290.     };
  291.  
  292.     unsigned sparse : 1;
  293. };
  294.  
  295. #define PTR_PER_PTRBLOCK                                                       \
  296.     ((BLOCKSIZE - sizeof(offset_t)) / sizeof(struct block_t))
  297. struct ptrblock_t {
  298.     struct block_t blocks[PTR_PER_PTRBLOCK];
  299.     offset_t next;
  300. };
  301.  
  302. struct direntry_t {
  303.     char name[CHAR_LIMIT_INCLUDE_NULL];
  304.     inodenum_t num; //!< inode number of file
  305. };
  306.  
  307. struct inode_t {
  308.     union {
  309.         offset_t size;  //!< How big is the file in bytes
  310.         link_t entries; //!< Number of entries in this directory
  311.     };
  312.     link_t links; //!< Number of hard links to this file
  313.  
  314.     struct timespec atime;
  315.     struct timespec mtime;
  316.  
  317.     uid_t uid;
  318.     gid_t gid;
  319.     mode_t mode; //!< May be tested for is directory with S_ISDIR().
  320.  
  321.     struct block_t block[INODE_INLINE_COUNT];
  322.     offset_t ptr_block;
  323.  
  324.     unsigned isfree : 1; //!< 1 indicates inode is unused
  325. };
  326.  
  327. struct freespace_t {
  328.     offset_t
  329.       prev; //!< How far from the previous. Not absolute. If 0, none preceed.
  330.     offset_t next; //!< How far to the next. Not absolute. If 0, none follow.
  331.  
  332.     offset_t freeblocks; //!< Number of blocks free
  333. };
  334.  
  335. // Why? Prevents unaligned load/store errors if say struct start_t is
  336. // aligned to a 2-byte boundary and we try to load a 4-byte item after.
  337. #define START_SIZE                                                             \
  338.     ((sizeof(struct start_t) > sizeof(struct inode_t)) ? sizeof(struct start_t)  \
  339.                                                        : sizeof(struct inode_t))
  340. #define INODE_T_ZERO(start) ((struct inode_t *)((void *)start + START_SIZE))
  341. #define MAX_ENTRIES_IN_DIRENTRY(direntry)                                      \
  342.     ((direntry).num * BLOCKSIZE / sizeof(struct direntry_t))
  343.  
  344. /* End of helper types */
  345.  
  346. /* Helper functions */
  347.  
  348. //! Gets num free blocks, starting from the end, and marks it used.
  349. //! If there is not num blocks available, then will allocate as many
  350. //! as possible.
  351. //! Sets only block->sparse = 0, block->num, block->location
  352. //! Returns 0 on success, -1 on failure
  353. int getblock(struct block_t *block, offset_t request, struct start_t *start,
  354.              int *errnoptr) {
  355.     if (start->firstfree == 0) {
  356.         *errnoptr = ENOSPC;
  357.         return -1;
  358.     }
  359.     struct freespace_t *ptr = (void *)start + start->firstfree;
  360.     struct freespace_t *largest = ptr;
  361.  
  362.     //Continues until a big enough one is found, or there's no more in the list
  363.     while (1) {
  364.         if (ptr->freeblocks >= request) {
  365.             largest = ptr;
  366.             break;
  367.         }
  368.         else if (ptr->freeblocks > largest->freeblocks) {
  369.             largest = ptr;
  370.         }
  371.         if (ptr->next == 0) {
  372.             break;
  373.         }
  374.         ptr = (void *)ptr + ptr->next;
  375.     }
  376.  
  377.     ptr = largest; // Whoops, used the wrong one. And ptr is easier to type.
  378.     // Wow, this is a really bad mess of if statements.
  379.     // Needs to set the previous and next correctly,
  380.     // handling what happens at the beginning/end.
  381.     if (ptr->freeblocks == 0) {
  382.         *errnoptr = ENOSPC;
  383.         return -1;
  384.     }
  385.  
  386.     block->sparse = 0;
  387.     block->location = (void *)ptr - (void *)start;
  388.  
  389.     //If we need to create a next ptr
  390.     if (ptr->freeblocks > request) {
  391.         block->num = request;
  392.  
  393.         offset_t nextblockoffset = (BLOCKSIZE * request);
  394.         ((struct freespace_t *)((void *)ptr + nextblockoffset))->next =
  395.           (ptr->next == 0) ? 0 : (ptr->next - nextblockoffset);
  396.         ((struct freespace_t *)((void *)ptr + nextblockoffset))->freeblocks =
  397.             ptr->freeblocks - request;
  398.         ptr->next = nextblockoffset;
  399.         //All other attributes of the next are set in cases following
  400.     }
  401.     // We found a huge block
  402.     else {
  403.         block->num = ptr->freeblocks;
  404.         // Need to create next ptr
  405.     }
  406.  
  407.     //Four cases: prev exists/doesn't and next exists/doesn't
  408.     if (ptr->prev == 0) {
  409.         if (ptr->next == 0) {
  410.             start->firstfree = 0;
  411.             //Nothing following to set
  412.         }
  413.         else {
  414.             start->firstfree = ((void *)ptr - (void *)start + ptr->next);
  415.             ((struct freespace_t *)((void *)ptr - ptr->next))->next = 0;
  416.         }
  417.     }
  418.     else {
  419.         if (ptr->next == 0) {
  420.             ((struct freespace_t *)((void *)ptr - ptr->prev))->next = 0;
  421.             // Nothing following to set
  422.         }
  423.         else {
  424.             ((struct freespace_t *)((void *)ptr + ptr->next))->prev += ptr->prev;
  425.             ((struct freespace_t *)((void *)ptr - ptr->prev))->next += ptr->next;
  426.         }
  427.     }
  428.     return 0;
  429. }
  430.  
  431. //! Gets a free inode, starting from 0, and marks it used.
  432. //! Returns 0 on success, -1 on failure
  433. int getinode(inodenum_t *inode, struct start_t *start, int *errnoptr) {
  434.     // find first free inode
  435.     struct inode_t *inodeptr = INODE_T_ZERO(start);
  436.     for (inodenum_t i = 0; i < start->inodes; i++, inodeptr++) {
  437.         if (inodeptr->isfree == 1) {
  438.             // TODO decide what gets set in getinode
  439.             inodeptr->isfree = 0;
  440.             for (size_t i = 0; i < INODE_INLINE_COUNT; i++) {
  441.                 inodeptr->block[i] = (struct block_t){.location = 0, .num = 0};
  442.             }
  443.             inodeptr->ptr_block = 0;
  444.             inodeptr->size = 0;
  445.             inodeptr->links = 0;
  446.             *inode = i;
  447.             return 0;
  448.         }
  449.     }
  450.     *errnoptr = ENOSPC;
  451.     return -1;
  452. }
  453.  
  454. //! Marks count number of blocks as free from end of block, maximum of
  455. //! block->num. Pass 0 for count to free whole block.
  456. //! Caller should set block->num and related appropriately after.
  457. void freeblock(struct block_t *block, offset_t count, struct start_t *start) {
  458.     if (count == 0 || count > block->num) {
  459.         count = block->num;
  460.     }
  461.  
  462.     offset_t pos = start->firstfree;
  463.     //! Location to be added as freespace_t position
  464.     offset_t freelocation = block->location + (block->num - count) * BLOCKSIZE;
  465.  
  466.     //Case where we have an earlier block than the already free blocks
  467.     if (pos < freelocation) {
  468.         start->firstfree = freelocation;
  469.         ((struct freespace_t *)(start->firstfree))->freeblocks = block->num;
  470.         ((struct freespace_t *)(start->firstfree))->prev = 0;
  471.         ((struct freespace_t *)(start->firstfree))->next = pos;
  472.         if (pos != 0) {
  473.             ((struct freespace_t *)((void *)start + pos))->prev = freelocation;
  474.         }
  475.         return;
  476.     }
  477.  
  478.     offset_t next = ((struct freespace_t *)((void *)start + pos))->next;
  479.     while (!(next == 0 || next > freelocation)) {
  480.         // While there is next pointers and we haven't gone past the correct point
  481.         pos = next;
  482.         next = ((struct freespace_t *)((void *)start + pos))->next;
  483.     }
  484.     // Pos will be the one *BEFORE* freelocation
  485.  
  486.     //Combine blocks if possible
  487.     if (freelocation + count * BLOCKSIZE == next) {
  488.         // Combine with next
  489.         //! newnext = block after next
  490.         offset_t newnext = ((struct freespace_t *)((void *)start + next))->next;
  491.         ((struct freespace_t *)((void *)start + pos))->next = freelocation;
  492.         ((struct freespace_t *)((void *)start + freelocation))->prev = pos;
  493.         ((struct freespace_t *)((void *)start + freelocation))->freeblocks =
  494.           ((struct freespace_t *)((void *)start + next))->freeblocks + count;
  495.         ((struct freespace_t *)((void *)start + freelocation))->next = newnext;
  496.         if (newnext != 0) {
  497.             //Set next->next->prev to freelocation
  498.             ((struct freespace_t *)((void *)start + newnext))->prev = freelocation;
  499.         }
  500.         next = newnext;
  501.     }
  502.     else {
  503.         //Create normal block
  504.         ((struct freespace_t *)((void *)start + pos))->next = freelocation;
  505.         ((struct freespace_t *)((void *)start + freelocation))->prev = pos;
  506.         ((struct freespace_t *)((void *)start + freelocation))->next = next;
  507.         ((struct freespace_t *)((void *)start + freelocation))->freeblocks = count;
  508.         if (next != 0) {
  509.             ((struct freespace_t *)((void *)start + next))->prev = freelocation;
  510.         }
  511.     }
  512.  
  513.     // Is pos and freelocation combinable?
  514.     if (pos + ((struct freespace_t *)((void *)start + pos))->freeblocks
  515.         == freelocation) {
  516.         //They are, merge them.
  517.         ((struct freespace_t *)((void *)start + pos))->freeblocks +=
  518.           ((struct freespace_t *)((void *)start + freelocation))->freeblocks;
  519.         ((struct freespace_t *)((void *)start + pos))->next = next;
  520.         if (next != 0) {
  521.             ((struct freespace_t *)((void *)start + next))->prev = pos;
  522.         }
  523.     }
  524. }
  525.  
  526. //! Gets a new block, and blanks it for use as a ptrblock.
  527. //! Creates one block inside it, of requested size using getblock.
  528. //! Will fail if call to getblock fails.
  529. //! Caller must set all members of ptrblock[0] appropriately, except for
  530. //! those set when calling getblock().
  531. //! Returns 0 on success, -1 on failure
  532. int getptrblock(offset_t *ptrblock, offset_t request, struct start_t *start,
  533.                 int *errnoptr) {
  534.     struct block_t new;
  535.     if (getblock(&new, 1, start, errnoptr) == -1) {
  536.         // Haven't started moving anything, so just fail.
  537.         return -1;
  538.     }
  539.     struct ptrblock_t *newptrblock;
  540.     newptrblock = (struct ptrblock_t *)((void *)start + new.location);
  541.     if (getblock(newptrblock->blocks, 1, start, errnoptr) == -1) {
  542.         // Need to free allocated block
  543.         freeblock(&new, 1, start);
  544.         return -1;
  545.     }
  546.  
  547.     //Blank the remaining ones in the ptrblock
  548.     for (size_t i = 1; i < PTR_PER_PTRBLOCK; i++) {
  549.         newptrblock->blocks[i] = (struct block_t){};
  550.     }
  551.     newptrblock->next = 0;
  552.  
  553.     *ptrblock = new.location;
  554.     return 0;
  555. }
  556.  
  557. //! Marks an inode as free and all its data blocks.
  558. void freeinode(inodenum_t inode, struct start_t *start) {
  559.     // TODO free all data blocks
  560.     (INODE_T_ZERO(start))[inode].isfree = 1;
  561.     //TODO replace with a linked list of free inodes?
  562. }
  563.  
  564. //! Should only be used by function entry_helper.
  565. //! If found, places position in block in *found,
  566. //! if not found place proceding position.
  567. //! Returns -1 if not present, 0 if found, 1 if may be in proceeding
  568. static inline int entry_searchblocks(const char *name,
  569.                                      const struct direntry_t *block,
  570.                                      const link_t entries, link_t *found) {
  571.     if (entries == 0) {
  572.         *found = 0;
  573.         return -1;
  574.     }
  575.     int present = strcmp(block[entries - 1].name, name);
  576.     // Will be < 0 if not in this block.
  577.     if (present < 0) {
  578.         return 1;
  579.     }
  580.     else if (present == 0) {
  581.         //Well, wasn't that easy?
  582.         *found = entries - 1;
  583.         return 0;
  584.     }
  585.  
  586.     //So, we know it's got to be in this block, at least.
  587.     //Binary search!
  588.     link_t start = 0;         //!< Points to start
  589.     link_t end = entries - 1; //!< Points to after end
  590.     link_t mid;
  591.     while (start < end) {
  592.         mid = (start + end) / 2;
  593.         present = strcmp(block[mid].name, name);
  594.  
  595.         if (present < 0) {
  596.             start = mid + 1;
  597.         }
  598.         else if (present > 0) {
  599.             end = mid;
  600.         }
  601.         else /* present == 0 */ {
  602.             *found = mid;
  603.             return 0;
  604.         }
  605.     }
  606.     // Nope, wasn't found! But start points to the proceeding
  607.     *found = start;
  608.     return -1;
  609. }
  610.  
  611. //! A helper function, returns in entrynum the index of the requested
  612. //! entry or the proceeding if not present.
  613. //! entrynum might refer to entries + 1, if all the entries preceeded the name.
  614. //! WARNING: Will not check if inode is even a directory!
  615. //! Returns 0 if found, -1 if not found.
  616. int entry_helper(const char *name, const inodenum_t directory,
  617.                  offset_t *entrynum, const struct start_t *start) {
  618.     struct inode_t *inode = INODE_T_ZERO(start) + directory;
  619.     link_t remaining = inode->entries;
  620.  
  621.     offset_t blocknumber = 0; //!< Which block_t are we in?
  622.     for (offset_t i = 0; i < INODE_INLINE_COUNT; blocknumber = ++i) {
  623.         struct direntry_t *ptr = ((void *)start + inode->block[i].location);
  624.  
  625.         link_t found;
  626.         if (remaining <= inode->block[i].dircount) {
  627.             // If it's not in this block, there's no more blocks to search
  628.             int ret = entry_searchblocks(name, ptr, remaining, &found);
  629.             if (ret == 1) {
  630.                 *entrynum = inode->block[i].dirindex + remaining;
  631.             }
  632.             else {
  633.                 *entrynum = inode->block[i].dirindex + found;
  634.             }
  635.             return ret ? -1 : 0;
  636.         }
  637.         int ret = entry_searchblocks(name, ptr, inode->block[i].dircount, &found);
  638.         if (ret == -1) {
  639.             *entrynum = inode->block[i].dirindex + found;
  640.             return -1;
  641.         }
  642.         if (ret == 0) {
  643.             *entrynum = inode->block[i].dirindex + found;
  644.             return 0;
  645.         }
  646.         remaining -= inode->block[i].dircount;
  647.     }
  648.  
  649.     // Loop through pointer blocks
  650.     struct ptrblock_t *ptrblock = (void *)start + inode->ptr_block;
  651.     while (1) {
  652.         for (offset_t i = 0; i < INODE_INLINE_COUNT; i++, blocknumber++) {
  653.             struct direntry_t *ptr = ((void *)start + ptrblock->blocks[i].location);
  654.             link_t maxentries = MAX_ENTRIES_IN_DIRENTRY(ptrblock->blocks[i]);
  655.             link_t found;
  656.             if (remaining <= maxentries) {
  657.                 // If it's not in this block, there's no more blocks to search
  658.                 int ret = entry_searchblocks(name, ptr, remaining, &found);
  659.                 if (ret == 1) {
  660.                     *entrynum = inode->block[i].dirindex + remaining;
  661.                 }
  662.                 else {
  663.                     *entrynum = inode->block[i].dirindex + found;
  664.                 }
  665.                 return ret ? -1 : 0;
  666.             }
  667.             int ret = entry_searchblocks(name, ptr, maxentries, &found);
  668.             if (ret == -1) {
  669.                 *entrynum = inode->block[i].dirindex + found;
  670.                 return -1;
  671.             }
  672.             if (ret == 0) {
  673.                 *entrynum = inode->block[i].dirindex + found;
  674.                 return 0;
  675.             }
  676.             remaining -= maxentries;
  677.         }
  678.  
  679.         //If we made it this far, there's another pointer block.
  680.         ptrblock = (void *)start + ptrblock->next;
  681.     }
  682. }
  683.  
  684.  
  685. //! Finds the end, and shuffles everything foward by one.
  686. //! Should only be called from function addentry.
  687. //! There is no ptrblock after these ones if ptrblock_off is 0, but
  688. //! *ptrblock_off may be changed if a new ptrblock needed to be created.
  689. //! stationaryentries should be the number of entries that need to remain.
  690. //! blockcount is the number of blocks in array blocks[].
  691. //! *insert should refer to the block to be inserted after stationaryentries.
  692. //! Returns 0 on success, -1 on failure
  693. static int entry_shuffleforward(const offset_t stationaryentries,
  694.                                 struct direntry_t *insert,
  695.                                 struct block_t *blocks,
  696.                                 const offset_t blockcount,
  697.                                 offset_t *ptrblock_off, struct start_t *start,
  698.                                 int *errnoptr) {
  699.     offset_t to = 0; //!< When shuffling, this many blocks. For if some are empty.
  700.     offset_t entries = 0; //!< Number of entries in blocks[] combined
  701.     // Count the number of entries in blocks
  702.     for (offset_t i = 0; i < blockcount; i++) {
  703.         to++; //If one of these is completely empty, it gets filled in later.
  704.         entries += blocks[i].dircount;
  705.         // #ifdef DEBUG
  706.         // assert((blocks[i].num == 0 && blocks[i].sparse == 0));
  707.         // #endif
  708.         if (blocks[i].dircount < MAX_ENTRIES_IN_DIRENTRY(blocks[i])
  709.             || (blocks[i].num == 0)) {
  710.             //There's no more.
  711.             break;
  712.         }
  713.     }
  714.  
  715.     offset_t remaining; //!< Remaining to not shuffle
  716.     //Overflow detection
  717.     remaining =
  718.       (entries < stationaryentries) ? entries : (entries - stationaryentries);
  719.  
  720.     //! If the last block with things in it is full
  721.     int lastisfull =
  722.       (blocks[to - 1].dircount == MAX_ENTRIES_IN_DIRENTRY(blocks[to - 1]));
  723.     // Do we need to recurse?
  724.     if (*ptrblock_off != 0
  725.         || (to == blockcount && lastisfull && blocks[to - 1].dircount != 0)) {
  726.         //Do we need to allocate a new block?
  727.         struct ptrblock_t *next; //!< Next ptrblock
  728.         if (*ptrblock_off == 0) {
  729.             if (getptrblock(ptrblock_off, 1, start, errnoptr) == -1) {
  730.                 return -1;
  731.             }
  732.             //Since allocating succeeded, set new things all correctly
  733.             next = (void *)start + *ptrblock_off;
  734.             next->blocks[0].dirindex =
  735.               blocks[to - 1].dirindex + blocks[to - 1].dircount;
  736.         }
  737.         else {
  738.             next = (void *)start + *ptrblock_off;
  739.         }
  740.  
  741.         // Do we need to insert the last block or *insert?
  742.         // Could only ever fail at allocating a block, before any kind of
  743.         // shuffling occured, so don't need to worry about undoing what we did.
  744.         // and if we just allocated a block, we know we don't need to allocate
  745.         // a new one
  746.         if (remaining != 0) {
  747.             struct direntry_t *toinsert;
  748.             struct direntry_t *lastblock; //!< Last one in array blocks[]
  749.             lastblock = (void *)start + blocks[blockcount - 1].location;
  750.             toinsert = lastblock + blocks[blockcount - 1].dircount - 1;
  751.             if (entry_shuffleforward(remaining, toinsert, next->blocks,
  752.                                      PTR_PER_PTRBLOCK, &(next->next), start, errnoptr)
  753.                 == -1) {
  754.                 return -1;
  755.             }
  756.         }
  757.         else {
  758.             return entry_shuffleforward(remaining, insert, next->blocks,
  759.                                         PTR_PER_PTRBLOCK, &(next->next), start,
  760.                                         errnoptr);
  761.             // Don't need to shuffle this one if there's nothing remaining to shuffle.
  762.             // So, return.
  763.         }
  764.     }
  765.     // Do we need to add another block?
  766.     else if (lastisfull && blocks[to - 1].location == 0) {
  767.         // Add a new one at to
  768.         if (getblock(&(blocks[to - 1]), 1, start, errnoptr) == -1) {
  769.             return -1;
  770.         }
  771.         blocks[to - 1].dircount = 1;
  772.         blocks[to - 1].dirindex = blocks[to - 2].dirindex + blocks[to - 2].dircount;
  773.  
  774.  
  775.         struct direntry_t *to_set = ((void *)start + blocks[to - 1].location);
  776.         *to_set =
  777.           ((struct direntry_t *)((void *)start
  778.                                  + blocks[to - 1]
  779.                                      .location))[blocks[to - 1].dircount - 1];
  780.         //Don't need to copy out the direntry, it will be done later.
  781.     }
  782.     // The last one was not full
  783.     else {
  784.         blocks[to - 1].dircount += 1;
  785.     }
  786.     // Do the shuffle
  787.     // Sounds like the code is dancing
  788.     struct direntry_t *replace = NULL; //!< When shifting out, move it here.
  789.     offset_t i = to;
  790.  
  791.     // If remaining == 0 before something is assigned, then use *insert instead.
  792.     // Decrement remaining after something is assigned.
  793.     while (i) {
  794.         i--; //Runs for i = 0 too
  795.         struct direntry_t *direntries = (void *)start + blocks[i].location;
  796.         link_t j = blocks[i].dircount - 1;
  797.         if (replace != NULL) {
  798.             if (remaining-- == 0) {
  799.                 *replace = *insert;
  800.                 return 0;
  801.             }
  802.             *replace = direntries[j];
  803.         }
  804.         while (j) {
  805.             if (remaining-- == 0) {
  806.                 direntries[j] = *insert;
  807.                 return 0;
  808.             }
  809.             direntries[j] = direntries[j - 1];
  810.             j--;
  811.         }
  812.         replace = direntries;
  813.     }
  814.  
  815.     //The only way we could get here is if we need to replace the very first
  816.     *replace = *insert;
  817.  
  818.     return 0;
  819. }
  820.  
  821. //! Shuffles all entries in a directory back by one, effectively removing
  822. //! the item at index stationaryentries.
  823. //! Should only be called from function dropentry.
  824. //! There is no ptrblock after these ones if ptrblock_off is 0, but
  825. //! *ptrblock_off may be changed if there was no need for it anymore.
  826. //! blockcount is the number of blocks in array blocks[].
  827. //! *shuffleinto should be NULL by default, unless you want to save the item?
  828. //! at index stationaryentries.
  829. static void
  830. entry_shufflebackward(offset_t stationaryentries, struct block_t *blocks,
  831.                       const offset_t blockcount, offset_t *ptrblock_off,
  832.                       struct direntry_t *shuffleinto, struct start_t *start) {
  833.     __label__ doshuffle;
  834.     offset_t i = 0, j;
  835.     struct direntry_t *entries;
  836.     for (; i < blockcount; i++) {
  837.         for (j = 0; j < blocks[i].dircount; j++) {
  838.             if (stationaryentries == 0) {
  839.                 entries = (void *)start + blocks[i].location;
  840.                 goto doshuffle;
  841.             }
  842.             else {
  843.                 stationaryentries--;
  844.             }
  845.         }
  846.     }
  847.  
  848.     // Do the same thing, but shuffling things.
  849.     for (; i < blockcount; i++) {
  850.         entries = (void *)start + blocks[i].location;
  851.         for (j = 0; j < blocks[i].dircount; j++) {
  852.         doshuffle:
  853.             if (shuffleinto != NULL) {
  854.                 *shuffleinto =
  855.                   ((struct direntry_t *)((void *)start + blocks[i].location))[j];
  856.                 shuffleinto = NULL;
  857.             }
  858.             else {
  859.                 *shuffleinto =
  860.                   ((struct direntry_t *)((void *)start + blocks[i].location))[j];
  861.             }
  862.         }
  863.         shuffleinto = &entries[j - 1];
  864.     }
  865.  
  866.     // Do we need to keep going?
  867.     if (*ptrblock_off != 0) {
  868.         struct ptrblock_t *ptrblock = (void *)start + *ptrblock_off;
  869.         entry_shufflebackward(stationaryentries, ptrblock->blocks, PTR_PER_PTRBLOCK,
  870.                               &(ptrblock->next), shuffleinto, start);
  871.     }
  872. #ifdef DEBUG
  873.     //Make sure this succeeded
  874.     assert(stationaryentries == 0);
  875. #endif
  876.     return;
  877. }
  878.  
  879. //! Adds a new entry to a directory, associating a file name with an inode.
  880. //! Will fail if name already exists.
  881. //! WARNING: Will not check if inode is even a directory!
  882. //! Caller must set inode.links appropriately.
  883. //! Returns 0 on success, -1 on failure.
  884. int addentry(const char *name, inodenum_t parent, inodenum_t child,
  885.              struct start_t *start, int *errnoptr) {
  886.     offset_t entrynum; //!< index of where we need to add the entry
  887.     int helper = entry_helper(name, parent, &entrynum, start);
  888.     if (helper == 0) {
  889.         *errnoptr = EEXIST;
  890.         return -1;
  891.     }
  892.     struct direntry_t insert = {.num = child};
  893.     strcpy(insert.name, name);
  894.  
  895.     struct inode_t *parentinode = INODE_T_ZERO(start) + parent;
  896.     if (entry_shuffleforward(entrynum, &insert, parentinode->block,
  897.                              INODE_INLINE_COUNT, &(parentinode->ptr_block), start,
  898.                              errnoptr)
  899.         == -1) {
  900.         return -1;
  901.     }
  902.     parentinode->entries++;
  903.     return 0;
  904. }
  905.  
  906. //! Gets or sets the inode associated with a name in a given directory.
  907. //! *entry is not set on failure.
  908. //! WARNING: Will not check if inode is even a directory!
  909. //! Fails only if name doesn't exist.
  910. //! Returns 0 on success, -1 on failure.
  911. int getsetentry(const char *name, inodenum_t directory, inodenum_t *entry,
  912.                 int set, struct start_t *start, int *errnoptr) {
  913.     __label__ getinodeout;
  914.     offset_t entrynum;
  915.     int helper = entry_helper(name, directory, &entrynum, start);
  916.     if (helper == -1) {
  917.         *errnoptr = ENOENT;
  918.         return -1;
  919.     }
  920.  
  921.     // Go find the block entrynum is in
  922.     struct inode_t *inode = INODE_T_ZERO(start) + directory;
  923.     struct block_t *block = inode->block;
  924.     for (offset_t i = 0; i < INODE_INLINE_COUNT; i++) {
  925.         if (inode->block[i].dircount + inode->block[i].dirindex > entrynum) {
  926.             //Found it
  927.             block = inode->block + i;
  928.             goto getinodeout;
  929.         }
  930.     }
  931.     struct ptrblock_t *ptrblock = (void *)start + inode->ptr_block;
  932.     while (1) {
  933.         for (offset_t i = 0; i < INODE_INLINE_COUNT; i++) {
  934.             if (inode->block[i].dircount + inode->block[i].dirindex > entrynum) {
  935.                 //Found it
  936.                 block = ptrblock->blocks + i;
  937.                 goto getinodeout;
  938.             }
  939.         }
  940.         //If we made it this far, there's another pointer block.
  941. #ifdef DEBUG
  942.         assert(ptrblock->next != 0);
  943. #endif
  944.         ptrblock = (void *)start + ptrblock->next;
  945.     }
  946.  
  947. getinodeout:;
  948.     struct direntry_t *direntry0 = ((void *)start + block->location);
  949.     if (set) {
  950.         (direntry0 + (entrynum - block->dirindex))->num = *entry;
  951.     }
  952.     else {
  953.         *entry = (direntry0 + (entrynum - block->dirindex))->num;
  954.     }
  955.     return 0;
  956. }
  957.  
  958. //! Gets the inode associated with a name in a given directory.
  959. //! *entry is not set on failure.
  960. //! WARNING: Will not check if inode is even a directory!
  961. //! Fails only if name doesn't exist.
  962. //! Returns 0 on success, -1 on failure.
  963. int getentry(const char *name, inodenum_t directory, inodenum_t *entry,
  964.              struct start_t *start, int *errnoptr) {
  965.     return getsetentry(name, directory, entry, 0, start, errnoptr);
  966. }
  967. //! Sets the inode associated with a name in a given directory.
  968. //! *entry is not set on failure.
  969. //! WARNING: Will not check if inode is even a directory!
  970. //! Fails only if name doesn't exist.
  971. //! Returns 0 on success, -1 on failure.
  972. int setentry(const char *name, inodenum_t directory, inodenum_t *entry,
  973.              struct start_t *start, int *errnoptr) {
  974.     return getsetentry(name, directory, entry, 1, start, errnoptr);
  975. }
  976.  
  977. //! Removes the given name from a directory, if it exists.
  978. //! WARNING: Will not check if name is '.' or '..' or if the inode is even a
  979. //! directory!
  980. //! Caller must set inode.links appropriately.
  981. //! Fails only if name doesn't exist.
  982. //! Returns 0 on success, -1 on failure.
  983. int dropentry(const char *name, inodenum_t directory, struct start_t *start,
  984.               int *errnoptr) {
  985.     offset_t entrynum; //!< index of where we need to remove the entry
  986.     int helper = entry_helper(name, directory, &entrynum, start);
  987.     if (helper == -1) {
  988.         *errnoptr = ENOENT;
  989.         return -1;
  990.     }
  991.  
  992.     struct inode_t *parentinode = INODE_T_ZERO(start) + directory;
  993.     entry_shufflebackward(entrynum, parentinode->block, INODE_INLINE_COUNT,
  994.                           &(parentinode->ptr_block), NULL, start);
  995.     parentinode->entries--;
  996.     return 0;
  997. }
  998.  
  999. //! Attempts to resolve the given path, and places the corrosponding
  1000. //! inode number into *inode_return.
  1001. //! Returns 0 on success, -1 on failure.
  1002. int resolvepath(const char *path, inodenum_t *inode_return,
  1003.                 struct start_t *start, int *errnoptr) {
  1004.     inodenum_t inode;
  1005. #ifdef DEBUG
  1006.     assert((path[0] == '/'));
  1007. #else
  1008.     if (path[0] != '/') {
  1009.         //I don't know how to resolve this, fuse is meant to call with all absolute
  1010.         *errnoptr = ENOENT;
  1011.         return -1;
  1012.     }
  1013. #endif
  1014.  
  1015.     inode = 0;
  1016.     char name[CHAR_LIMIT_INCLUDE_NULL];
  1017.  
  1018.     size_t first = 1, len = 0;
  1019.  
  1020.     while (path[first] != '\0') {
  1021.         //Validate the path is in fact a directory
  1022.         if (!S_ISDIR(INODE_T_ZERO(start)[inode].mode)) {
  1023.             *errnoptr = ENOTDIR;
  1024.             return -1;
  1025.         }
  1026.  
  1027.         // Resolve all duplicate slashes
  1028.         if (path[first] == '/') {
  1029.             first++;
  1030.             continue;
  1031.         }
  1032.         const char *ptr = path + first;
  1033.         len = 0;
  1034.         while (ptr[len] != '/' && ptr[len] != '\0') {
  1035.             len++;
  1036.         }
  1037.         if (len >= CHAR_LIMIT_INCLUDE_NULL) {
  1038.             *errnoptr = ENOENT;
  1039.             return -1;
  1040.         }
  1041.         strncpy(name, ptr, len);
  1042.         name[len] = '\0';
  1043.         inodenum_t next;
  1044.         if (getentry(name, inode, &next, start, errnoptr) == -1) {
  1045.             return -1;
  1046.         }
  1047.         first += len;
  1048.         inode = next;
  1049.     }
  1050.  
  1051.     *inode_return = inode;
  1052.     return 0;
  1053. }
  1054.  
  1055. //! Pops the last directory off the top of the path in-place.
  1056. //! /etc/fstab would become /etc which would become /.
  1057. //! len is modified to have the new shorter length as well.
  1058. //! Len should not inlcude the null character and not be 0,
  1059. //! and path should not be null.
  1060. void popname(char *path, size_t *len) {
  1061. #ifdef DEBUG
  1062.     assert((path[0] == '/'));
  1063. #endif
  1064.     if (*len == 1) {
  1065.         //Can't shorten anymore
  1066.         return;
  1067.     }
  1068.     char *ptr = path + *len - 1;
  1069.     //Remove all trailing slashes
  1070.     while (*ptr == '/') {
  1071.         if (--(*len) == 1) {
  1072.             *ptr = '\0';
  1073.             return;
  1074.         }
  1075.         ptr--;
  1076.     }
  1077.     //Remove all charcters until the next slash
  1078.     while (*ptr != '/') {
  1079.         if (--(*len) == 1) {
  1080.             *ptr = '\0';
  1081.             return;
  1082.         }
  1083.         ptr--;
  1084.     }
  1085.     //Remove all trailing slashes
  1086.     while (*ptr == '/') {
  1087.         if (--(*len) == 1) {
  1088.             *ptr = '\0';
  1089.             return;
  1090.         }
  1091.         ptr--;
  1092.     }
  1093.     return;
  1094. }
  1095.  
  1096. //! Shortens the path, removing all . and .. as well as duplicate slashes.
  1097. //! Returned memory must be freed. Will do weird things if your path isn't
  1098. //! absolute. len may be NULL.
  1099. //! Returns NULL on error.
  1100. char *getshortname(const char *path, size_t *len) {
  1101.     char *newname;
  1102.     size_t oldlen = strlen(path), newlen = 0;
  1103.     newname = malloc(oldlen);
  1104.     if (newname == NULL) {
  1105.         return newname;
  1106.     }
  1107.  
  1108.     size_t i = 0;
  1109.     // When this loop reaches the beginning, we need to copy one slash then
  1110.     // a name.
  1111.     do {
  1112.         //Copy one slash and skip the rest
  1113.         if (newlen != 1) {
  1114.             // If we arrived at this position from a previous loop that ended with
  1115.             // newname being "/"
  1116.             newname[newlen++] = '/';
  1117.         }
  1118.         do {
  1119.             i++;
  1120.             if (path[i] == '\0') {
  1121.                 break;
  1122.             }
  1123.         } while (path[i] == '/');
  1124.         //Copy the next part of the name out
  1125.         do {
  1126.             newname[newlen++] = path[i++];
  1127.         } while (path[i] != '\0');
  1128.         //! Did we just copy "." or ".."?
  1129.         if (path[newlen - 1] == '.') {
  1130.             if (path[newlen - 2] == '/') {
  1131.                 //newlen is at least 2, don't need to check here.
  1132.                 popname(newname, &newlen);
  1133.             }
  1134.             else if (newlen >= 3 && path[newlen - 2] == '.'
  1135.                      && path[newlen - 3] == '/') {
  1136.                 popname(newname, &newlen);
  1137.                 popname(newname, &newlen);
  1138.             }
  1139.         }
  1140.     } while (path[i] != '\0');
  1141.  
  1142.     newname[newlen] = '\0';
  1143.     if (len != NULL) {
  1144.         *len = newlen;
  1145.     }
  1146.     return newname;
  1147. }
  1148.  
  1149. //! Creates a directory. Links already set to 1. Inode returned in inodenum if
  1150. //! call succeeded. Uses the lowest inode it can.
  1151. //! WARNING: Does not check if parent is actually a directory!
  1152. //! uid/gid/mode must be set by caller.
  1153. //! Returns 0 on success, -1 on failure.
  1154. int createdir(inodenum_t *child_return, struct start_t *start,
  1155.               inodenum_t parent, int *errnoptr) {
  1156.     if (getinode(child_return, start, errnoptr) == -1) {
  1157.         return -1;
  1158.     }
  1159.  
  1160.     struct block_t newblock;
  1161.     if (getblock(&newblock, 1, start, errnoptr) == -1) {
  1162.         freeinode(*child_return, start);
  1163.         return -1;
  1164.     }
  1165.     struct inode_t *inode = INODE_T_ZERO(start) + *child_return;
  1166.     inode->entries = 0;
  1167.     inode->block[0] = newblock;
  1168.     inode->block[0].dircount = 0;
  1169.     inode->block[0].dirindex = 0;
  1170.  
  1171.     if (addentry(".", *child_return, *child_return, start, errnoptr) == -1
  1172.         || addentry("..", parent, *child_return, start, errnoptr) == -1) {
  1173.         freeinode(*child_return, start);
  1174.         return -1;
  1175.     }
  1176.  
  1177.     inode->links = 1;
  1178.     clock_gettime(CLOCK_REALTIME, &inode->atime);
  1179.     inode->mtime = inode->atime;
  1180.     return 0;
  1181. }
  1182.  
  1183.  
  1184. //! Creates the filesystem, or does nothing if already created.
  1185. //! Returns 0 on success, -1 on failure
  1186. int createfs(void *fsptr, size_t fssize, int *errnoptr) {
  1187.     struct start_t *start = (struct start_t *)fsptr;
  1188.     if (start->magic == MAGIC_NUM) {
  1189.         return 0;
  1190.     }
  1191.  
  1192.     start->inodes = fssize / SIZE_PER_INODE;
  1193.     if (start->inodes < MINIMUM_INODE_COUNT) {
  1194.         // Ensure we have inodes, fills the whole first block.
  1195.         start->inodes = MINIMUM_INODE_COUNT;
  1196.         start->firstfree = BLOCKSIZE;
  1197.     }
  1198.     else {
  1199.         // How many blocks are used by inodes, cieling division
  1200.         offset_t blocks =
  1201.           (START_SIZE + sizeof(struct inode_t) * start->inodes + BLOCKSIZE - 1)
  1202.           / BLOCKSIZE;
  1203.         start->firstfree = blocks * BLOCKSIZE;
  1204.         //Set inode count to as many as can fill that space.
  1205.         start->inodes =
  1206.           ((blocks * BLOCKSIZE) - START_SIZE) / sizeof(struct inode_t);
  1207.     }
  1208.  
  1209.     // Mark all inodes as free
  1210.     struct inode_t *inode = INODE_T_ZERO(start);
  1211.     for (inodenum_t i = 0; i < start->inodes; i++, inode++) {
  1212.         inode->isfree = 1;
  1213.     }
  1214.  
  1215.     // Really silly way to ensure there's an integer number of blocks
  1216.     // likely optimized to shift right and shift left
  1217.     start->size = fssize / BLOCKSIZE * BLOCKSIZE;
  1218.  
  1219.     // Add linked list of free space
  1220.     struct freespace_t *firstfree =
  1221.       (struct freespace_t *)((void *)start + start->firstfree);
  1222.     firstfree->next = 0;
  1223.     firstfree->prev = 0;
  1224.     firstfree->freeblocks = (start->size - start->firstfree) / BLOCKSIZE;
  1225.  
  1226.     // Create root directory. Guarenteed to use inode 0, since there's no
  1227.     // inodes used yet.
  1228.     inodenum_t created;
  1229.     int err = createdir(&created, start, 0, errnoptr);
  1230.     if (err == -1) {
  1231.         return err;
  1232.     }
  1233.     struct inode_t *root = INODE_T_ZERO(start);
  1234.     root->gid = getgid();
  1235.     root->uid = getuid();
  1236.     root->mode = S_IFDIR | 0755;
  1237.     clock_gettime(CLOCK_REALTIME, &(root->atime));
  1238.     root->mtime = root->atime;
  1239.     start->magic = MAGIC_NUM;
  1240.     return 0;
  1241. }
  1242.  
  1243. //! Gets parent and child from path. Both permitted to not exist.
  1244. //! parent is allocated large enough to hold parent + "/" + child.
  1245. //! Caller must free both.
  1246. //! Ensures child is less than CHAR_LIMIT_INCLUDE_NULL.
  1247. //! Returns 0 on success, -1 on failure.
  1248. int getparentchild(const char *path, char **parentpath_return,
  1249.                    char **childname_return, struct start_t *start,
  1250.                    int *errnoptr) {
  1251.     size_t parentlen, childlen;
  1252.     if (path[0] != '/') {
  1253.         *errnoptr = ENOENT;
  1254.         return -1;
  1255.     }
  1256.     char *parentpath = getshortname(path, &parentlen), *childname;
  1257.     if (parentpath == NULL) {
  1258.         *errnoptr = ENOMEM;
  1259.         return -1;
  1260.     }
  1261.  
  1262.     //Ensure child length is less than CHAR_LIMIT_INCLUDE_NULL
  1263.     for (childlen = 1; childlen < parentlen; childlen++) {
  1264.         // Find the last trailing slash from the end
  1265.         if (parentpath[parentlen - childlen - 1] == '/') {
  1266.             break;
  1267.         }
  1268.     }
  1269.     if (childlen > CHAR_LIMIT_INCLUDE_NULL - 1) {
  1270.         free(parentpath);
  1271.         *errnoptr = ENAMETOOLONG;
  1272.         return -1;
  1273.     }
  1274.  
  1275.     // Copy child and shorten parent
  1276.     childname = strndup(parentpath + parentlen - childlen, childlen);
  1277.     if (childname == NULL) {
  1278.         free(parentpath);
  1279.         *errnoptr = ENOMEM;
  1280.         return -1;
  1281.     }
  1282. #ifdef DEBUG
  1283.     size_t fullchildlen = parentlen;
  1284.     popname(parentpath, &parentlen);
  1285.     assert(fullchildlen == (parentlen == 1 ? 1 : parentlen + 1) + childlen);
  1286. #else
  1287.     popname(parentpath, &parentlen);
  1288. #endif
  1289.  
  1290.     *parentpath_return = parentpath;
  1291.     *childname_return = childname;
  1292.     return 0;
  1293. }
  1294.  
  1295.  
  1296. //! Creates an entry in a directory with specified mode. Caller must finish
  1297. //! initialization as correct for each type of item (dir, link, file, etc).
  1298. //! Caller must set uid, gid, links.
  1299. //! Path must be rooted.
  1300. //! Returns 0 on success, -1 on failure
  1301. int mkentry(const char *path, mode_t mode, inodenum_t *child_return, uid_t uid,
  1302.             gid_t gid, struct start_t *start, int *errnoptr) {
  1303.     __label__ failexitandfree;
  1304.     char *parentpath, *childname;
  1305.     inodenum_t parentnum, childnum;
  1306.     if (path[0] != '/') {
  1307.         *errnoptr = ENOENT;
  1308.         return -1;
  1309.     }
  1310.  
  1311.     if (getparentchild(path, &parentpath, &childname, start, errnoptr) == -1) {
  1312.         return -1;
  1313.     }
  1314.  
  1315.     //Ensure parent exists and is directory
  1316.     if (resolvepath(parentpath, &parentnum, start, errnoptr) == -1) {
  1317.         goto failexitandfree;
  1318.     }
  1319.     if (!S_ISDIR(INODE_T_ZERO(start)[parentnum].mode)) {
  1320.         *errnoptr = ENOTDIR;
  1321.         goto failexitandfree;
  1322.     }
  1323.     //Ensure child doesn't exist
  1324.     if (getentry(childname, parentnum, &childnum, start, errnoptr) == 0) {
  1325.         *errnoptr = EEXIST;
  1326.         goto failexitandfree;
  1327.     }
  1328.  
  1329.     //Get an inode for the kid
  1330.     if (getinode(&childnum, start, errnoptr) == -1) {
  1331.         goto failexitandfree;
  1332.     }
  1333.  
  1334.     struct inode_t *inode = INODE_T_ZERO(start) + childnum;
  1335.     inode->mode = mode;
  1336.     inode->uid = uid;
  1337.     inode->gid = gid;
  1338.     clock_gettime(CLOCK_REALTIME, &inode->atime);
  1339.     inode->mtime = inode->atime;
  1340.     inode->links = 1;
  1341.     if (S_ISDIR(mode)) {
  1342.         inode->entries = 0;
  1343.         struct block_t newblock;
  1344.         if (getblock(&newblock, 1, start, errnoptr) == -1) {
  1345.             freeinode(childnum, start);
  1346.             goto failexitandfree;
  1347.         }
  1348.         inode->block[0] = newblock;
  1349.         inode->block[0].dircount = 0;
  1350.         inode->block[0].dirindex = 0;
  1351.         if (addentry(".", childnum, childnum, start, errnoptr) == -1
  1352.             || addentry("..", childnum, parentnum, start, errnoptr) == -1) {
  1353.             freeinode(childnum, start);
  1354.             goto failexitandfree;
  1355.         }
  1356.     }
  1357.     else if (S_ISREG(mode)) {
  1358.         inode->size = 0;
  1359.     }
  1360.     else {
  1361. #ifdef DEBUG
  1362.         assert(0);
  1363. #endif
  1364.         freeinode(childnum, start);
  1365.         goto failexitandfree;
  1366.     }
  1367.  
  1368.     //Add it to parent
  1369.     if (addentry(childname, parentnum, childnum, start, errnoptr) == -1) {
  1370.         //Free inode if it fails.
  1371.         freeinode(childnum, start);
  1372.         goto failexitandfree;
  1373.     }
  1374.  
  1375.     free(parentpath);
  1376.     free(childname);
  1377.     return 0;
  1378.  
  1379. failexitandfree:
  1380.     free(parentpath);
  1381.     free(childname);
  1382.     return -1;
  1383. }
  1384.  
  1385. /* End of helper functions */
  1386.  
  1387. /* Implements an emulation of the stat system call on the filesystem
  1388.    of size fssize pointed to by fsptr.
  1389.  
  1390.    If path can be followed and describes a file or directory
  1391.    that exists and is accessable, the access information is
  1392.    put into stbuf.
  1393.  
  1394.    On success, 0 is returned. On failure, -1 is returned and
  1395.    the appropriate error code is put into *errnoptr.
  1396.  
  1397.    man 2 stat documents all possible error codes and gives more detail
  1398.    on what fields of stbuf need to be filled in. Essentially, only the
  1399.    following fields need to be supported:
  1400.  
  1401.    st_uid      the value passed in argument
  1402.    st_gid      the value passed in argument
  1403.    st_mode     (as fixed values S_IFDIR | 0755 for directories,
  1404.                                 S_IFREG | 0755 for files)
  1405.    st_nlink    (as many as there are subdirectories (not files) for directories
  1406.                 (including . and ..),
  1407.                 1 for files)
  1408.    st_size     (supported only for files, where it is the real file size)
  1409.    st_atim
  1410.    st_mtim
  1411.  
  1412. */
  1413. int __myfs_getattr_implem(void *fsptr, size_t fssize, int *errnoptr, uid_t uid,
  1414.                           gid_t gid, const char *path, struct stat *stbuf) {
  1415.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1416.         return -1;
  1417.     }
  1418.     struct start_t *start = fsptr;
  1419.  
  1420.     inodenum_t inode;
  1421.     if (resolvepath(path, &inode, start, errnoptr) == -1) {
  1422.         return -1;
  1423.     }
  1424.  
  1425.     struct inode_t *inodeptr = INODE_T_ZERO(start) + inode;
  1426.  
  1427.     stbuf->st_uid = inodeptr->uid;
  1428.     stbuf->st_gid = inodeptr->gid;
  1429.     stbuf->st_mode = inodeptr->mode;
  1430.     stbuf->st_nlink = inodeptr->links;
  1431.     stbuf->st_size = inodeptr->size;
  1432.     stbuf->st_atim = inodeptr->atime;
  1433.     stbuf->st_mtim = inodeptr->mtime;
  1434.     return 0;
  1435. }
  1436.  
  1437. /* Implements an emulation of the readdir system call on the filesystem
  1438.    of size fssize pointed to by fsptr.
  1439.  
  1440.    If path can be followed and describes a directory that exists and
  1441.    is accessable, the names of the subdirectories and files
  1442.    contained in that directory are output into *namesptr. The . and ..
  1443.    directories must not be included in that listing.
  1444.  
  1445.    If it needs to output file and subdirectory names, the function
  1446.    starts by allocating (with calloc) an array of pointers to
  1447.    characters of the right size (n entries for n names). Sets
  1448.    *namesptr to that pointer. It then goes over all entries
  1449.    in that array and allocates, for each of them an array of
  1450.    characters of the right size (to hold the i-th name, together
  1451.    with the appropriate '\0' terminator). It puts the pointer
  1452.    into that i-th array entry and fills the allocated array
  1453.    of characters with the appropriate name. The calling function
  1454.    will call free on each of the entries of *namesptr and
  1455.    on *namesptr.
  1456.  
  1457.    The function returns the number of names that have been
  1458.    put into namesptr.
  1459.  
  1460.    If no name needs to be reported because the directory does
  1461.    not contain any file or subdirectory besides . and .., 0 is
  1462.    returned and no allocation takes place.
  1463.  
  1464.    On failure, -1 is returned and the *errnoptr is set to
  1465.    the appropriate error code.
  1466.  
  1467.    The error codes are documented in man 2 readdir.
  1468.  
  1469.    In the case memory allocation with malloc/calloc fails, failure is
  1470.    indicated by returning -1 and setting *errnoptr to EINVAL.
  1471.  
  1472. */
  1473. int __myfs_readdir_implem(void *fsptr, size_t fssize, void *buf,
  1474.                           fuse_fill_dir_t filler, int *errnoptr,
  1475.                           const char *path) {
  1476.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1477.         return -1;
  1478.     }
  1479.     inodenum_t inodenum;
  1480.     struct start_t *start = fsptr;
  1481.     // Verify path exists
  1482.     if (resolvepath(path, &inodenum, start, errnoptr) == -1) {
  1483.         return -1;
  1484.     }
  1485.     struct inode_t *inode = INODE_T_ZERO(start) + inodenum;
  1486. #ifdef DEBUG
  1487.     assert(inode->entries >= 2); // Should always contain . and ..
  1488. #endif
  1489.  
  1490.     //Loop through directory
  1491.     link_t entrynum = 0; //!< The entry we are currently interested in
  1492.     struct direntry_t *entries;
  1493.     //For each block
  1494.     for (size_t blocknum = 0; blocknum < INODE_INLINE_COUNT; blocknum++) {
  1495.         struct block_t *block = &(inode->block[blocknum]); //!< The current block
  1496.         entries = (void *)start + block->location;         //!< The array of entries
  1497.         //For each entry in block
  1498.         for (size_t i = 0; i < block->dircount; i++) {
  1499.             if (filler(buf, entries[i].name, NULL, 0) != 0) {
  1500.                 *errnoptr = ENOMEM;
  1501.                 return -1;
  1502.             }
  1503.             if (++entrynum == inode->entries) {
  1504.                 //Have copied all, return
  1505.                 return 0;
  1506.             }
  1507.         }
  1508.     }
  1509.  
  1510.     //There must be pointer blocks if we made it this far
  1511. #ifdef DEBUG
  1512.     assert(inode->ptr_block != 0);
  1513. #endif
  1514.     struct ptrblock_t *ptrblock = (void *)start + inode->ptr_block;
  1515.     while (1) {
  1516.         //For each block
  1517.         for (size_t blocknum = 0; blocknum < PTR_PER_PTRBLOCK; blocknum++) {
  1518.             struct block_t *block =
  1519.               &(ptrblock->blocks[blocknum]);           //!< The current block
  1520.             entries = (void *)start + block->location; //!< The array of entries
  1521.             //For each entry in block
  1522.             for (size_t i = 0; i < block->dircount; i++) {
  1523.                 if (filler(buf, entries[i].name, NULL, 0) != 0) {
  1524.                     *errnoptr = ENOMEM;
  1525.                     return -1;
  1526.                 }
  1527.                 if (++entrynum == inode->entries) {
  1528.                     //Have copied all, return
  1529.                     return 0;
  1530.                 }
  1531.             }
  1532.         }
  1533.  
  1534.         //There's still more pointer blocks
  1535.         ptrblock = (void *)start + ptrblock->next;
  1536.     }
  1537. }
  1538.  
  1539. /* Implements an emulation of the mknod system call for regular files
  1540.    on the filesystem of size fssize pointed to by fsptr.
  1541.  
  1542.    This function is called only for the creation of regular files.
  1543.  
  1544.    If a file gets created, it is of size zero and has default
  1545.    ownership and mode bits.
  1546.  
  1547.    The call creates the file indicated by path.
  1548.  
  1549.    On success, 0 is returned.
  1550.  
  1551.    On failure, -1 is returned and *errnoptr is set appropriately.
  1552.  
  1553.    The error codes are documented in man 2 mknod.
  1554.  
  1555. */
  1556. int __myfs_mknod_implem(void *fsptr, size_t fssize, int *errnoptr,
  1557.                         const char *path) {
  1558.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1559.         return -1;
  1560.     }
  1561.  
  1562.     inodenum_t child;
  1563.     return mkentry(path, S_IFREG | 0755, &child, getuid(), getgid(), fsptr,
  1564.                    errnoptr);
  1565. }
  1566.  
  1567. /* Implements an emulation of the unlink system call for regular files
  1568.    on the filesystem of size fssize pointed to by fsptr.
  1569.  
  1570.    This function is called only for the deletion of regular files.
  1571.  
  1572.    On success, 0 is returned.
  1573.  
  1574.    On failure, -1 is returned and *errnoptr is set appropriately.
  1575.  
  1576.    The error codes are documented in man 2 unlink.
  1577.  
  1578. */
  1579. int __myfs_unlink_implem(void *fsptr, size_t fssize, int *errnoptr,
  1580.                          const char *path) {
  1581.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1582.         return -1;
  1583.     }
  1584.     if (path[0] != '/') {
  1585.         *errnoptr = ENOENT;
  1586.         return -1;
  1587.     }
  1588.     struct start_t *start = fsptr;
  1589.     inodenum_t parentnum, childnum;
  1590.     struct inode_t *childinode;
  1591.  
  1592.     //Ensure path exists and isn't a directory
  1593.     if (resolvepath(path, &childnum, start, errnoptr) == -1) {
  1594.         return -1;
  1595.     }
  1596.     childinode = INODE_T_ZERO(start) + childnum;
  1597.     if (S_ISDIR(childinode->mode)) {
  1598.         *errnoptr = EISDIR;
  1599.         return -1;
  1600.     }
  1601.  
  1602.     //Get parent directory
  1603.     char *parentpath, *childname;
  1604.     if (getparentchild(path, &parentpath, &childname, start, errnoptr) == -1) {
  1605.         *errnoptr = ENOMEM;
  1606.         return -1;
  1607.     }
  1608.  
  1609.     // Parent directories must exist, since we successfully got the child
  1610.     // Drop the entry from the parent.
  1611. #ifdef DEBUG
  1612.     assert(resolvepath(parentpath, &parentnum, start, errnoptr) == 0
  1613.            && dropentry(childname, parentnum, start, errnoptr) == 0);
  1614. #else
  1615.     resolvepath(parentpath, &parentnum, start, errnoptr);
  1616.     dropentry(childname, parentnum, start, errnoptr);
  1617. #endif
  1618.  
  1619.     childinode->links--;
  1620.     if (childinode->links == 0) {
  1621.         freeinode(childnum, start);
  1622.     }
  1623.  
  1624.     free(parentpath);
  1625.     free(childname);
  1626.     return 0;
  1627. }
  1628.  
  1629. /* Implements an emulation of the rmdir system call on the filesystem
  1630.    of size fssize pointed to by fsptr.
  1631.  
  1632.    The call deletes the directory indicated by path.
  1633.  
  1634.    On success, 0 is returned.
  1635.  
  1636.    On failure, -1 is returned and *errnoptr is set appropriately.
  1637.  
  1638.    The function call must fail when the directory indicated by path is
  1639.    not empty (if there are files or subdirectories other than . and ..).
  1640.  
  1641.    The error codes are documented in man 2 rmdir.
  1642.  
  1643. */
  1644. int __myfs_rmdir_implem(void *fsptr, size_t fssize, int *errnoptr,
  1645.                         const char *path) {
  1646.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1647.         return -1;
  1648.     }
  1649.     if (path[0] != '/') {
  1650.         *errnoptr = ENOENT;
  1651.         return -1;
  1652.     }
  1653.     struct start_t *start = fsptr;
  1654.     inodenum_t parentnum, childnum;
  1655.     struct inode_t *childinode;
  1656.  
  1657.     //Ensure path exists and is a directory
  1658.     if (resolvepath(path, &childnum, start, errnoptr) == -1) {
  1659.         return -1;
  1660.     }
  1661.     childinode = INODE_T_ZERO(start) + childnum;
  1662.     if (!S_ISDIR(childinode->mode)) {
  1663.         *errnoptr = ENOTDIR;
  1664.         return -1;
  1665.     }
  1666.  
  1667.     // Ensure isn't the root, you can't remove that.
  1668.     if (childnum == 0) {
  1669.         *errnoptr = EBUSY; // Is there maybe a more appropriate one?
  1670.         return -1;
  1671.     }
  1672.  
  1673.     // Ensure the directory is empty
  1674. #ifdef DEBUG
  1675.     assert(childinode->entries >= 2);
  1676. #endif
  1677.     if (childinode->entries != 2) {
  1678.         *errnoptr = ENOTEMPTY;
  1679.         return -1;
  1680.     }
  1681.  
  1682.     //Get parent directory
  1683.     char *parentpath, *childname;
  1684.     if (getparentchild(path, &parentpath, &childname, start, errnoptr) == -1) {
  1685.         *errnoptr = ENOMEM;
  1686.         return -1;
  1687.     }
  1688.  
  1689.     // Parent directories must exist, since we successfully got the child
  1690.     // and the child isn't the root
  1691.     // Drop the entry from the parent.
  1692. #ifdef DEBUG
  1693.     assert(resolvepath(parentpath, &parentnum, start, errnoptr) == 0
  1694.            && dropentry(childname, parentnum, start, errnoptr) == 0);
  1695. #else
  1696.     resolvepath(parentpath, &parentnum, start, errnoptr);
  1697.     dropentry(childname, parentnum, start, errnoptr);
  1698. #endif
  1699.  
  1700.     childinode->links--;
  1701.     if (childinode->links == 0) {
  1702.         freeinode(childnum, start);
  1703.     }
  1704.  
  1705.     free(parentpath);
  1706.     free(childname);
  1707.     return 0;
  1708.     return -1;
  1709. }
  1710.  
  1711. /* Implements an emulation of the mkdir system call on the filesystem
  1712.    of size fssize pointed to by fsptr.
  1713.  
  1714.    The call creates the directory indicated by path.
  1715.  
  1716.    On success, 0 is returned.
  1717.  
  1718.    On failure, -1 is returned and *errnoptr is set appropriately.
  1719.  
  1720.    The error codes are documented in man 2 mkdir.
  1721.  
  1722. */
  1723. int __myfs_mkdir_implem(void *fsptr, size_t fssize, int *errnoptr,
  1724.                         const char *path) {
  1725.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1726.         return -1;
  1727.     }
  1728.  
  1729.     inodenum_t child;
  1730.     return mkentry(path, S_IFDIR | 0755, &child, getuid(), getgid(), fsptr,
  1731.                    errnoptr);
  1732. }
  1733.  
  1734. /* Implements an emulation of the rename system call on the filesystem
  1735.    of size fssize pointed to by fsptr.
  1736.  
  1737.    The call moves the file or directory indicated by from to to.
  1738.  
  1739.    On success, 0 is returned.
  1740.  
  1741.    On failure, -1 is returned and *errnoptr is set appropriately.
  1742.  
  1743.    Caution: the function does more than what is hinted to by its name.
  1744.    In cases the from and to paths differ, the file is moved out of
  1745.    the from path and added to the to path.
  1746.  
  1747.    The error codes are documented in man 2 rename.
  1748.  
  1749. */
  1750. int __myfs_rename_implem(void *fsptr, size_t fssize, int *errnoptr,
  1751.                          const char *from, const char *to) {
  1752.     __label__ freenamesreturn;
  1753.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1754.         return -1;
  1755.     }
  1756.     inodenum_t fromnum, tonum;
  1757.     struct inode_t *frominode, *toinode;
  1758.     int toexists, istodir, isfromdir;
  1759.     struct start_t *start = fsptr;
  1760.  
  1761.     //Ensure paths make sense
  1762.     if (to[0] != '/' || from[0] != '/') {
  1763.         *errnoptr = ENOENT;
  1764.         return -1;
  1765.     }
  1766.  
  1767.     //Get inodes
  1768.     if (resolvepath(from, &fromnum, start, errnoptr) == -1) {
  1769.         return -1;
  1770.     }
  1771.     toexists = (resolvepath(from, &tonum, start, errnoptr) == -1) ? 0 : 1;
  1772.  
  1773.     // Hard links referring to the same file
  1774.     if (toexists && fromnum == tonum) {
  1775.         return 0;
  1776.     }
  1777.     //Trying to create something that's a parent of itself
  1778.     // Other EINVAL is handled by to never being empty if it is a directory
  1779.     // would be ENOTEMPTY.
  1780.     if (fromnum == 0) /* from == '/' */ {
  1781.         *errnoptr = EINVAL;
  1782.         return -1;
  1783.     }
  1784.     //Can't replace root, it's not empty.
  1785.     if (toexists && tonum == 0) {
  1786.         *errnoptr = ENOTEMPTY;
  1787.         return -1;
  1788.     }
  1789.  
  1790.     // Get children and parents
  1791.     char *fromparentpath, *fromchildname, *toparentpath, *tochildname;
  1792.     if (getparentchild(from, &fromparentpath, &fromchildname, start, errnoptr)
  1793.         == -1) {
  1794.         *errnoptr = ENOMEM;
  1795.         return -1;
  1796.     }
  1797.     if (getparentchild(from, &toparentpath, &tochildname, start, errnoptr)
  1798.         == -1) {
  1799.         free(fromparentpath);
  1800.         free(fromchildname);
  1801.         *errnoptr = ENOMEM;
  1802.         return -1;
  1803.     }
  1804.     int returnval = 0;
  1805.  
  1806.     // to's parent must exist and be a directory.
  1807.     inodenum_t toparentnum, fromparentnum;
  1808.     if (resolvepath(toparentpath, &toparentnum, start, errnoptr) == -1) {
  1809.         returnval = -1;
  1810.         goto freenamesreturn;
  1811.     }
  1812.     if (!S_ISDIR(INODE_T_ZERO(start)[toparentnum].mode)) {
  1813.         *errnoptr = ENOTDIR;
  1814.         returnval = -1;
  1815.         goto freenamesreturn;
  1816.     }
  1817.  
  1818.     // Get from's parent
  1819.     // Cannot fail, as we resolved from successfully
  1820. #ifdef DEBUG
  1821.     assert(resolvepath(fromparentpath, &fromparentnum, start, errnoptr) == 0);
  1822. #else
  1823.     resolvepath(fromparentpath, &fromparentnum, start, errnoptr);
  1824. #endif
  1825.  
  1826.     // To doesn't exist, move the file/directory
  1827.     // Create a new entry in toparent, and remove the entry from fromparent
  1828.     if (!toexists) {
  1829.         // Add the entry first, that might fail
  1830.         if (addentry(tochildname, toparentnum, fromnum, start, errnoptr) == -1) {
  1831.             returnval = -1;
  1832.             goto freenamesreturn;
  1833.         }
  1834.         dropentry(fromchildname, fromparentnum, start, errnoptr);
  1835.         returnval = 0;
  1836.         goto freenamesreturn;
  1837.     }
  1838.  
  1839.     isfromdir = S_ISDIR((frominode = INODE_T_ZERO(start) + fromnum)->mode);
  1840.     istodir = S_ISDIR((toinode = INODE_T_ZERO(start) + tonum)->mode);
  1841.  
  1842.     // is from a directory?
  1843.     if (isfromdir) {
  1844.         // to must be a directory or nonexistant.
  1845.         if (toexists && !istodir) {
  1846.             *errnoptr = ENOTDIR;
  1847.             returnval = -1;
  1848.             goto freenamesreturn;
  1849.         }
  1850.         // is to an empty directory (entries = 2)?
  1851.         // else fail.
  1852.         if (toinode->entries != 2) {
  1853. #ifdef DEBUG
  1854.             assert(toinode->entries > 2);
  1855. #endif
  1856.             *errnoptr = ENOTEMPTY;
  1857.             returnval = -1;
  1858.             goto freenamesreturn;
  1859.         }
  1860.     }
  1861.     // is tonum a directory? fail.
  1862.     else if (istodir) {
  1863.         *errnoptr = EISDIR;
  1864.         returnval = -1;
  1865.         goto freenamesreturn;
  1866.     }
  1867.  
  1868.     // If we've arrived here, then we need to change the inode in toparentnum
  1869.     // and remove fromchildname in fromparentnum.
  1870.     // We've already determined these all exist, so there's no failures possible.
  1871. #ifdef DEBUG
  1872.     assert(setentry(tochildname, toparentnum, &fromnum, start, errnoptr) == 0
  1873.            && dropentry(fromchildname, fromparentnum, start, errnoptr) == 0);
  1874. #else
  1875.     setentry(tochildname, toparentnum, &fromnum, start, errnoptr);
  1876.     dropentry(fromchildname, fromparentnum, start, errnoptr);
  1877. #endif
  1878.     toinode->links--;
  1879.     if (toinode->links == 0) {
  1880.         freeinode(tonum, start);
  1881.     }
  1882.     returnval = 0;
  1883.  
  1884. freenamesreturn:
  1885.     free(fromparentpath);
  1886.     free(fromchildname);
  1887.     free(toparentpath);
  1888.     free(tochildname);
  1889.     return returnval;
  1890. }
  1891.  
  1892. /* Implements an emulation of the truncate system call on the filesystem
  1893.    of size fssize pointed to by fsptr.
  1894.  
  1895.    The call changes the size of the file indicated by path to offset
  1896.    bytes.
  1897.  
  1898.    When the file becomes smaller due to the call, the extending bytes are
  1899.    removed. When it becomes larger, zeros are appended.
  1900.  
  1901.    On success, 0 is returned.
  1902.  
  1903.    On failure, -1 is returned and *errnoptr is set appropriately.
  1904.  
  1905.    The error codes are documented in man 2 truncate.
  1906.  
  1907. */
  1908. int __write_to_file(struct start_t *start, struct inode_t *inode, const char *buffer, size_t size, off_t offset);
  1909. int __get_rem_space_in_file_block(struct start_t *start, struct inode_t *inode, off_t offset, size_t *rem_space);
  1910. int __request_blocks(struct start_t *start, struct inode_t *inode, int *errnoptr, size_t bytes_requested);
  1911.  
  1912. int __myfs_truncate_implem(void *fsptr, size_t fssize, int *errnoptr,
  1913.                            const char *path, off_t offset) {
  1914.     inodenum_t inodenum;
  1915.     size_t rem_space, file_size;
  1916.     struct start_t *start;
  1917.     struct inode_t *inode;
  1918.  
  1919.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  1920.     *errnoptr = EFAULT;
  1921.       return -1;
  1922.     }
  1923.  
  1924.     start = fsptr;
  1925.  
  1926.     // Verify path exists
  1927.     if (resolvepath(path, &inodenum, start, errnoptr) == -1) {
  1928.       *errnoptr = ENOENT;
  1929.       return -1;
  1930.     }
  1931.  
  1932.     inode = INODE_T_ZERO(start) + inodenum;
  1933.  
  1934.     // check if directory
  1935.     if (S_ISDIR(inode->mode)) {
  1936.     *errnoptr = EISDIR;
  1937.     return -1;
  1938.     }
  1939.  
  1940.     file_size = inode->size;
  1941.  
  1942.     if (offset < file_size) {
  1943.         // if shrinking the file size
  1944.         size_t null_size = file_size - offset;
  1945.         void *null_buffer = malloc(null_size);
  1946.         if (null_buffer == NULL) {
  1947.             *errnoptr = ENOMEM;
  1948.             return -1;
  1949.         }
  1950.         memset(null_buffer, 0, null_size);
  1951.         __write_to_file(start, inode, null_buffer, null_size, offset);
  1952.     }
  1953.     else if (offset > file_size) {
  1954.         size_t expand_by = offset-file_size;
  1955.         // if expanding the file size
  1956.         if (__get_rem_space_in_file_block(start, inode, file_size, &rem_space) == -1) {
  1957.         // seek error;
  1958.             *errnoptr = EOVERFLOW;
  1959.         return -1;
  1960.         }
  1961.  
  1962.         int space_needed = (int)expand_by - (int)rem_space;
  1963.         if (space_needed > 0l) {
  1964.         __request_blocks(start, inode, errnoptr, (size_t)space_needed);
  1965.         }
  1966.         // should have space to write contiguously now
  1967.         void *zero_buffer = malloc(expand_by);
  1968.         if (zero_buffer == NULL) {
  1969.             *errnoptr = ENOMEM;
  1970.             return -1;
  1971.         }
  1972.         memset(zero_buffer, 0, expand_by);
  1973.         __write_to_file(start, inode, zero_buffer, expand_by, file_size);
  1974.     }
  1975.     return 0;
  1976. }
  1977.  
  1978. /* Implements an emulation of the open system call on the filesystem
  1979.    of size fssize pointed to by fsptr, without actually performing the opening
  1980.    of the file (no file descriptor is returned).
  1981.  
  1982.    The call just checks if the file (or directory) indicated by path
  1983.    can be accessed, i.e. if the path can be followed to an existing
  1984.    object for which the access rights are granted.
  1985.  
  1986.    On success, 0 is returned.
  1987.  
  1988.    On failure, -1 is returned and *errnoptr is set appropriately.
  1989.  
  1990.    The two only interesting error codes are
  1991.  
  1992.    * EFAULT: the filesystem is in a bad state, we can't do anything
  1993.  
  1994.    * ENOENT: the file that we are supposed to open doesn't exist (or a
  1995.              subpath).
  1996.  
  1997.    It is possible to restrict ourselves to only these two error
  1998.    conditions. It is also possible to implement more detailed error
  1999.    condition answers.
  2000.  
  2001.    The error codes are documented in man 2 open.
  2002.  
  2003. */
  2004. int __myfs_open_implem(void *fsptr, size_t fssize, int *errnoptr,
  2005.                        const char *path) {
  2006.     inodenum_t ret;
  2007.     return resolvepath(path, &ret, fsptr, errnoptr);
  2008. }
  2009.  
  2010. /*  Returns the ith block in the file's pointer blocks, i goes from 0 to n-1
  2011.         get_file_block_from_ptrblock(0) -> returns the file's 0th file block in the ptrs.
  2012.         ie file block 8 is ptr block 0 */
  2013. struct block_t* __get_file_block_from_ptrblock(struct start_t *start, struct inode_t *inode, size_t index) {
  2014.     if (inode->ptr_block == 0) {
  2015.         // indicate that the inode needs to setup a new ptr block
  2016.         return NULL;
  2017.     }
  2018.   struct ptrblock_t *this_ptrblock = (void*)start + inode->ptr_block;
  2019.   size_t rem_index = index;
  2020.   while (rem_index >= PTR_PER_PTRBLOCK) {
  2021.       rem_index -= PTR_PER_PTRBLOCK;
  2022.       this_ptrblock = (void*)start + this_ptrblock->next;
  2023.         if (this_ptrblock->next == 0) {
  2024.             // basically new ptr block must be set up
  2025.             return NULL;
  2026.         }
  2027.   }
  2028.   return &this_ptrblock->blocks[rem_index];
  2029. }
  2030.  
  2031. /*  Find the ith (index) block from the inode including ptrblock blocks
  2032.         Return NULL if block is not initialized
  2033.         Index goes from 0 to n-1 */
  2034. struct block_t* __get_file_block(struct start_t *start, struct inode_t *inode, size_t index) {
  2035.   if (index < INODE_INLINE_COUNT) {
  2036.     return &inode->block[index];
  2037.   } else {
  2038.     return __get_file_block_from_ptrblock(start, inode, index-INODE_INLINE_COUNT);
  2039.   }
  2040. }
  2041.  
  2042. /*  finds the inode's block that corresponds to the offset,
  2043.         changes rem_offset to the remaining_offset for that block ie 4108 -> 12
  2044.         returns -1 if the offset seeks over empty blocks*/
  2045. int __get_file_block_at_offset(struct start_t *start, struct inode_t *inode, size_t *file_index, off_t *rem_offset) {
  2046.   size_t block_index = 0;
  2047.   struct block_t *file_block;
  2048.  
  2049.   file_block = __get_file_block(start, inode, block_index);
  2050.   while (file_block->bytecount < *rem_offset) {
  2051.       if (file_block->bytecount == 0){
  2052.           // cannot seek as file is not there
  2053.       return -1;
  2054.       }
  2055.       *rem_offset -= file_block->bytecount;
  2056.       block_index++;
  2057.       file_block = __get_file_block(start, inode, block_index);
  2058.   }
  2059.   *file_index = block_index;
  2060.   return 0;
  2061. }
  2062.  
  2063. /*  Read starting from inode's ith block at first_offset,
  2064.         continue reading from every block for size bytes */
  2065. size_t __read_from_file_block(struct start_t *start, struct inode_t *inode, char *buffer, size_t *block_i, off_t *first_offset, size_t size) {
  2066.   size_t rem_size = size;
  2067.   size_t read_in = 0;
  2068.   struct block_t *file_block = __get_file_block(start, inode, *block_i);
  2069.  
  2070.   void *data_location = (void*)start+file_block->location+*first_offset;
  2071.   size_t data_remaining = file_block->bytecount - *first_offset;
  2072.   size_t max_data_in_block = file_block->num*BLOCKSIZE - *first_offset;
  2073.  
  2074.   while (rem_size > 0) {
  2075.       // if there's more data that needs to be put into buffer than what is left in the current file's block
  2076.     if (data_remaining >= max_data_in_block) {
  2077.       // put whole block at a time into buffer
  2078.       memcpy(buffer, data_location, max_data_in_block);
  2079.       rem_size -= max_data_in_block;
  2080.       read_in += max_data_in_block;
  2081.       // point to next block
  2082.       *block_i += 1;
  2083.       file_block = __get_file_block(start, inode, *block_i);
  2084.       data_location = (void*)start+file_block->location;
  2085.       data_remaining = file_block->bytecount;
  2086.       max_data_in_block = file_block->num*BLOCKSIZE;
  2087.     }
  2088.     else {
  2089.       // put rest of data
  2090.       memcpy(buffer, data_location, data_remaining);
  2091.       rem_size -= data_remaining;
  2092.       read_in += data_remaining;
  2093.       break;
  2094.     }
  2095.   }
  2096.   return read_in;
  2097. }
  2098.  
  2099. size_t __read_file_from_offset(struct start_t *start, struct inode_t *inode, char* buffer, off_t offset, size_t size) {
  2100.   off_t rem_offset = offset;
  2101.   size_t file_index = 0;
  2102.   size_t read_in = 0;
  2103.  
  2104.   __get_file_block_at_offset(start, inode, &file_index, &rem_offset);
  2105.   read_in = __read_from_file_block(start, inode, buffer, &file_index, &rem_offset, size);
  2106.   return read_in;
  2107. }
  2108.  
  2109. /* Implements an emulation of the read system call on the filesystem
  2110.    of size fssize pointed to by fsptr.
  2111.  
  2112.    The call copies up to size bytes from the file indicated by
  2113.    path into the buffer, starting to read at offset. See the man page
  2114.    for read for the details when offset is beyond the end of the file etc.
  2115.  
  2116.    On success, the appropriate number of bytes read into the buffer is
  2117.    returned. The value zero is returned on an end-of-file condition.
  2118.  
  2119.    On failure, -1 is returned and *errnoptr is set appropriately.
  2120.  
  2121.    The error codes are documented in man 2 read.
  2122. */
  2123. int __myfs_read_implem(void *fsptr, size_t fssize, int *errnoptr,
  2124.                        const char *path, char *buf, size_t size, off_t offset) {
  2125.  
  2126.   inodenum_t inodenum;
  2127.   struct start_t *start;
  2128.   struct inode_t *inode;
  2129.   size_t size_copied;
  2130.  
  2131.   if (createfs(fsptr, fssize, errnoptr) == -1) {
  2132.       *errnoptr = EFAULT;
  2133.       return -1;
  2134.   }
  2135.  
  2136.   start = fsptr;
  2137.  
  2138.   // Verify path exists
  2139.   if (resolvepath(path, &inodenum, start, errnoptr) == -1) {
  2140.       *errnoptr = ENOENT;
  2141.       return -1;
  2142.   }
  2143.  
  2144.   inode = INODE_T_ZERO(start) + inodenum;
  2145.  
  2146.   // check if directory
  2147.   if (S_ISDIR(inode->mode)) {
  2148.      *errnoptr = EISDIR;
  2149.      return -1;
  2150.   }
  2151.  
  2152.   size_copied = __read_file_from_offset(start, inode, buf, offset, size);
  2153.   return (int)size_copied;
  2154. }
  2155.  
  2156.  
  2157. int __write_to_file(struct start_t *start, struct inode_t *inode, const char *buffer, size_t size, off_t offset) {
  2158.   size_t rem_size = size;
  2159.   size_t block_i = 0;
  2160.   off_t rem_offset = offset;
  2161.   struct block_t *file_block;
  2162.   void *data_location;
  2163.   int max_size;
  2164.   size_t write_out = 0;
  2165.  
  2166.   if (__get_file_block_at_offset(start, inode, &block_i, &rem_offset) == -1) {
  2167.       return -1;
  2168.   }
  2169.   // offset 0 or offset 10 but empty block or has 9 chars
  2170.   file_block = __get_file_block(start, inode, block_i);
  2171.   data_location = (void*)start + file_block->location+rem_offset;
  2172.  
  2173.   max_size = file_block->num*BLOCKSIZE-rem_offset;
  2174.     while (rem_size > 0) {
  2175.       if (max_size < rem_size) {
  2176.           memcpy(data_location, buffer, max_size);
  2177.           rem_size -= (size_t)max_size;
  2178.           write_out += (size_t)max_size;
  2179.           file_block->bytecount += max_size;
  2180.           inode->size += max_size;
  2181.           // next block
  2182.           block_i += 1;
  2183.           file_block = __get_file_block(start, inode, block_i);
  2184.                 if (file_block->location == 0) {
  2185.                     // file block was not allocated
  2186.                     return -1;
  2187.                 }
  2188.           data_location = (void*)start + file_block->location;
  2189.           max_size = file_block->num*BLOCKSIZE;
  2190.       } else {
  2191.           memcpy(data_location, buffer, rem_size);
  2192.           file_block->bytecount += rem_size;
  2193.           inode->size += rem_size;
  2194.           write_out += rem_size;
  2195.           rem_size -= rem_size;
  2196.       }
  2197.     }
  2198.   return (int)write_out;
  2199. }
  2200.  
  2201.  
  2202. int __get_rem_space_in_file_block(struct start_t *start, struct inode_t *inode, off_t offset, size_t *rem_space) {
  2203.   size_t block_i = 0;
  2204.   off_t rem_offset = offset;
  2205.   struct block_t *file_block;
  2206.  
  2207.   if (__get_file_block_at_offset(start, inode, &block_i, &rem_offset) == -1) {
  2208.       // seeking beyond what's already written skipping over blocks, assuming file has to be 1 continuous string
  2209.       return -1;
  2210.   }
  2211.   file_block = __get_file_block(start, inode, block_i);
  2212.   if (offset > file_block->bytecount+1) {
  2213.       // seeking beyond what is already written within block
  2214.       return -1;
  2215.   }
  2216.   *rem_space = file_block->num*BLOCKSIZE-offset;
  2217.   return 0;
  2218. }
  2219.  
  2220. int __request_ptr_block(struct start_t *start, struct inode_t *inode, int *errnoptr, size_t blocks_needed) {
  2221.     offset_t next_ptr_offset;
  2222.     struct ptrblock_t *next_ptr_block;
  2223.     int check_success;
  2224.  
  2225.     next_ptr_offset = inode->ptr_block;
  2226.     while (next_ptr_offset != 0) {
  2227.         next_ptr_block = (struct ptrblock_t*)((void*)start+next_ptr_offset);
  2228.         next_ptr_offset = next_ptr_block->next;
  2229.     }
  2230.  
  2231.     // make next_ptr_offset point to an actual ptr_block
  2232.     check_success = getptrblock(&next_ptr_offset, blocks_needed, start, errnoptr);
  2233.     if (check_success == -1) {
  2234.         return -1;
  2235.     }
  2236.     return 0;
  2237. }
  2238.  
  2239. int __request_blocks(struct start_t *start, struct inode_t *inode, int *errnoptr, size_t bytes_requested) {
  2240.   size_t blocks_needed = (bytes_requested / BLOCKSIZE) + 1;
  2241.   size_t block_i = 0;
  2242.   struct block_t *new_block;
  2243.   int check_success = 0;
  2244.  
  2245.   while (blocks_needed > 0) {
  2246.       new_block = __get_file_block(start, inode, block_i);
  2247.             if (new_block == NULL) {
  2248.                 __request_ptr_block(start, inode, errnoptr, blocks_needed);
  2249.                 new_block = __get_file_block(start, inode, block_i);
  2250.                 if (new_block == NULL) {
  2251.                     // out of space / could not set up new ptr block
  2252.                     return -1;
  2253.                 }
  2254.                 blocks_needed -= new_block->num;
  2255.             }
  2256.       if (new_block->location == 0) {
  2257.           check_success = getblock(new_block,(off_t)blocks_needed, start, errnoptr);
  2258.           if (check_success == -1) {
  2259.               return -1;
  2260.           }
  2261.           blocks_needed -= new_block->num;
  2262.       }
  2263.             block_i += 1;
  2264.   }
  2265.   return 0;
  2266. }
  2267.  
  2268. /* Implements an emulation of the write system call on the filesystem
  2269.    of size fssize pointed to by fsptr.
  2270.  
  2271.    The call copies up to size bytes to the file indicated by
  2272.    path into the buffer, starting to write at offset. See the man page
  2273.    for write for the details when offset is beyond the end of the file etc.
  2274.  
  2275.    On success, the appropriate number of bytes written into the file is
  2276.    returned. The value zero is returned on an end-of-file condition.
  2277.  
  2278.    On failure, -1 is returned and *errnoptr is set appropriately.
  2279.  
  2280.    The error codes are documented in man 2 write.
  2281.  
  2282. */
  2283. int __myfs_write_implem(void *fsptr, size_t fssize, int *errnoptr,
  2284.                         const char *path, const char *buf, size_t size,
  2285.                         off_t offset) {
  2286.     inodenum_t inodenum;
  2287.     struct start_t *start;
  2288.     struct inode_t *inode;
  2289.     size_t rem_space;
  2290.  
  2291.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  2292.       *errnoptr = EFAULT;
  2293.       return -1;
  2294.     }
  2295.  
  2296.     start = fsptr;
  2297.  
  2298.     // Verify path exists
  2299.     if (resolvepath(path, &inodenum, start, errnoptr) == -1) {
  2300.       *errnoptr = ENOENT;
  2301.       return -1;
  2302.     }
  2303.  
  2304.     inode = INODE_T_ZERO(start) + inodenum;
  2305.  
  2306.     // check if directory
  2307.     if (S_ISDIR(inode->mode)) {
  2308.         *errnoptr = EISDIR;
  2309.         return -1;
  2310.     }
  2311.  
  2312.     if (__get_rem_space_in_file_block(start, inode, offset, &rem_space) == -1) {
  2313.         // seek error;
  2314.             *errnoptr = EOVERFLOW;
  2315.         return -1;
  2316.     }
  2317.  
  2318.     int space_needed = (int)size - (int)rem_space;
  2319.     if (space_needed > 0l) {
  2320.         __request_blocks(start, inode, errnoptr, (size_t)space_needed);
  2321.     }
  2322.     // should have space to write contiguously now
  2323.     return __write_to_file(start, inode, buf, size, offset);
  2324. }
  2325. /* Implements an emulation of the utimensat system call on the filesystem
  2326.    of size fssize pointed to by fsptr.
  2327.  
  2328.    The call changes the access and modification times of the file
  2329.    or directory indicated by path to the values in ts.
  2330.  
  2331.    On success, 0 is returned.
  2332.  
  2333.    On failure, -1 is returned and *errnoptr is set appropriately.
  2334.  
  2335.    The error codes are documented in man 2 utimensat.
  2336.  
  2337. */
  2338. int __myfs_utimens_implem(void *fsptr, size_t fssize, int *errnoptr,
  2339.                           const char *path, const struct timespec ts[2]) {
  2340.     inodenum_t inodenum;
  2341.     struct inode_t *inode;
  2342.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  2343.         return -1;
  2344.     }
  2345.  
  2346.     if (resolvepath(path, &inodenum, fsptr, errnoptr) == -1) {
  2347.         return -1;
  2348.     }
  2349.     inode = INODE_T_ZERO((struct start_t *)fsptr) + inodenum;
  2350.     inode->atime = ts[0];
  2351.     inode->mtime = ts[1];
  2352.     return 0;
  2353. }
  2354.  
  2355. /* Implements an emulation of the statfs system call on the filesystem
  2356.    of size fssize pointed to by fsptr.
  2357.  
  2358.    The call gets information of the filesystem usage and puts in
  2359.    into stbuf.
  2360.  
  2361.    On success, 0 is returned.
  2362.  
  2363.    On failure, -1 is returned and *errnoptr is set appropriately.
  2364.  
  2365.    The error codes are documented in man 2 statfs.
  2366.  
  2367.    Essentially, only the following fields of struct statvfs need to be
  2368.    supported:
  2369.  
  2370.    f_bsize   fill with what you call a block (typically 1024 bytes)
  2371.    f_blocks  fill with the total number of blocks in the filesystem
  2372.    f_bfree   fill with the free number of blocks in the filesystem
  2373.    f_bavail  fill with same value as f_bfree
  2374.    f_namemax fill with your maximum file/directory name, if your
  2375.              filesystem has such a maximum
  2376.  
  2377. */
  2378. int __myfs_statfs_implem(void *fsptr, size_t fssize, int *errnoptr,
  2379.                          struct statvfs *stbuf) {
  2380.     if (createfs(fsptr, fssize, errnoptr) == -1) {
  2381.         return -1;
  2382.     }
  2383.     stbuf->f_namemax = CHAR_LIMIT_INCLUDE_NULL - 1;
  2384.     stbuf->f_bsize = BLOCKSIZE;
  2385.     stbuf->f_blocks = ((struct start_t *)fsptr)->size / BLOCKSIZE;
  2386.     stbuf->f_bfree = 0;
  2387.  
  2388.     // If start->firstfree is not 0, add up all blocks.
  2389.     if (((struct start_t *)fsptr)->firstfree != 0) {
  2390.         struct freespace_t *space = fsptr + ((struct start_t *)fsptr)->firstfree;
  2391.         while (1) {
  2392.             stbuf->f_bfree += space->freeblocks;
  2393.             if (space->next == 0) {
  2394.                 break;
  2395.             }
  2396.             space = (void *)space + space->next;
  2397.         }
  2398.     }
  2399.     stbuf->f_bavail = stbuf->f_bfree;
  2400.     return 0;
  2401. }
  2402.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement