【发布时间】:2023-03-19 08:50:01
【问题描述】:
Linux 3.2.2 内核
我检查了 fs/namei.c 中的源代码,发现 do_unlinkat 执行整个操作但需要一个用户空间指针。我想知道是否有一个函数可以在内核空间之外执行此操作(我确实有文件、inode 和 dentry 的结构路径) 我可以将函数中的代码复制到我自己的代码中。但我只是想知道它是如何工作的?
【问题讨论】:
Linux 3.2.2 内核
我检查了 fs/namei.c 中的源代码,发现 do_unlinkat 执行整个操作但需要一个用户空间指针。我想知道是否有一个函数可以在内核空间之外执行此操作(我确实有文件、inode 和 dentry 的结构路径) 我可以将函数中的代码复制到我自己的代码中。但我只是想知道它是如何工作的?
【问题讨论】:
你了解fs/namei.c:do_unlinkat() 函数吗?它完全符合您的要求,只是它使用
error = user_path_parent(dfd, pathname, &nd, &name);
if (error)
return error;
查找struct nameidata。如果成功,pathname 的内核端副本将存储在name 中(必须使用putname(name) 释放)。
但是,同一个文件也包含user_path_parent() 函数的实现。它非常简单,因此您应该毫不费力地理解它的作用。实际上,除了将用户空间-pathname 复制到内核端 name 之外,它实际上只是进行了一次 do_path_lookup() 调用,它仅在内核端结构上运行。
使用上述方法,您可以对do_unlinkat() 进行细微修改,以实现您自己的内核端unlinkat() 功能。如果这是您希望有朝一日被上游接受的代码,您还应该重构这两个函数,以免重复代码。可能类似于
/*
* Make sure that the actual truncation of the file will occur outside its
* directory's i_mutex. Truncate can take a long time if there is a lot of
* writeout happening, and we don't want to prevent access to the directory
* while waiting on the I/O.
*/
static long kern_unlinkat(int dfd, const char *name)
{
long error;
struct dentry *dentry;
struct nameidata nd;
struct inode *inode = NULL;
error = do_path_lookup(dfd, name, LOOKUP_PARENT, &nd);
if (error)
return error;
error = -EISDIR;
if (nd.last_type != LAST_NORM)
goto exit1;
nd.flags &= ~LOOKUP_PARENT;
error = mnt_want_write(nd.path.mnt);
if (error)
goto exit1;
mutex_lock_nested(&nd.path.dentry->d_inode->i_mutex, I_MUTEX_PARENT);
dentry = lookup_hash(&nd);
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)
goto slashes;
ihold(inode);
error = security_path_unlink(&nd.path, dentry);
if (error)
goto exit2;
error = vfs_unlink(nd.path.dentry->d_inode, dentry);
exit2:
dput(dentry);
}
mutex_unlock(&nd.path.dentry->d_inode->i_mutex);
if (inode)
iput(inode); /* truncate the inode here */
mnt_drop_write(nd.path.mnt);
exit1:
path_put(&nd.path);
return error;
slashes:
error = !dentry->d_inode ? -ENOENT :
S_ISDIR(dentry->d_inode->i_mode) ? -EISDIR : -ENOTDIR;
goto exit2;
}
static long do_unlinkat(int dfd, const char __user *pathname)
{
long error;
char *name = getname(pathname);
if (IS_ERR(name))
return PTR_ERR(name);
error = kern_unlinkat(dfd, name);
putname(name);
return error;
}
EXPORT_SYMBOL(kern_unlink);
如果上述方法可行——我没有费心去测试它;这一切都取决于您——然后您可以在自己的内核模块中使用kern_unlinkat(directoryfd, pathname) 或kern_unlinkat(AT_FDCWD, pathname) 通过在内核端字符串pathname 中指定文件的路径名来删除文件。
如果您已经拥有nameidata,那么您显然可以更改上面的内容以匹配哪些代码是常见的,哪些不是。但是,考虑到细节的复杂性(尤其是dentry 锁定),如果我是你,我只会使用路径名。
祝黑客愉快,祝你好运。
【讨论】: