当前位置:首页 >探索 >Overlay FS联合文件系统源码解析系列—目录接口详解 统源但是码解万变不离其宗

Overlay FS联合文件系统源码解析系列—目录接口详解 统源但是码解万变不离其宗

2024-06-30 15:11:53 [百科] 来源:避面尹邢网

Overlay FS联合文件系统源码解析系列—目录接口详解

作者:蒋卫峰 钟文清 开发 前端 本文重点介绍Overlay FS的联列目录接目录相关的关键数据结构和目录的相关接口,让读者能体会到所谓的合文Overlay文件系统的特点,很多实现细节是系解有别于传统的文件系统,均需考虑upper层和lower层不同情况,统源但是码解万变不离其宗,我们学习文件系统必须抓住文件系统的析系主要数据和接口的实现,这对C语言实现其它底层软件开发也是口详一种必备技能。

想了解更多关于开源的联列目录接内容,请访问:

Overlay FS联合文件系统源码解析系列—目录接口详解 统源但是码解万变不离其宗

51CTO 开源基础软件社区

Overlay FS联合文件系统源码解析系列—目录接口详解 统源但是码解万变不离其宗

https://ost.51cto.com

Overlay FS联合文件系统源码解析系列—目录接口详解 统源但是码解万变不离其宗

引言

在前篇中介绍了Linux是合文如何挂载Overlay文件系统的,重点关注了Overlay的系解挂载流程的实现和关键数据结构的关系,而在本文中主要介绍Overlay FS的统源目录相关的关键数据结构和目录的相关接口,其中比较复杂的码解接口将会在下一篇中展开介绍。

一、析系Overlay 关键数据结构

1、口详目录文件

struct ovl_dir_file {     bool is_real;    bool is_upper;    struct ovl_dir_cache *cache;    struct list_head *cursor;    struct file *realfile;    struct file *upperfile;};

2、联列目录接目录接口

// fs/overlayfs/readdir.cconst struct file_operations ovl_dir_operations = {     .read       = generic_read_dir,    .open       = ovl_dir_open,    .iterate    = ovl_iterate,    .llseek     = ovl_dir_llseek,    .fsync      = ovl_dir_fsync,    .release    = ovl_dir_release,};

3、目录inode操作

// fs/overlayfs/dir.cconst struct inode_operations ovl_dir_inode_operations = {     .lookup     = ovl_lookup,   // fs/overlayfs/namei.c    .mkdir      = ovl_mkdir,    // fs/overlayfs/dir.c    .symlink    = ovl_symlink,    .unlink     = ovl_unlink,    .rmdir      = ovl_rmdir,    .rename     = ovl_rename,    .link       = ovl_link,    .setattr    = ovl_setattr,    .create     = ovl_create,    .mknod      = ovl_mknod,    .permission = ovl_permission,    .getattr    = ovl_getattr,    .listxattr  = ovl_listxattr,    .get_acl    = ovl_get_acl,    .update_time    = ovl_update_time,};

二、目录操作

1、创建目录

(1)创建目录系统调用

// ./fs/namei.cSYSCALL_DEFINE2(mkdir, const char __user *, pathname, umode_t, mode)|-> do_mkdirat(AT_FDCWD, pathname, mode);    |-> struct dentry *dentry;    |-> struct path path;    | // (1)创建目录项:创建ovl_dentry,同时将其加入到其父目录的子目录项链表中    |-> dentry = user_path_create(dfd, pathname, &path, lookup_flags); // path保存父目录路径;dentry保存新建目录项内存对象    |            |-> filename_create(dfd, getname(pathname), path, lookup_flags);    |                |-> name = filename_parentat(dfd, name, lookup_flags, path, &last, &type);    |                |-> dentry = __lookup_hash(&last, path->dentry, lookup_flags)    |                             |-> dentry = d_alloc(base, name);    |    | // (2)创建ovl inode:调用对应文件系统目录ops中的mkdir创建inode,并关联inode到dentry    |-> vfs_mkdir(path.dentry->d_inode, dentry, mode);        |-> dir->i_op->mkdir(dir, dentry, mode) // 参数:父目录inode、新目录的目录项、模式

(2)Overlay FS中ovl_mkdir创建目录的过程

在Overlay fs联合文件系统下,创建目录有2种情况:
(1)upper层和lower层均无同名目录,则直接在upper层创建。
(2)<u>lower层有同名目录,但是之后被删除了</u>,则需要在已经存在whiteout文件的情况下创建目录。

