檔案系統掛載 API

概述

新掛載的建立現在將透過多步驟過程完成

  1. 建立檔案系統上下文。

  2. 解析引數並將其附加到上下文。引數預期將從使用者空間單獨傳遞,但也可以處理傳統的二進位制引數。

  3. 驗證並預處理上下文。

  4. 獲取或建立一個超級塊和可掛載根。

  5. 執行掛載。

  6. 返回附加到上下文的錯誤訊息。

  7. 銷燬上下文。

為支援此功能,file_system_type 結構體新增了兩個欄位

int (*init_fs_context)(struct fs_context *fc);
const struct fs_parameter_description *parameters;

第一個用於設定檔案系統上下文的檔案系統特定部分,包括額外空間;第二個指向引數描述,用於註冊時驗證和未來系統呼叫的查詢。

請注意,安全初始化是在檔案系統被呼叫之後完成的,以便可以首先調整名稱空間。

檔案系統上下文

超級塊的建立和重新配置由檔案系統上下文控制。這由 fs_context 結構體表示

struct fs_context {
        const struct fs_context_operations *ops;
        struct file_system_type *fs_type;
        void                    *fs_private;
        struct dentry           *root;
        struct user_namespace   *user_ns;
        struct net              *net_ns;
        const struct cred       *cred;
        char                    *source;
        char                    *subtype;
        void                    *security;
        void                    *s_fs_info;
        unsigned int            sb_flags;
        unsigned int            sb_flags_mask;
        unsigned int            s_iflags;
        enum fs_context_purpose purpose:8;
        ...
};

fs_context 欄位如下

  • const struct fs_context_operations *ops
    

    這些是對檔案系統上下文可以執行的操作(見下文)。這必須透過 ->init_fs_context() file_system_type 操作設定。

  • struct file_system_type *fs_type
    

    指向正在構建或重新配置的檔案系統的 file_system_type 的指標。這會保留對型別所有者的引用。

  • void *fs_private
    

    指向檔案系統私有資料的指標。檔案系統需要在此處儲存它解析的任何選項。

  • struct dentry *root
    

    指向可掛載樹根(並間接指向其超級塊)的指標。這由 ->get_tree() 操作填充。如果已設定,則還必須持有對 root->d_sb 的活動引用。

  • struct user_namespace *user_ns
    struct net *net_ns
    

    這些是呼叫程序正在使用的名稱空間的子集。它們保留對每個名稱空間的引用。訂閱的名稱空間可能會被檔案系統替換,以反映其他來源,例如自動掛載時的父掛載超級塊。

  • const struct cred *cred
    

    掛載者的憑據。這會保留對憑據的引用。

  • char *source
    

    這指定了來源。它可能是一個塊裝置(例如 /dev/sda1)或更不尋常的來源,例如 NFS 所需的“host:/path”。

  • char *subtype
    

    這是一個字串,將新增到 /proc/mounts 中顯示的型別以對其進行限定(由 FUSE 使用)。如果需要,檔案系統可以設定此項。

  • void *security
    

    LSM(Linux 安全模組)用於存放其超級塊安全資料的位置。相關的安全操作在下面描述。

  • void *s_fs_info
    

    新超級塊的提議 s_fs_info,由 sget_fc() 在超級塊中設定。這可用於區分超級塊。

  • unsigned int sb_flags
    unsigned int sb_flags_mask
    

    super_block::s_flags 中哪些 SB_* 標誌位將被設定/清除。

  • unsigned int s_iflags
    

    當建立超級塊時,這些將與 s->s_iflags 進行位或操作。

  • enum fs_context_purpose
    

    這表明了上下文的預期用途。可用值如下

    FS_CONTEXT_FOR_MOUNT

    用於顯式掛載的新超級塊

    FS_CONTEXT_FOR_SUBMOUNT

    現有掛載的新自動子掛載

    FS_CONTEXT_FOR_RECONFIGURE

    更改現有掛載

