rawfs.c
Skip to line: 8050 - 8100 - 8150 - 8200 - 8250 - 8300


Highlighted entries were made in the last day
Select a different time increment to highlight entries
Current GMT time: Jul 23 2008 21:46:18

If you have a comment for rawfs.c, please click here.
Expand/Collapse Item
Expand/Collapse Item8000    /*      rawfs.c - Raw Minix file system support.        Author: Kees J. Bot
Expand/Collapse Item8001     *                                                              23 Dec 1991
Expand/Collapse Item8002     *                                           Based on readfs by Paul Polderman
Expand/Collapse Item8003     */
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.
Expand/Collapse Item8004  #define nil 0
#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.
Expand/Collapse Item8005    #define _POSIX_SOURCE   1
Expand/Collapse Item8006    #define _MINIX          1
_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.
Expand/Collapse Item8007    #include <sys/types.h>
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>
Expand/Collapse Item8012    #include <errno.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.
Name: Christos Basil Karayiannis - Karditsa GREmail:  christos@kar.forthnet.grDate: Mar 18 2004 15:41:11 GMT
Subject:  Where is errno declared?
Reponse: At line 8012 we include header file errno.h. By doing this, we use variables defined at errno.c (/minix/src/lib/other/errno.c). All this file does is declaring errno:

------------------
#include
/* errno.c - declare variable errno
Author: F. Meulenbroeks */

int errno = 0;
------------------
 
