Ext2文件系统深度剖析|扩展属性分析

Ext2文件系统深度剖析|扩展属性分析文件的扩展属性扩展属性提供了一个机制用来将键值对永久地关联到文件,让现有的文件系统得以支持在原始设计中未提供的功能。

大家好,欢迎来到IT知识分享网。

Ext2文件系统深度剖析|扩展属性分析

文件的扩展属性

扩展属性(xattrs)提供了一个机制用来将键值对(Key/Value)永久地关联到文件,让现有的文件系统得以支持在原始设计中未提供的功能。扩展属性是文件系统不可知论者,应用程序可以通过一个标准的接口来操纵他们,此接口不因文件系统而异。每个扩展属性可以通过唯一的键来区分,键的内容必须是有效的UTF-8,格式为namespace.attribute,每个键采用完全限定的形式,也就是键必需有一个确定的前缀(例如user)。

Linux操作系统有如下集中扩展属性:

  • system:用于实现利用扩展属性的内核功能,例如访问控制表。eg:system.posix_acl_access便是位于此用户空间的扩展属性,用户是否可以读取或写入这些属性取决于所使用的安全模块。
  • security:用于实现安全模块。
  • trusted:把受限制的信息存入用户空间。
  • user:一般进程所使用的标准命名空间,经过一般文件权限位来控制此命名空间的访问。

Ext2扩展属性是怎么在磁盘存储的

本文主要介绍一下Ext2文件系统中扩展属性的相关内容,包括磁盘数据布局和创建流程等。在Ext2文件系统中,扩展属性存储在一个单独的磁盘逻辑块中,其位置由inode中的i_file_acl成员指定。如图所示是键值对在该逻辑块中的布局示意图。其前32个字节是一个描述头(ext2_xattr_header),描述该逻辑块基本使用情况。而下面紧跟着的是扩展属性项(ext2_xattr_entry),扩展属性项描述了扩展属性的键名称等信息,同时包含值的偏移等内容。这里需要说明的是扩展属性项是从上往下生长的,而值则是从下往上生长

Ext2文件系统深度剖析|扩展属性分析

图1 扩展属性布局图

如下代码是描述头(ext2_xattr_header)的结构体定义,里面有魔数、引用计数和哈希值等内容。这里魔数的作用是确认该逻辑块的内容是扩展属性逻辑块,避免代码Bug或者磁盘损坏等情况下给用户返回错误的结果。引用计数和哈希值的作用是实现多文件的扩展属性共享。所谓扩展属性共享是指,如果多个文件的扩展属性完全一样的情况下,这些文件的扩展属性将采用相同的磁盘逻辑块存储,这样可以大大的节省存储空间。另外,Ext2借用的哈希缓存,将文件属性的哈希值存储在其中,用于快速判断文件是否存在相同的扩展属性逻辑块。

struct ext2_xattr_header {
 __le32 h_magic; /* magic number for identification */
 __le32 h_refcount; /* reference count */
 __le32 h_blocks; /* number of disk blocks used */
 __le32 h_hash; /* hash value of all attributes */
 __u32 h_reserved[4]; /* zero right now */
};

扩展属性项在磁盘上是从上往下生长的,但需要注意的是由于每个扩展属性的键名称的长度不一定一样,因此该结构体的大小也是变化的。由于上述原因,我们无法直接找到某一个扩展属性项的位置,必需从头到位进行遍历。由于描述头的大小是确定的,这样第一个扩展属性项就可以找到,而下一个扩展属性项就可以根据本扩展属性项的位置及其中的e_name_len成员计算得到。

struct ext2_xattr_entry {
 __u8 e_name_len; /* length of name */
 __u8 e_name_index; /* attribute name index */
 __le16 e_value_offs; /* offset in disk block of value */
 __le32 e_value_block; /* disk block attribute is stored on (n/i) */
 __le32 e_value_size; /* size of attribute value */
 __le32 e_hash; /* hash value of name and value */
 char e_name[0]; /* attribute name */
};

设置扩展属性的VFS流程

操作系统提供了一些API来设置文件的扩展属性,分别是setxattr、fsetxattr和lsetxattr。这几个函数应用场景略有差异,但功能基本一致。本文以fsetxattr为例进行介绍。假设用户调用该接口为某个文件设置user前缀的扩展属性,此时的整个函数调用栈如图所示。本调用栈包含三部分内容,分别是用户态接口、VFS调研栈和Ext2文件系统调用栈。

Ext2文件系统深度剖析|扩展属性分析

图2 扩展属性设置流程

通过上图可以看出在VFS做了很多事情,最后通过函数指针的方式调用到Ext2文件系统的扩展属性设置接口。整个流程中除了Ext2文件系统实现的设置扩展属性的函数(ext2_xattr_set)逻辑相对复杂外,整个函数栈的代码逻辑非常简单,这里就不过多介绍了。但是,这里有几点需要说明的:

属性设置的公共接口