掛載上下文透過呼叫 vfs_new_fs_context()vfs_dup_fs_context() 建立,並使用 put_fs_context() 銷燬。請注意,此結構體不進行引用計數。

VFS、安全和檔案系統掛載選項透過 vfs_parse_mount_option() 單獨設定。舊的 mount(2) 系統呼叫提供的作為資料頁的選項可以使用 generic_parse_monolithic() 進行解析。

掛載時,檔案系統可以從任何指標獲取資料並將其附加到超級塊(或任何其他地方),前提是它清除了掛載上下文中的指標。

檔案系統也可以分配資源並將其與掛載上下文關聯。例如,NFS 可能會關聯適當的協議版本模組。

檔案系統上下文操作

檔案系統上下文指向一個操作表

struct fs_context_operations {
        void (*free)(struct fs_context *fc);
        int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
        int (*parse_param)(struct fs_context *fc,
                           struct fs_parameter *param);
        int (*parse_monolithic)(struct fs_context *fc, void *data);
        int (*get_tree)(struct fs_context *fc);
        int (*reconfigure)(struct fs_context *fc);
};

這些操作在掛載過程的各個階段被呼叫,以管理檔案系統上下文。它們如下:

  • void (*free)(struct fs_context *fc);
    

    當上下文被銷燬時,呼叫此函式以清理檔案系統上下文的檔案系統特定部分。它應該知道上下文的部分內容可能已經被 ->get_tree() 移除並置為 NULL。

  • int (*dup)(struct fs_context *fc, struct fs_context *src_fc);
    

    當檔案系統上下文被複制以複製檔案系統私有資料時呼叫。可能會返回錯誤以指示此操作失敗。

    警告

    請注意,即使此操作失敗,put_fs_context() 也會緊隨其後被呼叫,因此 ->dup() 必須使檔案系統私有資料對於 ->free() 是安全的。

  • int (*parse_param)(struct fs_context *fc,
                       struct fs_parameter *param);
    

    當引數被新增到檔案系統上下文時呼叫。param 指向鍵名和可能的數值物件。VFS 特定的選項將被剔除,並且上下文中的 fc->sb_flags 會被更新。安全選項也將被剔除,並且 fc->security 會被更新。

    引數可以使用 fs_parse()fs_lookup_param() 進行解析。請注意,來源被呈現為名為“source”的引數。

    如果成功,應返回 0;否則返回負錯誤碼。

  • int (*parse_monolithic)(struct fs_context *fc, void *data);
    

    當呼叫 mount(2) 系統呼叫一次性傳遞整個資料頁時呼叫。如果預期這只是一個由逗號分隔的“key[=val]”項列表,則可以將其設定為 NULL。

    返回值與 ->parse_param() 相同。

    如果檔案系統(例如 NFS)需要首先檢查資料,然後發現它是標準的鍵值列表,則可以將其傳遞給 generic_parse_monolithic()

  • int (*get_tree)(struct fs_context *fc);
    

    呼叫此函式以獲取或建立可掛載根和超級塊,使用儲存在檔案系統上下文中的資訊(重新配置透過不同的向量進行)。它可以將檔案系統上下文中所需的任何資源分離出來,並將其轉移到它建立的超級塊。

    成功時,它應將 fc->root 設定為可掛載根並返回 0。如果發生錯誤,則應返回負錯誤碼。

    使用者空間驅動的上下文的階段將被設定為只允許在任何特定上下文上呼叫一次此函式。

  • int (*reconfigure)(struct fs_context *fc);
    

    呼叫此函式以使用儲存在檔案系統上下文中的資訊來重新配置超級塊。它可以將檔案系統上下文中所需的任何資源分離出來,並將其轉移到超級塊。超級塊可以從 fc->root->d_sb 中找到。

    成功時,它應返回 0。如果發生錯誤,則應返回負錯誤碼。

    注意

    reconfigure 旨在替代 remount_fs

檔案系統上下文安全性

