Chapter 5: File Systems
Chapter 5: File Systems
The information about all files is kept in the directory structure, which also resides on
secondary storage. Typically, a directory entry consists of the file’s name and its unique
identifier.
Deleting a file: To delete a file, we search the directory for the named file. Having
found the associated directory entry, we release all file space, so that it can be reused
by other files, and erase the directory entry.
Truncating a file: The user may want to erase the contents of a file but keep its
attributes. Rather than forcing the user to delete the file and then recreate it, this
function allows all attributes to remain unchanged-except for file length.
These six basic operations comprise the minimal set of required file operations. Other
common operations include appending new information to the end of an existing file and
renaming an existing file. These primitive operations can then be combined to perform other
file operations. For instance, we can create a copy of a file, or copy the file to another I/O
device, such as a printer or a display, by creating a new file and then reading from the old and
writing to the new. We also want to have operations that allow a user to get and set the
various attributes of a file.
Most of the file operations mentioned involve searching the directory for the entry associated
with the named file. To avoid this constant searching, many systems require that an open()
system call be made before a file is first used actively. The operating system keeps a small
table, called the open-file table, containing information about all open files. When a file
operation is requested, the file is specified via an index into this table, so no searching is
required. When the file is no longer being actively used, it is closed by the process, and the
operating system removes its entry from the open-file table, create and delete are system calls
that work with closed rather than open files.
The open() operation takes a file name and searches the directory, copying the directory entry
into the open-file table.
A read operation reads the next portion of the file and automatically advances a file pointer,
which tracks the I/O location. The write operation appends to the end of the file and advances
to the end of the newly written material
For the direct-access method, the file operations must be modified to include the block
number as a parameter. Thus, we have read n, where n is the block number, rather than read
next, and write n rather than write next.
Not all operating systems support both sequential and direct access for files. Some systems
allow only sequential file access; others allow only direct access.
Search for a file: We need to be able to search a directory structure to find the entry
for a particular file.
Create a file: New files need to be created and added to the directory.
Delete a file: When a file is no longer needed, we want to be able to remove it from
the directory.
List a directory: We need to be able to list the files in a directory and the contents of
the directory entry for each file in the list.
Rename a file: Because the name of a file represents its contents to its users, we must
be able to change the name when the contents or use of the file changes. Renaming a
file may also allow its position within the directory structure to be changed.
Traverse the file system: We may wish to access every directory and every file
within a directory structure.
In the following sections, we describe the most common schemes for defining the logical
structure of a directory.
user are immediately visible to the other (Figure 5.7). A common way of implementing
shared files and directories is to create a new directory entry called a link, which is
effectively a pointer to another file or subdirectory. A link can be implemented as an absolute
or relative path name.
When the same files need to be accessed in more than one place in the directory
structure (e.g. because they are being shared by more than one user/process), it can be
useful to provide an acyclic-graph structure. (Note the directed arcs from parent to
child).
UNIX provides two types of links for implementing the acyclic-graph structure.
o A hard link (usually just called a link) involves multiple directory entries that
both refer to the same file. Hard links are only valid for ordinary files in the
same filesystem.
o A symbolic link, that involves a special file, containing information about
where to find the linked file. Symbolic links may be used to link directories
and/or files in other filesystems, as well as ordinary files in the current
filesystem.
Windows only supports symbolic links, termed shortcuts.
Hard links require a reference count, or link count for each file, keeping track of how
many directory entries are currently referring to this file. Whenever one of the
references is removed the link count is reduced, and when it reaches zero, the disk
space can be reclaimed.
For symbolic links there is some question as what to do with the symbolic links when
the original file is moved or deleted:
o One option is to find all the symbolic links and adjust them also.
o Another is to leave the symbolic links dangling, and discover that they are no
longer valid the next time they are used.
system name space. The basic idea behind mounting file systems is to combine multiple file
systems into one large tree structure.
The mount procedure is straightforward. The operating system is given the name of the
device and the mount point-the location within the file structure where the file system is to
be attached. Typically, a mount point is an empty directory. For instance, on a UNIX system,
a file system containing a user's home directories might be mounted as /home; then, to access
the directory structure within that file system, we could precede the directory names with
/home, as in /homc/jane. Mounting that file system under /users would result in the path name
/users/jane, which we could use to reach the same directory.
Next, the operating system verifies that the device contains a valid file system. It does so by
asking the device driver to read the device directory and verifying that the directory has the
expected format. Finally, the operating system notes in its directory structure that a file
system is mounted at the specified mount point. This scheme enables the operating system to
traverse its directory structure, switching among file systems as appropriate.
Let us consider an example. Nearly all personal computers have one or more floppy disk
drives into which floppy disks can be inserted and removed. To provide an elegant way to
deal with removable media, UNIX allows the file system on a floppy disk to be attached
(mounted) to the main tree. Consider the situation of Figure 5.8(a). Before the mount call, the
root file system, on the hard disk, and a second file system, on a floppy disk, are separate and
unrelated. However, the file system on the floppy cannot be used, because there is no way to
specify path names on it, UNIX does not allow path names to be prefixed by a drive name or
number; that would be precisely the kind of device dependence that operating systems ought
to eliminate. Instead, the mount system call allows the file system on the floppy to be
attached to the root file system wherever the program wants it to be. In Figure 5.8(b) the file
system on the floppy has been mounted on directory b, thus allowing access to files /b/x
and /b/y. If directory b had contained any files they would not be accessible while the floppy
was mounted, since /b would refer to the root directory of the floppy. If a system contains
multiple hard disks, they can all be mounted into a single tree as well.
Figure 5.8: (a) Before mounting, the files on drive 0 are not accessible. (b) After mounting,
they are part of the file hierarchy.
Let us consider another example. Many computers have two or more disks. On mainframes at
banks, for example, it is frequently necessary to have 100 or more disks on a single machine,
in order to hold the huge databases required. Even personal computers normally have at least
two disks-a hard disk and a diskette drive. When there are multiple disk drives, the question
arises of how to handle them.
One solution is to put a self-contained file system on each one and just keep them separate.
Consider, for example, the situation depicted in Figure 5.9(a). Here we have a hard disk,
which we will call C :, and a diskette, which we will call A :. Each has its own root directory
and files. With this solution, the user has to specify both the device and the file when
anything other than the default is needed. For example, to copy the file x to the directory d ,
(assuming C : is the default), one would type
cp A:/x /a/d/x
This is the approach taken by systems like MS-DOS and Windows operating systems.
The UNIX solution is to allow one disk to be mounted in another disk’s file tree. In our
example, we could mount the diskette on the directory /b, yielding the file system of Figure
5.9(b). The user now sees a single file tree, and no longer has to be aware of which file
resides on which device. The above copy command now becomes
cp /b/x /a/d/x
exactly the same as it would have been if everything had been on the hard disk in the first
place.
To implement sharing and protection, the system must maintain more file and
directory attributes than are needed on a single-user system. Most systems use the
concepts of file (or directory) owner (or user) and group.
The owner is the user who can change attributes and grant access and who has the
most control over the file. The group attribute defines a subset of users who can share
access to the file.
The owner and group IDs of a given file (or directory) are stored with the other file
attributes. When a user requests an operation on a file, the user ID can be compared
with the owner attribute to determine if the requesting user is the owner of the file.
Likewise, the group IDs can be compared. The result indicates which permissions are
applicable. The system then applies those permissions to the requested operation and
allows or denies it.
failure, and host-adapter failure. User or systems-administrator failure can also cause
files to be lost or entire directories or volumes to be deleted. Many of these failures
will cause a host to crash and an error condition to be displayed, and human
intervention will be required to repair the damage.
Remote file systems have even more failure modes. Because of the complexity of
network systems and the required interactions between remote machines, many more
problems can interfere with the proper operation of remote file systems.
o In the case of networks, the network can be interrupted between two hosts.
Such interruptions can result from hardware failure, poor hardware
configuration, or networking implementation issues.
o Consider a client in the midst of using a remote file system. It has files open
from the remote host; among other activities, it may be performing directory
lookups to open files, reading or writing data to files, and closing files.
Suddenly, the remote file system is no longer reachable. This scenario is rather
common, so it would not be appropriate for the client system to act as it would
if a local file system were lost. Rather, the system can either terminate all
operations to the lost server or delay operations until the server is again
reachable. These failure semantics are defined and implemented as part of the
remote-file-system protocol.
o Termination of all operations can result in users’ losing data-and patience.
Thus, most remote-file-system protocol either enforce or allow delaying of
file-system operations to remote hosts, with the hope that the remote host will
become available again.
5.6 Protection
When information is stored in a computer system, we want to keep it safe from physical
damage (reliability) and improper access (protection). Reliability is generally provided by
duplicate copies of files. Protection can be provided in many ways. For a small single-user
system, we might provide protection by physically removing the floppy disks and locking
them in a desk drawer or file cabinet. In a multiuser system, however, other mechanisms are
needed.
Other operations, such as renaming, copying, and editing the file, may also be controlled.
5.6.2 Access Control
The most common approach to the protection problem is to make access dependent on the
identity of the user. Different users may need different types of access to a file or directory.
The most general scheme to implement identity dependent access is to associate with each
file and directory an access-control list (ACL) specifying user names and the types of access
allowed for each user.
When a user requests access to a particular file, the operating system checks the access list
associated with that file. If that user is listed for the requested access, the access is allowed.
Otherwise, a protection violation occurs, and the user job is denied access to the file. This
approach has the advantage of enabling complex access methodologies. The main problem
with access lists is their length. If we want to allow everyone to read a file, we must list all
users with read access. This technique has two undesirable consequences:
Constructing such a list may be a tedious and unrewarding task, especially if we do
not know in advance the list of users in the system.
The directory entry, previously of fixed size, now needs to be of variable size,
resulting in more complicated space management.
These problems can be resolved by use of a condensed version of the access list. To condense
the length of the access-control list, many systems recognize three classifications of users in
connection with each file:
Owner: The user who created the file is the owner.
Group: A set of users who are sharing the file and need similar access is a group, or
work group.
Universe: All other users in the system constitute the universe.
To illustrate, consider a person, Sara, who is writing a new book. She has hired three graduate
students (Jim, Dawn, and Jill) to help with the project. The text of the book is kept in a file
named book. The protection associated with this file is as follows:
Sara should be able to invoke all operations on the file.
Jim, Dawn, and Jill should be able only to read and write the file; they should not be
allowed to delete the file.
All other users should be able to read, but not write, the file. (Sara is interested in
letting as many people as possible read the text so that she can obtain appropriate
feedback.)
To achieve such protection, we must create a new group-say, text- with members Jim, Dawn,
and Jill. The name of the group, text, must then be associated with the file book, and the
access rights must be set in accordance with the policy we have outlined.
For this scheme to work properly, permissions and access lists must be controlled tightly.
This control can be accomplished in several ways. For example, in the UNIX system, groups
can be created and modified only by the manager of the facility (or by any supervisor).
Contiguous allocation requires that each file occupy a set of contiguous blocks on the
disk as shown in figure 5.10.
Performance is very fast, because reading successive blocks of the same file generally
requires no movement of the disk heads, or at most one small step to the next adjacent
cylinder.
Storage allocation involves the same issues discussed earlier for the allocation of
contiguous blocks of memory (first fit, best fit, fragmentation problems, etc.). The
distinction is that the high time penalty required for moving the disk heads from spot
to spot may now justify the benefits of keeping files contiguously when possible.
Even file systems that do not by default store files contiguously can benefit from
certain utilities that compact the disk and make all files contiguous in the process.
Problems can arise when files grow, or if the exact size of a file is unknown at
creation time:
o Over-estimation of the file’s final size increases external fragmentation and
wastes disk space.
o Under-estimation may require that a file be moved or a process aborted if the
file grows beyond its originally allocated space.
o If a file grows slowly over a long time period and the total final space must be
allocated initially, then a lot of space becomes unusable before the file fills the
space.
A variation is to allocate file space in large contiguous chunks, called extents. When a
file outgrows its original extent, then an additional one is allocated. An extent may be
the size of a complete track or even cylinder, aligned on an appropriate track or
cylinder boundary.
For example, a file of five blocks might start at block 9 and continue at block 16, then
block 1, then block 10, and finally block 25 (Figure 5.11).
Each block contains a pointer to the next block. These pointers are not made available
to the user. Thus, if each block is 512 bytes in size, and a disk address (the pointer)
requires 4 bytes, then the user sees blocks of 508 bytes.
Linked allocation involves no external fragmentation, does not require pre-known file
sizes, and allows files to grow dynamically at any time.
Unfortunately linked allocation is only efficient for sequential access files, as random
access requires starting at the beginning of the list for each new location access.
Allocating clusters of blocks reduces the space wasted by pointers, at the cost of
internal fragmentation.
Another big problem with linked allocation is reliability if a pointer is lost or
damaged. Doubly linked lists provide some protection, at the cost of additional
overhead and wasted space.
The File Allocation Table (FAT) used by DOS is a variation of linked allocation,
where all the links are stored in a separate table at the beginning of the disk. The
benefit of this approach is that the FAT table can be cached in memory, greatly
improving random access speeds (Figure 5.12).