这个调用就是上图中i_op->setxattr的调用,其中i_op是inode中的一个成员,这个指针是分配inode节点的时候初始化的。这个函数屏蔽了不同的扩展属性(上文已经交代Linux文件系统有trusted和user等多种扩展属性)的处理方法集合。需要注意的是Ext2文件系统并没有实现自己的特有函数,而是调用了VFS提供的公共函数(generic_setxattr)。上述i_op是一个结构体指针,其中包含属性及扩展属性操作的所有接口,如下代码所示。

const struct inode_operations ext2_file_inode_operations = { 
#ifdef CONFIG_EXT2_FS_XATTR
 .setxattr = generic_setxattr,
 .getxattr = generic_getxattr,
 .listxattr = ext2_listxattr,
 .removexattr = generic_removexattr,
#endif
 .setattr = ext2_setattr,
 .get_acl = ext2_get_acl,
 .set_acl = ext2_set_acl,
 .fiemap = ext2_fiemap,
};

关于设置扩展属性的公共实现函数比较简单,具体如下所示。

int generic_setxattr(struct dentry *dentry, 
 const char *name, 
 const void *value, 
 size_t size, 
 int flags)
{ 
 const struct xattr_handler *handler;
 if (size == 0)
 value = ""; /* empty EA, do not remove */
 /* 根据扩展属性的键(Key)获取处理该动作的函数集指针handler。
 * 例如设置user类型的扩展属性,则name格式为user.xxx */
 handler = xattr_resolve_name(dentry->d_sb->s_xattr, &name); 
 if (!handler) 
 return -EOPNOTSUPP; 
 /* 调用具体类型扩展属性的处理函数, 例如对于user扩展属性,则
 * 调用ext2_xattr_user_handler进行处理。 */
 return handler->set(dentry, name, value, size, 
 flags, handler->flags);
}

user扩展属性设置接口

这个调用就是上图中handler->set的调用,这个指针是在文件系统挂载的时候初始化的,其内容初始化了超级块的成员变量s_xattr。其中handler指针是根据用户传入的键名称确定的。具体获取是在函数xattr_resolve_name中实现的,其对超级块中的s_xattr变量进行遍历(匹配扩展属性的键前缀,流入user),从而找到可以处理该扩展属性的handler。如下代码是Ext2文件系统初始化超级快s_xattr用的数据,里面包含Ext2文件系统支持的所有扩展属性类型。

const struct xattr_handler *ext2_xattr_handlers[] = {
 &ext2_xattr_user_handler,
 &ext2_xattr_trusted_handler, 
#ifdef CONFIG_EXT2_FS_POSIX_ACL
 &posix_acl_access_xattr_handler,
 &posix_acl_default_xattr_handler, 
#endif#ifdef CONFIG_EXT2_FS_SECURITY
 &ext2_xattr_security_handler, 
#endif 
 NULL };

这样,经过几层调用之后就可以调用到Ext2文件系统中关于user扩展属性的设置接口。

Ext2扩展属性接口

对于user类型的扩展属性,其函数集为ext2_xattr_user_handler,如下是具体的定义。这里面实现了该类型扩展属性的查询和设置等接口。

const struct xattr_handler ext2_xattr_user_handler = { 
 .prefix = XATTR_USER_PREFIX,
 .list = ext2_xattr_user_list,
 .get = ext2_xattr_user_get,
 .set = ext2_xattr_user_set,
};

设置扩展属性的接口是ext2_xattr_user_set,如下是该函数的具体实现,从代码中可以看出该函数主要调用了ext2_xattr_set函数,这个函数实现了对添加扩展属性的操作。

static int ext2_xattr_user_set(struct dentry *dentry, 
 const char *name, 
 const void *value, 
 size_t size, 
 int flags, 
 int type){ 
 if (strcmp(name, "") == 0) 
 return -EINVAL; 
 if (!test_opt(dentry->d_sb, XATTR_USER)) 
 return -EOPNOTSUPP; 
 return ext2_xattr_set(d_inode(dentry), 
 EXT2_XATTR_INDEX_USER,
 name, value, size, flags);
}

函数ext2_xattr_set的实现非常长,大概300多行,因此这里就不贴代码了。本文具体介绍一下该函数的实现逻辑主要是查找键,然后根据查找的结果进行处理。如果能找到该键则更新值,如果找不到则新建一个键值对。这里有几个注意点,具体实现细节请自行阅读代码。

  • 设置扩展属性时可能是第一个,此时会分配新的磁盘空间
  • 设置扩展属性需要计算剩余空间,如果剩余空间不够,则创建失败
  • 设置扩展属性接口同时实现了更新扩展属性值的功能,但如果新值得长度大于旧值,则需要进行特殊处理
  • 在数据布局的次序上,键和值并没有严格的顺序

在实现逻辑中需要注意的一点是,用户在调用接口的时候可以传递附加标识,比如XATTR_REPLACE和XATTR_CREATE等。例如XATTR_REPLACE表示用户期望进行扩展属性值得替换操作,如果没有找到扩展属性的键,则返回失败。XATTR_CREATE则表示只进行创建操作,如果已经存在期望的键则失败。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/61070.html

(0)

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信