文件系统操作整体架构
系统调用层 → VFS层 → 具体文件系统层
符号链接创建 (symlink)
功能:创建软链接,包含目标路径字符串
核心流程:
sys_symlink → path_lookup父目录 → lookup_create目录项 → vfs_symlink
关键特性:
- 可跨文件系统
- 链接文件有独立的inode
- 存储目标路径字符串
- 目标不存在也可创建
硬链接创建 (link)
功能:创建硬链接,多个目录项指向同一inode
核心流程:
sys_link → 检查同文件系统 → lookup_create目录项 → vfs_link
关键特性:
- 必须在同一文件系统
- 共享同一inode,增加链接计数
- 所有链接地位平等
- 不能链接目录(防循环)
文件删除 (unlink)
功能:删除文件链接,减少inode链接计数
核心流程:
sys_unlink → path_lookup父目录 → lookup_hash查找目标 → vfs_unlink
关键特性:
- 只是减少链接计数,不一定立即释放
- 当链接计数为0且无进程打开时才真正删除
- 不能删除目录
- 不能删除挂载点
关键技术组件详解
1. 路径解析组件
path_lookup:解析文件路径
- LOOKUP_PARENT:查找父目录
- LOOKUP_FOLLOW:跟踪符号链接
lookup_create:为创建操作准备目录项
- 检查路径组件合法性
- 获取父目录信号量锁
- 创建或查找目标目录项
2. 权限和安全检查
may_create/may_delete:基础权限检查
- 文件系统权限位检查
- 目录写权限验证
安全模块钩子:
- security_inode_symlink/link/unlink:操作前检查
- security_inode_post_*:操作后处理
3. 通知机制
目录变更通知:
- inode_dir_notify:发送目录事件通知
- DN_CREATE/DN_DELETE:创建/删除事件
- 通过SIGIO信号通知监视进程
创建一个符号链接文件sys_symlink
asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname)
{
	int error = 0;
	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 *dentry;
		struct nameidata nd;
		error = path_lookup(to, LOOKUP_PARENT, &nd);
		if (error)
			goto out;
		dentry = lookup_create(&nd, 0);
		error = PTR_ERR(dentry);
		if (!IS_ERR(dentry)) {
			error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
			dput(dentry);
		}
		up(&nd.dentry->d_inode->i_sem);
		path_release(&nd);
out:
		putname(to);
	}
	putname(from);
	return error;
}
函数声明和变量定义
asmlinkage long sys_symlink(const char __user * oldname, const char __user * newname)
{
    int error = 0;
    char * from;
    char * to;
- asmlinkage:告诉编译器参数通过堆栈传递,这是系统调用的标准调用约定
- const char __user *:指针指向用户空间的内存,需要特殊处理
- oldname:源文件路径(符号链接指向的目标)
- newname:新创建的符号链接路径
- error:错误返回值,初始化为0表示成功
- from,- to:内核空间的文件名字符串指针
获取源文件名
    from = getname(oldname);
    if(IS_ERR(from))
        return PTR_ERR(from);
- getname(oldname):从用户空间复制文件名字符串到内核空间
- IS_ERR(from):检查是否复制成功,如果失败返回错误码
- PTR_ERR(from):将错误指针转换为错误码值
获取目标文件名
    to = getname(newname);
    error = PTR_ERR(to);
    if (!IS_ERR(to)) {
- getname(newname):同样复制目标文件名到内核空间
- 先将返回值预设为可能的错误码
- !IS_ERR(to):如果成功获取目标文件名,继续执行符号链接创建
路径查找和准备
        struct dentry *dentry;
        struct nameidata nd;
        error = path_lookup(to, LOOKUP_PARENT, &nd);
        if (error)
            goto out;
- dentry:目录项结构,表示文件系统中的一个节点
- nameidata:路径查找结果的数据结构
- path_lookup(to, LOOKUP_PARENT, &nd):查找目标路径的父目录
- LOOKUP_PARENT:标志表示查找父目录而不是文件本身
- 如果查找失败,跳转到清理代码
创建目录项
        dentry = lookup_create(&nd, 0);
        error = PTR_ERR(dentry);
        if (!IS_ERR(dentry)) {
- lookup_create(&nd, 0):在父目录中创建新的目录项
- 第二个参数0表示创建文件
- 检查目录项创建是否成功
实际创建符号链接
            error = vfs_symlink(nd.dentry->d_inode, dentry, from, S_IALLUGO);
            dput(dentry);
        }
- vfs_symlink():虚拟文件系统层的符号链接创建函数
- 参数:
- nd.dentry->d_inode:父目录的- inode
- dentry:新创建的目录项
- from:符号链接指向的目标路径
- S_IALLUGO:文件权限标志(所有用户权限)
 
- dput(dentry):释放目录项的引用计数
清理资源
        up(&nd.dentry->d_inode->i_sem);
        path_release(&nd);
out:
        putname(to);
    }
    putname(from);
    return error;
}
- up(&nd.dentry->d_inode->i_sem):释放父目录- inode的信号量锁
- path_release(&nd):释放路径查找相关的资源
- putname(to)和- putname(from):释放复制的文件名内存
- 返回错误码(0表示成功)
函数功能
功能:创建一个符号链接文件
作用:
- 在指定路径 newname创建一个符号链接文件
- 该符号链接指向 oldname指定的目标
- 符号链接是一个特殊的文件,其内容是指向另一个文件或目录的路径
使用场景:
- 在文件系统中创建软链接
- 类似于 shell 中的 ln -s命令
返回值:
- 0:成功创建符号链接
- 负数:错误码,表示创建失败的原因
为文件创建准备或查找目录项lookup_create
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
	struct dentry *dentry;
	down(&nd->dentry->d_inode->i_sem);
	dentry = ERR_PTR(-EEXIST);
	if (nd->last_type != LAST_NORM)
		goto fail;
	nd->flags &= ~LOOKUP_PARENT;
	dentry = lookup_hash(&nd->last, nd->dentry);
	if (IS_ERR(dentry))
		goto fail;
	if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
		goto enoent;
	return dentry;