Respond to Christos Basil Karayiannis - Karditsa GR's comment.
 
 
8013   #include <minix/config.h>
8014   #include <minix/const.h>
8015   #include <minix/type.h>
Expand/Collapse Item8016    #include <fs/const.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>
Expand/Collapse Item8018    #include <fs/buf.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
Expand/Collapse Item8023    void readblock(off_t blockno, char *buf);
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
Expand/Collapse Item8025    /* The following code handles two file system types: Version 1 with small
Expand/Collapse Item8026     * inodes and 16-bit disk addresses and Version 2 with big inodes and 32-bit
Expand/Collapse Item8027     * disk addresses.
Expand/Collapse Item8028    #ifdef FLEX
Expand/Collapse Item8029     * To make matters worse, Minix-vmd knows about the normal Unix Version 7
Expand/Collapse Item8030     * directories and directories with flexible entries.
Expand/Collapse Item8031    #endif
Expand/Collapse Item8032     */
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
8034 /* File system parameters. */
Expand/Collapse Item8035    static unsigned nr_dzones;      /* Fill these in after reading superblock. */
Expand/Collapse Item8036    static unsigned nr_indirects;
Expand/Collapse Item8037    static unsigned inodes_per_block;
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.
Expand/Collapse Item8038    #ifdef FLEX
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
8044
Expand/Collapse Item8045  static struct super_block super;        /* Superblock of file system */
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).
Expand/Collapse Item8046    static struct inode curfil;            /* Inode of file under examination */
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).
Expand/Collapse Item8047    static char indir[BLOCK_SIZE];          /* Single indirect block. */
Expand/Collapse Item8048    static char dindir[BLOCK_SIZE];         /* Double indirect block. */
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).
Expand/Collapse Item8049    static char dirbuf[BLOCK_SIZE];         /* Scratch/Directory block. */
Expand/Collapse Item8050    #define scratch dirbuf
dirbuf[] is used in several functions as a scratch buffer (see lines 8067 and 8103).
8051
Expand/Collapse Item8052    static block_t a_indir, a_dindir;       /* Addresses of the indirects. */
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).
Expand/Collapse Item8053    static off_t dirpos;                    /* Reading pos in a dir. */
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
Expand/Collapse Item8055    #define fsbuf(b)        (* (struct buf *) (b))
The macro fsbuf() is used (and explained) in r_stat() (line 8087) and r_vir2abs() (line 8196)
8056
Expand/Collapse Item8057    #define zone_shift      (super.s_log_zone_size) /* zone to block ratio */
The default for the minix file systems is 1 block per zone.  Read section 5.6.3 for the motivation behind zones.
8058
Expand/Collapse Item8059    off_t r_super(void)
Expand/Collapse Item8060    /* Initialize variables, return size of file system in blocks,
Expand/Collapse Item8061     * (zero on error).
Expand/Collapse Item8062     */
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   {
Expand/Collapse Item8064            /* Read superblock. */
Expand/Collapse Item8065           readblock(SUPER_BLOCK, scratch);
Expand/Collapse Item8066
Expand/Collapse Item8067            memcpy(&super, scratch, sizeof(super));
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
Expand/Collapse Item8069            /* Is it really a MINIX file system ? */
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   }
Expand/Collapse Item8086
Expand/Collapse Item8087    void r_stat(Ino_t inum, struct stat *stp)
Expand/Collapse Item8088    /* Return information about a file like stat(2) and remember it. */
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.
8089   {
8090          block_t block;
8091          block_t ino_block;
8092           ino_t ino_offset;
8093
Expand/Collapse Item8094            /* Calculate start of i-list */
Expand/Collapse Item8095            block = SUPER_BLOCK + 1 + super.s_imap_blocks + super.s_zmap_blocks;
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.
8096
Expand/Collapse Item8097            /* Calculate block with inode inum */
Expand/Collapse Item8098            ino_block = ((inum - 1) / inodes_per_block);
Expand/Collapse Item8099            ino_offset = ((inum - 1) % inodes_per_block);
Expand/Collapse Item8100            block += ino_block;
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 */
Expand/Collapse Item8103           readblock(block,scratch);
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
Expand/Collapse Item8105            if (super.s_magic == SUPER_V2) {
Expand/Collapse Item8106                    d2_inode *dip;
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.
Name: Christos KarayiannisEmail:  christos@nospamkar.forthnet.gr (remove "nospam")Date: Dec 01 2003 14:08:01 GMT
Subject:  Why d2_inode is not preceded by the keyword struct
Reponse: When a struct is declared e.g.
struct date{int month, day, year;} d;
we define variable d of type struct date.
One other option is to declare it as:
typedef struct date{int month, day, year;} d;
so that d, now a new variable type (and not a variable) is defined. Now we can use the new type (d) to declare other variables e.g. variable x as:
d x;
 
Respond to Christos Karayiannis's comment.
 
 
Name: Brad ZhuEmail:  zlmblue@nospam163.net (remove "nospam")Date: 08.02.03
Subject:  no subject
Reponse: struct d1_inode and d2_inode are declarated in :
/* Declaration of the V1 inode as it is on the disk (not in core). */
typedef struct { /* V1.x disk inode */
mode_t d1_mode; /* file type, protection, etc. */
uid_t d1_uid; /* user id of the file's owner */
off_t d1_size; /* current file size in bytes */
time_t d1_mtime; /* when was file data last changed */
gid_t d1_gid; /* group number */
nlink_t d1_nlinks; /* how many links to this file */
u16_t d1_zone[V1_NR_TZONES]; /* block nums for direct, ind, and dbl ind */
} d1_inode;

/* Declaration of the V2 inode as it is on the disk (not in core). */
typedef struct { /* V2.x disk inode */
mode_t d2_mode; /* file type, protection, etc. */
u16_t d2_nlinks; /* how many links to this file. HACK! */
uid_t d2_uid; /* user id of the file's owner. */
u16_t d2_gid; /* group number HACK! */
off_t d2_size; /* current file size in bytes */
time_t d2_atime; /* when was file data last accessed */
time_t d2_mtime; /* when was file data last changed */
time_t d2_ctime; /* when was inode data last changed */
zone_t d2_zone[V2_NR_TZONES]; /* block nums for direct, ind, and dbl ind */
} d2_inode;

 
Respond to Brad Zhu's comment.
 
 
8107                   int i;
8108
Expand/Collapse Item8109                    dip= &fsbuf(scratch).b_v2_ino[ino_offset];
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
Expand/Collapse Item8111                  curfil.i_mode= dip->d2_mode;
i_mode specifies the access rights (rwx bits) and the inode type (regular file, directory, special file, etc.).
Expand/Collapse Item8112                  curfil.i_nlinks= dip->d2_nlinks; 
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.
Expand/Collapse Item8113                  curfil.i_uid= dip->d2_uid;
Expand/Collapse Item8118                  curfil.i_gid= dip->d2_gid;
i_uid and i_gid specify the user and group id of the inode owner.
Expand/Collapse Item8115                  curfil.i_size= dip->d2_size;
i_size is the size of the file or directory in bytes.
Expand/Collapse Item8116                  curfil.i_atime= dip->d2_atime;
Expand/Collapse Item8117                  curfil.i_mtime= dip->d2_mtime;
Expand/Collapse Item8117                  curfil.i_ctime= dip->d2_ctime;
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.