檔案系統上下文包含一個安全指標,LSM 可以使用它為要掛載的超級塊構建安全上下文。新的掛載程式碼為此目的使用了許多操作:

  • int security_fs_context_alloc(struct fs_context *fc,
                                  struct dentry *reference);
    

    呼叫此函式以初始化 fc->security(預設為 NULL)並分配所需的任何資源。成功時應返回 0,失敗時返回負錯誤碼。

    如果上下文是為超級塊重新配置(FS_CONTEXT_FOR_RECONFIGURE)而建立的,則 reference 將非 NULL,在這種情況下它指示要重新配置的超級塊的根目錄項。在子掛載(FS_CONTEXT_FOR_SUBMOUNT)的情況下,它也將非 NULL,在這種情況下它指示自動掛載點。

  • int security_fs_context_dup(struct fs_context *fc,
                                struct fs_context *src_fc);
    

    呼叫此函式以初始化 fc->security(預設為 NULL)並分配所需的任何資源。原始檔案系統上下文由 src_fc 指向,並可用作參考。成功時應返回 0,失敗時返回負錯誤碼。

  • void security_fs_context_free(struct fs_context *fc);
    

    呼叫此函式以清理附加到 fc->security 的任何內容。請注意,內容可能已在 get_tree 期間轉移到超級塊,並且指標已清除。

  • int security_fs_context_parse_param(struct fs_context *fc,
                                        struct fs_parameter *param);
    

    為每個掛載引數呼叫此函式,包括來源。引數與 ->parse_param() 方法的引數相同。它應返回 0 表示引數應傳遞給檔案系統,返回 1 表示引數應被丟棄,或返回錯誤表示引數應被拒絕。

    param 指向的值可以被修改(如果是字串)或“竊取”(如果值指標被置為 NULL)。如果被“竊取”,則必須返回 1 以防止其傳遞給檔案系統。

  • int security_fs_context_validate(struct fs_context *fc);
    

    在所有選項解析完畢後呼叫此函式,以整體驗證集合並進行任何必要的分配,從而降低 security_sb_get_tree()security_sb_reconfigure() 失敗的可能性。它應返回 0 或負錯誤碼。

    在重新配置的情況下,目標超級塊將透過 fc->root 訪問。

  • int security_sb_get_tree(struct fs_context *fc);
    

    在掛載過程中呼叫此函式,以驗證指定的超級塊是否允許被掛載,並將安全資料傳輸到那裡。它應返回 0 或負錯誤碼。

  • void security_sb_reconfigure(struct fs_context *fc);
    

    呼叫此函式以將任何重新配置應用於 LSM 的上下文。它不得失敗。錯誤檢查和資源分配必須提前由引數解析和驗證鉤子完成。

  • int security_sb_mountpoint(struct fs_context *fc,
                               struct path *mountpoint,
                               unsigned int mnt_flags);
    

    在掛載過程中呼叫此函式,以驗證附加到上下文的根目錄項是否允許附加到指定的掛載點。成功時應返回 0,失敗時返回負錯誤碼。

VFS 檔案系統上下文 API