ovl_mkdir(struct inode *dir, struct dentry *dentry, umode_t mode) // fs/overlayfs/dir.c|-> ovl_create_object(dentry, (mode & 07777) | S_IFDIR, 0, NULL)    |   // (1)创建ovl_inode节点    |-> inode = ovl_new_inode(dentry->d_sb, mode, rdev);    |-> inode->i_state |= I_CREATING;    |    |   // (2)联合文件系统调用(实际文件系统)创建节点,并连接(link)虚拟文件系统inode和实际文件系统inode    |   // 实际文件系统创建新目录,并将实际文件系统的inode赋值给虚拟文件系统的目录项(dentry),同时虚拟文件系统的目录项作为虚拟文件系统目录项的一个别名    |-> ovl_create_or_link(dentry, inode, &attr, false) // 参数:虚拟文件系统目录项、虚拟文件系统inode节点、属性、非原始节点        | // 父目录进行copyup:将parent的lower目录内容copyup到upper层。若存在upper层,则直接返回。        |-> ovl_copy_up(parent);// copyup将会在后续的文章中详细介绍        |-> if (!ovl_dentry_is_whiteout(dentry))        |       // 不存在whiteout文件,则直接在upper层创建新目录        |->     ovl_create_upper(dentry, inode, attr);        |-> else        |       // 存在whiteout文件,则在upper层创建新目录并..???..        |->     ovl_create_over_whiteout(dentry, inode, attr);
(3)直接在upper层创建新目录
参数inode为ovl_inode,而dentry则是与之对应的overlayfs文件系统目录节点的目录项。ovl_create_upper(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr)| // 将overlayfs目录转换成源文件路径|-> upperdir = ovl_dentry_upper(dentry->d_parent); // 原始实际文件系统中的父目录|-> struct inode *udir = upperdir->d_inode;|-> struct dentry *newdentry;| // (1)创建父目录的upper层创建新目录|-> newdentry = ovl_create_real(udir, lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len), attr);|               |-> ovl_mkdir_real(dir, &newdentry, attr->mode);|                   |-> ovl_do_mkdir(dir, dentry, mode)|                       |-> vfs_mkdir()|                           |-> dir->i_op->mkdir(dir, dentry, mode); || // 实例化overlayfs目录项和ovl_inode:| // (1)ovl_inode的i_private和__upperdentry分别指向upper的inode和目录项;(2)ovl目录项的d_inode指向ovl_inode|-> ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink); // ovl: dentry; ovl: inode; realfs: newdentry, ..    |-> struct ovl_inode_params oip = {     |      .upperdentry = newdentry, // 实际文件系统目录项    |      .newinode = inode, // ovl_inode    |   };    |    | // 返回ovl_inode的指针:ovl_inode的i_private和__upperdentry分别指向upper的inode和目录项    |-> inode = ovl_get_inode(dentry->d_sb, &oip);    |           | // 返回ovl_inode的指针,并将realinode作为ovl_inode私有数据    |           |-> inode = ovl_iget5(sb, oip->newinode, key);    |           | // 设置ovlinode的__upperdentry指向upper的目录项upperdentry    |           |-> ovl_inode_init(inode, oip, ino, fsid);    |               |-> OVL_I(inode)->__upperdentry = oip->upperdentry;    |    | // 设置ovl目录项的d_inode指向ovl_inode    |-> d_instantiate(dentry, inode); // overlayfs文件系统目录项和inode        |-> __d_instantiate(entry, inode);            |-> __d_set_inode_and_type(dentry, inode, add_flags);            |-> dentry->d_inode = inode;

ovl_create_upper()执行完后,新创建的overlayfs目录结构,如下图所示:

overlayfs:                                       struct ovl_inode                                  struct dentry                         +---------------------------------+               +--------------------------------+    |redirect: char*;                 |               |d_parent: struct dentry *;      |    |mnt_mountpoint: struct dentry*;  |               |d_name: struct qstr;            |    |vfs_inode: struct inode;         |               |d_inode: struct inode*;  -------+--->|+-struct inode -----------------+|               |d_op: struct dentry_operations*;|    ||i_op: struct inode_operations*;||               |d_sb: struct super_block;       |    ||i_sb: struct super_block *;    ||               |d_fsdata: void *;               |    || ...                           ||               | ...                            |    ||i_private: void *  ------------++---+           +--------------------------------+    |+-------------------------------+|   |                                                 |__upperdentry: struct dentry*; --+-+ |                                                 |lower: struct inode*;            | | |                                                 |...                              | | |                                                 +---------------------------------+ | |                                                                                     | |upper:     +-------------------------------------------------------------------------+ |           |                                     +-------------------------------------+           |                                     |                                                 V                                     V                                                 struct dentry                         struct inode                           +--------------------------------+ +->+-------------------------------+                 |d_parent: struct dentry *;      | |  |i_op: struct inode_operations*;|                 |d_name: struct qstr;            | |  |i_sb: struct super_block *;    |                 |d_inode: struct inode*; --------+-+  |i_ino: unsigned long;          |                 |d_op: struct dentry_operations*;|    | ...                           |                 |d_sb: struct super_block;       |    +-------------------------------+                 | ...                            |                                                      +--------------------------------+
(4)创建新目录,但lower层存在被删除的同名目录
参数ovl_create_upper()一样:inode为ovl_inode,而dentry则是与之对应的overlayfs文件系统目录节点的目录项。ovl_create_over_whiteout(struct dentry *dentry, struct inode *inode, struct ovl_cattr *attr)|-> struct dentry *workdir = ovl_workdir(dentry);|-> struct inode *wdir = workdir->d_inode;|-> struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);   // upper父目录项|-> struct inode *udir = upperdir->d_inode;                         // upper父目录inode|-> struct dentry *upper;                                           // ovl目录项|-> struct dentry *newdentry;                                       // upper目录项|| // (1)在upper层父目录中查找目录项(whiteout)|-> upper = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len);| // (2)在workdir目录下创建临时目录|-> newdentry = ovl_create_temp(workdir, cattr);|               |-> ovl_create_real(d_inode(workdir), ovl_lookup_temp(workdir), attr);| // (3)设置worddir下新建临时目录属性为opaque|-> ovl_set_opaque(dentry, newdentry);| // (4)workdir新建的临时目录与upper的whiteout目录做交互rename|-> ovl_do_rename(wdir, newdentry, udir, upper, RENAME_EXCHANGE);| // (5)清除rename后workdir下的废弃文件(whiteout)|-> ovl_cleanup(wdir, upper);|| // 实例化overlayfs目录项和ovl_inode:| // (1)ovl_inode的i_private和__upperdentry分别指向upper的inode和目录项;(2)ovl目录项的d_inode指向ovl_inode.|-> ovl_instantiate(dentry, inode, newdentry, !!attr->hardlink);

