mount: 未知的文件系统类型“vboxsf”_新一代mount系统调用(3)——fsconfig实现(上)...

本文详细介绍了Linux内核中新一代mount系统调用的第二个关键步骤——fsconfig。fsconfig主要用于配置文件系统上下文,文中通过分析fsconfig的实现,解析了参数设置、数据结构及命令处理流程,特别关注了FSCONFIG_SET_STRING和FSCONFIG_CMD_CREATE的用法。目前fsconfig的实现仍在继续,文章预告下篇将深入探讨剩余的代码细节。
摘要由CSDN通过智能技术生成

5de00bf3e487c1c7a10e26c799a34b26.png

前言

上文(醉卧沙场:新一代mount系统调用(2)——fsopen实现)我们讲到了新一代mount类系统调用中fsopen的实现。我们还提到了挂载一个文件系统被分成的六个步骤:

  1. 创建一个文件系统上下文实例。
  2. 解析挂载的参数,把他们附在文件系统上下文实例里。这些参数是通过用户层分开传送进来的。
  3. 预处理这个文件系统上下文实例,验证各参数间的合理关系(这步原来单独成步,后被合并到此步中)。创建(得到)一个超级块实例,以及一个root dentry。
  4. 实际执行挂载操作。
  5. 把返回的可能的错误信息附在文件系统上下文实例里。
  6. 销毁文件系统上下文实例

现在我们继续围绕挂载一个普通文件系统的过程讲述第二系统调用:

int main(int argc, char *argv[])
{
        int fsfd, mfd;

        fsfd = fsopen("xfs", FSOPEN_CLOEXEC);
        if (fsfd == -1) {
                perror("fsopen");
                exit(1);
        }
        fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "/dev/sdb1", 0);
        fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);
....
....
}

fsopen的主要目的就是创建一个文件系统上下文,然后把它和一个文件描述符挂钩,返回文件描述符。fsopen后面就是fsconfig,从字面意思应该可以猜到,我们上面通过fsopen创建了一个文件系统上下文,下面的fsconfig可能就是用来配置文件系统上下文里的内容的。事实上fsconfig确实主要是做这个配置工作的,除了文件系统上下文,同时它还支持其它的工作。下面我们就来具体看一下fsconfig。

fsconfig(上)

我们下面依据mainline upstream linux 5.5-rc4,看一下fsconfig的实现。因为有点长,为了方便讲述,我们将其分成三个部分来讲。第一部分如下:

