Operating Systems 2021F Lecture 14

From Soma-notes
Jump to navigation Jump to search

Video

Video from the lecture given on November 4, 2021 is now available:

Video is also available through Brightspace (Resources->Class zoom meetings->Cloud Recordings tab)

Notes

Lecture 14
----------
 - Happy Diwali! (for those who celebrate)

 - grading: will upload A2 after class, midterm will be soon
   (by next class)
 - schedule will be posted when midterm grades are posted, will
   announce on Teams hopefully this weekend

 - today: more about files & filesystems for T6, which I'll post this afternoon

 - 3rd party builds of Firefox can't use the firefox branding without
   special agreement.  Many linux distributions can use the logos of firefox but internally they make the build names a bit different

(For the final I'll probably try to message on Teams to schedule interviews)


Files & filesystems
 - a file is a mapping between a hierarchical key (the filename) with an arbitrarily-sized value (the contents of the file)
 - there's also metadata associated with each file
    - permissions, ownership, timestamps, etc
 - metadata is associated with the value, not the key
    - goes with file contents, not the file name

So files (in UNIX-like systems) are actually a mapping between a name and
a data structure that represents the value and associated metadata
  - that data structure is known as an "inode"
    (I think the i stands for indirection but I'm not sure.)

stat system call returns the metadata associated with an inode
 - there's also a command line wrapper around stat

Example:

  File: foo
  Size: 0         	Blocks: 0          IO Block: 4096   regular empty file
Device: fd00h/64768d	Inode: 45220577    Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/    soma)   Gid: ( 1000/    soma)
Access: 2021-11-04 10:13:10.897903575 -0400
Modify: 2021-11-04 10:13:10.897903575 -0400
Change: 2021-11-04 10:13:10.897903575 -0400
 Birth: 2021-11-04 10:13:10.897903575 -0400


From the stat man page:
    struct stat {
        dev_t     st_dev;         /* ID of device containing file */
        ino_t     st_ino;         /* Inode number */
        mode_t    st_mode;        /* File type and mode
	                             (permission bits) */
        nlink_t   st_nlink;       /* Number of hard links */
        uid_t     st_uid;         /* User ID of owner */
        gid_t     st_gid;         /* Group ID of owner */
        dev_t     st_rdev;        /* Device ID (if special file) */
        off_t     st_size;        /* Total size, in bytes */
        blksize_t st_blksize;     /* Block size for filesystem I/O */
        blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

        struct timespec st_atim;  /* Time of last access */
        struct timespec st_mtim;  /* Time of last modification */
        struct timespec st_ctim;  /* Time of last status change */
    };


Key concepts that we haven't covered:
 - blocks
 - multiple timestamps
 - hard links (NOT symbolic links)
 - device IDs
 - inode number

(Mentioned mounting, will discuss more soon)

So why three time stamps?
 - Access: when last read
 - Modify: when last written (data change)
 - Change: when inode last changed   <--- chmod
           (metadata change)
 - Birth: file creation (mostly not specified/used on UNIX)
    - not in the stat struct?

A quirk of UNIX files
 - a file access (read) results in a disk write (to update the accessed
   time stamp value in the inode)
 - on modern systems, this is often a bad idea (too much SSD writing)
    - so depending on how the filesystem was mounted,
      accessed timestamp may be approximate or just wrong

If something is done "lazily" in CS, it means it is put off as long as possible
 - try to minimize the work since if it is put off long enough it may
   not need to be done

So with reading, if you do 100 reads from a file, technically it should be writing the same inode to disk 100 times
 - but if we're lazy and only do the write, say, every few seconds,
   we'd only do one write to the inode for the accessed timestamp

Buffering I/O is a way to be lazy
  - reduces number of writes in exchange for higher latency in some circumstances


inode numbers
 - unique ID for an inode
 - filenames are really name -> inode number mappings
 - inode numbers are unique *within a given filesystem*
    - each filesystem has its own inode number namespace
   (the inode numbers on your usb disk filesystem have nothing
    to do with the inodes in your SSD filesystem)

If you give the -i option to ls, it will show you inode numbers.

A filename is better thought of as a pathname
 - it is the combination of names of every containing directory
   plus the final name

A directory is a data structure (really, a type of inode) which maps names to inode numbers

When you're in a directory, "." refers to this directory
 - "." is a filename like any other that refers to an inode

Hard link count
 - number of names an inode has
   - basically, a reference count
 - regular files will normally have a link count of 1
   - for its filename
 - directories will have a link count of at least 2
   - the directory name in the containing folder
   - the "." link in its own directory
   - every subdirectory has a parent link, those increase
     the link count too

So in every directory there are always two entries
 - "."  <-- this directory (like "self" in an OO language)
 - ".." <-- the parent (containing) directory

By default . is not in your path
 - considered a security vulnerability
 - imagine a web server where you can upload files to a special directory
 - sysadmin is working in the directory, attacker has uploaded a file "ls"
 - now when the sysadmin types "ls" they could get the attacker's ls, not
   /bin/ls

What does it mean to delete a file on a UNIX-like system?
 - you don't!
 - you just "unlink" files
    - remove directory entries
 - when an inode ref count goes to 0, it is considered deleted
   and is de-allocated

In general files are never immediately deleted
 - just like with a program, if you de-allocate a data structure it is
   still there in memory
 - and yes, this is lazy

If you want data to be erased from disk when you delete, use an
encrypted filesystem
 - because the key for the file will be written over when it is deleted
 - (encrypted data with lost key is considered erased if your encryption
    is any good)

inode ref count is the hard link count

Note that any commands that try to overwrite the data on disk won't work,
especially on an SSD.
  - need to explain filesystems a bit first to see why
  - data will be overwritten, but eventually (could be very long)

So what's a filesystem?
 - an "on disk" data structure for storing files
   (i.e., inodes and associated data)


To understand filesystems, you have to understand how disks store data

What does a "disk" store?  What does it appear to the OS as?

 - array of fixed-sized blocks
 - block number: which block
 - block contents: 512, 1024, 4096, 8192, or another power of two
   chunk of bytes

This is why UNIX has block devices & character devices
 - block device: you access data on them a block at a time
 - character device: you access data one character (byte) at a time

So a filesystem is a data structure for storing inodes & file data in blocks of some fixed size
 - some blocks are inode blocks
 - rest are data blocks

(Non-UNIX filesystems will divide these into directory and data blocks)

Each devices has a native block size
 - nowadays 4K or 8K
 - so, decided by the OS (firmware) on the disk
    - yes, disks of any kind are really their own computers

The filesystem block size should be a multiple of the device block size
 - because we always read or write an entire block at once


Character devices are for anything that isn't mass storage
 - so keyboards, mouse/touchpad, terminals
   - typically things for interacting with humans

look at /dev to see what different kinds of devices you have

you can't delete an inode directly, you can just remove its names
 - unlink all its names

When we "read" a file, we are just accessing data in its underlying block device

Instead of using a real block device, we can use a simulated one
for playing with filesystems
 - turn a file into a block device
 - can then make filesystems in it that can be mounted

this is all stuff for tutorial 6, so don't worry if you're lost
 - you may want to come back to what I've done here
 - we'll do it again a few times

symbolic links are filenames that refer filenames, not inodes