由上面流程可见,与ovl_create_upper()相比,ovl_create_over_whiteout()先在work目录下创建一个临时目录,并设置目录属性为opaque,以屏蔽lower层同名目录。然后将workdir目录下临时目录与upper层的whiteout文件做重命名互换,并清除互换后的垃圾文件。

2、删除目录ovl_rmdir()

将要被删除的联合文件系统目录主要有3种途径
(1)目录只来自upper层,则直接删除即可;
(2)目录只来自lower层,则需要建立对lower层同名目录的whiteout文件;
(3)目录是由uppwer层和lower层目录合成,则既要删除upper层目录,又要建立lower层同名目录的whiteout文件;

但是上面的情况,经过简化合并为两种情况:

  • 不存在于lower层,则直接删除即可
  • 存在于lower层,需要生成whiteout文件屏蔽底层目录。

下面代码为Overlay fs联合文件系统删除目录的过程,留意下列过程中根据lower_positive是否为true,即是否存在lower层,分别调用了函数ovl_remove_upper()或者ovl_remove_and_whiteout()。

ovl_rmdir(struct inode *dir, struct dentry *dentry)|-> ovl_do_remove(dentry, true);    |-> ovl_check_empty_dir(dentry, &list);    |   | // 获取目录下的所有目录和文件列挂到list链表    |   |-> ovl_dir_read_merged(dentry, list, &root);    |   |   |-> for (idx = 0; idx != -1; idx = next) {     |   |   |->     next = ovl_path_next(idx, dentry, &realpath);    |   |   |->     ovl_dir_read(&realpath, &rdd);    |   |   |        |-> realfile = ovl_path_open(realpath, O_RDONLY | O_LARGEFILE);    |   |   |        |-> iterate_dir(realfile, &rdd->ctx);    |   |   |-> }    |   |->     |    | // copyup父目录:若存在upper层,则ovl_copy_up()直接返回。    |-> ovl_copy_up(dentry->d_parent);    |-> if (!lower_positive)    |->     err = ovl_remove_upper(dentry, is_dir, &list);    |-> else    |->     err = ovl_remove_and_whiteout(dentry, &list);    |    |-> clear_nlink(dentry->d_inode);    |    |-> upperdentry = ovl_dentry_upper(dentry);    |-> if (upperdentry)    |->     ovl_copyattr(d_inode(upperdentry), d_inode(dentry));

(1)删除不存在lower层的目录

ovl_remove_upper(dentry, is_dir, &list)|-> struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent)|-> struct inode *dir = upperdir->d_inode|-> struct dentry *opaquedir = NULL|-> opaquedir = ovl_clear_empty(dentry, list) // 返回opaque目录|-> upper = ovl_lookup_upper(ofs, dentry->d_name.name, upperdir,dentry->d_name.len)// 找到需要删除的dengtry|-> err = ovl_do_rmdir(ofs, dir, upper)| |-> vfs_rmdir(ovl_upper_mnt_userns(ofs), dir, dentry) // 直接调用vfs_mkdir进行删除| |...

(2)删除存在lower层的目录