SYSCALL_DEFINE5(fsconfig,
                int, fd,
                unsigned int, cmd,
                const char __user *, _key,
                const void __user *, _value,
                int, aux)
{
        struct fs_context *fc;
        struct fd f;
        int ret;

        struct fs_parameter param = {
                .type   = fs_value_is_undefined,
        };

        if (fd < 0)
                return -EINVAL;

        switch (cmd) {
        case FSCONFIG_SET_FLAG:
                if (!_key || _value || aux)
                        return -EINVAL;
                break;
        case FSCONFIG_SET_STRING:
                if (!_key || !_value || aux)
                        return -EINVAL;
                break;
        case FSCONFIG_SET_BINARY:
                if (!_key || !_value || aux <= 0 || aux > 1024 * 1024)
                        return -EINVAL;
                break;
        case FSCONFIG_SET_PATH:
        case FSCONFIG_SET_PATH_EMPTY:
                if (!_key || !_value || (aux != AT_FDCWD && aux < 0))
                        return -EINVAL;
                break;
        case FSCONFIG_SET_FD:
                if (!_key || _value || aux < 0)
                        return -EINVAL;
                break;
        case FSCONFIG_CMD_CREATE:
        case FSCONFIG_CMD_RECONFIGURE:
                if (_key || _value || aux)
                        return -EINVAL;
                break;
        default:
                return -EOPNOTSUPP;
        }

        f = fdget(fd);
        if (!f.file)
                return -EBADF;
        ret = -EINVAL;
        if (f.file->f_op != &fscontext_fops)
                goto out_f;

        fc = f.file->private_data;
        if (fc->ops == &legacy_fs_context_ops) {
                switch (cmd) {
                case FSCONFIG_SET_BINARY:
                case FSCONFIG_SET_PATH:
                case FSCONFIG_SET_PATH_EMPTY:
                case FSCONFIG_SET_FD:
                        ret = -EOPNOTSUPP;
                        goto out_f;
                }
        }
...
...

这一部分其实很简单,我们主要说一下这里出现的几个重要数据结构和宏定义。首先是一个结构体变量,如下:

        struct fs_parameter param = {
                .type   = fs_value_is_undefined,
        };

这个变量将用于下面的一系列操作,我们先看一下其定义:

struct fs_parameter {
        const char              *key;           /* Parameter name */
        enum fs_value_type      type:8;         /* The type of value here */
        union {
                char            *string;
                void            *blob;
                struct filename *name;
                struct file     *file;
        };
        size_t  size;
        int     dirfd;
};
  • const char *key;

用来接受fsconfig的key参数。对于挂载一个文件系统来说,在挂载时可以指定很多参数,比如文件系统所在位置(如一个本地设备)和各种挂载选项等。它们构成很多个[key:value]的组合。而key就是索引,用来在这些诸多可配参数中指明要操作的对象,比如key="source",代表要对源对象进行设置,key="allocsize",代表要对“allocsize”这个挂载选项(如果面向的文件系统有的话)进行设置。

  • enum fs_value_type type:8;

这个表示要设置的参数的类型,比如字符串,布尔变量,一个flag等等。目前可使用的参数类型如下:

enum fs_parameter_type {
        __fs_param_wasnt_defined,
        fs_param_is_flag,
        fs_param_is_bool,
        fs_param_is_u32,
        fs_param_is_u32_octal,
        fs_param_is_u32_hex,
        fs_param_is_s32,
        fs_param_is_u64,
        fs_param_is_enum,
        fs_param_is_string,
        fs_param_is_blob,
        fs_param_is_blockdev,
        fs_param_is_path,
        fs_param_is_fd,
        nr__fs_parameter_type,
};
  • union {
  • char *string;
  • void *blob;
  • struct filename *name;
  • struct file *file;
  • };

这是一个联合体,它对应的是fsconfig的value参数,根据我们上面说的[key:value]组合,我们有了key,总要有一个地方指向value。而这里就是保存value用的。因为我们上面也说了参数可能有很多种type,所以这里value有多种不同的可供使用的类型(和type并不一一对应)。

  • size_t size;

这个size一般用于指明上面的value的有效范围。比如value是一个string,这个size就是字符串长度。

  • int dirfd;

这是一个可能被使用的域,常对应fsconfig的aux参数。aux的全称是“Additional information for the value”,也就是说是辅助value而使用的一个参数。比如这样的使用方式:

dirfd = open("/dev/", O_PATH);
fsconfig(sfd, FSCONFIG_SET_PATH, "journal", "sdd4", dirfd);

或者

fd = open("/overlays/mine/", O_PATH);
fsconfig(sfd, FSCONFIG_SET_PATH_EMPTY, "lower_dir", "", fd);

我们既然说完了fsconfig的key, value和aux参数,那还剩一个cmd参数。cmd其实就是command,代表当前要求fsconfig做什么操作指令。目前可使用的操作种类包括:

enum fsconfig_command {
        FSCONFIG_SET_FLAG       = 0,    /* Set parameter, supplying no value */
        FSCONFIG_SET_STRING     = 1,    /* Set parameter, supplying a string value */
        FSCONFIG_SET_BINARY     = 2,    /* Set parameter, supplying a binary blob value */
        FSCONFIG_SET_PATH       = 3,    /* Set parameter, supplying an object by path */
        FSCONFIG_SET_PATH_EMPTY = 4,    /* Set parameter, supplying an object by (empty) path */
        FSCONFIG_SET_FD         = 5,    /* Set parameter, supplying an object by fd */
        FSCONFIG_CMD_CREATE     = 6,    /* Invoke superblock creation */
        FSCONFIG_CMD_RECONFIGURE = 7,   /* Invoke superblock reconfiguration */
};

其中我们在本文前言,或者说 醉卧沙场:新一代mount系统调用(1)——接口初探 文章中使用的例子中,使用了FSCONFIG_SET_STRING和FSCONFIG_CMD_CREATE两个参数,如下:

fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "/dev/sdb1", 0);
fsconfig(fsfd, FSCONFIG_CMD_CREATE, NULL, NULL, 0);