enoent:
	dput(dentry);
	dentry = ERR_PTR(-ENOENT);
fail:
	return dentry;
}
函数声明和变量定义
struct dentry *lookup_create(struct nameidata *nd, int is_dir)
{
    struct dentry *dentry;
- struct nameidata *nd:路径查找结果的数据结构,包含查找状态和相关信息
- int is_dir:标志位,表示要创建的是目录(1)还是文件(0)
- dentry:返回的目录项指针
获取inode信号量锁
    down(&nd->dentry->d_inode->i_sem);
- down():获取信号量(互斥锁)
- nd->dentry->d_inode->i_sem:父目录- inode的信号量
- 这确保了在创建操作期间对父目录的独占访问,防止竞态条件
检查路径组件类型
    dentry = ERR_PTR(-EEXIST);
    if (nd->last_type != LAST_NORM)
        goto fail;
- 预设返回值为 -EEXIST(文件已存在错误)
- nd->last_type:最后一个路径组件的类型
- LAST_NORM:表示正常的文件名组件
- 如果最后一个组件不是普通文件名(可能是.、..或根目录),直接失败
清除LOOKUP_PARENT标志
    nd->flags &= ~LOOKUP_PARENT;
- nd->flags:路径查找的标志位
- LOOKUP_PARENT:表示查找父目录的标志
- &= ~LOOKUP_PARENT:清除这个标志,因为现在要查找的是目标文件本身而不是其父目录
执行哈希查找
    dentry = lookup_hash(&nd->last, nd->dentry);
    if (IS_ERR(dentry))
        goto fail;
- lookup_hash(&nd->last, nd->dentry):在目录项缓存中查找指定的文件名或者创建一个- dentry
- &nd->last:要查找的最后一个路径组件(文件名)
- nd->dentry:父目录的目录项
- 如果查找出错,跳转到fail标签
检查文件不存在的情况
    if (!is_dir && nd->last.name[nd->last.len] && !dentry->d_inode)
        goto enoent;
- !is_dir:要创建的不是目录(即普通文件或符号链接)
- nd->last.name[nd->last.len]:检查文件名末尾是否有特殊字符(非零值)- 表示文件名后面不是"\0",有可能是"/",代表目录
 
- 表示文件名后面不是
- !dentry->d_inode:查找的目录项没有对应的- inode(文件不存在)
- 如果这三个条件都满足,说明要创建一个不存在的文件但路径有问题,跳转到enoent
成功返回
    return dentry;
- 如果所有检查都通过,返回找到或新创建的目录项
文件不存在错误处理
enoent:
    dput(dentry);
    dentry = ERR_PTR(-ENOENT);
- dput(dentry):释放目录项的引用计数
- 设置返回值为 -ENOENT(文件不存在错误)
通用错误处理
fail:
    return dentry;
}
- 返回预设的错误值
函数功能详解
主要功能:为文件创建操作准备或查找目录项
具体作用:
- 安全性检查:验证路径组件的有效性
- 并发控制:通过信号量确保对父目录的独占访问
- 目录项查找:在目录项缓存中查找或创建目标文件的目录项
- 状态验证:检查文件是否存在以及路径的合法性
参数说明:
- nd:包含完整路径查找信息的结构体
- is_dir:区分创建目录(1)还是文件(0),影响验证逻辑
实际创建符号链接vfs_symlink
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
{
	int error = may_create(dir, dentry, NULL);
	if (error)
		return error;
	if (!dir->i_op || !dir->i_op->symlink)
		return -EPERM;
	error = security_inode_symlink(dir, dentry, oldname);
	if (error)
		return error;
	DQUOT_INIT(dir);
	error = dir->i_op->symlink(dir, dentry, oldname);
	if (!error) {
		inode_dir_notify(dir, DN_CREATE);
		security_inode_post_symlink(dir, dentry, oldname);
	}
	return error;
}
static inline void inode_dir_notify(struct inode *inode, unsigned long event)
{
	if (inode->i_dnotify_mask & (event))
		__inode_dir_notify(inode, event);
}
void __inode_dir_notify(struct inode *inode, unsigned long event)
{
	struct dnotify_struct *	dn;
	struct dnotify_struct **prev;
	struct fown_struct *	fown;
	int			changed = 0;
	spin_lock(&inode->i_lock);
	prev = &inode->i_dnotify;
	while ((dn = *prev) != NULL) {
		if ((dn->dn_mask & event) == 0) {
			prev = &dn->dn_next;
			continue;
		}
		fown = &dn->dn_filp->f_owner;
		send_sigio(fown, dn->dn_fd, POLL_MSG);
		if (dn->dn_mask & DN_MULTISHOT)
			prev = &dn->dn_next;
		else {
			*prev = dn->dn_next;
			changed = 1;
			kmem_cache_free(dn_cache, dn);
		}
	}
	if (changed)
		redo_inode_mask(inode);
	spin_unlock(&inode->i_lock);
}
vfs_symlink 函数
权限检查
int vfs_symlink(struct inode *dir, struct dentry *dentry, const char *oldname, int mode)
{
	int error = may_create(dir, dentry, NULL);
	if (error)
		return error;
- dir:父目录的- inode
- dentry:要创建的符号链接的目录项
- oldname:符号链接指向的目标路径
- mode:文件权限模式
- may_create(dir, dentry, NULL):检查是否有在目录中创建文件的权限
- 如果权限检查失败,立即返回错误
文件系统操作检查
	if (!dir->i_op || !dir->i_op->symlink)
		return -EPERM;
- dir->i_op:- inode操作函数表指针
- dir->i_op->symlink:具体的符号链接创建函数指针
- 如果文件系统不支持符号链接操作,返回 -EPERM(操作不允许)
安全模块检查
	error = security_inode_symlink(dir, dentry, oldname);
	if (error)
		return error;
- security_inode_symlink():Linux安全模块(LSM)的钩子函数
- 允许安全模块(如SELinux)检查符号链接创建权限
- 如果安全模块拒绝操作,返回错误
磁盘配额初始化
	DQUOT_INIT(dir);
- DQUOT_INIT(dir):初始化磁盘配额检查
- 确保用户没有超出磁盘空间配额限制
执行符号链接创建
	error = dir->i_op->symlink(dir, dentry, oldname);
- 调用具体文件系统实现的符号链接创建函数
- 这是实际创建符号链接的核心操作
成功处理和后置操作
	if (!error) {
		inode_dir_notify(dir, DN_CREATE);
		security_inode_post_symlink(dir, dentry, oldname);
	}
	return error;
}
- 如果创建成功:
- inode_dir_notify(dir, DN_CREATE):发送目录通知
- security_inode_post_symlink():安全模块的后置处理钩子
 
