“The world is what it is…”

CS644 week 2: File I/O, part 2

Last week

Link: /week1

Review

Solutions

Linux distinguishes between several types of files:

In fact, hard links aren't really a different type of file – all regular files are effectively hard links. But normally we don't call them hard links unless we've created a second link to an existing file.

Under the hood: inodes

Within the kernel, files are represented by a data structure called an inode. The inode holds metadata about the file, and the location of the file's content on disk. (See man 7 inode for details.)

A directory is just a mapping from path names to inodes. Two path names can point to the same inode (this is what a hard link is). Only when the last path pointing to an inode is removed, will the underlying file contents be marked for deletion.

Other things can hold on to references to inodes, too, such as running programs. When you call open, the kernel will keep the file alive until you call close, even if another program deletes it.

So, the filesystem effectively has a reference-counting garbage collector. To extend the analogy further: symbolic links are like weak references and hard links are like strong references.

Try this:

touch test.txt
# make a hard link
ln test.txt test2.txt
ls -i test*.txt

ls -i will print the same inode number for the two files.

File permissions

Linux has inherited the classic file permissions model from Unix. There are three permission bits, each of which has a different meaning for regular files versus directories:

Owners and groups

Every file has an owner and a group. The three permissions (read, write, execute) can be separately set for the owner, the group, and everyone else. We can write a file's permission as a nine-character string, such as rwxr-xr-x, where the first three bits are the owner's permissions, the next bits the group's, and the last bits everyone else's.

Octal notation

Instead of a nine-character string, we can represent permissions more concisely as a three-digit octal number, for example 755. To break it down:

Exercise: Copying a file tree

Exercise: Write a function copytree(inpath, outpath) that recursively copies the directory inpath and all files and directories underneath it.


You will need to use os.listdir or os.scandir (what's the difference?) to enumerate the entries in a directory.

This Python API is analogous to but slightly different from the C APIs, opendir which opens the directory for enumeration and readdir which returns one entry at a time. These C APIs are in turn wrappers around the underlying syscall, getdents, whose man page says "These are not the interfaces you are interested in."

To create a new empty directory, use os.mkdir.

To copy the files themselves, you can reuse the duplicate function that you wrote last week.

Follow-up

Exercise: Deleting a file tree

Exercise: Write a function rmtree(inpath) that recursively removes a directory and all its children.

Be careful to only run this on a test directory!


Follow-up

Final project milestone

Modify your program to create the database file with permissions locked down to the file's owner. Change your on-disk format to use multiple files: you can have one file per key, or do something fancier like allowing keys to have multiple parts such as a.b.c and store all a.* keys in the same file. Add commands to list all keys and to delete a key from the database.

Bonus material