ovl_remove_and_whiteout(dentry, &list);|-> struct ovl_fs *ofs = OVL_FS(dentry->d_sb);|-> workdir = ovl_workdir(dentry);| // 返回opaque目录:已经完成了目标目录的删除|-> opaquedir = ovl_clear_empty(dentry, list);|               |-> opaquedir = ovl_create_temp(workdir, OVL_CATTR(stat.mode));|               |-> ovl_copy_xattr(dentry->d_sb, upper, opaquedir);|               |-> ovl_set_opaque(dentry, opaquedir);|               | // rename后opaquedir与upper的inode内容互换|               |-> ovl_do_rename(wdir, opaquedir, udir, upper, RENAME_EXCHANGE);|               | // 递归删除目标目录下的upper层内容|               |-> ovl_cleanup_whiteouts(upper, list);|               | // 删除目标目录|               |-> ovl_cleanup(wdir, upper);|                   |-> ovl_do_rmdir(wdir, wdentry);|                       |-> vfs_rmdir(dir, dentry);| // 此时,upper即opaquedir|-> upper = lookup_one_len(dentry->d_name.name, upperdir, dentry->d_name.len);|-> ovl_cleanup_and_whiteout(ofs, d_inode(upperdir), upper);    |-> whiteout = ovl_whiteout(ofs);    |              |-> whiteout = ovl_lookup_temp(workdir);    |              |              |-> snprintf(name, sizeof(name), "#%x", atomic_inc_return(&temp_id));    |              |              |-> temp = lookup_one_len(name, workdir, strlen(name));    |              |-> ovl_do_whiteout(wdir, whiteout);    |-> ovl_do_rename(wdir, whiteout, dir, dentry, flags);    |-> ...

如果 要删除的文件是upper层覆盖lower层的文件,要删除的目录是上下层合并的目录,其实是前两个场景的合并,Overlay fs即需要删除upper层对应文件系统中的文件或目录,也需要在对应位置创建同名whiteout文件,让upper层的文件被删除后不至于lower层的文件被暴露出来。

3、ovl_dir_open打开目录

假设目录是merged目录:

ovl_dir_open(struct inode *inode, struct file *file) // fs/overlayfs/readdir.c|-> struct ovl_dir_file *od;|-> od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL);| // 获取目录路径:若目录是merged或者upper目录,则获得upper路径;否则,获得lower路径。|-> type = ovl_path_real(file->f_path.dentry, &realpath);|          |-> type = ovl_path_type(dentry);|          |          |-> type = __OVL_PATH_UPPER;|          |          |-> type  |= __OVL_PATH_MERGE;|          ||          |-> ovl_path_lower(dentry, path);|          | // < or >|          |-> ovl_path_upper(dentry, path);|              |-> path->mnt = ovl_upper_mnt(ofs);|              |-> path->dentry = ovl_dentry_upper(dentry);|| // 打开upper或者lower目录|-> realfile = ovl_dir_open_realfile(file, &realpath);|              |-> struct file *res;|              |-> res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));|                        |-> dentry_open(path, flags, current_cred());|                            |-> struct file *f;|                            |-> f = alloc_empty_file(flags, cred);|                            |-> vfs_open(path, f);|                                |-> open = f->f_op->open(inode, f);|| // realfile作为ovldir_file的实际文件;ovl_dir_file又作为file的私有数据|-> od->realfile = realfile;|-> od->is_real = ovl_dir_is_real(file->f_path.dentry);|-> od->is_upper = OVL_TYPE_UPPER(type);|-> file->private_data = od;

目录打开后形成如下结构:

struct file                                 +------------------------------+            |f_path: struct path;          |            |f_inode: struct inode;        |            |f_op: struct file_operations*;|            |private_data: void *; --------+-+           | ...                          | |           +------------------------------+ |                                    +-------+Overlay fs:                          |  struct ovl_dir_file                                                     +->+-----------------------------+                                       |is_real: bool                |                                       |is_upper: unsigned;          |                                       |cache: struct ovl_dir_cache*;|                                       |cursor: struct list_head*;   |                                       |realfile: struct file*; -----+-+                                       |upperfile: struct file*;     | |                                       +-----------------------------+ |                                                              +--------+                                                              |                                                                                               |  struct file                     upper:                                                        +->+------------------------------+                                                                 |f_path: struct path;          |                                                                 |f_inode: struct inode;        |                                                                 |f_op: struct file_operations*;|                                                                 |private_data: void *;         |                                                                 | ...                          |                                                                 +------------------------------+

(1)OVL目录内查找目标目录或者文件

每当在这样一个合并的目录中请求查找时,就会在每个实际的目录中进行查找,而合并的结果则缓存在属于覆盖文件系统的目录中。结果被缓存在属于叠加文件系统的目录中。如果两个实际的查询都能找到目录,那么这两个查询都会被存储起来,并且创建一个合并的目录。创建一个合并的目录,否则只存储一个:如果上层目录存在,则存储下层目录。只有来自目录的名称列表被合并。其他内容,如元数据和扩展属性,只报告给上层目录,下层目录的这些属性被隐藏。