- 返回操作结果(0表示成功)
inode_dir_notify 函数
static inline void inode_dir_notify(struct inode *inode, unsigned long event)
{
	if (inode->i_dnotify_mask & (event))
		__inode_dir_notify(inode, event);
}
- 内联函数,检查是否需要发送目录通知
- inode->i_dnotify_mask:目录通知的事件掩码
- 如果事件在掩码中,调用实际的通知函数
__inode_dir_notify 函数
变量定义和锁获取
void __inode_dir_notify(struct inode *inode, unsigned long event)
{
	struct dnotify_struct *	dn;
	struct dnotify_struct **prev;
	struct fown_struct *	fown;
	int			changed = 0;
	spin_lock(&inode->i_lock);
- dn:目录通知结构指针
- prev:用于链表遍历的二级指针
- fown:文件所有者信息
- changed:标记链表是否发生变化
- spin_lock(&inode->i_lock):获取- inode的自旋锁,保护并发访问
遍历目录通知链表
	prev = &inode->i_dnotify;
	while ((dn = *prev) != NULL) {
		if ((dn->dn_mask & event) == 0) {
			prev = &dn->dn_next;
			continue;
		}
- 从inode的目录通知链表头开始遍历
- 检查每个通知结构的掩码是否包含当前事件
- 如果不包含,继续遍历下一个节点
发送信号通知
		fown = &dn->dn_filp->f_owner;
		send_sigio(fown, dn->dn_fd, POLL_MSG);
- 获取文件所有者信息
- send_sigio():向监视进程发送SIGIO信号,通知目录变化
处理单次/多次通知
		if (dn->dn_mask & DN_MULTISHOT)
			prev = &dn->dn_next;
		else {
			*prev = dn->dn_next;
			changed = 1;
			kmem_cache_free(dn_cache, dn);
		}
- DN_MULTISHOT:如果设置多次通知,保留通知结构继续使用
- 否则(单次通知):
- 从链表中移除该节点
- 标记链表已变化
- 释放通知结构内存
 
清理工作
	if (changed)
		redo_inode_mask(inode);
	spin_unlock(&inode->i_lock);
}
- 如果链表发生变化,重新计算inode的通知掩码
- 释放inode自旋锁
函数功能详解
vfs_symlink 主要功能:
- 权限验证:检查创建文件的权限
- 能力检查:验证文件系统支持符号链接操作
- 安全审查:通过Linux安全模块进行访问控制
- 配额管理:检查磁盘空间配额
- 实际操作:调用具体文件系统的符号链接创建
- 事件通知:向监视进程发送目录变化通知
目录通知系统功能:
- 实现dnotify机制,允许进程监视目录变化
- 通过信号(SIGIO)异步通知监视进程
- 支持单次和多次通知模式
- 维护进程级的目录监视配置
磁盘配额初始化DQUOT_INIT
static __inline__ void DQUOT_INIT(struct inode *inode)
{
	BUG_ON(!inode->i_sb);
	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode))
		inode->i_sb->dq_op->initialize(inode, -1);
}
#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \
				  sb_has_quota_enabled(sb, GRPQUOTA))
