Linux内核2.4.18创建硬链接的系统调用sys_link

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 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;
}

简易脉搏血氧计:使用简单,功能强
中国科学院上海硅酸盐研究所:可拉伸、透气、自粘附与多模态传感的动态健康监测电子皮肤
NEPCON快讯:国际大牌展商追光而来 电子制造新品首发出道
linux数据库乱码怎么解决
全自动农药残留检测仪可提供智能化的检测服务
Linux内核2.4.18创建硬链接的系统调用sys_link
西门子PLC PROFINET组态拓扑V12.2版启动步骤
总投资RMB20亿元的丰达兴5G产业园项目于江西奠基!
知道什么是集线器吗?
可穿戴的电子系统发展--人类的嵌入式系统的结合
切粒机滚刀电路图原理是什么
华为自研WiFi 6+技术有哪些值得期待的地方
基于高通(QUALCOMM)CSRB31024的汽车无钥匙进入的解决方案
数字服贸 智启未来 服贸会透露高水平开放新动向
警惕,全球晶圆厂产能面临大幅下降可能
黑鲨和红魔游戏手机哪个好
国星光电助力筑牢全民免疫的屏障
美光发布基于1-alpha技术制造的LPDDR5X芯片
2023第三届中国数字化人才国际峰会
PLC控制的晶闸管交流电子开关设计分析