/*   DESCRIPTION: 在特定文件系统中,在目录dir下查找dentry中包含  PARAMS:    @dir: 父目录,在该目录下查找目标目录或者文件,但是函数内未使用,父目录由dentry的d_parent获得。    @dentry: 目标目录或者文件的目录项,包含name,但是缺少inode    @flags*/ovl_lookup(struct inode *dir, struct dentry *dentry, unsigned int flags) // fs/overlayfs/dir.c| // (1)在upper层父目录中查找目标文件或者目录:| // (1.1)从ovl目录项的ovl_inode反向索引到实际文件系统的dentry|-> upperdir = ovl_dentry_upper(dentry->d_parent);| // (1.2)在upper层目录下搜索目标文件或者目录,若存在则使用局部目录项指针upperdentry指向目标目录项|-> ovl_lookup_layer(upperdir, &d, &upperdentry, true); // 参数:实际文件系统中的父目录项、...、目标实际文件系统中目录项返回值、...|   |-> ovl_lookup_single(base, d, d->name.name, d->name.len,0, "", ret, drop_negative) // ret,即&upperdentry|       |-> this = ovl_lookup_positive_unlocked(name, base, namelen, drop_negative);|       |          |-> lookup_one_len_unlocked(name, base, len);|       |              |-> lookup_slow(&this, base, 0); |       |                   |-> __lookup_slow(name, dir, flags);|       |                       |-> dentry = d_alloc_parallel(dir, name, &wq);|       |                       |-> old = inode->i_op->lookup(inode, dentry, flags);|       |                       |-> dentry = old;|       |-> *ret = this;|| // (2)在所有lower层父目录中查找目标文件或者目录|-> for (i = 0; !d.stop && i < poe->numlower; i++) { |->     struct ovl_path lower = poe->lowerstack[i];|->     d.last = i == poe->numlower - 1;|->     ovl_lookup_layer(lower.dentry, &d, &this, false);|->     ...|->     stack[ctr].dentry = this;|->     stack[ctr].layer = lower.layer;|->     ctr++;|-> }||   // (3)分配ovl_entry,其中包括根据堆叠层次确定的ovl_path层次数|-> oe = ovl_alloc_entry(ctr);|-> memcpy(oe->lowerstack, stack, sizeof(struct ovl_path) * ctr);|-> dentry->d_fsdata = oe;||   // 关联实际文件系统denty和inode到虚拟文件系统inode|-> inode = ovl_get_inode(dentry->d_sb, &oip);

根据上面的流程,在overlayfs文件系统的一个目录下查询一个目录或者文件的流程为,通过ovl系统中父目录dentry找到实际文件系统的目录项和inode,找到目标目录或者文件对应的dentry和inode。

struct ovl_entry                                                      +==>+-------------------------------+                                     H   |lowerstack: struct ovl_path[]; |                                     H   |numlower: unsigned;            |                                     H   | ...                           |                                     H   +-------------------------------+                                     H                                     H   struct ovl_inode                      struct dentry(d_parent)           H   +---------------------------------+          +--------------------------------+H   |redirect: char*;                 |              |d_parent: struct dentry *;      |H   |mnt_mountpoint: struct dentry*;  |                     |d_name: struct qstr;            |H   |vfs_inode: struct inode;         |                  |d_inode: struct inode*;==========H==>|+-struct inode -----------------+|                       |d_op: struct dentry_operations*;|H   ||i_op: struct inode_operations*;||                       |d_sb: struct super_block;       |H   ||i_sb: struct super_block *;    ||                   |d_fsdata: void *;  ==========(2)=H   ||i_ino: unsigned long;          ||   | ...                            |    || ...                           ||   +--------------------------------+    |+-------------------------------+|      struct dentry(base dir in realfs)                                               |__upperdentry: struct dentry*; ==(1)===>+--------------------------------+                                               |lower: struct inode*;            |      |d_parent: struct dentry *;      |                                              |...                              |      |d_name: struct qstr;            |                                                                             +---------------------------------+      |d_inode: struct inode*; ============H                                                                                                                    |d_op: struct dentry_operations*;|   H                                                                                                                              |d_sb: struct super_block;       |   H                                                                                  | ...                            |   H                                                                                  +--------------------------------+   H                                                                                       struct inode(lowerendtry->inode)V                                                                                        +-------------------------------+                                                                                        |i_op: struct inode_operations*;| ==> 实际文件系统的lookup()                                                                                        |i_sb: struct super_block *;    |                                                                                        |i_ino: unsigned long;          |                                                                                        | ...                           |                                                                                        +-------------------------------+

查找到的目录或者文件,在ovl文件系统中表示如下:

struct ovl_entry                                                      +->+------------------------------+                                     |  | ...                          |                                     |  |numlower: unsigned;           |                                     |  |lowerstack: struct ovl_path[];|                                     |  +==============================+                                     |  |struct ovl_path {              |                                     |  |  layer: struct ovl_layer*;   |                                     |  |  dentry: struct dentry*;     |                                     |  |};                            |                                     |  +------------------------------+                                     |  |struct ovl_path {              |                                     |  |  layer: struct ovl_layer*;   |                                     |  |  dentry: struct dentry*;     |                                     |  |};                            |                                     |  +------------------------------+                                     |  |                              |                                     |  |                              |                                     |                                     |  struct ovl_inode                      struct dentry                     |  +---------------------------------+          +--------------------------------+|  |redirect: char*;                 |              |d_parent: struct dentry *;      ||  |mnt_mountpoint: struct dentry*;  |                     |d_name: struct qstr;            ||  |vfs_inode: struct inode;         |                  |d_inode: struct inode*;----------+->|+-struct inode -----------------+|                       |d_op: struct dentry_operations*;||  ||i_op: struct inode_operations*;||                       |d_sb: struct super_block;       ||  ||i_sb: struct super_block *;    ||                   |d_fsdata: void *;  -(3)----------+  ||i_ino: unsigned long;          ||   | ...                            |   || ...                           ||   +--------------------------------+   |+-------------------------------+|                                        |__upperdentry: struct dentry*;   |                                        |lower: struct inode*;            |                                        |...                              |                                                                      +---------------------------------+

