【问题标题】:linux kernel proper way to unlink a filelinux内核取消链接文件的正确方法
【发布时间】:2023-03-19 08:50:01
【问题描述】:

Linux 3.2.2 内核

我检查了 fs/namei.c 中的源代码,发现 do_unlinkat 执行整个操作但需要一个用户空间指针。我想知道是否有一个函数可以在内核空间之外执行此操作(我确实有文件、inode 和 dentry 的结构路径) 我可以将函数中的代码复制到我自己的代码中。但我只是想知道它是如何工作的?

【问题讨论】:

    标签: c linux kernel


    【解决方案1】:

    你了解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 锁定),如果我是你,我只会使用路径名。

    祝黑客愉快,祝你好运。

    【讨论】:

      猜你喜欢
      • 2015-05-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-07-04
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多