zoukankan      html  css  js  c++  java
  • TLPI读书笔记第16章-扩展属性

    文件的扩展属性(EA),即以名称-值对形式将任意元数据与文件 i 节点关联起来的技术

    16.1 概述

    EA 可用于实现访问列表(第 17 章)和文件能力(第 39 章)。但就设计而论,其能力绝不仅限于此。例如,还可利用 EA 去记录文件的版本号、与文件的 MIME 类型/字符集有关的信息,或是指向图符的指针。 SUSv3 并未对 EA 加以规范。但少数其他 UNIX 实现却提供了类似的特性,其中知名的有现代 BSD(详见 extattr(2))系列和 Solaris 9 及其后续版本(详见 fsattr(5))。 EA 需要有底层文件系统来提供支撑, Btrfs、 ext2、 ext3、 ext4、 JFS、 Reiserfs 以及 XFS等文件系统都支持 EA。

    EA 命名空间

    EA 的命名格式为 namespace.name。其中 namespace 用来把 EA 从功能上划分为截然不同的几大类,而 name 则用来在既定命名空间内唯一标识某个 EA。 可供 namespace 使用的值有 4 个: user、 trusted、 system 以及 security。这 4 类 EA 的用途如下所示。 1.user EA 将在文件权限检查的制约下由非特权级进程操控。欲获取 user EA 值,需要有文件的读权限; 欲改变 user EA 值, 则需要写权限。(若无所需权限, 将会导致 EACCES错误。)在 ext2、 ext3、 ext4 或 Reiserfs 文件系统上,如欲将 user EA 与一文件关联,在装配底层文件系统时需带有 user_xattr 选项。

    mount -o user_xattr device directory

    2.trusted EA 也可由用户进程“驱使”, 这点与 user EA 相似。 而区别则在于, 要操纵 trustedEA,进程必须具有特权(CAP_SYS_ADMIN)。

    3.system EA 供内核使用,将系统对象与一文件关联。目前仅支持访问控制列表(第17 章)。

    4.security EA 的作用有二:其一,用来存储服务于操作系统安全模块的文件安全标签;其二,将可执行文件与能力关联起来(39.9.2 节)。而发明 security EA 的初衷是为了支持安全强化版的 Linux(SELinux)。 一个 i 节点可以拥有多个相关 EA,其所从属的命名空间可以相同,也可不同。在各命名空间内的 EA 名均自成一体。在 user 和 trusted 命名空间内, EA 名可以为任意字符串。而在system 命名空间内,只有经内核明确认可的(例如,用于访问控制列表的)命名方可使用

    通过 shell 创建并查看 EA

    在 shell 中,可执行 setfattr(1)和 getfattr(1)命令来设置和查看文件的 EA。

    touch tfile
    setfattr -n user.x -v "the past is not dead" tfile
    setfattr -n user.y -v "In fact,it's not even past" tfile
    getfattr -n user.x tfile
    #获取所有user属性
    getfattr -d tfile
    #将属性置为空字符串
    setfattr -n user.x  tfile
    #删除一个属性
    setfattr -x user.y  tfile

    以上 shell 会话所展示的要点之一是, EA 值可以为空字符串,这不同于未定义的 EA 值。(由 shell 会话结尾处的例子可知, user.x 的值为空字符串, user.y 的值为未定义。) 默认情况下, getfattr 只会列出 user EA 值。还可利用-m 选项来指定一正则表达式,来筛选想要显示的 EA 名:

    getfattr -m 'pattern' tfile

    pattern 的默认值为“^user.”。可执行如下命令,列出一个文件的所有 EA 值

    getfattr -m - tfile

    16.2 扩展属性的实现细节

    本节是对上一节内容的延伸,描述 EA 实现的部分细节。

    对 user 扩展属性的限制

    user EA 只能施之于文件或目录,之所以将其他文件类型排除在外,原因如下。 1.对于符号链接,会对所有用户开启所有权限,且不容更改。这意味着,无法利用权限来阻止任意用户将 user EA 置于符号链接之上。要想解决这个问题,就得防止所有用户针对符号链接创建 user EA。

    2.对于设备文件、套接字以及 FIFO 而言,授予用户权限,意在对其针对底层对象所执行的 I/O 操作加以控制。如欲操控这些权限,转而求取对创建 user EA 的控制,则二者间会产生冲突。 此外,若某一目录启用了粘性位(sticky 位)(15.4.5 节),且为其他用户所拥有,则非特权进程不能将一 user EA 置于该目录之上。惟其如此,才能防止任一用户将 EA 附着于诸如/tmp之类的目录,由于其可写权限对所有用户开放(从而导致任意用户均可操纵此目录的 EA),而设置粘性位,意在防止用户删除该目录下为其他用户所拥有的文件。

    EA 在实现方面的限制

    Linux VFS 针对所有文件系统上的 EA 均施以如下限制。 1.EA 名称的长度不能超过 255 个字节。

    2.EA 值的容量为 64KB。

    此外,某些文件系统对可与文件挂钩的 EA 数量及其大小还有更为严格的限制。

    1.在 ext2、 ext3 以及 ext4 文件系统上,与一文件关联的所有 EA 命名和 EA 值的总字节数不会超过单个逻辑磁盘块(14.3 节)的大小: 1024 字节、 2048 字节或 4096 字节。

    2.在 JFS 上,为某一文件所使用的所有 EA 名和 EA 值的总字节数上限为 128KB。

    16.3 操控扩展属性的系统调用

    本节将会介绍用来更新、获取以及删除 EA 的系统调用。

    创建和修改 EA

    系统调用 setxattr()、 lsetxattr()以及 fsetxattr()用来设置文件的 EA 值之一。

    #include<sys/xattr.h>
    int setxattr(const char *pathname,const char *name,const void *value,size_t size,int flags)
    int lsetxattr(const char *pathname,const char *name,const void *value,size_t size,int flags)
    int fsetxattr(int fd,const char *name,const void *value,size_t size,int flags)

    这 3 个系统调用之间的区别类似于 stat()、 lstat()以及 fstat()(15.1 节)三者间的差异。 1.setxattr()通过 pathname 来标识文件,若文件名为符号链接,则对其解引用。 2.lsetxattr()通过 pathname 来标识文件,但不会对符号链接解引用。 3.fsetxattr()则通过打开文件描述符 fd 来标识文件。 以上 3 者之间的差异同样适用于本节下面将要介绍的其他各组系统调用。 参数 name 是一个以空字符结尾的字符串,定义了 EA 的名称。参数 value 是一个指向缓冲区的指针,包含了为 EA 定义的新值。参数 size 则指明了缓冲区大小。 默认情况下,若具有给定名称(name)的 EA 不存在,上述系统调用会创建一个新 EA。若 EA 已经存在,则将替换 EA 值。可利用参数 flags 将这一行为控制得更为精准。将该参数指定为 0,以获得默认行为,或者可将其指定为如下常量之一。 XATTR_CREATE 若具有给定名称(name)的 EA 已经存在,则失败。 XATTR_REPLACE 若具有给定名称(name)的 EA 不存在,则失败。 下例使用 setxattr()创建了一个 user EA

    char *value
    value ="the past is not dead";
    if(setxattr(pathname,'user.x',value,strlen(value),0)==-1)
        errExit("setxattr")
    获取 EA 值

    可利用系统调用 getxattr()、 lgetxattr()以及 fgetxattr()来获取 EA 值。

    #include<sys/xattr.h>
    size_t getxattr(const char *pathname,const char *name, void *value,size_t size)
    size_t lgetxattr(const char *pathname,const char *name, void *value,size_t size)
    size_t fgetxattr(int fd,const char *name,const void *value,size_t size)

    参数 name 是一个以空字符结尾的字符串,用来标识欲取值的 EA。返回的 EA 值保存于参数 value 所指向的缓冲区中。该缓冲区必须由调用者分配,其大小应在 size 中指定。若调用成功,上述系统调用会返回复制到 value 所指缓冲区中的字节数 若文件不含名为“name”的属性 ,上述系统调用则会失败,并会返回错误 ENODATA。 若 size 值过小,上述系统调用也会失败,并返回错误 ERANGE。 可把 size 指定为 0,对于这种情况,将忽略 vlaue 值,但系统调用仍将返回 EA 值的大小。 可利用这一机制来确定后续系统调用在实际获取 EA 值时所需的 value 缓冲区大小。但是应当注意, 这并不能保证后续在通过系统调用获取 EA 值时, 上述返回值就足够大。 系统调用期间另一进程可能为文件的这一属性分配了较大的值,或是将其完全删除。

    删除 EA

    系统调用 removexattr()、 lremovexattr()以及 fremovexattr()用来删除文件的 EA

    #include<sys/xattr.h>
    int removexattr(const char *pathname,const char *name);
    int lremovexattr(const char *pathname,const char *name);
    int fremovexattr(int fd,const char *name);

    name 所含以空字符结尾的字符串,用于标识打算删除的 EA。若试图删除不存在的 EA,调用将失败,并会返回错误 ENODATA。

    获取与文件相关联的所有 EA 的名称

    执行系统调用 listxattr()、 llistxattr()以及 flistxattr(),所返回的列表会包含与某文件关联的所有 EA 的名称。

    #include<sys/xattr.h>
    size_t listxattr(const char *pathname, char *list,size_t size);
    size_t llistxattr(const char *pathname,char *list,size_t size);
    size_t flistxattr(int fd,char *list,size_t size);

    调用将 EA 的名称列表以一系列以空字符结尾的字符串形式置于 list 所指向的缓冲区中。缓冲区的大小由 size 指定。一旦成功,上述系统调用会返回复制到 list 中的字节数 与 getxattr()一样,也可将 size 指定为 0,系统调用将忽略 list,并返回后续调用实际获取EA 名称列表(假定该列表尚未改变)时所需的缓冲区大小。 想获取与某文件相关联的 EA 名列表,只需对文件拥有“访问”权限(亦即对 pathname下的所有路径均拥有执行权限),对文件本身则无需任何权限。 出于安全考虑, list 中返回的 EA 名称可能不包含调用进程无权访问的属性名。 比方说,在非特权进程中调用 listxattr()时,大多数文件系统都会略去 trusted 属性。请注意上一句中的“可能”二字,这表明文件系统实现并非一定要如此。因而,使用 list 中返回的 EA 名 去调用 getxattr(),是有可能失败的,因为进程并不具有获得该 EA 值所需的特权。(同样,当另一进程在 listxattr()和 getxattr()调用之间将该属性删除,也会发生类似错误。)

    程序示例

    程序清单 16-1 所示程序将获取并显示命令行所列文件的所有 EA 名和 EA 值。该程序使用 listxattr(),去获取与每个文件相关联的所有 EA 名称,随后循环调用 getxattr(),为每个名称获取相应的值。默认以纯文本方式显示属性值。若带有-x 选项,那么属性值将以十六进制字符串形式显示。以下 shell 会话记录展示了该程序的使用。

    设置属性

     

    #include<sys/xattr.h>
    #include <stdio.h>
    #include <string.h>
    
    /**设置EA属性*/
    int main() {
        char *value;
        value = "the past is not dead";
        if (setxattr("/opt/tlpi-dist/xattr/Makefile", "user.x", value, strlen(value), 0) == -1)
            printf("setxattr");
        value = "the past is coming";
        if (setxattr("/opt/tlpi-dist/xattr/Makefile", "user.y", value, strlen(value), 0) == -1)
            printf("setxattr");
    
    }

    获取属性

    #include <sys/xattr.h>
    #include "tlpi_hdr.h"
    
    #define XATTR_SIZE 10000
    
    static void
    usageError(char *progName)
    {
        fprintf(stderr, "Usage: %s [-x] file...
    ", progName);
        exit(EXIT_FAILURE);
    }
    
    int
    main(int argc, char *argv[])
    {
        /**list是一个char数组,存放xattr的key,key字符串以结尾,所有才需要+1
         * value也是一个char数组,存放xattr的value,后面会详细将如何控制输出*/
        char list[XATTR_SIZE], value[XATTR_SIZE];
        ssize_t listLen, valueLen;
        int ns, j, k, opt;
        Boolean hexDisplay;
    
        hexDisplay = 0;
        while ((opt = getopt(argc, argv, "x")) != -1) {
            switch (opt) {
            case 'x': hexDisplay = 1;       break;
            case '?': usageError(argv[0]);
            }
        }
    
        if (optind >= argc)
            usageError(argv[0]);
    
        for (j = optind; j < argc; j++) {
            /**列出文件的所有EA属性的size,存入list缓冲区*/
            listLen = listxattr(argv[j], list, XATTR_SIZE);
            if (listLen == -1)
                errExit("listxattr");
            /**打印文件名*/
            printf("%s:
    ", argv[j]);
    
            /** listLen实际读到的缓冲区大小,遍历打印出每个属性 */
    
            for (ns = 0; ns < listLen; ns += strlen(&list[ns]) + 1) {
                /**使用指针打印数组中的属性名,到为止*/
                printf("        name=%s; ", &list[ns]);
    
                valueLen = getxattr(argv[j], &list[ns], value, XATTR_SIZE);
                if (valueLen == -1) {
                    printf("couldn't get value");
                } else if (!hexDisplay) {
                /** printf("格式",输出列表),格式为:[标志][输出最小宽度][.精度][长度]类型
                 * 逆序来看:
                 * 类型就是输出类型,比如d=十进制整数,o=八进制,x=十六进制,f=浮点数,c=单字符,s=字符串
                 * 长度格式符为 h,l等,h 表示按短整型量输出,l 表示按长整型量输出
                 * 精度格式符以.开头,后跟十进制整数。本项是:如果输出数字,则表示小数的位数;如果输出的是字符,则表示输出字符的个数;若实际位数大于所定义的精度数,则截去超过的部分
                 * 最小宽度:用十进制整数来表示输出的最少位数。 若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或 0
                 * 标志:标志字符为-、+、#、空格四种,其意义下表所示:
                   -  结果左对齐,右边填空格
                   +  输出符号(正号或负号)
                   空格  输出值为正时冠以空格,为负时冠以负号
                   #  对 c,s,d,u类无影响;对 o 类,在输出时加前缀 o;对 x 类,在输出时加前缀 0x;对 e,g,f 类当结果有小数时才给出小数点
                 * 本例中.*以星号代替数值,类似于width中的*,在输出参数列表中指定精度*/
                    printf("value=%.*s", (int) valueLen, value);
                } else {
                    printf("value=");
                    for (k = 0; k < valueLen; k++)
                        printf("%02x ", (unsigned char) value[k]);
                }
    
                printf("
    ");
            }
    
            printf("
    ");
        }
    
        exit(EXIT_SUCCESS);
    }
  • 相关阅读:
    多表关联查询_resultMap_集合对象
    mybatis多表关联查询之resultMap单个对象
    十二.filter
    十二.函数式编程
    十一.列表生成式
    十,迭代
    九.高级特性
    八.函数
    七.条件判断和循环
    六.使用list和tuple
  • 原文地址:https://www.cnblogs.com/wangbin2188/p/14661730.html
Copyright © 2011-2022 走看看