1、磁盤目錄項結構
/*
?* The new version of the directory entry.? Since EXT2 structures are
?* stored in intel byte order, and the name_len field could never be
?* bigger than 255 chars, it's safe to reclaim the extra byte for the
?* file_type field.
?*/
struct ext2_dir_entry_2 {
??? __u32??? inode;??????????? /* Inode number */
??? __u16??? rec_len;??????? /* Directory entry length */
??? __u8??? name_len;??????? /* Name length */
??? __u8??? file_type;
??? char??? name[EXT2_NAME_LEN];??? /* File name */
};
inode:inode節點號
rec_len:目錄項長度
name_len:實際的目錄項名稱長度(一般不等于EXT2_NAME_LEN)
file_type:文件類型編碼
name:目錄項名稱(不含'\0')
2、調用路徑:sys_link->vfs_link->ext2_link->ext2_add_nondir->ext2_add_link
3、函數分析
/*
?* Hardlinks are often used in delicate situations.? We avoid
?* security-related surprises by not following symlinks on the
?* newname.? --KAB
?*
?* We don't follow them on the oldname either to be compatible
?* with linux 2.0, and to avoid hard-linking to directories
?* and other special files.? --ADM
?*/
asmlinkage long sys_link(const char * oldname, const char * newname)
{
??? int error;
??? char * from;
??? char * to;
??? from = getname(oldname);
??? if(IS_ERR(from))
??????? return PTR_ERR(from);
??? to = getname(newname);
??? error = PTR_ERR(to);
??? if (!IS_ERR(to)) {
??????? struct dentry *new_dentry;
??????? struct nameidata nd, old_nd;
??????? error = 0;
??????? if (path_init(from, LOOKUP_POSITIVE, &old_nd))
??????????? error = path_walk(from, &old_nd);
??????? if (error)
??????????? goto exit;
??????? if (path_init(to, LOOKUP_PARENT, &nd))
??????????? error = path_walk(to, &nd);
??????? if (error)
??????????? goto out;
??????? error = -EXDEV;
??????? if (old_nd.mnt != nd.mnt)
??????????? goto out_release;
??????? new_dentry = lookup_create(&nd, 0);
??????? error = PTR_ERR(new_dentry);
??????? if (!IS_ERR(new_dentry)) {
??????????? error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
??????????? dput(new_dentry);
??????? }
??????? up(&nd.dentry->d_inode->i_sem);
out_release:
??????? path_release(&nd);
out:
??????? path_release(&old_nd);
exit:
??????? putname(to);
??? }
??? putname(from);
??? return error;
}
//作為系統調用API和指定文件系統層的接口,處于VFS層。
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
??? struct inode *inode;
??? int error;
??? down(&dir->i_zombie);
??? error = -ENOENT;
??? inode = old_dentry->d_inode;
??? if (!inode)
??????? goto exit_lock;
??? error = may_create(dir, new_dentry);//檢查是否具備創建權限
??? if (error)
??????? goto exit_lock;
??? error = -EXDEV;
??? if (dir->i_dev != inode->i_dev) //硬鏈接不能跨設備,跨文件系統建立。
??????? goto exit_lock;
??? /*
???? * A link to an append-only or immutable file cannot be created.
???? */
??? error = -EPERM;
??? if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
??????? goto exit_lock;
??? if (!dir->i_op || !dir->i_op->link)
??????? goto exit_lock;
??? DQUOT_INIT(dir);
??? lock_kernel();
??? error = dir->i_op->link(old_dentry, dir, new_dentry);//調用ext2_link
??? unlock_kernel();
exit_lock:
??? up(&dir->i_zombie);
??? if (!error)
??????? inode_dir_notify(dir, DN_CREATE);
??? return error;
}
//ext2文件系統的創建鏈接函數,調用ext2_add_nondir。
static int ext2_link (struct dentry * old_dentry, struct inode * dir,
??? struct dentry *dentry)
{
??? struct inode *inode = old_dentry->d_inode;
??? if (S_ISDIR(inode->i_mode)) //不能創建指向目錄節點的硬鏈接
??????? return -EPERM;
??? if (inode->i_nlink >= EXT2_LINK_MAX)
??????? return -EMLINK;
??? inode->i_ctime = CURRENT_TIME;
??? ext2_inc_count(inode);
??? atomic_inc(&inode->i_count);
??? return ext2_add_nondir(dentry, inode);
}
//在dentry->parent目錄中創建指向節點inode(節點號inode->i_ino)的鏈接,并將對應的內存目錄項dentry和inode建立關聯
static inline int ext2_add_nondir(struct dentry *dentry, struct inode *inode)
{
??? int err = ext2_add_link(dentry, inode);
??? if (!err) {
??????? d_instantiate(dentry, inode);
??????? return 0;
??? }
??? ext2_dec_count(inode);
??? iput(inode);
??? return err;
}
/*
?*??? Parent is locked.
?*/
int ext2_add_link (struct dentry *dentry, struct inode *inode)
{
??? struct inode *dir = dentry->d_parent->d_inode;
??? const char *name = dentry->d_name.name;
??? int namelen = dentry->d_name.len;
??? unsigned reclen = EXT2_DIR_REC_LEN(namelen);//根據創建目錄項名稱長度計算出目錄項長度reclen
??? unsigned short rec_len, name_len;
??? struct page *page = NULL;
??? ext2_dirent * de;
??? unsigned long npages = dir_pages(dir);//根據目錄長度,推算出目錄文件的頁面數
??? unsigned long n;
??? char *kaddr;
??? unsigned from, to;
??? int err;
??? /* We take care of directory expansion in the same loop */
??? for (n = 0; n <= npages; n++) {//循環檢測每一頁,尋找目錄項空位
??????? page = ext2_get_page(dir, n);
??????? err = PTR_ERR(page);
??????? if (IS_ERR(page))
??????????? goto out;
??????? kaddr = page_address(page);//本頁起始地址:頁結構指針轉換為線性地址
??????? de = (ext2_dirent *)kaddr;//當前目錄項地址
??????? kaddr += PAGE_CACHE_SIZE - reclen;//kaddr=本頁最后一個目錄項地址(頁起始地址+頁長-新目錄項長度)
??????? while ((char *)de <= kaddr) { //頁內循環檢測每個目錄項,一旦下個檢測地址高于kaddr,那么說明本頁沒有適合的位置,必須看下頁
??????????? err = -EEXIST;
??????????? if (ext2_match (namelen, name, de))//目錄中存在同名的目錄項,則退出,返回-EEXIST
??????????????? goto out_page;
??????????? name_len = EXT2_DIR_REC_LEN(de->name_len);//name_len=當前目錄項需要的長度
??????????? rec_len = le16_to_cpu(de->rec_len); //rec_len=當前目錄項實際占用的長度
??????????? if (!de->inode && rec_len >= reclen)//情況1:如果當前目錄項空閑,且長度合適,那么可以放置新的目錄項,轉向got_it。
??????????????? goto got_it;
??????????? if (rec_len >= name_len + reclen)//情況2:如果當前目錄項長度>=當前目錄項需要長度+新目錄項需要長度,那么可以從中分出尾部部分放置新目錄項
??????????????? goto got_it;
??????????? de = (ext2_dirent *) ((char *) de + rec_len);//否則看下個目錄項
??????? }
??????? ext2_put_page(page);
??? }
??? BUG();
??? return -EINVAL;
got_it:
??? from = (char*)de - (char*)page_address(page);
??? to = from + rec_len;
??? lock_page(page);
??? err = page->mapping->a_ops->prepare_write(NULL, page, from, to);
??? if (err)
??????? goto out_unlock;
??? if (de->inode) {//針對情況2
??????? ext2_dirent *de1 = (ext2_dirent *) ((char *) de + name_len);//新目錄項的放置位置de1在當前目錄項后邊,即當前目錄項位置de+當前目錄項實際需要的長度name_len
??????? de1->rec_len = cpu_to_le16(rec_len - name_len);//新目錄項的實際占用長度de1->rec_len=當前目錄項實際占用長度rec_len - 當前目錄項需要的長度name_len
??????? de->rec_len = cpu_to_le16(name_len);//修改當前目錄項實際占用長度rec_len為需要的長度name_len
??????? de = de1;//de指向新目錄項的位置
??? }
??? de->name_len = namelen;
??? memcpy (de->name, name, namelen);//拷貝目錄項名稱字符串
??? de->inode = cpu_to_le32(inode->i_ino);//關聯i節點號
??? ext2_set_de_type (de, inode);
??? err = ext2_commit_chunk(page, from, to);//提交頁面的修改部分(相關bh置為臟),如果塊設備有MS_SYNCHRONOUS標志或者目錄文件有S_SYNC標志,則立刻同步的磁盤。
??? dir->i_mtime = dir->i_ctime = CURRENT_TIME;//修改目錄節點的時間戳。
??? mark_inode_dirty(dir);//目錄文件節點置為臟
??? /* OFFSET_CACHE */
out_unlock:
??? UnlockPage(page);
out_page:
??? ext2_put_page(page);
out:
??? return err;
}
?
評論
查看更多