Overlay file system is a stack file system overlaid on the basic file system, so it is also a wrapfs. Documentation is described below.
An overlay-filesystem tries to present a filesystem which is the result over overlaying one filesystem on top of the other.
Because of memory problems, stacked file systems cannot be stacked all the time. This article mainly introduces the mount process of Overlay to sort out how to limit the number of stacking layers of stacked file systems. The following code is based on Kernel 5.10 source code. Directly from Mount_ The callback function registered in nodev looks like a normal initialization process.
static int ovl_fill_super(struct super_block *sb, void *data, int silent) { struct path upperpath = { }; struct dentry *root_dentry; struct ovl_entry *oe; struct ovl_fs *ofs; struct ovl_layer *layers; struct cred *cred; char *splitlower = NULL; unsigned int numlower; int err; err = -EIO; if (WARN_ON(sb->s_user_ns != current_user_ns())) goto out; sb->s_d_op = &ovl_dentry_operations; //Register dentry operation function err = -ENOMEM; ofs = kzalloc(sizeof(struct ovl_fs), GFP_KERNEL); if (!ofs) goto out; err = -ENOMEM; ofs->creator_cred = cred = prepare_creds(); if (!cred) goto out_err; /* Is there a reason anyone would want not to share whiteouts? */ ofs->share_whiteout = true; ofs->config.index = ovl_index_def; ofs->config.uuid = true; ofs->config.nfs_export = ovl_nfs_export_def; ofs->config.xino = ovl_xino_def(); ofs->config.metacopy = ovl_metacopy_def; err = ovl_parse_opt((char *) data, &ofs->config); //Resolve mount parameter if (err) goto out_err; err = -EINVAL; if (!ofs->config.lowerdir) { if (!silent) pr_err("missing 'lowerdir'\n"); goto out_err; } err = -ENOMEM; //Get each lower dir passed in by mount splitlower = kstrdup(ofs->config.lowerdir, GFP_KERNEL); if (!splitlower) goto out_err; err = -EINVAL; numlower = ovl_split_lowerdirs(splitlower); if (numlower > OVL_MAX_STACK) { //The maximum number of lower dir s cannot exceed 500 pr_err("too many lower directories, limit is %d\n", OVL_MAX_STACK); goto out_err; } err = -ENOMEM; layers = kcalloc(numlower + 1, sizeof(struct ovl_layer), GFP_KERNEL); if (!layers) goto out_err; ofs->layers = layers; /* Layer 0 is reserved for upper even if there's no upper */ ofs->numlayer = 1; sb->s_stack_depth = 0; //Initialize s_stack_depth is 0 sb->s_maxbytes = MAX_LFS_FILESIZE; //Set maximum file limit atomic_long_set(&ofs->last_ino, 1); /* Assume underlaying fs uses 32bit inodes unless proven otherwise */ if (ofs->config.xino != OVL_XINO_OFF) { ofs->xino_mode = BITS_PER_LONG - 32; if (!ofs->xino_mode) { pr_warn("xino not supported on 32bit kernel, falling back to xino=off.\n"); ofs->config.xino = OVL_XINO_OFF; } } /* alloc/destroy_inode needed for setting up traps in inode cache */ sb->s_op = &ovl_super_operations; //Register inode operator functions if (ofs->config.upperdir) { //upper dir is specified in the mount directive struct super_block *upper_sb; err = -EINVAL; if (!ofs->config.workdir) { pr_err("missing 'workdir'\n"); goto out_err; } //Get the information of upper dir path and file system err = ovl_get_upper(sb, ofs, &layers[0], &upperpath); if (err) goto out_err; upper_sb = ovl_upper_mnt(ofs)->mnt_sb; if (!ovl_should_sync(ofs)) { ofs->errseq = errseq_sample(&upper_sb->s_wb_err); if (errseq_check(&upper_sb->s_wb_err, ofs->errseq)) { err = -EIO; pr_err("Cannot mount volatile when upperdir has an unseen error. Sync upperdir fs to clear state.\n"); goto out_err; } } err = ovl_get_workdir(sb, ofs, &upperpath); if (err) goto out_err; if (!ofs->workdir) sb->s_flags |= SB_RDONLY; //Set s in upper dir_ stack_ Depth gives the currently mounted s_stack_depth sb->s_stack_depth = upper_sb->s_stack_depth; sb->s_time_gran = upper_sb->s_time_gran; } oe = ovl_get_lowerstack(sb, splitlower, numlower, ofs, layers); err = PTR_ERR(oe); if (IS_ERR(oe)) goto out_err; ... return err; }
ovl_fill_super is the callback function of the mount process from super. Because overlay FS is designed to have a hierarchical relationship between lower and upper, there are many lower dir and upper dir operations in the mount process. This article focuses on the number of stack layers of the stack file system, so only focus on this process, and others are briefly introduced.
s_ stack_ The depth member represents the number of stacking layers. As can be seen in the above code, the value given to the member variable is 0 at the beginning. When upper dir exists, it will be s in the file system in which upper dir is located_ stack_ The depth value is assigned to the current overlay FS. The final logical judgment is in OVL_ get_ In the lowerstack function, let's see how this function handles s_stack_depth this value.
static struct ovl_entry *ovl_get_lowerstack(struct super_block *sb, const char *lower, unsigned int numlower, struct ovl_fs *ofs, struct ovl_layer *layers) { int err; struct path *stack = NULL; unsigned int i; struct ovl_entry *oe; if (!ofs->config.upperdir && numlower == 1) { pr_err("at least 2 lowerdir are needed while upperdir nonexistent\n"); return ERR_PTR(-EINVAL); } stack = kcalloc(numlower, sizeof(struct path), GFP_KERNEL); if (!stack) return ERR_PTR(-ENOMEM); err = -EINVAL; for (i = 0; i < numlower; i++) { //Get lower dir path and corresponding file system information err = ovl_lower_dir(lower, &stack[i], ofs, &sb->s_stack_depth); if (err) goto out_err; lower = strchr(lower, '\0') + 1; } err = -EINVAL; sb->s_stack_depth++; //Auto increment indicates that the current mount is stacked again //Judge whether there are more than 2 layers stacked if (sb->s_stack_depth > FILESYSTEM_MAX_STACK_DEPTH) { pr_err("maximum fs stacking depth exceeded\n"); goto out_err; } err = ovl_get_layers(sb, ofs, stack, numlower, layers); if (err) goto out_err; err = -ENOMEM; oe = ovl_alloc_entry(numlower); if (!oe) goto out_err; for (i = 0; i < numlower; i++) { oe->lowerstack[i].dentry = dget(stack[i].dentry); oe->lowerstack[i].layer = &ofs->layers[i+1]; } out: for (i = 0; i < numlower; i++) path_put(&stack[i]); kfree(stack); return oe; out_err: oe = ERR_PTR(err); goto out; }
In the above code, the key lies in a for loop, in which the file system where each lower dir is located is s_ stack_ Get depth, and then keep the larger one as the s of the current overlay FS_ stack_ After the depth, for loop is executed, its s_stack_depth plus 1 means that a stack file system is added to the previous lower and upper. Then with filesystem_ MAX_ STACK_ Compare depth. If it exceeds, mount fails.
FILESYSTEM_ MAX_ STACK_ The value of depth is 2, that is, as long as any of the file directories in lower dir and upper dir has a two-tier stacked file system, the mount fails. This is the key to limiting the number of stacked layers of the stacked file system.
When using the Overlay file system, if the mount result has the following information, consider that the number of stacking layers exceeds the limit. Check whether the lower dir or upper dir has reached layer 2 from the root.
maximum fs stacking depth exceeded