#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \
	(sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED))
DQUOT_INIT 函数
函数声明和BUG检查
static __inline__ void DQUOT_INIT(struct inode *inode)
{
	BUG_ON(!inode->i_sb);
- static __inline__:静态内联函数,编译时直接展开,减少函数调用开销
- struct inode *inode:参数为要初始化配额的- inode
- BUG_ON(!inode->i_sb):内核调试宏,检查- inode是否有有效的超级块- !inode->i_sb:如果- inode的超级块指针为NULL
- 如果条件为真,触发内核BUG,导致系统panic
 
配额启用检查
	if (sb_any_quota_enabled(inode->i_sb) && !IS_NOQUOTA(inode))
- sb_any_quota_enabled(inode->i_sb):检查文件系统是否启用了任何配额
- !IS_NOQUOTA(inode):检查- inode是否不受配额限制- IS_NOQUOTA(inode):检查- inode的标志位,如特殊文件可能不受配额限制
 
- 两个条件都满足时才执行配额初始化
执行配额初始化
		inode->i_sb->dq_op->initialize(inode, -1);
}
- inode->i_sb->dq_op:超级块的磁盘配额操作函数表
- ->initialize(inode, -1):调用配额初始化函数- inode:要初始化配额的- inode
- -1:表示未知的配额类型,通常用于初始化默认配额结构
 
宏定义解析
sb_any_quota_enabled 宏
#define sb_any_quota_enabled(sb) (sb_has_quota_enabled(sb, USRQUOTA) | \
				  sb_has_quota_enabled(sb, GRPQUOTA))
- 检查超级块是否启用了用户配额或组配额
- 使用位或操作 |:如果任一配额启用就返回真
- USRQUOTA:用户配额类型常量
- GRPQUOTA:组配额类型常量
sb_has_quota_enabled 宏
#define sb_has_quota_enabled(sb, type) ((type)==USRQUOTA ? \
	(sb_dqopt(sb)->flags & DQUOT_USR_ENABLED) : (sb_dqopt(sb)->flags & DQUOT_GRP_ENABLED))
- 三元条件运算符检查特定类型的配额是否启用
- sb_dqopt(sb):获取超级块的配额选项结构
- sb_dqopt(sb)->flags:配额标志位
- DQUOT_USR_ENABLED:用户配额启用标志位
- DQUOT_GRP_ENABLED:组配额启用标志位
- 位与操作 &:检查特定标志位是否设置
函数功能详解
主要功能:初始化inode的磁盘配额信息
具体作用:
- 
安全性验证: - 确保inode有有效的超级块引用
- 防止对无效inode进行操作
 
- 确保
- 
配额系统检查: - 检查文件系统是否启用了配额系统
- 区分用户配额和组配额
- 检查特定inode是否豁免配额限制
 
- 
配额初始化: - 为inode设置初始配额结构
- 跟踪用户的磁盘使用情况
- 为后续的配额检查和强制执行做准备
 
