****Jesse Dhillon, cs162-bp File Descriptors =============================================================================== An inode is the file descriptor in Unix. It contains the following information: *Reference count: the number of times this file is open concurrently *Link count: the number of hard links to this file A hard link is a directory entry which refers to this inode; of which there can by many. When all of the links are removed, the filesystem knows it can safely delete the file because the link count is 0. *Owner's user and group id *File size, in bytes *Dates of creation, latest access, and modification *Index blocks: direct and indirect addresses to file blocks *Flags: locked, modified, waiting *Protection/permisions: Read/Write/eXecute bits for owner, group, and world. Also, setuid and sticky bits for directory. Sticky preserves owner exclusiveness in a directoy: when a user writes a file to a sticky directory only himself and root can modify it unless its permissions are changed. Setuid allows the file to run with owner's permissions when run by another, unpriveleged user. *Shared lock count, number of locks on this inode *Exclusive lock count, number of exclusive locks on this inode. Apparently Unix supports multiple "exclusive" locks on an inode. *Unique id, the inode number *Filesystem this inode is associate with *Quota structure for this file When a file is open, the inode is kept in main memory. When the file is closed, the descriptor is written back to the disk, if modified. The process keeps a per-process table of open files. Every time a process opens a file, an entry is placed into this table. The index is a handle (an integer index) and the value is an inode number, which will be used to index into the system's inode table. This table maps inodes to disk block addresses where the file associated with that inode can be found. For every process, the following indices are reserved: *0 standard input *1 standard output *2 standard error The system also maintains a table for caching, the system open file table. This table maps strings, the name of the file, to inodes. This table has a 70%+ hit rate, because files which have been accessed recently are usually needed in the future too. Resolving a string into an inode is expensive, so if the result of that name resolution can be cached, alot of work is saved. Directories =============================================================================== Users need a logical way to reference the files they store on disk. Historically there have been at least three approaches to this domain, all using directories. *A single directory for the entire disk *Problems *When one user takes a name, that name is globally unavailable *If a user forgets the name of a file, all files on the disk must be searched to find it *All users can see all files on disk, which is insecure *A separate directory for each user *Relieves aforementioned security problem *Problems *Still requires a search through potentially a great number of files to find the one desired *Generalized tree structure *Directories are stored on disk as a regular file *Directories can contain other directories *User can manipulate directories (almost) like any other file A directory contains a list of tuples. The file pointed to by the index may be another directory, hence making a hierarchical structure possible. The '/' (forward slash) character is the delimiter in a filename, separating directories in the path to the file. There is a special directory, which contains everything else in the filesystem; this directory is root. It is not named and indicated by a leading / at the head of the path. The root directory is pointed at by inode 2. A directory consists of some number of blocks, DIRBLKSIZ, which is a particular size which may be transferred atomically. Each directory block contains some number of directory entry structures of variable length. Each entry has information containing the inode number, the length of this entry, and the length of this filename. Then the filename follows, padded to a four byte boundary. In Unix, the filename is not the name of the file, the inode number is. This is because an inode can have multiple hard links in different directories, thus there are different paths in the directory heierarchies to the same file. Therefore the filename cannot be the name of the file because it is not unique or exclusive. But those links will always point to the same inode, so the inode remains unique. Deleting a file means decrementing the link count on the inode associated with that file. When the count reaches 0, the blocks of that file are put on the free list. The data remains on disk, but at some point in the future the blocks will be overwritten with new data. Additionally, the filesystem can contain symbolic links, which maps one path name is mapped to another. When referencing the symlink, the kernel follows the path referenced. Symlinks introduce the possibility for cycles in the directory tree. A symlink could link up to a parent tree, then it could be possible to access the first symlink again, through itself. Example: [luser@host|/] ls -R /foo /foo/bar -> / /* symlink to root */ [luser@host|/]$ cd /foo/bar/foo/bar/foo/bar/foo/bar /* This could go on forever */ In Unix, every process has a working directory, which is the current directory it is working in. Also, every user has a search path, which is a lisy of directories in which to search first when resolving names. The search path is maintained by the shell. For an example, type "echo $PATH" at the prompt in bash. File Operations =============================================================================== *Open, put a file descriptor into the process open file table. These are the files which are useable by the process. May require the acquisition of locks and increment of reference count. *Close, the inverse of the open operation. *Create, this is sometimes done automatically when opening a non-existent file. *Remove, drop a link to the file. If the last link was removed, place the file's blocks back on the free list. *Read, read a record from the file. *Write, write a record to the file, acquiring additional disk space if necessary. *Rename, change the name of the file. Unix renames the file by moving, which is potentially dangerous. mv destroys the target, if it exists, with the new file. *Seek, go to a specified offset in the file. *Synch, sometimes called flush. Force the writing of blocks from cache to the disk. *Chattr, change the attributes of an inode. *Link, create a link, either hard or symbolic. *Lock/Unlock, acquire or release a lock on the file. In general, Unix is not required to respect the locks present in the inode, therefore a program should take responsibility for ensuring that a file does not change while the process is using it. This can be done by making a temporary copy or copying the file into memory. *Truncate, remove all bytes after a particular offset.