说完这几个必要数据结构和宏定义后(后面还有),我们继续看这第一部分的fsconfig实现。

在定义了一个struct fs_parameter param变量后,一大堆的switch判断是针对不同的cmd而做的参数检查,这里就不多说了。参数检查后,通过fdget(fd)得到fd所对应的文件对象。

f = fdget(fd);

其实这里这个f不是文件实例(struct file)本身,而是struct fd,它区别于整数类型的纯数字表示的fd,它是一个结构体,里面饱含了struct file。

下面这句:

if (f.file->f_op != &fscontext_fops)
        goto out_f;

呼应了我们在上一篇(醉卧沙场:新一代mount系统调用(2)——fsopen实现)临近结尾时说到的用来对应文件系统上下文的文件实例,其文件方法列表不同于一般的文件,而是使用一个叫fscontext_fops的方法集。这里就是判断当前我们通过fd得到的文件实例的方法集是不是fscontext_fops。也就是说判断当前我们得到的文件实例是不是一个文件系统上下文对应的文件实例。

下面一步还是呼应我们在上一篇临近结尾时讲到的将文件实例和文件系统上下文实例关联起来的时候,是将文件系统上下文实例放到了文件实例的private_data域里。现在我们就将其取出来:

fc = f.file->private_data;

后面对于&legacy_fs_context_ops的判断可以忽略,第一部分就到这里。在第一部分里我们主要就是定义了一个struct fs_parameter param变量,以及通过文件描述符(fd)得到了文件实例(struct file),进而也得到了文件系统上下文(fs_context)。

fsconfig(中)

fsconfig的第二部分代码(单纯是因为长,所以我拆开说,实际这还是在一个fsconfig函数里)主要就是对struct fs_parameter param进行设置:

...
        if (_key) {
                param.key = strndup_user(_key, 256);
                if (IS_ERR(param.key)) {
                        ret = PTR_ERR(param.key);
                        goto out_f;
                }
        }
        switch (cmd) {
        case FSCONFIG_SET_FLAG:
                param.type = fs_value_is_flag;
                break;
        case FSCONFIG_SET_STRING:
                param.type = fs_value_is_string;
                param.string = strndup_user(_value, 256);
                if (IS_ERR(param.string)) {
                        ret = PTR_ERR(param.string);
                        goto out_key;
                }
                param.size = strlen(param.string);
                break;
        case FSCONFIG_SET_BINARY:
                param.type = fs_value_is_blob;
                param.size = aux;
                param.blob = memdup_user_nul(_value, aux);
                if (IS_ERR(param.blob)) {
                        ret = PTR_ERR(param.blob);
                        goto out_key;
                }
                break;
        case FSCONFIG_SET_PATH:
                param.type = fs_value_is_filename;
                param.name = getname_flags(_value, 0, NULL);
                if (IS_ERR(param.name)) {
                        ret = PTR_ERR(param.name);
                        goto out_key;
                }
                param.dirfd = aux;
                param.size = strlen(param.name->name);
                break;
        case FSCONFIG_SET_PATH_EMPTY:
                param.type = fs_value_is_filename_empty;
                param.name = getname_flags(_value, LOOKUP_EMPTY, NULL);
                if (IS_ERR(param.name)) {
                        ret = PTR_ERR(param.name);
                        goto out_key;
                }
                param.dirfd = aux;
                param.size = strlen(param.name->name);
                break;
        case FSCONFIG_SET_FD:
                param.type = fs_value_is_file;
                ret = -EBADF;
                param.file = fget(aux);
                if (!param.file)
                        goto out_key;
                break;
        default:
                break;
        }