- 为
硬链接创建sys_link
asmlinkage long sys_link(const char __user * oldname, const char __user * newname)
{
	struct dentry *new_dentry;
	struct nameidata nd, old_nd;
	int error;
	char * to;
	to = getname(newname);
	if (IS_ERR(to))
		return PTR_ERR(to);
	error = __user_walk(oldname, 0, &old_nd);
	if (error)
		goto exit;
	error = path_lookup(to, LOOKUP_PARENT, &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);
	return error;
}
变量声明和初始化
asmlinkage long sys_link(const char __user * oldname, const char __user * newname)
{
	struct dentry *new_dentry;
	struct nameidata nd, old_nd;
	int error;
	char * to;
- asmlinkage:系统调用标准调用约定,参数通过堆栈传递
- oldname:源文件路径(已存在的文件)
- newname:新创建的硬链接路径
- new_dentry:新链接的目录项结构
- nd:新链接路径的查找数据
- old_nd:源文件路径的查找数据
- error:错误返回值
- to:内核空间的新链接路径字符串
获取新链接路径名
	to = getname(newname);
	if (IS_ERR(to))
		return PTR_ERR(to);
- getname(newname):从用户空间复制新链接路径到内核空间
- IS_ERR(to):检查复制是否成功
- PTR_ERR(to):如果失败,将错误指针转换为错误码并返回
查找源文件路径
	error = __user_walk(oldname, 0, &old_nd);
	if (error)
		goto exit;
- __user_walk(oldname, 0, &old_nd):解析源文件路径
- 第二个参数 0:查找标志,表示普通查找
- &old_nd:存储源文件的路径查找结果
- 如果查找失败,设置错误码并跳转到清理代码
查找新链接的父目录
	error = path_lookup(to, LOOKUP_PARENT, &nd);
	if (error)
		goto out;
- path_lookup(to, LOOKUP_PARENT, &nd):查找新链接路径的父目录
- LOOKUP_PARENT:标志表示查找父目录而不是文件本身
- &nd:存储父目录的路径查找结果
- 如果查找失败,跳转到清理代码
检查文件系统边界
	error = -EXDEV;
	if (old_nd.mnt != nd.mnt)
		goto out_release;
- -EXDEV:跨文件系统错误码
- old_nd.mnt != nd.mnt:比较源文件和新链接的挂载点
- 如果不在同一个文件系统,硬链接不能创建,跳转到释放资源
创建新链接的目录项
	new_dentry = lookup_create(&nd, 0);
	error = PTR_ERR(new_dentry);
	if (!IS_ERR(new_dentry)) {
- lookup_create(&nd, 0):在父目录中创建新的目录项
- 第二个参数 0:表示创建的是文件而不是目录
- 预设错误值为可能的错误码
- 如果目录项创建成功,继续执行链接操作
执行硬链接创建
		error = vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry);
		dput(new_dentry);
	}
- vfs_link(old_nd.dentry, nd.dentry->d_inode, new_dentry):虚拟文件系统层的硬链接创建- old_nd.dentry:源文件的目录项
- nd.dentry->d_inode:父目录的- inode
- new_dentry:新链接的目录项
 
- dput(new_dentry):释放目录项的引用计数
释放父目录信号量
	up(&nd.dentry->d_inode->i_sem);
- up(&nd.dentry->d_inode->i_sem):释放父目录- inode的信号量锁
- 这个锁是在 lookup_create中获取的,现在需要释放
资源清理部分
out_release:
	path_release(&nd);
out:
	path_release(&old_nd);
exit:
	putname(to);
	return error;
}
- out_release:释放新链接路径资源
- path_release(&nd):释放新链接的路径查找资源
- out:释放源文件路径资源
- path_release(&old_nd):释放源文件的路径查找资源
- exit:释放路径名字符串
- putname(to):释放复制的路径名内存
- 返回最终的错误码
函数功能详解
主要功能:创建硬链接(hard link)
硬链接特性:
- 多个目录项指向同一个inode
- 所有硬链接地位平等,删除一个不影响其他
- 必须在同一个文件系统中
- 不能为目录创建硬链接(防止循环)
与符号链接的区别:
- 硬链接:直接指向inode,同文件系统限制
- 符号链接:包含路径字符串,可跨文件系统
实际创建硬链接vfs_link
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
	struct inode *inode = old_dentry->d_inode;
	int error;
	if (!inode)
		return -ENOENT;
	error = may_create(dir, new_dentry, NULL);
	if (error)
		return error;
	if (dir->i_sb != inode->i_sb)
		return -EXDEV;
	/*
	 * A link to an append-only or immutable file cannot be created.
	 */
	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
		return -EPERM;
	if (!dir->i_op || !dir->i_op->link)
		return -EPERM;
	if (S_ISDIR(old_dentry->d_inode->i_mode))
		return -EPERM;
	error = security_inode_link(old_dentry, dir, new_dentry);
	if (error)
		return error;
	down(&old_dentry->d_inode->i_sem);
	DQUOT_INIT(dir);
	error = dir->i_op->link(old_dentry, dir, new_dentry);
	up(&old_dentry->d_inode->i_sem);
	if (!error) {
		inode_dir_notify(dir, DN_CREATE);
		security_inode_post_link(old_dentry, dir, new_dentry);
	}
	return error;
}
变量声明和初始化
int vfs_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
	struct inode *inode = old_dentry->d_inode;
	int error;
