Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/57.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 在linux 3.5.4中如何从自定义系统调用调用系统调用_C_Linux_Filesystems_Kernel_System Calls - Fatal编程技术网

C 在linux 3.5.4中如何从自定义系统调用调用系统调用

C 在linux 3.5.4中如何从自定义系统调用调用系统调用,c,linux,filesystems,kernel,system-calls,C,Linux,Filesystems,Kernel,System Calls,我正在linux中实现自己的系统调用。它正在调用其中的重命名系统调用。它使用一个用户参数(下面是代码)将代码传递给重命名 以下是基本代码: int sys_mycall(const char __user * inputFile) { // // Code to generate my the "fileName" // // old_fs = get_fs(); set_fs(KERNEL_DS); ans = sys_renameat(AT_FDCWD, fileName

我正在linux中实现自己的系统调用。它正在调用其中的重命名系统调用。它使用一个用户参数(下面是代码)将代码传递给重命名

以下是基本代码:

int sys_mycall(const char __user * inputFile)   {

//
// Code to generate my the "fileName"
//
//

old_fs = get_fs();
set_fs(KERNEL_DS);

    ans =  sys_renameat(AT_FDCWD, fileName, AT_FDCWD, inputFile);

set_fs(old_fs);

    return ans;

}
我有两个疑问

  • 我使用的是
    old_fs=get_fs()
    设置fs(内核DS)和<代码>设置文件(旧文件)
    绕过对
    sys\u rename
    的实际调用,因为出现错误。我从这个问题中得到了答案:。。。这是一个正确的工作方法吗
  • 如何从系统调用中调用其他系统调用
  • 编辑:

    int sys_myfunc(const char __user * inputFileUser)   {
    
    
        char inputFile[255];
        int l = 0;
        while(inputFileUser[l] != '\0') l++;
    
        if(l==0)
            return -10; 
    
        if(copy_from_user(inputFile,inputFileUser,l+1)< 0 ) return -20;
    //
    //GENERATE fileName here
    //
    //
    
        char fileName[255];
        return  sys_renameat(AT_FDCWD, inputFile, AT_FDCWD, fileName);
    
    }
    
    int sys\u myfunc(const char\u user*inputFileUser){
    字符输入文件[255];
    int l=0;
    while(inputFileUser[l]!='\0')l++;
    如果(l==0)
    返回-10;
    if(从用户复制用户(inputFile,inputFileUser,l+1)<0)返回-20;
    //
    //在此处生成文件名
    //
    //
    字符文件名[255];
    返回sys_renameat(AT_FDCWD,inputFile,AT_FDCWD,fileName);
    }
    

    下面仍然返回-1。为什么?我将数据复制到内核空间。

    Hm
    linux-3.6.2/fs/namei.c
    包含许多类似的情况。例如,
    rename
    syscall实际上定义为

    SYSCALL_DEFINE2(rename, const char __user *, oldname, const char __user *, newname)
    {
        return sys_renameat(AT_FDCWD, oldname, AT_FDCWD, newname);
    }
    
    换句话说,从另一个系统调用调用一个系统调用没有问题。问题是指针参数是用户空间指针,而您正试图提供内核指针:您的
    文件名
    应该在用户空间中分配,但您的在内核空间中

    正确的解决方案是从两个函数(您的函数和
    fs/namei.c
    中的
    sys\u renameat()
    )中分离出公共代码,然后从两个系统调用中调用该函数。假设您不想将其包含在上游——如果是的话,那么这是重构和重新思考的时间——您可以轻松地将
    sys\u renameat
    的内容复制到您自己的函数中;没那么大。熟悉像这样的文件系统操作所需的检查和锁定也是一个有用的要点


    为解释问题和解决方案而编辑:

    在非常真实的意义上,由正常进程分配的内存(用户空间内存)和由内核分配的内存(内核空间)被内核用户空间屏障完全分开

    你的代码忽略了这个障碍,根本不应该工作。(它可能在x86上工作,因为内核用户空间的障碍很容易从该体系结构的内核端突破。)您还可以使用256字节的堆栈作为文件名,这是一个禁忌:内核堆栈是一个非常有限的资源,应该少用

    普通进程(用户空间进程)无法访问任何内核内存。你可以试试,它不会起作用的。这就是障碍存在的原因。(有些嵌入式系统的硬件根本不支持这样的障碍,但在本次讨论中,让我们忽略这些障碍。请记住,即使在x86上,障碍很容易从内核端突破,但并不意味着它不存在。不要像个傻瓜一样假设,因为它似乎对您有效,它确实存在不知怎么说是对的。)

    屏障的本质是,在大多数体系结构上,内核也存在屏障

    为了帮助内核程序员,指向用户空间的指针被标记为
    \uu user
    。这意味着您不能仅仅取消对它们的引用并期望它们工作;您需要使用
    copy\u from\u user()
    copy\u to\u user()
    。这不仅仅是系统调用参数:当您从内核访问用户空间数据时,您需要使用这两个函数

    所有系统调用都处理用户空间数据。您看到的每个指针都(或应该!)标记为
    \uu user
    。每个系统调用都执行从用户空间访问数据的所有必要工作

    您的问题是试图向系统调用提供内核空间数据,
    inputFile
    。它将不起作用,因为系统调用总是试图通过屏障到达,但是
    inputFile
    位于屏障的同一侧

    要将
    inputFile
    复制到屏障的另一边,确实没有明智的方法。我的意思是,当然有办法做到这一点,这甚至没有那么困难,但这并不明智

    因此,让我们探讨一下我上面描述的正确解决方案,footy已经拒绝过一次了

    首先,让我们看看当前(3.6.2)Linux内核中的
    renameat
    syscall实际上是什么样子的(请记住,此代码是在GPLv2下授权的)。
    rename
    syscall只需使用
    sys\u rename调用它(AT\u FDCWD,oldname,AT\u FDCWD,newname)
    。我将插入我对代码作用的解释:

    SYSCALL_DEFINE4(renameat, int, olddfd, const char __user *, oldname,
                    int, newdfd, const char __user *, newname)
    {
            struct dentry *old_dir, *new_dir;
            struct dentry *old_dentry, *new_dentry;
            struct dentry *trap;
            struct nameidata oldnd, newnd;
            char *from;
            char *to;
            int error;
    
    在内核中,堆栈是一种有限的资源。您可以使用相当多的变量,但任何局部数组都将是一个严重的问题。上面的局部变量列表几乎是典型系统调用中最大的一个

    对于重命名调用,函数必须首先找到包含文件名的父目录:

            error = user_path_parent(olddfd, oldname, &oldnd, &from);
            if (error)
                    goto exit;
    
    注意:在此之后,旧目录和路径必须在使用后通过调用
    path\u put(&oldnd.path)释放;putname(来自)

    注意:在此之后,新目录和路径必须在使用后通过调用
    path\u put(&newnd.path)释放;putname(to)

    下一步是检查这两个文件是否驻留在同一个文件系统上:

            error = -EXDEV;
            if (oldnd.path.mnt != newnd.path.mnt)
                    goto exit2;
    
    目录中的最后一个组件必须是普通目录:

            old_dir = oldnd.path.dentry;
            error = -EBUSY;
            if (oldnd.last_type != LAST_NORM)
                    goto exit2;
    
            new_dir = newnd.path.dentry;
            if (newnd.last_type != LAST_NORM)
                    goto exit2;
    
    并且包含目录的挂载必须是可写的。请注意,如果成功,这将对装载应用锁,并且在系统调用返回之前,必须始终与
    mnt\u drop\u write(oldnd.path.mnt)
    调用配对

            error = mnt_want_write(oldnd.path.mnt);
            if (error)
                    goto exit2;
    
    接下来,更新nameidata查找标志,以反映目录已经已知:

            oldnd.flags &= ~LOOKUP_PARENT;
            newnd.flags &= ~LOOKUP_PARENT;
            newnd.flags |= LOOKUP_RENAME_TARGET;
    
    其次,这两个问题是可怕的
            oldnd.flags &= ~LOOKUP_PARENT;
            newnd.flags &= ~LOOKUP_PARENT;
            newnd.flags |= LOOKUP_RENAME_TARGET;
    
            trap = lock_rename(new_dir, old_dir);
    
            old_dentry = lookup_hash(&oldnd);
            error = PTR_ERR(old_dentry);
            if (IS_ERR(old_dentry))
                    goto exit3;
            /* source must exist */
            error = -ENOENT;
            if (!old_dentry->d_inode)
                    goto exit4;
            /* unless the source is a directory trailing slashes give -ENOTDIR */
            if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
                    error = -ENOTDIR;
                    if (oldnd.last.name[oldnd.last.len])
                            goto exit4;
                    if (newnd.last.name[newnd.last.len])
                            goto exit4;
            }
            /* source should not be ancestor of target */
            error = -EINVAL;
            if (old_dentry == trap)
                    goto exit4;
    
            new_dentry = lookup_hash(&newnd);
            error = PTR_ERR(new_dentry);
            if (IS_ERR(new_dentry))
                    goto exit4;
            /* target should not be an ancestor of source */
            error = -ENOTEMPTY;
            if (new_dentry == trap)
                    goto exit5;
    
            error = security_path_rename(&oldnd.path, old_dentry,
                                         &newnd.path, new_dentry);
            if (error)
                    goto exit5;
    
            error = vfs_rename(old_dir->d_inode, old_dentry,
                               new_dir->d_inode, new_dentry);
    
    exit5:
            dput(new_dentry);
    exit4:
            dput(old_dentry);
    exit3:
            unlock_rename(new_dir, old_dir);
            mnt_drop_write(oldnd.path.mnt);
    exit2:
            path_put(&newnd.path);
            putname(to);
    exit1:
            path_put(&oldnd.path);
            putname(from);
    exit:
            return error;
    }
    
    char *result = __getname();  /* Reserve PATH_MAX+1 bytes of kernel memory for one file name */
    in    len;
    
    len = strncpy_from_user(result, old/newname, PATH_MAX);
    if (len <= 0) {
        __putname(result);
        /* An error occurred, abort! */
    }
    
    if (len >= PATH_MAX) {
        __putname(result);
        /* path is too long, abort! */
    }
    
    /* Finally, add it to the audit context for the current process. */
    audit_getname(result);
    
    putname(result);
    
    SYSCALL_DEFINE1(myfunc, const char __user *, oldname)
    {
    
        struct dentry *old_dir, *new_dir;
        struct dentry *old_dentry, *new_dentry;
        struct dentry *trap;
        struct nameidata oldnd, newnd;
        char *from;
        char *to = __getname();
        int error;
    
        from = getname(oldname);
        if (IS_ERR(from)) {
            error = PTR_ERR(from);
            goto exit;
        }
    
        error = do_path_lookup(AT_FDCWD, from, LOOKUP_PARENT, &oldnd);
        if (error)
            goto exit0;
    
        error = do_path_lookup(AT_FDCWD, to, LOOKUP_PARENT, &newnd);
        if (error)
            goto exit1;
    
        error = -EXDEV;
        if (oldnd.path.mnt != newnd.path.mnt)
            goto exit2;
    
        old_dir = oldnd.path.dentry;
        error = -EBUSY;
        if (oldnd.last_type != LAST_NORM)
            goto exit2;
    
        new_dir = newnd.path.dentry;
        if (newnd.last_type != LAST_NORM)
            goto exit2;
    
        error = mnt_want_write(oldnd.path.mnt);
        if (error)
            goto exit2;
    
        oldnd.flags &= ~LOOKUP_PARENT;
        newnd.flags &= ~LOOKUP_PARENT;
        newnd.flags |= LOOKUP_RENAME_TARGET;
    
        trap = lock_rename(new_dir, old_dir);
    
        old_dentry = lookup_hash(&oldnd);
        error = PTR_ERR(old_dentry);
        if (IS_ERR(old_dentry))
            goto exit3;
        /* source must exist */
        error = -ENOENT;
        if (!old_dentry->d_inode)
            goto exit4;
        /* unless the source is a directory trailing slashes give -ENOTDIR */
        if (!S_ISDIR(old_dentry->d_inode->i_mode)) {
            error = -ENOTDIR;
            if (oldnd.last.name[oldnd.last.len])
                goto exit4;
            if (newnd.last.name[newnd.last.len])
                goto exit4;
        }
        /* source should not be ancestor of target */
        error = -EINVAL;
        if (old_dentry == trap)
            goto exit4;
        new_dentry = lookup_hash(&newnd);
        error = PTR_ERR(new_dentry);
        if (IS_ERR(new_dentry))
            goto exit4;
        /* target should not be an ancestor of source */
        error = -ENOTEMPTY;
        if (new_dentry == trap)
            goto exit5;
    
        error = security_path_rename(&oldnd.path, old_dentry,
                         &newnd.path, new_dentry);
        if (error)
            goto exit5;
    
        error = vfs_rename(old_dir->d_inode, old_dentry,
                       new_dir->d_inode, new_dentry);
    
    exit5:
        dput(new_dentry);
    exit4:
        dput(old_dentry);
    exit3:
        unlock_rename(new_dir, old_dir);
        mnt_drop_write(oldnd.path.mnt);
    exit2:
        path_put(&newnd.path);
    exit1:
        path_put(&oldnd.path);
    exit0:
        putname(from);
    exit:
        __putname(to);
        return error;
    }