CS372: Solutions for Homework 11
Problem 1:
Is it fundamentally necessary to store on disk the information about the
unallocated disk sectors? Explain why.
Solution:
No. This information can be computed at startup
by traversing the file system tree. However, it is a safety issue. The
job of a utility like fsck is simplified and is rendered more secure by
having such a list.
Problem 2:
The FastFile file system uses an inode array to
organize the files on disk. Each inode consists of a user id (2 bytes),
three time stamps (4 bytes each), protection bits (2 bytes), a reference
count (2 byte), a file type (2 bytes) and the size (4 bytes). Additionally,
the inode contains 13 direct indexes, 1 index to a 1st-level index table,
1 index to a 2nd-level index table, and 1 index to a 3rd level index table.
The file system also stores the first 436 bytes of each file in the inode.
- Assume a disk sector is 512 bytes, and assume
that any auxilliary index table takes up an entire sector, what is the
maximum size for a file in this system.
- Is there any benefit for including the first 436
bytes of the file in the inode?
Solution:
- Note: The first thing you need to determine is the
size of an index (2 bytes or 4 bytes). Given that we have a 3-level indexing
scheme, you can quickly compute the number of sectors that you can get
by 2 byte index and 4 bytes indexes. You will see that the 2-byte indexing
does not work. This would give you up to 256 indexes per sector, with a
maximum file size of 436 + 13 * 512 + 1 * 256 * 512 + 1 * 256 * 256 * 512
+ 1 * 256 * 256 * 256 * 512 = whatever. The problem with this analysis
is that you have far more disk sector for this scheme to work than can
be encoded in 2 bytes. You can use 3 bytes, but this can get ugly. So,
we go with 4-byte indexes, giving us 128 indexes per sector, and the correct
answer is: 436 + 13 * 512 + 1 * 128 * 512 + 1 * 128 * 128 * 512 + 1 * 128
* 128 * 128 * 512 = 1082203060, roughly 1G.
-
Yes. Most files are small. If the file size is 436
bytes or less, then the entire file can be read and written in one disk
operation without having to do a separate access to the inode.
Problem 3:
Can we implement hard links in DOS? Why?
Solution:
No, we cannot. The reason is that the name space
in DOS is fused with the file data structures unlike in UNIX, where the
name space is in a directory structure that is separate from the file data
structures (inode). See the lectures note for more detail.
Problem 4:
In some early release of an operating system that
shall remain nameless, when a file was deleted, its sectors reverted to
the free list but they were not erased. What problems do you think may
result from this? Why do you think the blocks were not erased?
Solution:
The problem is that if these free blocks are later
allocated to a file, there is a potential that the blocks may later be
allocated to a file belonging to a different user. If the file system allows
such blocks to be read by another process (for example, in a system that
does not maintain the size of the file in bytes, or in a system that allows
files to be mapped to main memory), then a different user can read the
data that belonged to another user.
Problem 5:
You are designing a file system from scratch.
The disk driver allows you complete control over the placement of data
on the disk. Assuming that you have settled for a File Allocation Table
(FAT) architecture, where would be the best place to store the table on
disk?
Solution:
The best place would be the middle track within
the disk. The reason is that under an elevator scheduling policy, the middle
track would be visited by the disk head twice in one round trip, with the
time between the two visits being the maximum for any track. This is the
most desirable condition for a track that will be heavily accessed.
Problem 6:
Pooh Software Ltd. is selling a file system that
uses a UNIX-like file system with multi-level indexing. For more reliability,
the inode array is actually replicated on the disk in two different places.
The intent is that if one or a group of sectors that are storing either
replica of the array become bad, the system can always recover from the
replica. Discuss the effect of having this replicated data structure on
performance.
Solution:
Updates to inodes will have to be done to both
copies. This will decrease the performance of operations that attempt to
modify the inodes, such as allocating a new file, deleting a file, extending
the size of a file, or opening a file for updates (among perhaps many others).
However, reading information from an inode can be made faster with clever
disk scheduling. To read a particular inode, we schedule a read from either
track that is closest to the current head position.
Problem 7:
The FAT file system uses 16-bit numbers to represent
the cluster number that starts the linked list of the clusters that are
implementing a file. Explain the implications of limiting the cluster numbers
to 16 bits. Modify the FAT file system to use 32-bit numbers to represent
the clusters and show how the limitations of FAT can be lifted.
Solution:
Using 16-bit numbers to express the clusters allows only up to 32K clusters
per disk (because negative numbers are needed to express end of file and
end of table symbols).. For a large disk the cluster size, which is the
unit of allocation for files, must become excessively large (e.g.
a cluster must be 64K in a 2GB disk paritition.). This results in tremendous
internal fragmentation for files. Also, it limits the size of a disk partition
using FAT to only 2GB.
To lift these restrictions, we use 32-bit numbers to express the clusters.
However, one must be carefuly because this arrangement potentially allows
up to 2G entries in the FAT table, each consisting of 4 bytes! Therefore,
we need to store the actual size of the FAT table somewhere on disk, and
use only the minimum number of entries that would allow the FAT table to
use 32-bit numbers for a desired cluster size. For example, a 4GB disk
can be subdivided to 8M clusters of 512 bytes each. The size of the table
required would be 32MB, or about less than 1% of the disk size.
Problem 8:
Contiguous allocation of files leads to disk fragmentation.
Is this internal or external fragmentation?
Solution:
Both! A file is still allocated in disk block
units, which means the last block in a file would be half empty (internal
fragmentation). Additionally, contiguous allocation results in external
fragmentation because it could be the case that there many small unallocated
fragments between files and they cannot be used to allocate a file whose
size is greater than the size of any fragment, but less than the total
size of the free space.
Problem 9:
Can we implement symbolic links in DOS (FAT file
system)? If so, show how, and if not, explain why.
Solution:
Yes. It can be done by assigning a bit in the directory to indicate
that this entry is a symbolic name. Then, the name is stored as part of
the file data of the symbolic link name. The operating system must then
be modified to recognize the bit that expresses whether this is a symbolic
link, and therefore perform an extra search during lookups.
Problem 10:
Consider an indexed file allocation using index
nodes (inodes). An inode contains among other things, 7 indexes, one indirect
index, one double index, and one triple index.
-
What usually is stored in the inode in addition to the indexes?
-
What is the disadvantage of storing the file name
in the inode? Where should the file name be stored?
-
If the disk sector is 512 bytes, what is the maximum
file size in this allocation scheme?
-
Suppose we would like to enhance the file system
by supporting versioning. That is, when a file is updated, the system creates
new version leaving the previous one intact. How would you modify the inode
allocation scheme to support versioning? You answer should consider how
a new version is created and deleted.
-
In a file system supporting versioning, would
you put information about the version number in the inode, or in the directory
tree? Justify your answer.
Solution:
- An inode usually stores indexes and:
- the file size in bytes,
- special flags to indicate if the file is of a special
kind (directory, symbolic links)
- time stamps (creation date, modification date, last read date),
- a reference count of how many names refer to that file from the name space,
- owner identification (e.g. in UNIX, user id and group id), and
- security crendential (who should be able to read the file)
-
Storing the file name in the inode limits the flexibility
of the file system and precludes the use of hard links. Also, since it
is desirable to have relatively long file names, it would be cumbersome
to store variable size character arrays in the inode structure (or wasteful
if a certain maximum is defined).
-
We must first determine the size of an index. For
a 2-byte index, we can have 65536 disk blocks, i.e. 512 * 65536 = 32MB.
But a triple index structure can express more disk blocks. Therefore, we
go to a 4-byte indexing scheme (3-byte indexing scheme are not attractive
and are not sufficient). A 4-byte indexing scheme therefore gives a maximum
file size of 7*512 + 128*512 + 128*128*512 + 128*128*128*512 = 108219952
or about 1Gbytes
-
We augment the inode structure such that different
versions of the file share the common blocks. When a new version is created,
we copy the information from the older version into the new one. Also,
we will need to add a reference count to every disk block, and set that
reference count to 1 when a block is allocated to a single file. The reference
count is incremented whenever a block is shared among different versions.
Then, when a version is modified, we perform a copy-on-write like policy
and decrement the reference count of the disk blocks and actually copy
the modified disk blocks and make them only accessible to the new version.
-
It is better put in the directory space in order
to make it easier for the user to relate the different versions of the
same file.
Problem 11:
The LoneStar backup system for UNIX works as follows:
At the beginning of each week, the backup system traverses the file system
tree structure and saves all directories and files. This stage is called
full backup. Then, on a daily basis, it performs incremental backups. It
does so by traversing the directory tree, and saving only the files whose
time stamps show that they have been modified since the previous incremental
backup. Note that the LoneStar system does not follow the symbolic links
when saving files, instead storing them as symbolic links. This preserves
the integrity of the symbolic link.
-
What happens if the backup system were to follow
the symbolic links and save the files the link points to?
-
Identify problems with this backup scheme.
-
Instead of doing the above, the KeyStone backup
system requires the file system to maintain a bit map for every sector
on disk. If a disk sector is updated, the file system sets the corresponding
bit to one. When the KeyStone system performs an incremental backup, it
saves only the disk sectors whose bits are set, then it resets the entire
bit map. Compare between the two backup schemes. What are the common
problems between them? What are the key advantages of each? Which one would
you use?
Solution:
-
That would be unfortunate, because should the file
is restored from the backup version, it will no longer be a symbolic link.
Instead, it will be the file that happened to be pointed to by the link
at the time of the backup, which is not semantically correct. For correct
operation, the backup system must restore a symbolic link.
-
For large files, it is sufficient that a single byte
be modified to have the backup system save the entire file. Ie., the backup
system is not smart about trying to reduce the amount of space that need
to be saved (it saves the entire file if it has been modified, instead
of just saving the difference between the old and new file). However, this
is not a serious problem.
-
The main difference between the two backup systems
is that the KeyStone operates at the level of the flat file system, while
the LoneStar system operates at the level of the name space. The KeyStone
may look smarter in that it will avoid saving disk blocks that have not
been modified, but it has several serious problems. First, it requires
the filesystem to manipulate the bitmap, which adds serious overhead to
day-to-day operation. Second, since it operates at the level of the flat-file
system, it becomes problematic when individual files are to be restored
to a different file system. Third, it may still save disk blocks that have
been modified but are currently on the free list (if they have been modified
then deallocated).
Problem 12:
Explain what happens to the disk in UNIX when a user in a
text editor saves a new file called
"foo" into the current directory. Assume that foo is 15678 bytes in
length, that a disk block is 1024 bytes, that the file system supports
up to 2^32
sectors, and that an inode contains 10 direct block pointers, 1 indirect block
pointer, 1 doubly-indirect block pointer, and 1 triply-indirect block pointer.
The system uses on-disk free maps for tracking free blocks and free inodes.
Here's the set of actions that you may assume that the editor makes:
fd = open(foo, O_CREAT|O_WRONLY);
p = &editBuffer;
numBlocks = editBufferSize / 1024;
for (i = 0; i < numBlocks; i++) {
write(fd, p, 1024);
p += 1024;
}
lengthOfLastPartialBlock = editBufferSize % 1024;
write(fd, p, lengthOfLastPartialBlock);
close(fd);
Assume that creating a file is synchronous -- all writes to
disk complete before the call returns. Assume that write() calls are
asynchronous (they write data and metadata to memory, but don't force the
writes to disk), but that close() does not return until all writes to the file
and metadata are safely on disk. Assume that all of the data structures that
must be read to perform this action are already in the cache.
-
Assuming
the system does not use logging, explain what blocks get written out to the
disk and what each block contains.
Solution:
// fd = open(foo, O_CREAT|O_WRONLY) -- this creates the file
// suppose inode N is initially free and is allocated for this file
write free inode map (mark entry N as "used" to allocate the inode)
write inode N (all pointers are "NULL" -- 0-length file)
write file block for directory containing "foo" by adding entry "foo -> N"
(assume that this does not increase the number of blocks in that directory)
// 16 write() calls cause no disk accesses
// close
write updates to free block map to allocate 17 blocks -- 16 data blocks + 1 indirect block
write inode containing pointer to 10 data blocks and the indirect block and marking length of file as 15678 bytes
write indirect block containing pointer to 6 data blocks
write 16 data blocks
-
Suppose
the machine crashes after writing the inode, free map, and data blocks, but
before writing any indirect blocks. Describe how, if not fixed when the system
reboots, such an inconsistency could cause security violations in which one
user could access the files of another user (be specific: How would this
happen? Which blocks are vulnerable? Can any of the user's data be exposed to
other users? Can any other user's data be exposed to this user?)
Solution:
In this situation, the inode points to an indirect block that contains
garbage. This means that it could contain
pointers to blocks allocated to other files (and other users).
The user will be able to read up to 6 blocks of
other users (reading the last 6 blocks of the file will follow
these bogus pointers.) Other users may also read
this user's data (future writes by this user to those blocks
will follow these bad pointers and puts data into
other users' files; this data may now be read by other users.)
- Suppose
you were to implement write-ahead logging to fix this problem. Describe exactly
what your system would write to the log to satisfy this user's requests, when
the writes occur, and when the system can "apply" the updates --
copy them to their normal positions on disk.
Solution:
A simple answer is to make the open correspond to a "transaction begin"
and the close correspond to a "transaction end". Then all of the above
writes will first go to the log, then the close would also write a
"commit" to the log. Then all writes in the log can be appplied.
Problem 13:
-
An engineer has designed a FAT-like system and he has used 24 bits for
each entry. For a 32-GB disk, what is the minimum size of a file allocation
in this system?
Justify your answer.
Solution:
A 32 GB disk has 2^35 bytes of storage; if each entry in the FAT has
24 bits, then there can be at most 2^24 allocation chunks (one per FAT
entry), so each allocation chunk must be 2^11 bytes = 2KB
-
Consider an index-based file system with the inode containing 64 indexes,
1 indirect index pointing to a disk block containing an array of direct
indexes, and 1 2-level index in the usual way. Assume that each index takes
4 bytes.
- What is the maximum file size under this arrangement,
if a disk block is 1024 bytes? Explain how do you compute
this maximum size.
Solution:
An indirect block has 1024 bytes * 1 index/4 bytes = 256 pointers
So a file has at most 64 + 256 + 256*2 blocks and (64 + 256 * 256^2) *
1024 bytes.
- How many disk accesses does it take to read one disk block at location
3000321 within a file, assuming no caching. Justify your answer.
Solution:
Block 3000321 is block number 2930, which means we have to read
the inode, the 2-level index, a direct index, and the data block. 4 reads.
Problem 14:
Versioning: It is often desirable for users to
maintain different versions of the same file (for example, during program
development, for recovering in the case of wrong updates applied to a version,
etc.). The VMS file system implements versioning by creating a new copy
for the file every time it is opened for writing. The system also supported
versioning in the naming structure, appending a version number to the name
of each file. For instance, when foo.c;1 is updated, the system creates
foo.c;2, etc. The system implemented commands to manipulate versions, for
example, the PURGE command would delete all versions except the most recent
one. Also, users could disable versioning for some files. Discuss the pros
and cons for this scheme.
Solution:
The main advantage of this scheme is its simplicity
There really is no support that is fundamentally needed from the operating
system. Instead, application programs and the shell can be all linked with
a modified library that creates the new version whenever a file is open
for update. The creation of the file can be done with the available system
calls. For example, in a UNIX-like system this can be implemented by having
the open() library stub actually calls creat() with the new name, then
do the copy, then open the newly created file.
The obvious disadvantage of this scheme is performance
and overhead. Opening a file to update a single byte would have to make
an entire file copy. Also, there is an overhead in storage in that even
though multiple versions of the same file have common information, they
may not attempt to share the disk blocks that contain the common information.
This results in a waste in space. However, such sharing is possible only
with non-trivial modifications to the file system.
Problem 15:
An implementation of the FSCK program traverses
the file system tree and builds two lists of the disk blocks. One list
contains the sectors that are shown to be in use, while the other reads
the free disk-block information on disk. If the two lists for 4 blocks
were as shown:
In use |
0 | 1 | 0 |
1 |
Free |
0 | 0 | 1 |
1 |
Identify what are the problems, if any, and
what should fsck do for each block.
Solution:
The (0, 0) combination is a problem because it
shows the disk block not belonging to any file, and yet it is not on the
free list. Fsck should mark the block as used and attempts to find where
it should belong to. If it fails, it should create a recovered file in
the lost+found directory and includes that block there.
The (1,1) combination is a problem because it
shows the disk block pertaining to a file an yet also on the free list.
Fsck should mark the block as used and keep the block to the file where
it belongs.