- old_dentry:源文件的目录项(已存在的文件)
- dir:目标目录的- inode(新链接所在的目录)
- new_dentry:新链接的目录项
- inode:获取源文件对应的- inode
- error:错误返回值变量
源文件存在性检查
	if (!inode)
		return -ENOENT;
- 检查源文件的inode是否存在
- 如果inode为NULL,说明源文件不存在,返回-ENOENT
创建权限检查
	error = may_create(dir, new_dentry, NULL);
	if (error)
		return error;
- may_create(dir, new_dentry, NULL):检查在目标目录中创建新文件的权限
- 第三个参数NULL表示不检查特定的访问模式
- 如果权限检查失败,立即返回错误
文件系统一致性检查
	if (dir->i_sb != inode->i_sb)
		return -EXDEV;
- 比较目标目录和源文件的超级块
- dir->i_sb:目标目录的超级块
- inode->i_sb:源文件的超级块
- 如果不相同,返回 -EXDEV,硬链接不能跨文件系统
文件属性检查
	/*
	 * A link to an append-only or immutable file cannot be created.
	 */
	if (IS_APPEND(inode) || IS_IMMUTABLE(inode))
		return -EPERM;
- 检查源文件是否具有特殊属性:
- IS_APPEND(inode):只追加文件,内容只能追加不能修改
- IS_IMMUTABLE(inode):不可变文件,完全不能修改
 
- 如果具有这些属性,返回 -EPERM(Operation not permitted)
文件系统操作支持检查
	if (!dir->i_op || !dir->i_op->link)
		return -EPERM;
- dir->i_op:目标目录的- inode操作函数表
- dir->i_op->link:具体的硬链接创建函数
- 如果文件系统不支持硬链接操作,返回 -EPERM
目录链接限制检查
	if (S_ISDIR(old_dentry->d_inode->i_mode))
		return -EPERM;
- S_ISDIR(old_dentry->d_inode->i_mode):检查源文件是否为目录
- 如果是目录,返回 -EPERM,防止创建目录的硬链接(避免目录循环)
安全模块检查
	error = security_inode_link(old_dentry, dir, new_dentry);
	if (error)
		return error;
- security_inode_link():Linux安全模块(LSM)的钩子函数
- 允许SELinux等安全模块检查硬链接创建权限
- 如果安全模块拒绝操作,返回错误
执行硬链接创建
	down(&old_dentry->d_inode->i_sem);
	DQUOT_INIT(dir);
	error = dir->i_op->link(old_dentry, dir, new_dentry);
	up(&old_dentry->d_inode->i_sem);
- down(&old_dentry->d_inode->i_sem):获取源文件- inode的信号量锁,防止并发修改
- DQUOT_INIT(dir):初始化目标目录的磁盘配额
- dir->i_op->link(old_dentry, dir, new_dentry):调用具体文件系统的硬链接创建函数
- up(&old_dentry->d_inode->i_sem):释放源文件- inode的信号量锁
成功处理和后置操作
	if (!error) {
		inode_dir_notify(dir, DN_CREATE);
		security_inode_post_link(old_dentry, dir, new_dentry);
	}
	return error;
}
- 如果硬链接创建成功:
- inode_dir_notify(dir, DN_CREATE):发送目录创建通知
- security_inode_post_link():安全模块的后置处理钩子
 
