|
|
||||||||||||||
|
||||||||||||||
|
||||||||||||||
rawfs.c |
||||||||||||||
| Skip to line: 8050 - 8100 - 8150 - 8200 - 8250 - 8300 | ||||||||||||||
| If you have a comment for rawfs.c, please click here. | ||||||||||||||
|
|
||||||||||||||
|
Before going through this file, read sections 5.6.2, 5.6.3, and 5.6.4
in Operating
Systems. These sections deal with file system fundamentals and
describe super blocks, bit maps, zones, and inodes. You're not going
to understand rawfs.c until you read these 3 sections (they're only
a total of 8 pages anyway). I refer to specific pages from these
3 sections in my comments.
The functionality of the boot monitor's file system is a subset of the functionality of the minix OS file system (i.e. the boot monitor's file system is a "raw" file system). For example, a user can't create a new file from the boot monitor. The functionality of the boot monitor's file system is limited to looking up information about files, directories, and the file system itself. | ||
|
#define is a preprocessor command. The preprocessor replaces
all occurrences of the first string (in this case, "nil") with
the second string (in this case, "0") before compilation.
The string "nil" is used to increase readability. | ||
|
_POSIX_SOURCE and _MINIX are macros that are used
by library routines. In fact, nearly all macros that begin with an
underscore (_) are specific to library routines.
The POSIX standard was created to improve portability between UNIX systems. The 12th paragraph of section 2.6.2, lines 01184-011200, and lines 01241-01245 in Operating Systems describe the _POSIX_SOURCE and _MINIX macros. | ||
|
A function must be either defined or declared in a file before it can
be used. Header files (files ending in .h) make the task of declaring
variables easier.
Before compilation begins, the preprocessor replaces any #include <filename.h> statement with the contents of filename.h. If the filename is enclosed in < and >, the preprocessor searches for the file in a default directory (typically /usr/include) and other directories specified by the -I option of the compiler (see line 8016). If the filename is quoted (see line 8021), the preprocessor looks for the include file in the same directory that the source file is found. (In this case, rawfs.c is the source file and is found in the /usr/src/boot directory.) As an example, strncpy() (see line 8188) is declared in the file string.h (see line 8011). string.h is located in the directory /usr/include. Header files, in addition to containing function declarations, also frequently contain #defines. For example, ROOT_INO (see line 8273) is #defined in rawfs.h. Since rawfs.h is quoted (see line 8021), the preprocessor searches for rawfs.h in the same directory as rawfs.c and then the default directories. Indeed, both files are in the /usr/src/boot directory. | ||
| 8008 #include <sys/stat.h> |
| 8009 #include <stdlib.h> |
| 8010 #include <limits.h> |
| 8011 #include <string.h> |
| errno is declared in errno.h (see line 00230 in the book) as extern. Memory for a variable can be allocated in only one file (i.e. the variable is "defined") but the variable must be declared as extern in every other file that accesses it. However, memory is not allocated for errno in boothead.s, boot.c, bootimage.c, or rawfs.c. If you understand how memory is allocated for errno, please submit a comment to the site which will be displayed below. | ||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
8013 #include <minix/config.h>
8014 #include <minix/const.h> 8015 #include <minix/type.h> |
| If a filename is enclosed in < and >, the preprocessor searches for the file in the default directory (typically /usr/include) and other directories specified by the -I option of the compiler. In Makefile , the parent directory (..) is specified by the -I option. Makefile is found in /usr/src/boot; therefore, the parent directory is /usr/src. After the preprocessor unsuccessfully looks for const.h in the /usr/include/kernel directory, the preprocessor looks for (and finds) const.h in the /usr/src/fs directory. Note that the preprocessor can't find const.h in /usr/src/fs since this directory doesn't exist. | ||
| 8017 #include <fs/type.h> |
| buf.h (see line 20113 in the book) #includes <sys/dir.h> (line 02400 in the book), which declares struct direct (see line 8162). | ||
|
8019 #include <fs/super.h>
8020 #include <fs/inode.h> 8021 #include "rawfs.h" 8022 |
| readblock() is defined in boot.c. Since this is the only function from boot.c that is called from rawfs.c and since readblock() isn't declared in boot.h (so we can't just #include boot.h), readblock() is declared here. | ||
| 8024 |
|
Read page 459 in Operating
Systems to understand the differences between the V1 and V2 file systems.
I do not cover sections of code specific to Unix Version 7 (FLEX). | ||
| 8033 |
|
The scope of an external static variable is only within the
file in which the variable is defined. Note that "external" variables
are variables that are declared outside of all functions.
nr_dzones, nr_indirects, inodes_per_block are set on lines 8071-8073 for the V2 file system and 8077-8079 for the V1 file system. nr_dzones is the number of direct zones in an inode and is 7 for both the V1 and V2 file systems (section 5.6.4). nr_indirects is the number of zone addresses in an indirect block. A block is 1024 bytes for both the V1 and V2 file systems and zone addresses are 2 bytes for the V1 file system and 4 bytes for the V2 file system. Therefore nr_indirects is equal to 512 (1024/2) for the V1 file system and 256 (1024/4) for the V2 file system. A V1 inode is 32 bytes and a V2 inode is 64 bytes; therefore inodes_per_block is 32 (1024/32) for the V1 file system and 16 (1024/64) for the V2 file system. | ||
| I do not cover sections of code specific to Unix Version 7 (FLEX). | ||
|
8039 #include <dirent.h>
8040 #define direct _v7_direct 8041 #else 8042 #include <sys/dir.h> 8043 #endif |
| Super blocks are covered in section 5.6.2 and a diagram of the super block is shown in fig. 5-29. super is set on line 8067. struct super_block is declared in fs/super.h (see line 20721 in the book). | ||
| Inodes are covered in section 5.6.4 and a diagram of an inode is shown in fig. 5-30. curfil is set in r_stat() (see lines 8111-8120 and 8127-8136) and accessed in r_readdir() (see lines 8164 and 8166) and r_vir2abs() (line 8196). struct inode is declared in fs/inode.h (see line 20510 in the book). | ||
| Fig. 5-10 in section 5.3.1 describes single and double indirect blocks. Since a block is 1024 bytes (BLOCK_SIZE=1024) and each zone address is 2 bytes (V1 file system) or 4 bytes (V2 file system), indir[] and dindir[] hold 256 zone addresses (V1 file system) or 512 zone addresses (V2 file system). Both indir[] and dindir[] are set in r_vir2abs() (see lines 8251 and 8234) and are used in conjunction with a_indir and a_dindir (line 8052). | ||
| dirbuf[] is used in several functions as a scratch buffer (see lines 8067 and 8103). | ||
| 8051 |
| a_indir is the block number of the block that was last read into indir[] (line 8047) and d_dindir is the block number of the block that was last read into dindir[] (line 8048). | ||
| Each call to r_readdir() reads a directory entry and advances to the next entry (see line 8191). dirpos keeps track of the current directory entry. | ||
| 8054 |
| The macro fsbuf() is used (and explained) in r_stat() (line 8087) and r_vir2abs() (line 8196) | ||
| 8056 |
| The default for the minix file systems is 1 block per zone. Read section 5.6.3 for the motivation behind zones. | ||
| 8058 |
| r_super() fills in super (line 8045), sets the variables n_dzones, nr_indirects, and inodes_per_block, and returns the number of blocks in the file system. | ||
| 8063 { |
|
BOOT_BLOCK (=0) and SUPER_BLOCK (=1) are #defined
in <fs/const.h> (see lines 19560-19561 in the book). The boot
block is the first block in a bootable partition or subpartition and the
super block is the second block in a bootable partition or subpartition
(see fig. 5-28 in the book).
The super block is read into scratch (see lines 8049-8050) and then the first sizeof(super) bytes of the super block is copied to super (see line 8067). void *memcpy(void *s1, void *s2, size_t n) copies n characters from the address s2 to the address s1 and returns s1. | ||
| 8068 |
|
nr_dzones (line 8035) is the number of direct zones in an inode
and is 7 for both the V1 and V2 file systems (section 5.6.4).
nr_indirects (line 8036) is the number of zone addresses in an indirect block. A block is 1024 bytes for both the V1 and V2 file systems and zone addresses are 2 bytes for the V1 file system and 4 bytes for the V2 file system. So nr_indirects is equal to 512 (1024/2) for the V1 file system and 256 (1024/4) for the V2 file system. A V1 inode is 32 bytes and a V2 inode is 64 bytes; therefore inodes_per_block (line 8037) is 32 (1024/32) for the V1 file system and 16 (1024/64) for the V2 file system. super.s_zones (for the V2 file system) and super.s_nzones (for the V1 file system) are the respective sizes (in zones) of V2 and V1 file systems. All the macros on lines 8070-8079 are #defined in <fs/const.h> (line 19500 in the book). | ||
|
8070 if (super.s_magic == SUPER_V2) {
8071 nr_dzones= V2_NR_DZONES; 8072 nr_indirects= V2_INDIRECTS; 8073 inodes_per_block= V2_INODES_PER_BLOCK; 8074 return (off_t) super.s_zones << zone_shift; 8075 } else 8076 if (super.s_magic == SUPER_MAGIC) { 8077 nr_dzones= V1_NR_DZONES; 8078 nr_indirects= V1_INDIRECTS; 8079 inodes_per_block= V1_INODES_PER_BLOCK; 8080 return (off_t) super.s_nzones << zone_shift; 8081 } else { 8082 /* Filesystem not recognized as Minix. */ 8083 return 0; 8084 } 8085 } |
| r_stat() returns information about the file or directory with inode number inum in *stp and "remembers" the information in inode curfil (line 8046). The strategy is typically to call r_stat() and then call either r_readdir() (line 8157) or r_vir2abs() (line 8196) to give additional information about curfil. | ||
|
Fig. 5-28 describes the disk layout; the order is:
1) the boot block 2) the super block 3) the inode bit maps (one or more blocks) 4) the zone bit maps (one or more blocks) 5) inodes 6) data As an example, suppose a V2 file system (inodes_per_block=16) has 4 blocks of inode bit maps (super.s_imap_blocks=4) and 5 blocks of zone bit maps (super.s_zmap_blocks=5) and inum is 54. block = SUPER_BLOCK + 1 + super.s_imap_blocks + super.s_zmap_blocks = 1 + 1 + 4 + 5 = 11 block is the block number (0-indexed) in the booted partition where the first block containing inodes can be found. | ||
|
Continuing from lines 8094-8095 with our example:
ino_block = ((54-1)/16)=(53/16)=3 (remember - it's integer math) ino_offset = ((54-1)%16)=(53%16)=5 block = block+ino_block=11+3 block is the block number (0-indexed) in the booted partition where the inode inum=54 can be found. | ||
| 8101 |
| 8102 /* Fetch the block */ |
| block is read into scratch (see lines 8049-8050). This block contains either 16 inodes (V2 file system) or 32 inodes (V1 file system). A pointer to the struct inode (see line 20510 in the book) corresponding to inum is found on lines 8109 (V2 file system) and 8125 (V1 file system). | ||
| 8104 |
|
super must be set by r_super() (see line 8067) before
r_stat()
can be called.
In memory, an inode from a V1 file system and an inode from a V2 file system are both stored in a struct inode (see line 20510 in the book). On disk, an inode from a V1 file system is stored in a struct d1_inode (see line 19600 in the book) and an inode from a V2 file system is stored in a struct d2_inode (see line 19611 in the book). Whether the inode corresponding to inum is from a V1 file system or a V2 file system, its inode information is copied to curfil. I do not understand why d2_inode is not preceded by the keyword struct. If you understand why this keyword can be omited, please submit a comment to the site which will be displayed below. |
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||
| 8107 int i; |
| 8108 |
|
The pointer manipulations for the fsbuf() macro (line 8055)
are difficult.
fsbuf(scratch).b_v2_ino[ino_offset] is a struct d2_inode. &fsbuf(scratch).b_v2_ino[ino_offset] is the memory address of this struct d2_inode. dip= &fsbuf(scratch).b_v2_ino[ino_offset] dip= &(fsbuf(scratch).b_v2_ino[ino_offset]) ("." has higher precedence than "&") dip= &((* (struct buf *)(scratch)).b_v2_ino[ino_offset]) (expansion of fsbuf()) Let's replace this line of code: dip= &fsbuf(scratch).b_v2_ino[ino_offset] with several lines of code: struct buf *buf_ptr, buf1; d2_inode ino1; buf_ptr= (struct buf *)(scratch); buf1= *buf_ptr; ino1= buf1.b_v2_ino[ino_offset]; dip= &ino1; Note that this code is slow and wastes memory. In particular, the 2 lines buf_ptr= (struct buf *)(scratch); buf1= *buf_ptr; copy scratch (which has a size of 1024 bytes) to buf1; this copy was unnecessary in the original code. However, this new code accomplishes the same thing as the original code and since the code is broken down into several steps, it may help you to understand fsbuf(). struct buf (line 20115 in the book) contains a union. A union is a variable that can hold (at different times) objects of different types and sizes. A union is large enough to hold the largest type. For example, a long or a pointer (but only one at a time) can be stored in the following union: union { long long1; char *s1; } union1; and the members can be accessed in the following manner: union1.s1 = "hello"; union1.long1 = 50L; Note that after the second assignment (union1.long1 = 50L), union1 holds the long but no longer holds the pointer. | ||
| 8110 |
| i_mode specifies the access rights (rwx bits) and the inode type (regular file, directory, special file, etc.). | ||
| A (hard) link in minix (and Unix) is a file (or directory) that has a different name but refers to the same inode. When a file (or directory) is created, i_nlinks is equal to 1. Each additional link increments i_nlinks. Removing links or removing the original file or directory decrements i_nlinks. Only when i_nlinks equals 0 is the inode removed. | ||
| i_uid and i_gid specify the user and group id of the inode owner. | ||
| i_size is the size of the file or directory in bytes. | ||
|
i_atime is the time (in seconds since Jan. 1, 1970) of the
last access of the file or directory.
i_mtime is the time (in seconds since Jan. 1, 1970) of the last modification of the file or directory. i_ctime is the time (in seconds since Jan. 1, 1970) of the last change to the inode itself. | ||