有四個用於建立檔案系統上下文的操作,以及一個用於銷燬上下文的操作:

  • struct fs_context *fs_context_for_mount(struct file_system_type *fs_type,
                                            unsigned int sb_flags);
    

    分配一個檔案系統上下文,用於設定新的掛載,無論是使用新的超級塊還是共享現有超級塊。這會設定超級塊標誌,初始化安全性,並呼叫 fs_type->init_fs_context() 來初始化檔案系統私有資料。

    fs_type 指定將管理上下文的檔案系統型別,sb_flags 預設其中儲存的超級塊標誌。

  • struct fs_context *fs_context_for_reconfigure(
             struct dentry *dentry,
             unsigned int sb_flags,
             unsigned int sb_flags_mask);
    

    分配一個檔案系統上下文,用於重新配置現有超級塊。dentry 提供對要配置的超級塊的引用。sb_flagssb_flags_mask 指示哪些超級塊標誌需要更改以及更改為哪個值。

  • struct fs_context *fs_context_for_submount(
             struct file_system_type *fs_type,
             struct dentry *reference);
    

    分配一個檔案系統上下文,用於為自動掛載點或其他派生超級塊建立新的掛載。fs_type 指定將管理上下文的檔案系統型別,引用目錄項提供引數。名稱空間也從引用目錄項的超級塊傳播。

    請注意,參考目錄項不需要與 fs_type 具有相同的檔案系統型別。

  • struct fs_context *vfs_dup_fs_context(struct fs_context *src_fc);
    

    複製檔案系統上下文,複製所有記錄的選項,並複製或額外引用其中持有的任何資源。當檔案系統必須在現有掛載中獲取掛載時(例如 NFS4 透過內部掛載目標伺服器的根目錄然後私有路徑遍歷到目標目錄來實現),可以使用此功能。

    新上下文中的用途繼承自舊上下文。

  • void put_fs_context(struct fs_context *fc);
    

    銷燬檔案系統上下文,釋放其持有的任何資源。這會呼叫 ->free() 操作。建立檔案系統上下文的任何人都可以呼叫此函式。

    警告

    檔案系統上下文不進行引用計數,因此這會導致無條件銷燬。

在上述所有操作中,除了 put 操作外,返回值都是一個掛載上下文指標或一個負錯誤碼。

