1. Execute ext2_add_link. The function is defined as follows:
Int ext2_add_link (struct dentry * dentry, struct inode * inode)
{
Struct inode * dir = dentry-> d_parent-> d_inode; // inode of the parent node
Const char * name = dentry-> d_name.name; // directory name
Int namelen = dentry-> d_name.len; // Name Length
Unsigned chunk_size = ext2_chunk_size (DIR); // block size
Unsigned reclen = ext2_dir_rec_len (namelen); // multiple of 4
Unsigned short rec_len, name_len;
Struct page * page = NULL;
Ext2_dirent * de;
Unsigned long npages = dir_pages (DIR); // page number
Unsigned long N;
Char * kaddr;
Loff_t Pos;
Int err;
For (n = 0; n <= npages; n ++) {// traverse each page
Char * dir_end;
Page = ext2_get_page (Dir, n); // read the page from the page Cache
Err = ptr_err (PAGE );
If (is_err (page ))
Goto out;
Lock_page (PAGE );
Kaddr = page_address (PAGE); // obtain the linear address of the page.
Dir_end = kaddr + ext2_last_byte (Dir, n); // The last page is less than one page.
De = (ext2_dirent *) kaddr;
Kaddr + = page_cache_size-reclen;
While (char *) de <= kaddr ){
If (char *) DE = dir_end ){
Name_len = 0;
Rec_len = chunk_size;
De-> rec_len = ext2_rec_len_to_disk (chunk_size );
De-> inode = 0;
Goto got_it;
}
If (de-> rec_len = 0 ){
Ext2_error (Dir-> I _sb, _ FUNCTION __,
"Zero-length directory entry ");
Err =-EIO;
Goto out_unlock;
}
Err =-eexist;
// If the name matches
If (ext2_match (namelen, name, de ))
Goto out_unlock;
Name_len = ext2_dir_rec_len (de-> name_len );
Rec_len = ext2_rec_len_from_disk (de-> rec_len );
// A useless directory is obtained
If (! De-> inode & rec_len> = reclen)
Goto got_it;
// Or the directory item can be put down at another interval.
If (rec_len> = name_len + reclen)
Goto got_it;
// Jump to the next position if it is not found
De = (ext2_dirent *) (char *) De + rec_len );
}
Unlock_page (PAGE );
Ext2_put_page (PAGE );
}
Bug ();
Return-einval;
Got_it:
// There are two possibilities for pos. The first is a large enough interval between the preceding directory items, and the second is the discarded directory items.
Pos = page_offset (PAGE) + (char *) de-(char *) page_address (PAGE );
// Write Block
Err = _ ext2_write_begin (null, page-> mapping, POs, rec_len, 0, & page, null );
If (ERR)
Goto out_unlock;
// If it is a directory item before the interval
If (de-> inode ){
// De1 is the starting position of the new directory item
Ext2_dirent * de1 = (ext2_dirent *) (char *) De + name_len );
De1-> rec_len = ext2_rec_len_to_disk (rec_len-name_len );
// Modify the original directory item pointer
De-> rec_len = ext2_rec_len_to_disk (name_len );
De = de1;
}
De-> name_len = namelen;
Memcpy (de-> name, name, namelen );
De-> inode = cpu_to_le32 (inode-> I _ino );
Ext2_set_de_type (De, inode );
Err = ext2_commit_chunk (page, POs, rec_len );
Dir-> I _mtime = Dir-> I _ctime = current_time_sec;
Ext2_ I (DIR)-> I _flags & = ~ Ext2_btree_fl;
// Mark as dirty and prepare for write-back
Mark_inode_dirty (DIR );
Out_put:
Ext2_put_page (PAGE );
Out:
Return err;
Out_unlock:
Unlock_page (PAGE );
Goto out_put;
}
The general process is to constantly traverse the blocks of directory items and find an obsolete directory item or an interval that is large enough. The specific execution process is well understood from the annotations.
2. The index node allocation process ext2_new_inode. The function is defined as follows:
// Allocate an inode
Struct inode * ext2_new_inode (struct inode * Dir, int Mode)
{
Struct super_block * Sb;
Struct buffer_head * bitmap_bh = NULL;
Struct buffer_head * BH2;
Int group, I;
Ino_t ino = 0;
Struct inode * inode;
Struct ext2_group_desc * GDP;
Struct ext2_super_block * es;
Struct ext2_inode_info * EI;
Struct ext2_sb_info * SBI;
Int err;
SB = Dir-> I _sb;
// Allocate inode in VFS
Inode = new_inode (SB );
If (! Inode)
Return err_ptr (-enomem );
Ei = ext2_ I (inode );
SBI = ext2_sb (SB );
Es = SBI-> s_es;
/*
* Find a suitable block group
* 1. find_group_dir enforces the old-fashioned mode.
* 2. find_group_orlov tries to put the index block of the common file corresponding to the directory item in the same block group.
* A. Directory items with the root Root of the file system as the parent directory should be scattered in each block group
* B. This group does not contain too many directories & this group has enough idle index nodes & this group has enough
* Many idle blocks & this group has a small "debt", and the nested directory should be stored in the parent directory block group
* C. Select the first block group that meets the conditions from the block group that contains the parent directory. The condition is:
* The number of idle index nodes is greater than the average number of idle index nodes in each group.
* 3. If the new index node is not a directory, call find_group_other.
* A. Start from the block group containing the parent directory Dir and perform a quick log search
* B. If the algorithm does not find a block group containing idle index nodes
* The block group starts to perform a thorough linear search.
*/
If (s_isdir (mode )){
If (test_opt (SB, oldalloc ))
Group = find_group_dir (SB, DIR );
Else
Group = find_group_orlov (SB, DIR );
} Else
Group = find_group_other (SB, DIR );
If (group =-1 ){
Err =-enospc;
Goto fail;
}
// Sets the bitmap. This operation traverses all block groups at most once.
For (I = 0; I <SBI-> s_groups_count; I ++ ){
GDP = ext2_get_group_desc (SB, group, & BH2 );
Brelse (bitmap_bh );
Bitmap_bh = read_inode_bitmap (SB, group );
If (! Bitmap_bh ){
Err =-EIO;
Goto fail;
}
Ino = 0;
Repeat_in_this_group:
// Find the idle bit
Ino = ext2_find_next_zero_bit (unsigned long *) bitmap_bh-> B _data,
Ext2_inodes_per_group (SB), Ino );
// If you get to the last block group, go back and try the first block group.
If (ino> = ext2_inodes_per_group (SB )){
If (++ group = SBI-> s_groups_count)
Group = 0;
Continue;
}
If (ext2_set_bit_atomic (sb_bgl_lock (SBI, group), Ino, bitmap_bh-> B _data )){
If (++ ino> = ext2_inodes_per_group (SB )){
If (++ group = SBI-> s_groups_count)
Group = 0;
Continue;
}
Goto repeat_in_this_group;
}
Goto got;
}
Err =-enospc;
Goto fail;
Got:
// Set the BH of the bitmap to dirty
Mark_buffer_dirty (bitmap_bh );
If (Sb-> s_flags & ms_synchronous)
Sync_dirty_buffer (bitmap_bh );
Brelse (bitmap_bh );
Ino + = Group * ext2_inodes_per_group (SB) + 1;
If (ino <ext2_first_ino (SB) | ino> le32_to_cpu (ES-> s_inodes_count )){
Ext2_error (SB, "ext2_new_inode ",
"Reserved inode or inode> inodes count -"
"Block_group = % d, inode = % lu", group,
(Unsigned long) Ino );
Err =-EIO;
Goto fail;
}
Percpu_counter_add (& SBI-> s_freeinodes_counter,-1 );
If (s_isdir (mode ))
Percpu_counter_inc (& SBI-> s_dirs_counter );
Spin_lock (sb_bgl_lock (SBI, group ));
GDP-> bg_free_inodes_count = cpu_to_le16 (le16_to_cpu (GDP-> bg_free_inodes_count)-1 );
If (s_isdir (mode )){
If (SBI-> s_debts [group] <255)
SBI-> s_debts [group] ++;
GDP-> bg_used_dirs_count =
Cpu_to_le16 (le16_to_cpu (GDP-> bg_used_dirs_count) + 1 );
} Else {
If (SBI-> s_debts [group])
SBI-> s_debts [group] --;
}
Spin_unlock (sb_bgl_lock (SBI, group ));
Sb-> s_dirt = 1;
Mark_buffer_dirty (BH2 );
Inode-> I _uid = Current-> fsuid;
If (test_opt (SB, grpid ))
Inode-> I _gid = Dir-> I _gid;
Else if (Dir-> I _mode & s_isgid ){
Inode-> I _gid = Dir-> I _gid;
If (s_isdir (mode ))
Mode | = s_isgid;
} Else
Inode-> I _gid = Current-> fsgid;
// Set inode attributes
Inode-> I _mode = mode;
Inode-> I _ino = ino;
Inode-> I _blocks = 0;
Inode-> I _mtime = inode-> I _atime = inode-> I _ctime = current_time_sec;
Memset (EI-> I _data, 0, sizeof (EI-> I _data ));
EI-> I _flags = ext2_ I (DIR)-> I _flags &~ Ext2_btree_fl;
If (s_islnk (mode ))
EI-> I _flags & = ~ (Ext2_immutable_fl | ext2_append_fl );
If (! S_isdir (mode ))
EI-> I _flags & = ~ Ext2_dirsync_fl;
EI-> I _faddr = 0;
EI-> I _frag_no = 0;
EI-> I _frag_size = 0;
EI-> I _file_acl = 0;
EI-> I _dir_acl = 0;
EI-> I _dtime = 0;
EI-> I _block_alloc_info = NULL;
EI-> I _block_group = group;
EI-> I _dir_start_lookup = 0;
EI-> I _state = ext2_state_new;
Ext2_set_inode_flags (inode );
Spin_lock (& SBI-> s_next_gen_lock );
Inode-> I _generation = SBI-> s_next_generation ++;
Spin_unlock (& SBI-> s_next_gen_lock );
Insert_inode_hash (inode );
If (dquot_alloc_inode (inode )){
Err =-edquot;
Goto fail_drop;
}
Err = ext2_init_acl (inode, DIR );
If (ERR)
Goto fail_free_drop;
Err = ext2_init_security (inode, DIR );
If (ERR)
Goto fail_free_drop;
Mark_inode_dirty (inode );
Ext2_debug ("allocating inode % lu \ n", inode-> I _ino );
Ext2_preread_inode (inode );
Return inode;
Fail_free_drop:
Dquot_free_inode (inode );
Fail_drop:
Dquot_drop (inode );
Inode-> I _flags | = s_noquota;
Inode-> I _nlink = 0;
Iput (inode );
Return err_ptr (ERR );
Fail:
Make_bad_inode (inode );
Iput (inode );
Return err_ptr (ERR );
}
The general process of this function is still very simple. First allocate the index nodes in VFS (this is the easiest thing) and then find the appropriate block group, find the idle bit from the bitmap of the block group. Initialize some attributes of the index node. Finally, mark them as dirty so that they can be written back to the hard disk. The process of finding a block group is quite interesting.