5.2.2 Get the File’s i node
5.2.2.1 Get the i node of the Directory File
The process of program calling to get the i node is shown in Figure 5.4.
current->close_on_exec &= ~(1<<fd); //set close-flag as 0 (will be explained in Chapter 6) f = 0+file_table;
for (i=0 ; i<NR_FILE ; i++,f++) //fine free item in file_table[64]
if (!f->f_count) break;
if (i>=NR_FILE) // check file_table[64] is beyond limits or
//not(Maximum is 64)
return -EINVAL;
(current->filp[fd] = f)->f_count++; // bind *filp[20] of current process with corresponding …… //item in file_table[64] and add up amount of file
//handle
}
i node of root directory file inode_table[32] in the system i node of mnt file directory Super block
Root directory fileVisual diskDisk mnt directory item
i node of root directory file of disk
i node of user directory file i node of user1 directory file i node of user2 directory file i node of hello.txt file
Root directory file of diskUser directory fileUser1 directory fileUser2 directory file User directory itemUser1 directory itemUser2 directory itemHello.txt directory item Figure 5.3Process to parse the file path.
The goal of acquiring the i node of the directory file is achieved by calling open_
namei(). The code is as follows:
Open_namei() will first set the flag and mode of the opened file as per user’s request.
The code is as follows:
mnt User User1 User2
Execute open_namei and get the i node of hello.txt file
Execute dir_namei, analyze path and get the i node of user2 directory file
Execute get_dir
Traverse from root i node
Call find_entry, by given the i node of directory file and directory name, get the directory item
Call i_get function, by given the i node number provided by the directory item and the device number provided by i node, get i node of directory file
Call find_entry function, by given i node directory name, get the directory item
Call i_get function, by given the i node number provided by the directory item and the device number provided by i node, get the i node of directory file
hello.txt
Figure 5.4 Process of getting the i node.
//path name:fs/open.c:
int sys_open(const char * filename,int flag,int mode) {
……
if ((i = open_namei(filename,flag,mode,&inode))<0) { //get inode of hello.txt file.
current->filp[fd] = NULL; //*filp[20] are set Null if
//inode is not found
f->f_count = 0; //citation count in file_
//table[64] is set 0 if inode
//is not found.
return i;
} ……
}
After setting, dir_namei() is called to analyze the file path and traverse the i nodes of all directory files to find the i node of the last directory file, namely, the topmost i node.
Dir_namei() will call get_dir() to get the i node. The code is as follows:
//path name:fs/namei.c:
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name) //pathname points to/mnt/user/user1/user2/hello.txt {
//path name:include/fcntl.h: //octal form:
……
#define O_ACCMODE 00003 //file access mode mask
#define O_RDONLY 00 //read-only flag
#define O_WRONLY 01 //write-only flag
#define O_RDWR 02 //read-write flag
#define O_CREAT 00100 /* not fcntl */ //create new file flag
#define O_EXCL 00200 /* not fcntl */ //process exclusive flag
#define O_NOCTTY 00400 /* not fcntl */ //no control terminal flag
#define O_TRUNC 01000 /* not fcntl */ //truncate flag
#define O_APPEND 02000 //append flag
#define O_NONBLOCK 04000 /* not fcntl */ //non-block flag
#define O_NDELAYO_NONBLOCK
……
//path name:include/fcntl.h://binary form:(notice the rule in setting flag)
……
#define O_ACCMODE 0000 0000 0000 0011
#define O_RDONLY 0000 0000 0000 0000
#define O_WRONLY 0000 0000 0000 0001
#define O_RDWR 0000 0000 0000 0010
#define O_CREAT 0000 0000 0100 0000/* not fcntl */
#define O_EXCL 0000 0000 1000 0000/* not fcntl */
#define O_NOCTTY 0000 0001 0000 0000 /* not fcntl */
#define O_TRUNC 0000 0010 0000 0000 /* not fcntl */
#define O_APPEND 0000 0100 0000 0000
#define O_NONBLOCK 0000 1000 0000 0000 /* not fcntl */
#define O_NDELAYO_NONBLOCK
……
//path name:fs/namei.c:
int open_namei(const char * pathname, int flag, int mode,
struct m_inode ** res_inode) //pathname is/mnt/user/user1/user2/hello.txt {
const char * basename; //basename records the address of’/’
int inr,dev,namelen;
struct m_inode * dir, *inode;
struct buffer_head * bh;
struct dir_entry * de; //de points to directory content
if ((flag & O_TRUNC) && !(flag & O_ACCMODE)) //if file is read-only and length is 0 flag | = O_WRONLY; //set the file to write-only mode & = 0777 & ~current->umask;
mode | = I_REGULAR; //set the file to regular
if (!(dir = dir_namei(pathname,&namelen,&basename))) //parse path name and get topmost inode return -ENOENT;
if (!namelen) { /* special case: ‘/usr/’ etc */
if (!(flag & (O_ACCMODE|O_CREAT|O_TRUNC))) { *res_inode = dir;
return 0;
} iput(dir);
return -EISDIR;
}
bh = find_entry(&dir,basename,namelen,&de); //find directory entry of target file
//through topmost inode
……
}
Get_dir() will get the i node content. The process has been introduced preliminarily in Section 4.1.1: the work is complete through “find directory entry and get i node through directory entry” continuously.
The corresponding function in finding the directory entry is find_entry().
The corresponding function in getting the i node is iget().
These two functions are introduced in detail here. The code is as follows:
//pathname:fs/namei.c:
static struct m_inode * get_dir(const char * pathname) {
char c;
const char * thisname;
struct m_inode * inode;
struct buffer_head * bh;
int namelen,inr,idev;
struct dir_entry * de;
if (!current->root || !current->root->i_count) //current root inode does not exist or panic(“No root inode”); //the citation count is 0
if (!current->pwd || !current->pwd->i_count) //current directory root inode does not exist or panic(“No cwd inode”); //the citation count is 0
if ((c = get_fs_byte(pathname)) = =’/’) { //identify the first character of inode = current->root; //”/mnt/usr/usr1/usr2/hello.tet” is ‘/’
pathname++;
} else if (c)
inode = current->pwd;
else
return NULL; /* empty name is bad */
inode->i_count++; //citation count increases by 1
while (1) { //cycle the statements below until find topmost inode thisname = pathname; //thisnamepoints to ‘m’ first
if (!S_ISDIR(inode->i_mode) || !permission(inode,MAY_EXEC)) { iput(inode);
return NULL;
}
for(namelen = 0;(c = get_fs_byte(pathname++))&&(c! = ‘/’);namelen++) //the loop breaks each time when finding ’/’ in string or c is ’\0’
/* nothing */; //notice this ‘;’
if (!c)
return inode;
char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname))) //call get_dir() to parse path name and get inode return NULL;
basename = pathname;
while (c=get_fs_byte(pathname++)) //after tranversing, pathname points to ’/0’ in string.
// tranverse each string in “/mnt/user/user1/user2/hello.txt” and copy one character to c in //each cycle
if (c==’/’)
basename = pathname; //after tranversing string, basename points to the last ‘/’
*namelen = pathname-basename-1; //compute the name length of ”hello.txt”
*name = basename; //get the address of ‘/’ before hello.txt return dir;
}
The main task of find_entry() is as follows: First, the function determines the amount of directory entries in the directory file by the i node. Then, begins from the first logic block corresponding to the directory file, the logic blocks are continuously loaded into the buffer from the peripherals, and the function searches the specified directory entry until it is found.
The code is as follows:
if (!(bh = find_entry(&inode,thisname,namelen,&de))) {//get directory entry through inode iput(inode); //release the inode if assigned directory entry is not found return NULL; //and return NULL
}
inr = de->inode;//get inode number from directory entry idev = inode->i_dev;//get device number from inode brelse(bh);
iput(inode); //release all inodes of each directory file after used
//to avoid wasting space in inode_table
if (!(inode = iget(idev,inr))) //get inode return NULL;
} }
//code path:include/linux/fs.h:
#define BLOCK_SIZE 1024 //path name:fs/namei.c:
static struct buffer_head * find_entry(struct m_inode ** dir, const char * name, int namelen, struct dir_entry ** res_dir) //get directory entry of mnt
{
int entries;
int block,i;
struct buffer_head * bh;
struct dir_entry * de;
struct super_block * sb;
#ifdef NO_TRUNCATE
if (namelen > NAME_LEN)//return “NULL” if the name length exceeds 14 under the premise of NO_TRUNCATE return NULL;
#else
if (namelen > NAME_LEN)//or truncate the name length namelen = NAME_LEN;
#endif
entries = (*dir)->i_size/(sizeof (struct dir_entry));
//calculate the amount of directory entries according to file length information namely i_size
*res_dir = NULL;
if (!namelen)//examine the file name length is 0 or not return NULL;
/* check for ‘..’, as we might have to do some “magic” for it */
if (namelen = =2 && get_fs_byte(name) = =’.’ && get_fs_byte(name+1) = =’.’) { ……//handle the case if directory entry is..
}
if (!(block = (*dir)->i_zone[0]))
//determine the first logic block number of directory file is not 0 return NULL;
if (!(bh = bread((*dir)->i_dev,block)))//read logic blocks into specified buffer return NULL;
i = 0;
de = (struct dir_entry *) bh->b_data;//de points to the head address of buffer while (i < entries) {//search for mnt in all directory entries
if ((char *)de > = BLOCK_SIZE+bh->b_data) {
//if specified directory entry is not found in the logic block brelse(bh);
bh = NULL;
The main task of iget() is to get the i node according to the i node id and the device id of the directory entry. The specific access is as follows: First, the function searches the i node in inode_table[32] and uses it if the specified i node already exists. Since each file has only one i node and the same file could be referred by multiple processes, if the specified i node is already loaded by other processes, then loading the i node repeatedly would not only cause confusion but also waste time.
Besides, if any i node is mounted with the file system, the root i node of the file system would be loaded and it becomes the starting point for searching files in another file system.
The i node of “mnt” is the first one to be obtained. According to Section 5.1, the file system is mounted with the i node of mnt; thus, the root i node of the file system need to be loaded in the i node table.
The code is as follows:
//then continue to search for mnt directory file after reading the next logic block into buffer if (!(block = bmap(*dir,i/DIR_ENTRIES_PER_BLOCK)) ||
!(bh = bread((*dir)->i_dev,block))) { i + = DIR_ENTRIES_PER_BLOCK;
continue;
}
de = (struct dir_entry *) bh->b_data;
}
if (match(namelen,name,de)) {//match the directory entry *res_dir = de;//if mnt is found, pass it to *res_dir return bh;
} de++;
i++;
}
brelse(bh);
return NULL;//return NULL if mnt is not found after all directory file are examined }
//code path:fs/namei.c:
struct m_inode * iget(int dev,int nr) //get inode of mnt, dev and nr specify device id //and inode id
{
//struct m_inode * inode, * empty;
if (!dev)//if device number is null panic(“iget with dev = =0”);
empty = get_empty_inode(); //get an free inode item from inode_table[32]
inode = inode_table;
while (inode < NR_INODE+inode_table) {
//examine specified inode is loaded already or not, in this case the inode of mnt is loaded before if (inode->i_dev ! = dev || inode->i_num ! = nr) {
//compare device id and inode id to specified one inode++;
continue;
}
wait_on_inode(inode);
//wait until the inode is unlocked
if (inode->i_dev ! = dev || inode->i_num ! = nr) {//this inode may have been released inode = inode_table; //thus traverse inode_table[32] and if not removed continue; //the inode could be used, the inode of mnt is not
//removed in this case
}
inode->i_count++;
if (inode->i_mount) { //if the inode is mount with file system(in mnt case) int i;
After preparation, read_inode() is called to read i nodes from peripherals (hard disk), and the i nodes are loaded into inode_table[32].
The code is as follows:
After acquiring the root i node of the file system in the hard disk, get_dir() will con- tinuously call find_entry() and iget() to get the i node of user and user1, that is, the direc- tory files, in order to get the i node of user2, namely, the topmost i node. The execution procedure is the same as finding the i node of mnt; the difference lies in the fact that the
for (i = 0 ; i<NR_SUPER ; i++) // search for peripheral of file system,namely the
//super block of hd1
if (super_block[i].s_imount==inode) break;
if (i > = NR_SUPER) { //inode is not mount with file system printk(“Mounted inode hasn’t got sb\n”);
if (empty) iput(empty);
return inode;
}
iput(inode);
dev = super_block[i].s_dev; //get device id by super block of hd1
nr = ROOT_INO; //determine root inode of peripheral and ROOT_INO is1 inode = inode_table; // traverse inode of peripheral(hard disk) again and
//determine
//it has been mount already
continue;
}
if (empty) iput(empty);
return inode;
}
if (!empty) //there’s no free item in inode_table[32]
return (NULL);
//root inode of hd1 is not found so that it will be loaded inode = empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode); //read inode;
return inode; // because inode of mnt is mount with file system then root
//inode of hd1 is returned
}
//code path:fs/inode.c:
static void read_inode(struct m_inode * inode) //read inode {
struct super_block * sb;
struct buffer_head * bh;
int block;
lock_inode(inode); //lock specified inode in inode_table[32]
if (!(sb = get_super(inode->i_dev))) //get super block of inode, which has been loaded panic(“trying to read inode without dev”);
block = 2 + sb->s_imap_blocks + sb->s_zmap_blocks +
(inode->i_num-1)/INODES_PER_BLOCK; //determine the logic block which stores the
//specific inode
if (!(bh = bread(inode->i_dev,block))) //load the logic block into specified buffer panic(“unable to read inode block”);
*(struct d_inode *)inode = // load inode into specified location in
//inode_table[32]
((struct d_inode *)bh->b_data)
[(inode->i_num-1)%INODES_PER_BLOCK];
brelse(bh);
unlock_inode(inode); //unlock inode after table item operation }
i nodes of these directory files are not mounting with the file system, and there is also disparity in the execution route.
The code is as follows:
The function returns the i node of user2 after execution and gets back to dir_namei().
The code is as follows:
//code path:fs/namei.c:
struct m_inode * iget(int dev,int nr) //get inode of following directory files {
struct m_inode * inode, * empty;
if (!dev)//if device id is 0, system crashes.
panic(“iget with dev = =0”);
empty = get_empty_inode(); //get an free item from inode_table[32]
inode = inode_table;
while (inode < NR_INODE+inode_table) {
//examine whether the inode has been load already, inodes of other directory files are //never loaded.
if (inode->i_dev ! = dev || inode->i_num ! = nr) { //the loop breaks after compare
inode++;
continue;
} ……
}
if (!empty)
return (NULL);
//prepare to load the inode beacause it is not found in other directory files inode = empty;
inode->i_dev = dev;
inode->i_num = nr;
read_inode(inode); //read inode
return inode; //continue to search and return the inode of user, user1 and user2
//successively
}
//code path:fs/namei.c:
static struct m_inode * dir_namei(const char * pathname,
int * namelen, const char ** name) //pathname is the pointer to/mnt/user/user1/user2/
//hello.txt
{ char c;
const char * basename;
struct m_inode * dir;
if (!(dir = get_dir(pathname))) //the function to get inode return NULL;
while (c = get_fs_byte(pathname++)) // after traverse, pathname points to ’/0’ at the end //of string traverse each string in “/mnt/user/user1/user2/hello.txt” and copy one character //to c in each loop
if (c = =’/’)
basename = pathname; //basename points to the last ’/’ after traverse *namelen = pathname-basename-1; //calculate the name length of “hello.txt”
*name = basename; //get the address of ‘/’ before hello.txt
return dir;
}
Finally, the function gets back to open_namei() and returns the i node of user2, that is, the topmost i node.
After getting the topmost i node by analyzing the path name, the main task of open_
namei() is completed. The next procedure is to identify the i node of hello.txt through the topmost i node.