(2)打开目录

被打开的目录可能只来自于upper层或lower层,由或者是upper和lower目录merged而成,打开过程如下:

ovl_dir_open(struct inode *inode, struct file *file) // fs/overlayfs/readdir.c|-> struct ovl_dir_file *od;|-> od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL);| // (1)获取目录路径:若目录是merged或者upper目录,则获得upper路径;否则,获得lower路径。|-> type = ovl_path_real(file->f_path.dentry, &realpath);|          |-> type = ovl_path_type(dentry);|          |          |-> type = __OVL_PATH_UPPER;|          |          |-> type  |= __OVL_PATH_MERGE;|          ||          |-> ovl_path_lower(dentry, path);|          | // < or >|          |-> ovl_path_upper(dentry, path);|              |-> path->mnt = ovl_upper_mnt(ofs);|              |-> path->dentry = ovl_dentry_upper(dentry);|| // (2)打开实际文件系统目录文件:upper或者lower目录|-> realfile = ovl_dir_open_realfile(file, &realpath);|              |-> struct file *res;|              |-> res = ovl_path_open(realpath, O_RDONLY | (file->f_flags & O_LARGEFILE));|                        |-> dentry_open(path, flags, current_cred());|                            |-> struct file *f;|                            |-> f = alloc_empty_file(flags, cred);|                            |-> vfs_open(path, f);|                                |-> open = f->f_op->open(inode, f);|| // (3)将overlayfs目录文件和实际文件系统目录文件关联:realfile作为ovldir_file的实际文件;ovl_dir_file又作为file的私有数据|-> od->realfile = realfile;|-> od->is_real = ovl_dir_is_real(file->f_path.dentry);|-> od->is_upper = OVL_TYPE_UPPER(type);|-> file->private_data = od;

由上过程可知,Overlay fs目录文件被打开后,同时存在Overlay fs目录文件和实际文件系统目录文件,中间通过ovl_dir_file链接(ovl_dir_file作为Overlay fs目录文件的私有数据),他们直接的关系示意如下:

struct ovl_dir_file                                             +->+-----------------------------+                            |  |is_real: bool                |                            |  |is_upper: unsigned;          |                            |  |cache: struct ovl_dir_cache*;|                            |  |cursor: struct list_head*;   |                            |  |realfile: struct file*; -----+-+                            |  |upperfile: struct file*;     | |                            |  +-----------------------------+ |                            +-------+                  +-------+   struct file                      |                  |  struct file                        +------------------------------+ |                  +->+------------------------------+     |f_path: struct path;          | |                     |f_path: struct path;          |            |f_inode: struct inode;        | |                     |f_inode: struct inode;        |        |f_op: struct file_operations*;| |                     |f_op: struct file_operations*;|   |private_data: void *; --------+-+                     |private_data: void *;         |   | ...                          |                       | ...                          |   +------------------------------+                       +------------------------------+   overlayfs目录文件                                      实际文件系统目录文件

(3)遍历目录

遍历目录,即是所有各叠层相同目录下的所有文件列表。

(4)系统调用-遍历目录
YSCALL_DEFINE3(getdents, unsigned int, fd,,,,,) // fs/readdir.c|-> struct fd f;|-> struct getdents_callback buf = { |->     .ctx.actor = filldir,|->     .count = count,|->     .current_dir = dirent|-> };||-> iterate_dir(f.file, &buf.ctx);    |-> file->f_op->iterate(file, ctx);
(5)OVL函数iterate实现

假设目录是merged的目录:

