From 2b121db58271cea6d2c7a31cb0da568022c5ce02 Mon Sep 17 00:00:00 2001 From: Remzi Arpaci-Dusseau Date: Tue, 29 Nov 2022 11:41:29 -0600 Subject: [PATCH] more details in README --- filesystems-distributed-ufs/README.md | 76 ++++++++++++++++++--------- 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/filesystems-distributed-ufs/README.md b/filesystems-distributed-ufs/README.md index 15e1cc8..b62d88c 100644 --- a/filesystems-distributed-ufs/README.md +++ b/filesystems-distributed-ufs/README.md @@ -27,27 +27,40 @@ your file server will link with it and call its various routines. Your on-disk file system structures should roughly follow that of the very simple file system discussed [here](https://pages.cs.wisc.edu/~remzi/OSTEP/file-implement.pdf). On-disk, +the structures should be as follows: +- A single block (4KB) super block +- An inode bitmap (can be one or more 4KB blocks, depending on the number of inodes) +- A data bitmap (can be one or more 4KB blocks, depending on the number of data blocks) +- The inode table (a multiple of 4KB-sized blocks, depending on the number of inodes) +- The data region (some number of 4KB blocks, depending on the number of data blocks) -One other structure you'll have to manage on disk are -directories. Each directory has an inode, and points to one or more -data blocks that contain directory entries. Each directory entry -should be simple, and consist of 32 bytes: a name and an inode number -pair. The name should be a fixed-length field of size 28 bytes; the -inode number is just an integer (4 bytes). When a directory is -created, it should contain two entries: the name `.` (dot), which -refers to this new directory's inode number, and `..` (dot-dot), which -refers to the parent directory's inode number. For directory entries -that are not yet in use (in an allocated 4-KB directory block), the -inode number should be set to -1. This way, utilities can scan through -the entries to check if they are valid. +More details about on-disk structures can be found in the header [ufs.h](https://github.com/remzi-arpacidusseau/ostep-projects/blob/master/filesystems-distributed-ufs/ufs.h), which you should +use. Specifically, this has a very specific format for the super +block, inode, and directory entries. Bitmaps just have one bit per +allocated unit as described in the book. + +As for directories, here is a little more detail. Each directory has +an inode, and points to one or more data blocks that contain directory +entries. Each directory entry should be simple, and consist of 32 +bytes: a name and an inode number pair. The name should be a +fixed-length field of size 28 bytes; the inode number is just an +integer (4 bytes). When a directory is created, it should contain two +entries: the name `.` (dot), which refers to this new directory's +inode number, and `..` (dot-dot), which refers to the parent +directory's inode number. For directory entries that are not yet in +use (in an allocated 4-KB directory block), the inode number should be +set to -1. This way, utilities can scan through the entries to check +if they are valid. When your server is started, it is passed the name of the file system image file. The image is created by a tool we provide, called `mkfs`. +It is pretty self-explanatory and can be found +[here](https://github.com/remzi-arpacidusseau/ostep-projects/blob/master/filesystems-distributed-ufs/mkfs.c). When booting off of an existing image, your server should read in the -superblock, bitmaps, and inode table, and keep in-memory versions of these. - - +superblock, bitmaps, and inode table, and keep in-memory versions of +these. When writing to the image, you should update these on-disk +structures accordingly. ## Client library @@ -63,22 +76,24 @@ name does not exist in pinum. - `int MFS_Stat(int inum, MFS_Stat_t *m)`: `MFS_Stat()` returns some information about the file specified by inum. Upon success, return 0, otherwise -1. The exact info returned is defined by `MFS_Stat_t`. Failure modes: -inum does not exist. -- `int MFS_Write(int inum, char *buffer, int block)`: `MFS_Write()` writes a -block of size 4096 bytes at the block offset specified by `block`. Returns 0 -on success, -1 on failure. Failure modes: invalid inum, invalid block, not a -regular file (because you can't write to directories). -- `int MFS_Read(int inum, char *buffer, int block)`: `MFS_Read()` reads -a block specified by `block` into the buffer from file specified by +inum does not exist. File and directory sizes are described below. +- `int MFS_Write(int inum, char *buffer, int offset, int nbytes)`: +`MFS_Write()` writes a buffer of size `nbytes` (max size: 4096 bytes) at the byte +offset specified by `offset`. Returns 0 on success, -1 on +failure. Failure modes: invalid inum, invalid nbytes, invalid offset, not a +regular file (because you can't write to directories). +- `int MFS_Read(int inum, char *buffer, int offset, int nbytes)`: +`MFS_Read()` reads `nbytes` of data (max size 4096 bytes) specified by the +byte offset `offset` into the buffer from file specified by `inum`. The routine should work for either a file or directory; directories should return data in the format specified by `MFS_DirEnt_t`. Success: 0, failure: -1. Failure modes: invalid inum, -invalid block. +invalid offset, invalid nbytes. - `int MFS_Creat(int pinum, int type, char *name)`: `MFS_Creat()` makes a file (`type == MFS_REGULAR_FILE`) or directory (`type == MFS_DIRECTORY`) in the parent directory specified by `pinum` of name `name`. Returns 0 on success, -1 on failure. Failure modes: pinum does not exist, or name is too -long. If `name` already exists, return success (think about why). +long. If `name` already exists, return success. - `int MFS_Unlink(int pinum, char *name)`: `MFS_Unlink()` removes the file or directory `name` from the directory specified by `pinum`. 0 on success, -1 on failure. Failure modes: pinum does not exist, directory is NOT empty. Note @@ -88,6 +103,11 @@ this might be). of its data structures to disk and shutdown by calling `exit(0)`. This interface will mostly be used for testing purposes. +Size: The size of a file is the offset of the last valid byte written +to the file. Specifically, if you write 100 bytes to an empty file at +offset 0, the size is 100; if you write 100 bytes to an empty file at +offset 10, the size is 110. For a directory, it is the same (i.e., the +byte offset of the last byte of the last valid entry). ## Server Idempotency @@ -122,7 +142,8 @@ The command line arguments to your file server are to be interpreted as follows. - portnum: the port number that the file server should listen on. - file-system-image: a file that contains the file system image. -If the file system image does not exist, you should print out an error message and exit with exit code 1. +If the file system image does not exist, you should print out an error +message (`image does not exist\n`) and exit with exit code 1. Your client library should be called `libmfs.so`. It should implement the interface as specified by `mfs.h`, and in particular deal with @@ -144,6 +165,11 @@ To get you going, we have written some simple UDP code that can send a message and then receive a reply from a client to a server. It can be found in [here](https://github.com/remzi-arpacidusseau/ostep-code/tree/master/dist-intro). +There is also other code as mentioned above: +- [mfs.h](https://github.com/remzi-arpacidusseau/ostep-projects/blob/master/filesystems-distributed-ufs/mfs.h) +- [ufs.h](https://github.com/remzi-arpacidusseau/ostep-projects/blob/master/filesystems-distributed-ufs/ufs.h) +- [mkfs.c](https://github.com/remzi-arpacidusseau/ostep-projects/blob/master/filesystems-distributed-ufs/mkfs.c) + You'll also have to learn how to make a shared library. Read [here](https://tldp.org/HOWTO/Program-Library-HOWTO/shared-libraries.html) for more information.