...
...

首先处理key。这里也没多处理什么,就是如果参数给了key,就把这个字符串赋值到param.key里。

param.key = strndup_user(_key, 256);

后面就是根据fsconfig的不同cmd,结合传进来的 value和aux对param进行不同的处理,来继续设置struct fs_parameter param变量。因为我们在例子中使用的是:

fsconfig(fsfd, FSCONFIG_SET_STRING, "source", "/dev/sdb1", 0);

我们就以此为重点说一下。

首先,在明知是SET_STRING的前提下,我们将参数的类型明确标识为是一个字符串

param.type = fs_value_is_string;

然后将value以字符串对待,赋值给param中的联合体成员的string成员。

param.string = strndup_user(_value, 256);

最后设置param.size来表示上面字符串的长度:

param.size = strlen(param.string);

因为这个用法不需要使用aux参数,所以也没有设置param.dirfd这个域。

回头看一下struct fs_parameter的定义,我们在这部分就将其全部初始化了。

结语

由于fsconfig的逻辑过长,而且新的mount API的主要改变就集中在fsconfig上,所以为了过多内容造成本篇文章阅读时的困扰,本篇暂到这里,我们将在下一篇文章继续讲解fsconfig代码的剩下部分,下一篇的篇幅将很长。到此我们还处于挂载一个文件系统的六步中的第二步中。

根据引用、和的描述,"/sbin/mount.vboxsf: mounting failed with the error: No such device"错误通常会在使用共享文件夹功能时出现。这个错误表示虚拟机无法找到指定的设备来挂载共享文件夹。 解决这个错误的方法有几个步骤: 1. 确保你已经安装了Linux Guest Additions。这是必要的步骤,因为它提供了在虚拟机中使用共享文件夹功能所需的驱动程序和工具。你可以在VirtualBox菜单中选择"设备"->"安装增强功能"来安装Linux Guest Additions。 2. 检查你的共享文件夹设置是否正确。在VirtualBox中,选择你的虚拟机,然后点击"设置"。在"共享文件夹"选项卡中,确保你已经添加了正确的共享文件夹路径和名称。如果你需要重新添加共享文件夹,首先从列表中删除现有的共享文件夹,然后再重新添加。 3. 确保你的用户帐户有权限访问共享文件夹。在Linux中,你需要将你的用户帐户添加到vboxsf用户组中。打开终端并执行以下命令: sudo usermod -aG vboxsf <your_username> 4. 最后,重新启动你的虚拟机并尝试挂载共享文件夹。执行以下命令: sudo mount -t vboxsf <shared_folder_name> <mount_point> 如果你按照上述步骤进行操作,仍然遇到"/sbin/mount.vboxsf: mounting failed with the error: No such device"错误,请确保在VirtualBox主机和虚拟机之间的共享文件夹路径是正确的,并且没有其他冲突。你还可以尝试在VirtualBox中删除共享文件夹,并重新添加它,然后重新启动虚拟机。 希望这些步骤可以帮助你解决这个问题。如果你需要进一步的帮助,请参考引用中提供的链接,它包含了更详细的解决方案。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [/sbin/mount.vboxsf: mounting failed with the error: No such device](https://blog.csdn.net/daiyudong2020/article/details/58073520)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *2* [共享文件夹挂载失败的错误:/sbin/mount.vboxsf: mounting failed with the error: No such device](https://blog.csdn.net/qq_32162235/article/details/108495251)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] - *3* [ubuntu 下VirtualBox 如何实现共享设置](https://download.csdn.net/download/weixin_38746701/14108685)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 33.333333333333336%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值