struct ovl_dir_file                                                     +=>+-----------------------------+                                    H  |is_real: bool                |                                    H  |is_upper: unsigned;          |                                    H  |cache: struct ovl_dir_cache*;|                                    H  |cursor: struct list_head*;   |                                    H  |realfile: struct file*; -----+-+                                    H  +-----------------------------+ |                                    H                                  |    struct file                      H    struct file                   V     +------------------------------+ H    +------------------------------+     |f_path: struct path;  ==========+=+  |f_path: struct path;          |            |f_inode: struct inode;        | H H  |f_inode: struct inode;        |        |f_op: struct file_operations*;| H H  |f_op: struct file_operations*;|   |private_data: void *; ========+=+ H  |private_data: void *;         |   | ...                          |   H  | ...                          |   +------------------------------+   H  +------------------------------+     +================================+     V    struct path                         +-----------------------+    |mnt: struct vfsmount*; |    |dentry: struct dentry*;|==+    +-----------------------+  H     +=========================+     V     struct dentry                        struct ovl_inode                        +--------------------------------+   +---------------------------------+     |d_parent: struct dentry *;      |   |cache: struct ovl_dir_cache*;    |     |d_name: struct qstr;            |   |flags: unsigned long;            |     |d_inode: struct inode*;=========+==>|vfs_inode: struct inode;         |     |d_op: struct dentry_operations*;|   |+-struct inode -----------------+|     |d_sb: struct super_block;       |   ||i_op: struct inode_operations*;||     |d_fsdata: void *;  =============++  ||i_sb: struct super_block *;    ||     | ...                            |H  ||i_ino: unsigned long;          ||     +--------------------------------+H  || ...                           ||                                       H  |+-------------------------------+|                                       H  |__upperdentry: struct dentry*;   |                                       H  |...                              |                                       H  +---------------------------------+                                       H                                                         H  struct ovl_entry                                                       +=>+------------------------------+                                          | ...                          |                                          |numlower: unsigned;           |                                          |lowerstack: struct ovl_path[];|                                          +==============================+                                          |struct ovl_path {              |                                          |  layer: struct ovl_layer*;   |                                          |  dentry: struct dentry*;     |                                          |};                            |                                          +------------------------------+                                          |                              |                                          |                              |
ovl_iterate(struct file *file, struct dir_context *ctx)|-> struct ovl_dir_file *od = file->private_data;|-> struct ovl_dir_cache *cache;||-> cache = ovl_cache_get(dentry);|           |-> ovl_set_dir_cache(d_inode(dentry), NULL);|           |-> cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);|           |-> cache->refcount = 1;|           |-> INIT_LIST_HEAD(&cache->entries);|           |-> cache->root = RB_ROOT;|           |  // 遍历同名目录所在所有层次的文件列表,挂载文件列表到cache的红黑树节点|           |-> ovl_dir_read_merged(dentry, &cache->entries, &cache->root);|           |   |-> for (idx = 0; idx != -1; idx = next) { |           |   |       next = ovl_path_next(idx, dentry, &realpath);|           |   |              |-> struct ovl_entry *oe = dentry->d_fsdata;|           |   |              |-> if (idx == 0) ovl_path_upper(dentry, path);|           |   |              |        <or>|           |   |              |-> path->dentry = oe->lowerstack[idx - 1].dentry;|           |   |       if (next != -1) { |           |   |           // 遍历某一层中目录下文件列表,结果存放在ctx中|           |   |           err = ovl_dir_read(&realpath, &rdd);|           |   |                 |-> realfile = ovl_path_open(realpath, O_RDONLY | O_LARGEFILE);|           |   |                 |-> iterate_dir(realfile, &rdd->ctx);|           |   |       } else { |           |   |           err = ovl_dir_read(&realpath, &rdd);|           |   |       }|           |   |-> }|           |-> cache->root = RB_ROOT;|           |-> ovl_set_dir_cache(d_inode(dentry), cache);||-> od->cache = cache;|-> ovl_seek_cursor(od, ctx->pos);|   |-> od->cursor = p;

由上面的流程可以看出,若是目录是合成的,在遍历目录下文件列表时,是将各叠层目录下的所有文件列表保存在文件所有的红黑数上,结构如下:

struct ovl_dir_file                                                                    +=>+-----------------------------+                                                   H  |is_real: bool                |                                                   H  |is_upper: unsigned;          |                                                   H  |cache: struct ovl_dir_cache*;+--------+                                          H  |cursor: struct list_head*;   |        |                                          H  |realfile: struct file*; -----+-+      |                                            H  +-----------------------------+ |      |                                            H                                  |      |            struct file                      H    struct file                   V      |             +------------------------------+ H    +------------------------------+     |              |f_path: struct path;  ==========+=+  |f_path: struct path;          |     |                     |f_inode: struct inode;        | H H  |f_inode: struct inode;        |     |                 |f_op: struct file_operations*;| H H  |f_op: struct file_operations*;|     |            |private_data: void *; ========+=+ H  |private_data: void *;         |     |                         保存所有不同层次相同目录下的文件列表(红黑树)   | ...                          |   H  | ...                          |     |                        /   +------------------------------+   H  +------------------------------+     |                       V     +================================+                                       |  struct ovl_dir_cache                                         V                                                                        +->+------------------------+                   struct path                                                               |  |refcount: long;         |                   +-----------------------+                                                 |  |version: u64;           |                   |mnt: struct vfsmount*; |                                                 |  |entries: struct entries;|                   |dentry: struct dentry*;|==+                                              |  |root: struct rb_root;   |                   +-----------------------+  H                                              |  +------------------------+                    +=========================+                                              |                     V                                                                        |                     struct dentry                        struct ovl_inode                    |                     +--------------------------------+   +---------------------------------+ |                  |d_parent: struct dentry *;      |   |cache: struct ovl_dir_cache*; ---+-+                   |d_name: struct qstr;            |   |flags: unsigned long;            |     |d_inode: struct inode*;=========+==>|vfs_inode: struct inode;         |     |d_op: struct dentry_operations*;|   |+-struct inode -----------------+|     |d_sb: struct super_block;       |   ||i_op: struct inode_operations*;||     |d_fsdata: void *;               |   ||i_sb: struct super_block *;    ||     | ...                            |   ||i_ino: unsigned long;          ||     +--------------------------------+   || ...                           ||                                          |+-------------------------------+|                                          |__upperdentry: struct dentry*;   |                                          |...                              |                                          +---------------------------------+