對於其餘操作,如果發生錯誤,將返回負錯誤碼。

  • int vfs_parse_fs_param(struct fs_context *fc,
                           struct fs_parameter *param);
    

    向檔案系統上下文提供單個掛載引數。這包括來源/裝置的規範,它被指定為“source”引數(如果檔案系統支援,可以多次指定)。

    param 指定引數鍵名和值。在傳遞給檔案系統之前,會首先檢查引數是否對應標準掛載標誌(在這種情況下,它用於設定 SB_xxx 標誌並被消耗)或安全選項(在這種情況下,LSM 消耗它)。

    引數值是型別化的,可以是以下之一:

    fs_value_is_flag

    未賦值的引數

    fs_value_is_string

    值為字串

    fs_value_is_blob

    值為二進位制大物件

    fs_value_is_filename

    值為檔名* + dirfd

    fs_value_is_file

    值為一個開啟的檔案(file*

    如果存在值,該值將儲存在結構體的一個聯合體中,位於 param->{string,blob,name,file} 之一。請注意,函式可能會“竊取”並清除指標,但隨後負責處理該物件。

  • int vfs_parse_fs_string(struct fs_context *fc, const char *key,
                            const char *value, size_t v_size);
    

    vfs_parse_fs_param() 的一個包裝器,它複製傳遞給它的值字串。

  • int generic_parse_monolithic(struct fs_context *fc, void *data);
    

    解析 sys_mount() 資料頁,假設其形式為由逗號分隔的 key[=val] 選項文字列表。列表中的每個項都傳遞給 vfs_mount_option()。當 ->parse_monolithic() 方法為 NULL 時,這是預設行為。

  • int vfs_get_tree(struct fs_context *fc);
    

    獲取或建立可掛載根和超級塊,使用檔案系統上下文中的引數來選擇/配置超級塊。這會呼叫 ->get_tree() 方法。

  • struct vfsmount *vfs_create_mount(struct fs_context *fc);
    

    根據指定檔案系統上下文中的引數建立掛載。請注意,這不會將掛載附加到任何地方。

超級塊建立輔助函式

VFS 提供了許多輔助函式供檔案系統用於建立或查詢超級塊。

  • struct super_block *
    sget_fc(struct fs_context *fc,
            int (*test)(struct super_block *sb, struct fs_context *fc),
            int (*set)(struct super_block *sb, struct fs_context *fc));
    

    這是核心例程。如果 test 非 NULL,它會使用測試函式搜尋與 fs_context 中持有的條件匹配的現有超級塊。如果沒有找到匹配項,則會建立一個新的超級塊,並呼叫 set 函式進行設定。

    在呼叫 set 函式之前,fc->s_fs_info 將被傳輸到 sb->s_fs_info——如果 set 返回成功(即 0),fc->s_fs_info 將被清除。

以下輔助函式都封裝了 sget_fc()

  1. vfs_get_single_super

    系統中只能存在一個這樣的超級塊。任何進一步嘗試獲取新超級塊的操作都將獲取此超級塊(並且任何引數差異都將被忽略)。

  2. vfs_get_keyed_super

    這種型別的多個超級塊可能存在,並且它們以其 s_fs_info 指標為鍵(例如,這可能指一個名稱空間)。

  3. vfs_get_independent_super

    這種型別的多個獨立超級塊可能存在。此函式從不匹配現有的超級塊,並且總是建立一個新的。

引數描述

引數使用 linux/fs_parser.h 中定義的結構體進行描述。有一個核心描述結構體將所有內容連結在一起:

struct fs_parameter_description {
        const struct fs_parameter_spec *specs;
        const struct fs_parameter_enum *enums;
};

例如

enum {
        Opt_autocell,
        Opt_bar,
        Opt_dyn,
        Opt_foo,
        Opt_source,
};

static const struct fs_parameter_description afs_fs_parameters = {
        .specs          = afs_param_specs,
        .enums          = afs_param_enums,
};

成員如下:

  1. const struct fs_parameter_specification *specs;
    

    引數規範表,以空條目終止,其中條目型別為:

    struct fs_parameter_spec {
            const char              *name;
            u8                      opt;
            enum fs_parameter_type  type:8;
            unsigned short          flags;
    };
    

    “name”欄位是一個字串,用於精確匹配引數鍵(無萬用字元、模式且不區分大小寫),“opt”是在成功匹配時 fs_parser() 函式將返回的值。

    “type”欄位指示所需的值型別,並且必須是以下之一:

    型別名稱

    預期值

    結果在

    fs_param_is_flag

    無值

    不適用

    fs_param_is_bool

    布林值

    result->boolean

    fs_param_is_u32

    32 位無符號整型

    result->uint_32

    fs_param_is_u32_octal

    32 位八進位制整型

    result->uint_32

    fs_param_is_u32_hex

    32 位十六進位制整型

    result->uint_32

    fs_param_is_s32

    32 位有符號整型

    result->int_32

    fs_param_is_u64

    64 位無符號整型

    result->uint_64

    fs_param_is_enum

    列舉值名稱

    result->uint_32

    fs_param_is_string

    任意字串

    param->string

    fs_param_is_blob

    二進位制大物件

    fs_param_is_blockdev

    塊裝置路徑

    需要查詢

    • fs_param_is_path

    路徑

    fs_param_is_fd

    • fs_param_is_path

    檔案描述符

    fs_param_is_uid

    result->int_32

    使用者 ID (u32)

    result->uid

    fs_param_is_gid

    組 ID (u32)

    result->gid

    請注意,如果值為 fs_param_is_bool 型別,fs_parse() 將嘗試將任何字串值與“0”、“1”、“no”、“yes”、“false”、“true”進行匹配。

    每個引數也可以用“flags”限定:

    fs_param_v_optional

    值是可選的

    fs_param_neg_with_no

    如果鍵以“no”為字首,則 result->negated 被設定

    fs_param_neg_with_empty

    如果值為“”,則 result->negated 被設定

    fs_param_deprecated

    該引數已廢棄。

    這些被一些方便的包裝器封裝:

    指定

    fsparam_flag()

    fsparam_flag_no()

    fs_param_is_flag

    fs_param_is_flag, fs_param_neg_with_no

    fsparam_bool()

    fsparam_u32()

    fs_param_is_bool

    fsparam_u32oct()

    fs_param_is_u32

    fsparam_s32()

    fs_param_is_u32_octal

    fsparam_u64()

    fs_param_is_s32

    fsparam_enum()

    fs_param_is_u64

    fsparam_string()

    fs_param_is_enum

    fsparam_blob()

    fs_param_is_string

    fsparam_bdev()

    fs_param_is_blob

    fsparam_path()

    塊裝置路徑

    fsparam_fd()

    路徑

    fsparam_uid()

    檔案描述符

    fsparam_gid()

    使用者 ID (u32)

    所有這些都接受兩個引數,即名稱字串和選項編號——例如:

    組 ID (u32)

    還提供了一個附加宏 __fsparam(),它接受一對額外引數來指定不匹配上述任何宏的任何內容的型別和標誌。

    static const struct fs_parameter_spec afs_param_specs[] = {
            fsparam_flag    ("autocell",    Opt_autocell),
            fsparam_flag    ("dyn",         Opt_dyn),
            fsparam_string  ("source",      Opt_source),
            fsparam_flag_no ("foo",         Opt_foo),
            {}
    };
    

    列舉值名稱到整數對映的表,以空條目終止。其型別為:

  2. const struct fs_parameter_enum *enums;
    

    其中陣列是 { 引數 ID, 名稱 } 鍵控元素的無序列表,指示要對映到的值,例如:

    struct fs_parameter_enum {
            u8              opt;
            char            name[14];
            u8              value;
    };
    

    如果遇到型別為 fs_param_is_enum 的引數,fs_parse() 將嘗試在列舉表中查詢該值,並將結果儲存在解析結果中。

    static const struct fs_parameter_enum afs_param_enums[] = {
            { Opt_bar,   "x",      1},
            { Opt_bar,   "y",      23},
            { Opt_bar,   "z",      42},
    };
    

    解析器應由 file_system_type 結構體中的 parser 指標指向,因為這將提供註冊時的驗證(如果 CONFIG_VALIDATE_FS_PARSER=y),並允許使用 fsinfo() 系統呼叫從使用者空間查詢描述。

引數輔助函式

提供了許多輔助函式,以幫助檔案系統或 LSM 處理給定引數。

在名稱到整數對映表中按名稱查詢常量。該表是一個以下型別的元素陣列:

  • int lookup_constant(const struct constant_table tbl[],
                        const char *name, int not_found);
    

    如果找到匹配項,則返回相應的值。如果未找到匹配項,則返回 not_found 值。

    struct constant_table {
            const char      *name;
            int             value;
    };
    

    這會對引數描述執行一些驗證檢查。如果描述良好則返回 true,否則返回 false。如果驗證失敗,它將把錯誤記錄到核心日誌緩衝區。

  • bool fs_validate_description(const char *name,
                                 const struct fs_parameter_description *desc);
    

    這是引數的主要直譯器。它使用引數描述按鍵名查詢引數,並將其轉換為選項編號(並返回)。

  • int fs_parse(struct fs_context *fc,
                 const struct fs_parameter_description *desc,
                 struct fs_parameter *param,
                 struct fs_parse_result *result);
    

    如果成功,並且引數型別指示結果是布林型、整數型、列舉型、uid 型或 gid 型,則此函式會轉換該值,並將結果儲存在 result->{boolean,int_32,uint_32,uint_64,uid,gid} 中。

    如果最初未找到匹配項,並且鍵以“no”為字首且不存在值,則將嘗試查詢已移除字首的鍵。如果這與型別設定了 fs_param_neg_with_no 標誌的引數匹配,則將進行匹配,並且 result->negated 將設定為 true。

    如果引數不匹配,將返回 -ENOPARAM;如果引數匹配但值有誤,將返回 -EINVAL;否則將返回引數的選項編號。

    此函式接受一個攜帶字串或檔名型別的引數,並嘗試對其進行路徑查詢。如果引數期望一個塊裝置,則會檢查 inode 是否確實表示一個塊裝置。

  • int fs_lookup_param(struct fs_context *fc,
                        struct fs_parameter *value,
                        bool want_bdev,
                        unsigned int flags,
                        struct path *_path);
    

    如果成功則返回 0,並且 *_path 將被設定;否則返回負錯誤碼。

    ©核心開發社群。 | 由 Sphinx 5.3.0Alabaster 0.7.16 提供支援 | 頁面原始檔