As with previous assignments, we will be using GitHub to distribute skeleton code and collect submissions. Please refer to our Git Workflow guide for more details. Note that we will be using multiple tags for this assignment, for each deliverable part.
NOTE: If at all possible, please try to submit using x86. If one of your group
members owns an x86 machine, test on that machine prior to submitting, and do
not commit a .armpls
file. This will make grading much easier for us.
For students on arm64 computers (e.g. M1/M2 machines): if you want your
submission to be built/tested for ARM, you must create and submit a file called
.armpls
in the top-level directory of your repo; feel free to use the
following one-liner:
cd "$(git rev-parse --show-toplevel)" && touch .armpls && git add -f .armpls && git commit .armpls -m "ARM pls"
You should do this first so that this file is present in all parts.
There is a script in the skeleton code named run_checkpatch.sh
. It is a
wrapper over linux/scripts/checkpatch.pl
, which is a Perl script that
comes with the linux kernel that checks if your code conforms to the
kernel coding style.
Execute run_checkpatch.sh
to see if your code conforms to the kernel
style – it’ll let you know what changes you should make. You must make
these changes before pushing a tag. Passing run_checkpatch.sh
with no
warnings and no errors is required for this assignment.
A loop device is a pseudo-device that makes a file accessible as a block device. Files of this kind are often used for CD ISO images. Mounting a file containing a file system via such a loop mount makes the files within that file system accessible.
Create a loop device, build & mount an ext2 filesystem, and try creating directories and files. Below is a sample session you can follow that starts from my home directory. It goes without saying that you need to understand what’s going on at each step. Look at the man pages. Google stuff.
$ sudo su
# dd if=/dev/zero of=./ext2.img bs=1024 count=100
100+0 records in
100+0 records out
102400 bytes (102 kB, 100 KiB) copied, 0.000600037 s, 171 MB/s
# modprobe loop
# losetup --find --show ext2.img
/dev/loop0
# mkfs -t ext2 /dev/loop0
mke2fs 1.44.5 (15-Dec-2018)
Creating filesystem with 100 1k blocks and 16 inodes
Allocating group tables: done
Writing inode tables: done
Writing superblocks and filesystem accounting information: done
# mkdir mnt
# mount /dev/loop0 ./mnt
# cd mnt
# ls -al
total 17
drwxr-xr-x 3 root root 1024 Apr 21 02:22 .
drwxr-xr-x 37 hans hans 4096 Apr 21 02:22 ..
drwx------ 2 root root 12288 Apr 21 02:22 lost+found
# mkdir sub2
# ls -al
total 18
drwxr-xr-x 4 root root 1024 Apr 21 02:23 .
drwxr-xr-x 37 hans hans 4096 Apr 21 02:22 ..
drwx------ 2 root root 12288 Apr 21 02:22 lost+found
drwxr-xr-x 2 root root 1024 Apr 21 02:23 sub2
# cd sub2
# ls -al
total 2
drwxr-xr-x 2 root root 1024 Apr 21 02:23 .
drwxr-xr-x 4 root root 1024 Apr 21 02:23 ..
# mkdir sub2.1
# ls -al
total 3
drwxr-xr-x 3 root root 1024 Apr 21 02:24 .
drwxr-xr-x 4 root root 1024 Apr 21 02:23 ..
drwxr-xr-x 2 root root 1024 Apr 21 02:24 sub2.1
# touch file2.1
# ls -al
total 3
drwxr-xr-x 3 root root 1024 Apr 21 02:24 .
drwxr-xr-x 4 root root 1024 Apr 21 02:23 ..
-rw-r--r-- 1 root root 0 Apr 21 02:24 file2.1
drwxr-xr-x 2 root root 1024 Apr 21 02:24 sub2.1
# cd ../../
# umount mnt/
# losetup --find
/dev/loop1
# losetup --detach /dev/loop0
# losetup --find
/dev/loop0
# ls -al mnt/
total 8
drwxr-xr-x 2 root root 4096 Apr 21 02:22 .
drwxr-xr-x 37 hans hans 4096 Apr 21 02:22 ..
In the sample session shown above, files and directories are created. Make sure you see the number of links each file or directory has, and make sure you understand why.
Also try creating some hard links and symlinks. Make sure you understand how they affect the link counts.
In this assignment, we provide a compiled reference implementation of PantryFS for you to test your implementation against.
We provide reference implementations (ref/pantry-x86.ko
and
ref/pantry-arm.ko
), compiled against the stock Debian 11 kernel
(5.10.0-20-amd64
and 5.10.0-20-arm64
, respectively). You should boot into
this kernel for the rest of this assignment.
If your kernel name is slightly differerent (e.g. 5.10.0-21-amd64
), you may
get a versioning error when you try to insert the reference implementation. In
that case, you can try forcibly inserting the module with insmod -f
.
In this part, we will mount a loop device and format it as PantryFS. Then, we’ll use the reference implementation to interact with our newly formatted disk.
Create a disk image and assign it to a loop device:
$ dd bs=4096 count=200 if=/dev/zero of=~/pantry_disk.img
# losetup --find --show ~/pantry_disk.img
pantry_disk.img
and bind it to an available
loop device, probably /dev/loop0
. Now, /dev/loop0
can be used as if
it were a physical disk, and the data backing it will be stored in
pantry_disk.img
.Format the disk as PantryFS.
# ./format_disk_as_pantryfs /dev/loop<N>
format_disk_as_pantryfs.c
. Inspect it and then run it as
shown above.Mount the disk at /mnt/pantry
using the reference implementation:
# mkdir /mnt/pantry
# insmod pantry.ko
# mount -t pantryfs /dev/loop<N> /mnt/pantry
Explore the newly created filesystem. Edit hello.txt
and create some new
files.
The formatting utility creates the new filesystem’s root directory and places
hello.txt
in that directory. In this part, we will create another directory
and file.
Read the provided formatting utility, format_disk_as_pantryfs.c
. Make sure
you understand the on-disk format and what each line contributes toward
creating the filesystem. Test your understanding by thinking about the
following questions:
The program writes two inodes for the root directory and hello.txt
,
but does not zero out the rest of the inode block. Can you convince
yourself that this is OK?
However, for filling out the root directory block, the program ensures that the unused portion of the block is zeroed out. Can you see why this is necessary?
Extend the program to create a subdirectory called members
. The directory
should contain a single file, names.txt
, that lists the names of your team
members.
To make things easier, consider starting with an empty members
directory. Once you have that working, create names.txt
.
Be sure to set the directories’ link counts correctly.
Create and format a new disk using your modified program. Use the reference
implementation, pantry.ko
, to verify that the new file and directory were
created correctly. You can use the stat
command to see the size, inode
number, and other properties.
A modified version of format_disk_as_pantryfs.c
You do not need to submit any disk images
To submit this part, push the hw8p1handin
tag with the following:
$ git tag -a -m "Completed hw8 part1." hw8p1handin
$ git push origin master
$ git push origin hw8p1handin
The rest of this assignment is structured to guide you while implementing your filesystem driver. Each part represents a milestone toward a working driver, forcing you to develop incrementally. It also allows the graders to award partial credit if you don’t get the whole thing working in the end.
In this part, we will begin by writing the code that mounts disks. Later, we’ll add the ability to read files, modify existing files, create new files, delete files, and even create and remove directories.
Don’t worry about concurrency in your filesystem driver. That is, you may assume that each PantryFS disk will only have one thread of one program performing operations at any given time.
You are welcome to search the web for info. The Documentation/
directory in
the Linux source tree contains a wealth of information as well. Here are some
resources that might be useful:
LKD chapter 13
LKD chapter 14: pages 289 - 294
Read the skeleton code starting from its entry point, pantryfs_init()
.
This module is written so that it can be loaded simultaneously with the
reference implementation. How is this accomplished?
Unmount your PantryFS drive and try to mount it with the skeleton code instead. The following error message indicates that you are no longer mounting with the reference implementation:
mount: /mnt/pantry: permission denied.
This error is returned from pantryfs_fill_super()
via pantryfs_mount()
.
Implement pantryfs_fill_super()
so that you can mount disks. Note that
we’re only interested in making the mount
and umount
commands work
cleanly in this part. We won’t be reading any files or directories at this
time.
Use sb_set_blocksize()
to ensure that the block layer reads blocks of
the correct size.
Read the PantryFS superblock and inodes. Assign them to an instance of
struct pantryfs_sb_buffer_heads
. Store this struct in the s_fs_info
member of the VFS superblock. This way, we can always find the PantryFS
superblock and inodes by following the trail of pointers from the VFS
superblock.
The following diagram shows the relationship between these structs after this step.
You will have to fill out some additional members of the VFS superblock structure, such as the magic number and pointer to the ops struct.
Use iget_locked()
to create a new VFS inode for the root directory.
Read the kernel source to learn what this function does for you and get
some hints on how you’re supposed to use it. The only metadata you need
to set is the mode. Make the root directory drwxrwxrwx
for now.
After creating an inode for the root directory, you need to create a dentry associated with it. Make the VFS superblock point to the dentry.
Make sure to handle errors by returning an appropriate error code. For example, what if someone asks you to mount a filesystem that isn’t PantryFS?
mypantry.c
To submit this part, push the hw8p2handin
tag with the following:
$ git tag -a -m "Completed hw8 part2." hw8p2handin
$ git push origin master
$ git push origin hw8p2handin
In the previous part, we created a VFS inode without associating it with the corresponding PantryFS inode from disk. Update your code to associate the root VFS inode with the root PantryFS inode.
Use the i_private
member of the VFS inode to store a pointer to the
PantryFS inode. All of the PantryFS inodes live in the inode store that
we read from disk in the previous section.
Note that you must set the i_sb
and i_op
members so that VFS can
identify which filesystem the inode belongs to.
Consult the diagram in the PantryFS Specification section.
Add support for listing the root directory.
You should be able to run ls
and ls -a
. Here’s sample session:
# ls /mnt/pantry
hello.txt members
# ls -a /mnt/pantry
. .. hello.txt members
# ls /mnt/pantry/members
ls: cannot access '/mnt/pantry/members': No such file or directory
Note that we do not support listing the contents of a subdirectory yet.
The VFS framework will call the iterate
member of the
struct file_operations
. Inside your iterate
implementation, use
dir_emit()
to provide VFS with the contents of the requested
directory. VFS will continue to call iterate
until your implementation
returns without calling dir_emit()
.
For now, you can pass in DT_UNKNOWN
as the type
argument for
dir_emit()
. We will revisit this in part6.
You can use the ctx->pos
variable as a cursor to the directory entry
that you are about to emit. (Note that the dir_emit_dots()
function
modifies ctx->pos
.)
The following is an excerpt from the output of
strace ls /usr/bin > /dev/null
:
[...]
openat(AT_FDCWD, "/usr/bin", O_RDONLY|O_NONBLOCK|O_CLOEXEC|O_DIRECTORY) = 3
[...]
getdents64(3, /* 1003 entries */, 32768) = 32744
[...]
getdents64(3, /* 270 entries */, 32768) = 8888
[...]
getdents64(3, /* 0 entries */, 32768) = 0
close(3) = 0
The ls
program first opens the /usr/bin
directory file. Then, it
calls getdents64()
three times to retrieve the list of 1,273 files in
/usr/bin
. Finally, ls
closes the directory file.
Each call to getdents64()
will result in one call to iterate_dir()
,
which in turn will call your iterate
implementation. Consequently,
your iterate
implementation should call dir_emit()
until the given
buffer is full.
Running ls -l
might print error messages because the ls
program is
unable to stat
the files. This is the expected behavior for this part.
mypantry.c
To submit this part, push the hw8p3handin
tag with the following:
$ git tag -a -m "Completed hw8 part3." hw8p3handin
$ git push origin master
$ git push origin hw8p3handin
In this part, we’ll implement the lookup
function of inode_operations
. The
kernel calls this function repeatedly as it walks through a filepath such as
/a/b/c/d/e/f.txt
. For example, once it knows the inode of c
, it will ask you
for the inode associated with the name d
in the directory c
. Your job is to
retrieve the inode for d
from the filesystem.
To avoid repeated work when looking up similar paths, the kernel maintains a cache called the dentry cache. Learn how the dentry cache works by reading the materials given earlier.
Add support for looking up filepaths.
You should be able to cd
into directories and ls
the contents of
directories that aren’t the root.
As a side effect, the -l
flag and stat
command should work on both
files and directories now.
Here’s a sample session:
# ls /mnt/pantry/members
names.txt
# cd /mnt/pantry/members
# stat names.txt
File: names.txt
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 700h/1792d Inode: 4 Links: 1
Access: (0000/----------) Uid: ( 0/ root) Gid: ( 0/ root)
Access: 2017-03-30 02:42:27.629345430 -0400
Modify: 2017-03-30 02:42:27.629345430 -0400
Change: 2017-03-30 02:42:27.629345430 -0400
Birth: -
# stat does_not_exist.txt
stat: cannot stat 'does_not_exist.txt': No such file or directory
# ls -l ..
total 0
---------- 1 root root 0 Apr 3 23:31 hello.txt
d--------- 1 root root 0 Dec 31 1969 members
VFS does most of the heavy lifting when looking up a filepath. It splits up the given path and looks up each part in the dentry cache. If a part isn’t in the dentry cache, it asks you to add it. Before you add things to the dentry cache, you’re responsible for determining whether the given parent directory contains an entry with the given name.
Don’t worry about returning metadata correctly at this time (it isn’t correct in the above sample either). That is, it’s fine to show incorrect permission bits, create/modify/access times, owner, group, etc.
mypantry.c
To submit this part, push the hw8p4handin
tag with the following:
$ git tag -a -m "Completed hw8 part4." hw8p4handin
$ git push origin master
$ git push origin hw8p4handin
In this part, we are going to implement reading the contents of files. Our implementation is made very simple by the fact that each file occupies exactly one block.
Add support for reading the contents of files.
# cat /mnt/pantry/hello.txt
Hello world!
# cat /mnt/pantry/members/names.txt
Kevin Chen
Mitchell Gouzenko
John Hui
# dd if=/mnt/pantry/hello.txt
Hello world!
0+1 records in
0+1 records out
13 bytes copied, 4.5167e-05 s, 266 kB/s
# dd if=/mnt/pantry/hello.txt bs=1 skip=6
world!
7+0 records in
7+0 records out
7 bytes copied, 5.1431e-05 s, 117 kB/s
Add support for seeking through files.
By default vim
, places swap files in the current directory and seeks
through them upon opening a file using llseek
. You may have noticed
an error when trying to open files using vim
because PantryFS didn’t
implement llseek
yet. Fix it.
Hint: there’s already a generic implementation in the kernel for
llseek
so you shouldn’t reimplement it – just call it.
mypantry.c
To submit this part, push the hw8p5handin
tag with the following:
$ git tag -a -m "Completed hw8 part5." hw8p5handin
$ git push origin master
$ git push origin hw8p5handin
Fix all of the corners we cut in the previous section by making sure your code returns correct metadata for all files and directories. These include size, link count, timestamps, permissions, owner, and group.
Check out APUE chapter 4 (Files and Directories) if you’re not sure what these attributes mean.
Test by using ls -l
and stat
as before.
You should also pass the correct type to dir_emit()
in
pantryfs_iterate()
. Check out this StackOverflow
post
for why it matters. Hint: you should use S_DT()
.
At this point, you should stress test your PantryFS implementation. The rest of this assignment will be easier if you can depend on the reading functionality to report things correctly.
Here are some ideas:
Try copying all the files out of your pantry using cp
or rsync
.
Extend the formatting program again to create additional files and a more
complex directory structure. Be sure to include different file types. For
example, add a small team photo to the members
directory.
Overwrite the disk with random garbage from /dev/urandom
(instead of
/dev/zero
). Format it. After formatting, the random data should not
affect the normal operation of the filesystem.
Write a program that requests invalid offsets when reading files or iterating through directories.
An updated version of mypantry.c
Optional: Feel free to submit any test code or scripts you wrote for Task 2
To submit this part, push the hw8p6handin
tag with the following:
$ git tag -a -m "Completed hw8 part6." hw8p6handin
$ git push origin master
$ git push origin hw8p6handin
So far, we’ve only been reading what’s already on the filesystem. In this part, we’ll begin implementing functions for modifying the filesystem contents.
Add support for overwriting the contents of existing files.
If the existing file is smaller than the user buffer to be written, you don’t have to worry about changing the length of the file yet. You’ll do that in the next task. Just write into the buffer head representing the data block until you reach the end of the user buffer or exceed the file’s size.
Here’s a sample session. The notrunc
option is needed to prevent dd
from passing the O_TRUNC
flag to open()
, which sets the length of
the file to zero.
$ cd /mnt/pantry
$ echo -ne "4118" | dd of=hello.txt bs=1 seek=7 conv=notrunc
[...]
$ cat hello.txt
Hello w4118!
$ echo "Greetings and salutations, w4118!" | dd of=hello.txt conv=notrunc
[...]
$ cat hello.txt
Greetings and$
Note that since we didn’t update the file size, the full contents of the file are not shown. This behavior is technically incorrect – you will fix it in Task 2.
Writing to the buffer head only changes the contents in memory. It does not cause those changes to be written back to disk. Be sure to take the appropriate measures so that your modifications are written to disk.
Properly update the VFS inode’s size if the length of the write exceeds the file’s length.
Here’s a sample session:
$ ls -l hello.txt
-rw-rw-rw- 1 jezuss jezuss 13 Nov 26 22:09 hello.txt
$ echo "Greetings and salutations, w4118!" > hello.txt
$ cat hello.txt
Greetings and salutations, w4118!
$ ls -l hello.txt
-rw-rw-rw- 1 jezuss jezuss 34 Nov 26 22:11 hello.txt
$ echo "Hi w4118!" > hello.txt
$ cat hello.txt
Hi w4118!
$ ls -l hello.txt
-rw-rw-rw- 1 jezuss jezuss 10 Nov 26 22:11 hello.txt
You should also be able to edit files with the nano
editor, although
it will complain about fsync()
not being implemented.
Ensure that changes to the VFS inode are written back to disk. You
should do this by implementing pantryfs_write_inode()
. Of course, VFS
needs to be informed that the VFS inode is out of sync with the PantryFS
inode.
Test this by unmounting and remounting.
You may have noticed that vim
and nano
complain about fsync()
failing.
Fix it.
sync_dirty_buffer()
to have
a dirty buffer written to disk immediately.Add support for files opened with the O_APPEND
flag.
VFS does not handle this case for us – you’ll need to manually set the position of the file before writing to it.
Hint: Take a look at generic_write_checks()
, which performs various
checks before any writing actually happens. We recommend you look at
the Linux 4.0 implementation of the function, because newer kernel
versions use a different write API than PantryFS. Ignore the !isblk
check – how does generic_write_checks()
account for O_APPEND
?
Here’s a sample session using the bash append operator:
$ ls -l hello.txt
-rw-rw-rw- 1 jezuss jezuss 13 Apr 12 18:48 hello.txt
$ cat hello.txt
Hello world!
$ echo "We can now append!" >> hello.txt
$ ls -l hello.txt
-rw-rw-rw- 1 jezuss jezuss 32 Apr 12 18:48 hello.txt
$ cat hello.txt
Hello world!
We can now append!
mypantry.c
To submit this part, push the hw8p7handin
tag with the following:
$ git tag -a -m "Completed hw8 part7." hw8p7handin
$ git push origin master
$ git push origin hw8p7handin
Implement creating new files. That is, user programs should be able to call
open()
with a mode that includes O_CREAT
.
What you need to do in this part is a combination of what you did in Part 1 (“Creating additional files upon formatting”) and Task 3 of Part 2 (“The filesystem awakens”).
Here’s a sample session:
$ cd /mnt/pantry
$ ls
hello.txt members
$ touch world.txt
$ ls
hello.txt members world.txt
$ stat world.txt
File: world.txt
Size: 0 Blocks: 8 IO Block: 4096 regular empty file
Device: 700h/1792d Inode: 5 Links: 1
Access: (0644/-rw-r--r--) Uid: ( 1000/ hans) Gid: ( 1000/ hans)
Access: 2022-04-17 23:56:46.000000000 -0400
Modify: 2022-04-17 23:56:46.000000000 -0400
Change: 2022-04-17 23:56:46.000000000 -0400
Birth: -
$ cat > members/favorite_memes.txt
doge
chad
BigTime Tommie
https://youtu.be/TiC8pig6PGE
$ cat members/favorite_memes.txt
doge
chad
BigTime Tommie
https://youtu.be/TiC8pig6PGE
mypantry.c
To submit this part, push the hw8p8handin
tag with the following:
$ git tag -a -m "Completed hw8 part8." hw8p8handin
$ git push origin master
$ git push origin hw8p8handin
While testing the previous part, you probably created lots of files that are now cluttering your disk. Let’s implement a way to delete those files.
Review how the VFS dentry and inode caches interact with each other using the resources given earlier in this assignment.
Implement the unlink
and evict_inode
ops so that you can delete files.
You are not required to implement directory removal in this part, that will happen in part 10.
Ensure that you are reclaiming data blocks and PantryFS inodes when appropriate. To test this, see if you can repeatedly create and remove files.
for i in {1..10}; do touch {1..14}; rm {1..14}; done
In a Unix-like operating system, what is the correct behavior if one process unlinks a file while another process has the same file open? Here’s an experiment you can run on ext4 or the PantryFS reference implementation to find out:
Create a file named foo
.
In terminal window A, run tail -f foo
. This command will open foo,
print out all the contents, and wait for more lines to be written.
In terminal B, run cat > foo
. This reads from stdin and outputs
the result to foo
.
In terminal C, delete foo
.
Back in terminal B, type some text and press return.
The text should appear in terminal A.
You can also use this C program, which tests the same functionality without you having to open three terminal windows.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int wait_for_user(void)
{
printf("Press any key to continue...");
return getchar();
}
int main(int argc, char **argv)
{
int fd;
int ret;
char buf[4096];
ssize_t len;
if (argc != 2) {
printf("usage: %s /path/to/file\n", argv[0]);
return 1;
}
fd = open(argv[1], 0);
printf("=> Opened as fd %d\n", fd);
wait_for_user();
ret = unlink(argv[1]);
printf("=> Called unlink(%s) = %d\n", argv[1], ret);
printf("=> Running ls to show that hello.txt is unlinked:\n");
system("ls /mnt/pantry");
wait_for_user();
len = read(fd, buf, sizeof(buf));
if (len > 0) {
printf("=> Read %d bytes:\n", len);
fwrite(buf, 1, len, stdout);
}
wait_for_user();
ret = close(fd);
printf("=> close(fd) = %d\n", ret);
return 0;
}
mypantry.c
To submit this part, push the hw8p9handin
tag with the following:
$ git tag -a -m "Completed hw8 part9." hw8p9handin
$ git push origin master
$ git push origin hw8p9handin
Implement creating new directories. That is, user programs should be able
to call mkdir()
.
This should be very similar to what you did in part 8. You need to make sure that you’re setting a size and link count appropriate for a directory, rather than a regular file.
Hint: consider the link count of the parent directory of the newly created directory as well.
The robustness tests from part 6 should pass here as well. Think back to part 1, when we zeroed out the unused portion of the root directory’s data block. Is an analogous operation necessary here? Why or why not?
Implement deleting directories. User programs should be able to call
rmdir()
successfully on empty directories.
This should be very similar to what you did in part 9. Take a look at
simple_rmdir()
for some additional directory-specific steps.
simple_empty()
is not sufficient to check if a
directory is empty for our purposes, because the function simply
checks the dentry cache to see if a directory has children. Can you
think of a case where this would lead to incorrect behavior?Here’s a sample session:
$ ls -alF
total 16
drwxrwxrwx 3 hans hans 4096 Apr 17 23:51 ./
drwxr-xr-x 3 root root 4096 Apr 17 23:47 ../
-rw-rw-rw- 1 hans hans 32 Apr 17 23:51 hello.txt
drwxrwxrwx 2 hans hans 4096 Apr 17 23:51 members/
$ mkdir bigtime
$ ls -alF
total 20
drwxrwxrwx 4 hans hans 4096 Apr 17 23:51 ./
drwxr-xr-x 3 root root 4096 Apr 17 23:47 ../
drwxr-xr-x 2 hans hans 4096 Apr 18 00:17 bigtime/
-rw-rw-rw- 1 hans hans 32 Apr 17 23:51 hello.txt
drwxrwxrwx 2 hans hans 4096 Apr 17 23:51 members/
$ cd bigtime
$ touch tommie
$ ls -alF
total 12
drwxr-xr-x 2 hans hans 4096 Apr 18 00:17 ./
drwxrwxrwx 4 hans hans 4096 Apr 17 23:51 ../
-rw-r--r-- 1 hans hans 0 Apr 18 00:17 tommie
$ cd ..
$ rmdir bigtime
rmdir: failed to remove 'bigtime': Directory not empty
$ ls -alF
total 20
drwxrwxrwx 4 hans hans 4096 Apr 17 23:51 ./
drwxr-xr-x 3 root root 4096 Apr 17 23:47 ../
drwxr-xr-x 2 hans hans 4096 Apr 18 00:17 bigtime/
-rw-rw-rw- 1 hans hans 32 Apr 17 23:51 hello.txt
drwxrwxrwx 2 hans hans 4096 Apr 17 23:51 members/
$ rm bigtime/tommie
$ rmdir bigtime
$ ls -alF
total 16
drwxrwxrwx 3 hans hans 4096 Apr 17 23:51 ./
drwxr-xr-x 3 root root 4096 Apr 17 23:47 ../
-rw-rw-rw- 1 hans hans 32 Apr 17 23:51 hello.txt
drwxrwxrwx 2 hans hans 4096 Apr 17 23:51 members/
mypantry.c
To submit this part, push the hw8p10handin
tag with the following:
$ git tag -a -m "Completed hw8 part10." hw8p10handin
$ git push origin master
$ git push origin hw8p10handin
Implement hard links such that user programs can create new hard links
using link()
. This should be very similar to part8.
Note that hard links only work for regular files, not directories.
Think about the differences and similarities between creating a new file and creating a link to an existing file. For example, a hard link doesn’t require a new inode, but what data structure does it require?
Since we aren’t creating a new inode, you’ll need to increment the
existing inode’s reference count. Check out how a disk-based
filesystem, such as BFS, implements link()
.
Make sure to update any link counts as necessary.
Here’s a sample session:
$ ls -alFi
total 16
1 drwxrwxrwx 3 root root 0 Apr 12 18:48 ./
11534337 drwxr-xr-x 3 root root 4096 Apr 12 18:48 ../
2 -rw-rw-rw- 1 jezuss jezuss 32 Apr 12 18:48 hello.txt
3 drwxr-xr-x 2 jezuss jezuss 4096 Apr 12 19:31 members/
$ ln hello.txt link.txt
$ ls -alFi
total 20
1 drwxrwxrwx 3 root root 0 Apr 12 18:48 ./
11534337 drwxr-xr-x 3 root root 4096 Apr 12 18:48 ../
2 -rw-rw-rw- 2 jezuss jezuss 32 Apr 12 18:48 hello.txt
2 -rw-rw-rw- 2 jezuss jezuss 32 Apr 12 18:48 link.txt
3 drwxr-xr-x 2 jezuss jezuss 4096 Apr 12 19:31 members/
Note that the i
flag will show the inode numbers in the leftmost
column.
mypantry.c
To submit this part, push the hw8p11handin
tag with the following:
$ git tag -a -m "Completed hw8 part11." hw8p11handin
$ git push origin master
$ git push origin hw8p11handin
Implement symbolic links such that user programs can create new
symbolic links using symlink()
.
The symlink()
function is passed a string representing the path the
symlink should point to. This path gets stored in the symlink’s data
block.
In addition, you should keep the path in memory and have the symlink’s
inode’s i_link
field point to it. This is an optimization used by VFS
called “fast symlink”.
Hint: Check out debugfs_create_symlink()
to see how it sets the fast
symlink.
Don’t forget to set i_link
for uncached (i.e. I_NEW
is set) VFS inodes
after iget_locked()
calls.
Implement get_link()
. Hint: check out simple_get_link()
.
Implement free_inode()
from struct super_operations
.
This is necessary to prevent memory leaks due to the “fast symlink” optimization.
Hint: check out debugfs_free_inode()
.
mypantry.c
To submit this part, push the hw8p12handin
tag with the following:
$ git tag -a -m "Completed hw8 part12." hw8p12handin
$ git push origin master
$ git push origin hw8p12handin
This part is optional. However, if you successfully complete one or more tasks in this part, Jae will take it into consideration for boosting borderline grades at the end of the semester.
Implement creating special files using mknod()
. mknod
and mkfifo
should work.
Implement renaming files. mv
should work.
Implement support for renameat()
.
Implement support for renameat2()
– specifically, the
RENAME_EXCHANGE
and RENAME_NOREPLACE
flags.
See man rename
for details.
Implement statfs()
. df
should report correct filesystem usage
statistics.
To submit this part, push the hw8p13handin
tag with the following:
$ git tag -a -m "Completed hw8 part13." hw8p13handin
$ git push origin master
$ git push origin hw8p13handin
The PantryFS assignment and reference implementation are designed and implemented by the following TAs of COMS W4118 Operating Systems I, Spring 2017, Columbia University:
The PantryFS assignment and reference implementation was updated for 64-bit Linux version 4.9.81 by the following TAs of COMS W4118 Operating Systems, Spring 2018, Columbia University:
The PantryFS assignment and reference implementation was updated for 64-bit Linux version 4.19.50 by the following TAs of COMS W4118 Operating Systems, Spring 2020, Columbia University:
The PantryFS assignment and reference implementation was updated for 64-bit Linux version 5.10.57, along with support for creating directories, removing directories, hard links, and soft links, by the following TAs of COMS W4118 Operating Systems, Fall 2021, Columbia University:
The PantryFS assignment and reference implementation was updated with support
for renaming files (atomically), mknod
, and statfs
by the following TAs of
COMS W4118 Operating Systems, Spring 2022, Columbia University:
Last updated: 2023-04-23