- 返回最终的错误码(0表示成功)
函数功能详解
主要功能:在虚拟文件系统层创建硬链接
硬链接的核心特性:
- 多个目录项指向同一个inode
- 所有硬链接地位平等
- 增加inode链接计数
- 删除一个硬链接只是减少链接计数,不影响其他链接
unlink系统调用sys_unlink
asmlinkage long sys_unlink(const char __user * pathname)
{
	int error = 0;
	char * name;
	struct dentry *dentry;
	struct nameidata nd;
	struct inode *inode = NULL;
	name = getname(pathname);
	if(IS_ERR(name))
		return PTR_ERR(name);
	error = path_lookup(name, LOOKUP_PARENT, &nd);
	if (error)
		goto exit;
	error = -EISDIR;
	if (nd.last_type != LAST_NORM)
		goto exit1;
	down(&nd.dentry->d_inode->i_sem);
	dentry = lookup_hash(&nd.last, nd.dentry);
	error = PTR_ERR(dentry);
	if (!IS_ERR(dentry)) {
		/* Why not before? Because we want correct error value */
		if (nd.last.name[nd.last.len])
			goto slashes;
		inode = dentry->d_inode;
		if (inode)
			atomic_inc(&inode->i_count);
		error = vfs_unlink(nd.dentry->d_inode, dentry);
	exit2:
		dput(dentry);
	}
	up(&nd.dentry->d_inode->i_sem);
	if (inode)
		iput(inode);	/* truncate the inode here */
exit1:
	path_release(&nd);
exit:
	putname(name);
	return error;
slashes:
	error = !dentry->d_inode ? -ENOENT :
		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
	goto exit2;
}
变量声明和初始化
asmlinkage long sys_unlink(const char __user * pathname)
{
	int error = 0;
	char * name;
	struct dentry *dentry;
	struct nameidata nd;
	struct inode *inode = NULL;
- pathname:要删除的文件路径
- error:错误返回值,初始化为0(成功)
- name:内核空间的文件路径字符串
- dentry:要删除文件的目录项
- nd:路径查找数据
- inode:文件的- inode指针,初始为NULL
获取路径名
	name = getname(pathname);
	if(IS_ERR(name))
		return PTR_ERR(name);
- getname(pathname):从用户空间复制路径名到内核空间
- IS_ERR(name):检查复制是否成功
- 如果失败,立即返回错误码
查找父目录
	error = path_lookup(name, LOOKUP_PARENT, &nd);
	if (error)
		goto exit;
- path_lookup(name, LOOKUP_PARENT, &nd):查找路径的父目录
- LOOKUP_PARENT:标志表示查找父目录而不是文件本身
- 如果查找失败,跳转到清理代码
检查路径组件类型
	error = -EISDIR;
	if (nd.last_type != LAST_NORM)
		goto exit1;
- 预设错误为 -EISDIR(是一个目录)
- nd.last_type:最后一个路径组件的类型
- LAST_NORM:表示正常的文件名
- 如果不是普通文件名(如.、..或根目录),跳转到清理
获取父目录信号量
	down(&nd.dentry->d_inode->i_sem);
- down(&nd.dentry->d_inode->i_sem):获取父目录- inode的信号量锁
- 确保在删除操作期间对父目录的独占访问
查找目标文件目录项
	dentry = lookup_hash(&nd.last, nd.dentry);
	error = PTR_ERR(dentry);
	if (!IS_ERR(dentry)) {
- lookup_hash(&nd.last, nd.dentry):在父目录中查找目标文件的目录项
- 预设错误值为可能的错误码
- 如果查找成功,继续执行删除操作
检查路径格式
		/* Why not before? Because we want correct error value */
		if (nd.last.name[nd.last.len])
			goto slashes;
- nd.last.name[nd.last.len]:检查文件名后是否有额外字符(如斜杠)
- 如果有额外字符,说明路径格式有问题,跳转到slashes处理
获取inode引用
		inode = dentry->d_inode;
		if (inode)
			atomic_inc(&inode->i_count);
- 获取目录项对应的inode
- atomic_inc(&inode->i_count):增加- inode的引用计数
- 防止在删除过程中inode被释放
执行删除操作
		error = vfs_unlink(nd.dentry->d_inode, dentry);
- vfs_unlink(nd.dentry->d_inode, dentry):调用虚拟文件系统层的删除函数
- 参数:父目录的inode和要删除文件的目录项
释放目录项
	exit2:
		dput(dentry);
	}
- dput(dentry):释放目录项的引用计数
- 如果引用计数降为0,会真正释放目录项
释放父目录信号量
	up(&nd.dentry->d_inode->i_sem);
- up(&nd.dentry->d_inode->i_sem):释放父目录- inode的信号量锁
释放inode引用
	if (inode)
		iput(inode);	/* truncate the inode here */
- iput(inode):释放- inode的引用计数
资源清理
exit1:
	path_release(&nd);
exit:
	putname(name);
	return error;
- path_release(&nd):释放路径查找资源
- putname(name):释放复制的路径名字符串
- 返回最终的错误码
斜杠错误处理
slashes:
	error = !dentry->d_inode ? -ENOENT :
		S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
	goto exit2;
- 处理路径中有多余斜杠的情况
- 三元条件运算符确定具体错误:
- 如果inode不存在:-ENOENT
- 如果是目录:-EISDIR
- 否则:-ENOTDIR
 
- 如果
- 跳转到目录项释放代码
函数功能详解
主要功能:删除文件(unlink系统调用)
详细执行流程:
- 
参数验证: - 验证用户空间路径指针有效性
- 复制路径名到内核空间
 
- 
路径解析: - 查找要删除文件的父目录
- 验证路径组件的合法性
 
- 
并发控制: - 获取父目录的信号量锁,防止竞态条件
- 确保删除操作的原子性
 
- 
目标文件查找: - 在父目录中查找要删除的文件
- 验证路径格式的正确性
 
- 
实际删除: - 增加inode引用计数防止意外释放
- 调用vfs_unlink执行实际删除操作
- 减少inode的链接计数,如果为0则真正删除
 
- 增加
- 
资源清理: - 按正确顺序释放所有临时资源
- 返回操作结果
 
关键特性:
- 不是立即删除:unlink只是减少链接计数,当链接计数为0且没有进程打开文件时才会真正删除
- 目录限制:不能使用unlink删除目录
- 原子性:通过信号量确保操作的原子性
- 错误处理:提供精确的错误码指示失败原因
vfs_unlink
int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int error = may_delete(dir, dentry, 0);
	if (error)
		return error;
	if (!dir->i_op || !dir->i_op->unlink)
		return -EPERM;
	DQUOT_INIT(dir);
	down(&dentry->d_inode->i_sem);
	if (d_mountpoint(dentry))
		error = -EBUSY;
	else {
		error = security_inode_unlink(dir, dentry);
		if (!error)
			error = dir->i_op->unlink(dir, dentry);
	}
	up(&dentry->d_inode->i_sem);
	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
		d_delete(dentry);
		inode_dir_notify(dir, DN_DELETE);
	}
	return error;
}
函数声明和权限检查
int vfs_unlink(struct inode *dir, struct dentry *dentry)
{
	int error = may_delete(dir, dentry, 0);
	if (error)
		return error;
- dir:父目录的- inode
- dentry:要删除文件的目录项
- may_delete(dir, dentry, 0):检查是否有删除权限- 第三个参数 0表示不是目录删除
 
- 第三个参数 
- 如果权限检查失败,立即返回错误
文件系统操作支持检查
	if (!dir->i_op || !dir->i_op->unlink)
		return -EPERM;
- dir->i_op:父目录的- inode操作函数表
- dir->i_op->unlink:具体的unlink操作函数指针
- 如果文件系统不支持unlink操作,返回 -EPERM(Operation not permitted)
磁盘配额初始化
	DQUOT_INIT(dir);
- DQUOT_INIT(dir):初始化父目录的磁盘配额
- 确保在删除操作前配额系统处于正确状态
获取inode信号量锁
	down(&dentry->d_inode->i_sem);
- down(&dentry->d_inode->i_sem):获取要删除文件的- inode信号量锁
- 防止在删除过程中文件被并发修改
检查挂载点
	if (d_mountpoint(dentry))
		error = -EBUSY;
- d_mountpoint(dentry):检查目录项是否是挂载点
- 如果是挂载点,返回 -EBUSY
- 不能删除被挂载的文件或目录
安全检查和执行删除
	else {
		error = security_inode_unlink(dir, dentry);
		if (!error)
			error = dir->i_op->unlink(dir, dentry);
	}
- security_inode_unlink(dir, dentry):Linux安全模块(LSM)检查
- 如果安全检查通过,调用具体文件系统的unlink操作
- dir->i_op->unlink(dir, dentry):实际执行删除操作
释放inode信号量锁
	up(&dentry->d_inode->i_sem);
- up(&dentry->d_inode->i_sem):释放- inode的信号量锁
- 无论删除成功与否,都必须释放锁
目录项删除和通知
	/* We don't d_delete() NFS sillyrenamed files--they still exist. */
	if (!error && !(dentry->d_flags & DCACHE_NFSFS_RENAMED)) {
		d_delete(dentry);
		inode_dir_notify(dir, DN_DELETE);
	}
- !(dentry->d_flags & DCACHE_NFSFS_RENAMED):检查不是NFS特殊重命名文件
- d_delete(dentry):从目录项缓存中删除该目录项
- inode_dir_notify(dir, DN_DELETE):发送目录删除通知
返回错误码
	return error;
}
- 返回最终的操作结果(0表示成功)
函数功能详解
主要功能:在虚拟文件系统层删除文件
详细执行流程:
- 
权限验证: - 检查在父目录中删除文件的权限
- 验证调用者是否有足够的权限
 
- 
能力检查: - 确认文件系统支持unlink操作
- 检查底层文件系统的能力
 
- 
配额管理: - 初始化磁盘配额系统
- 为可能的磁盘空间更新做准备
 
- 
并发控制: - 获取文件inode锁,防止竞态条件
- 确保删除操作的原子性
 
- 获取文件
- 
特殊状况检查: - 检查是否为挂载点(不能删除)
- 检查NFS特殊重命名文件
 
- 
缓存清理: - 从目录项缓存中移除条目
- 发送文件系统通知
 
 
                   
                   
                   
                   
                            
 
                             
       
           
                 
                 
                 
                 
                 
                
               
                 
                 
                 
                 
                
               
                 
                 扫一扫
扫一扫
                     
                     
              
             
                   2003
					2003
					
 被折叠的  条评论
		 为什么被折叠?
被折叠的  条评论
		 为什么被折叠?
		 
		  到【灌水乐园】发言
到【灌水乐园】发言                                
		 
		 
    
   
    
   
             
					 
					 
					


 
            