总结

本文重点介绍Overlay FS的目录相关的关键数据结构和目录的相关接口,让读者能体会到所谓的Overlay文件系统的特点,很多实现细节是有别于传统的文件系统,均需考虑upper层和lower层不同情况,但是万变不离其宗,我们学习文件系统必须抓住文件系统的主要数据和接口的实现,这对C语言实现其它底层软件开发也是一种必备技能。

想了解更多关于开源的内容,请访问:

51CTO 开源基础软件社区

https://ost.51cto.com

责任编辑:jianghua 来源: 51CTO 开源基础软件社区 OverlayC语言

(责任编辑:焦点)

    推荐文章
    • 首家产品直联理财公司 超13万亿元理财产品迎穿透式监管

      首家产品直联理财公司 超13万亿元理财产品迎穿透式监管理财产品净值化转型接近尾声,产品的穿透式监管也提上日程。近期,信银理财的理财产品端直联系统通过了银行业理财登记托管中心的现场验收并上线,成为首家实现理财产品直联的理财公司。北京商报记者11月23日注意 ...[详细]
    • 银河麒麟系统安装中间件

      银河麒麟系统安装中间件银河麒麟系统安装中间件作者:不止dotNET 2023-06-12 08:13:29运维 系统运维 本文介绍下我们产品使用的中间件在国产操作系统银河麒麟的安装不一定是最优方式,但能用)。 现在越来越多 ...[详细]
    • 探讨制造业自动化的优点和缺点

      探讨制造业自动化的优点和缺点探讨制造业自动化的优点和缺点2023-09-08 15:29:41人工智能 从提高运营效率到重新定义工作场所安全,自动化的优点提供了令人信服的未来愿景。然而,应对初始投资、工作岗位流失和技术复杂性等挑 ...[详细]
    • 震撼大屏澎湃音效 创维75英寸巨屏4K电视75A7开售

      震撼大屏澎湃音效 创维75英寸巨屏4K电视75A7开售75A7是创维推出的一款巨屏电视,其采用75英寸超大屏幕,搭载炫彩画质、支持杜比和DTS双解码技术,零售价仅为7999元!这款电视采用琉璃金配色,带有一丝奢华、一点简约,无论远观还是近看都显得很有格调 ...[详细]
    • 四川阿坝州提高孤儿基本生活最低养育标准 2022年1月起执行

      四川阿坝州提高孤儿基本生活最低养育标准 2022年1月起执行日前,根据《四川省民政厅 四川省财厅关于提高全省孤儿基本生活最低养育标准的通知》要求,阿坝州民政局、州财政局联合发文提高阿坝州孤儿基本生活最低养育标准。此次调整后的标准为社会散居孤儿基本生活 ...[详细]
    • 聊聊PG等待事件清单

      聊聊PG等待事件清单聊聊PG等待事件清单作者:白鳝 2023-07-07 07:47:26数据库 其他数据库 今天我把这张清单发出和大家共享,如果有朋友对这项工作有兴趣,也可以和我联系,有关于这方面的知识也可以告诉我,我 ...[详细]
    • 证书管理:从手工到平台化

      证书管理:从手工到平台化证书管理:从手工到平台化作者:Peng Jiahong 2023-06-29 11:21:31运维 本文介绍了vivo业务运维证书管理从手工到平台化的历程。 一、背景以往,vivo 互联网业务的域名证 ...[详细]
    • 猜一猜:企鹅是恒温动物还是变温动物?蚂蚁庄园6.8日答案

      猜一猜:企鹅是恒温动物还是变温动物?蚂蚁庄园6.8日答案在支付宝蚂蚁庄园小程序中每日都会有问题来考大家,回答正确之后就可以领取食料来喂养小鸡了,那么6月8日问题的答案会是什么呢?下面就和小编来看一下吧。问题:猜一猜:企鹅是恒温动物还是变温动物?选项:A、恒 ...[详细]
    • 北京租房市场入冬 六成商圈租金环比下跌

      北京租房市场入冬 六成商圈租金环比下跌天气转冷,北京租赁市场也正式入冬,市场淡季叠加部分区域疫情反弹因素,11月北京租赁市场呈现加速降温趋势。11月29日,贝壳研究院发布数据显示,11月北京市租赁成交量环比减少超过10%,各城区租赁市场均 ...[详细]
    • 安装大势至文件防泄密系统客户端“用户 密码错误”怎么解决

      安装大势至文件防泄密系统客户端“用户 密码错误”怎么解决大势至电脑文件防泄密系统是一款保护电脑文件安全,防止员工随意将电脑文件外传的管理软件。系统分为两个版本,一个是单机版,适用于单机使用,另一个是网络版,适用于同一个局域网,可批量管理和修改用户电脑的软件 ...[详细]
    热点阅读