当前位置:网站首页>Overlayfs source code parsing

Overlayfs source code parsing

2022-07-23 14:05:00 LYSnowy

mount mount

Register the file system

OverlayFS In the kernel, it exists in the form of kernel modules , The corresponding initialization function and cleanup function are as follows :

module_init(ovl_init);
module_exit(ovl_exit);
static int __init ovl_init(void)
{
    
	return register_filesystem(&ovl_fs_type);
}

It involves register_filesystem function , The purpose of this function is to check linux Whether the file system with the same name already exists in the kernel , If it exists, return the file system structure , Otherwise, this file system (overlayfs) Insert into the linked list of the file system .

int register_filesystem(struct file_system_type * fs)
{
    
	int res = 0;
	struct file_system_type ** p;

	BUG_ON(strchr(fs->name, '.'));
	if (fs->next)// Description has been in the file system type linked list 
		return -EBUSY;
	write_lock(&file_systems_lock);
	// Find out whether there is a file system with the same name in the linked list 
	p = find_filesystem(fs->name, strlen(fs->name));
	if (*p)
		res = -EBUSY;
	else
		*p = fs;// If it does not exist, insert the file system into the corresponding linked list 
	write_unlock(&file_systems_lock);
	return res;
}

The whole linked list is shown in the figure below  Insert picture description here
Through the analysis of the process, we can know that this part is no different from other file systems . After successfully registering a file system, you need to mount the file , That is, you can access the correct files through the directory .

Mount file system

The mount command is as follows :

sudo mount -t overlay overlay -o lowerdir=lower,upperdir=upper,workdir=work merged

This command may appear special device overlay does not exist. This mistake , This high probability is because your path is written incorrectly , Although I don't know why the path is wrong, it is such an error message , But after testing, it is really a path problem .

The mount command is called ovl_mount function ( It should be that the system call finally calls a fs Of mount function , This function is written to fs Structure of the mount Function )

static struct file_system_type ovl_fs_type = {
    
	.owner		= THIS_MODULE,
	.name		= "overlay",
	.mount		= ovl_mount,// Mount function 
	.kill_sb	= kill_anon_super,
};
static struct dentry *ovl_mount(struct file_system_type *fs_type, int flags, const char *dev_name, void *raw_data)
{
    
	return mount_nodev(fs_type, flags, raw_data, ovl_fill_super);
}

struct dentry *mount_nodev(struct file_system_type *fs_type, int flags, void *data, int (*fill_super)(struct super_block *, void *, int))
{
    
	int error;
	//  Get superblock 
	struct super_block *s = sget(fs_type, NULL, set_anon_super, flags, NULL);

	if (IS_ERR(s))
		return ERR_CAST(s);
	//  Fill superblock 
	error = fill_super(s, data, flags & SB_SILENT ? 1 : 0);
	if (error) {
    
		deactivate_locked_super(s);
		return ERR_PTR(error);
	}
	s->s_flags |= SB_ACTIVE;
	//  Return to the root directory of the mounted file system ,s_root Itself is dentry, This is just to add a reference count 
	return dget(s->s_root);
}

You can see mount Function called directly mount_nodev function , The main function of this function is to fill super fast super block, Search first fs_supers Field refers to the linked list of mounted file system instances composed of super blocks , If you find a superblock associated with a block device , Then return its address , Otherwise, allocate and initialize a new superblock , And insert into OverlayFS The linked list of mounted file system instances and the linked list of global mounted file system instances .

The specific data structure relationship is shown in the figure below :
 Insert picture description here

Call function fill_super() Fill in this super_block, To include OverlayFS Specific information about , It is also the main part of the mount function , After filling, return dentry The information is successfully mounted . So it mainly introduces fill_super function .

First of all, you can know through the above function fill_super It's actually a function pointer passed from a function parameter , Finally, it shows that the function body is at the top ovl_mount in , Indicates that the function called is ovl_fill_super This function , That is, the function specified in the structure .

fill_super This function mainly deals with some upper、lower these overlayfs Special part .

fille_super It is mainly filled with the following two structures .
 Insert picture description here
 Insert picture description here

Read write directory

Directory search is mainly for ls merged Similar commands are parsed .
ls This command will call two system calls , among openat() Open Directory merged, Return the corresponding descriptor .getdents() according to openat() The returned descriptor searches the directory . therefore ,overlayfs Directory search in mainly accomplishes two things : Open Directory 、 search for directory

openat The system call opens the directory

openat The system call will eventually call the following code :

SYSCALL_DEFINE4(openat, int, dfd, const char __user *, filename, int, flags,umode_t, mode)
{
    
	if (force_o_largefile())
		flags |= O_LARGEFILE;

	return do_sys_open(dfd, filename, flags, mode);
}

In the end, I call do_sys_open function . The specific meaning of this function parameter is as follows :

  • Parameters filename : It's the file path , Can be a relative path ( That is not to say “/” start ), It could be an absolute path ( That is to “/” start ).
  • Parameters dirfd: Is the file descriptor obtained after opening a directory , As a base directory for relative paths . If the file path is relative , So in function openat Relative file descriptors dirfd List of references ,open Function is interpreted as relative to the current working directory of the calling process . If the file path is absolute , openat Ignore parameters dirfd.
  • Parameters flags: Contains multiple file creation flags and file status flags .
    The difference between the two groups of signs is : The file creation flag only affects the open operation , The file status flag affects subsequent read and write operations .
  • Parameters mode: Specifies the file mode when creating a new file . When parameters flags Specify flag bits O_CREAT or O_TMPFILE When , Parameter... Must be specified mode, In other cases, parameters are ignored mode.

such as flags by CREATE, Indicates that you want to create a file ,mode by 0744, Indicates the user rights of the file .

long do_sys_open(int dfd, const char __user *filename, int flags, umode_t mode)
{
    
	struct open_flags op;
	int fd = build_open_flags(flags, mode, &op);
	struct filename *tmp;

	if (fd)
		return fd;
	//1. The pathname of user space is copied to kernel space 
	//1. according to filename Get a corresponding struct filename Variable 
	tmp = getname(filename);
	if (IS_ERR(tmp))
		return PTR_ERR(tmp);

	fd = get_unused_fd_flags(flags);//2. Get an unused file descriptor 
	if (fd >= 0) {
    
		struct file *f = do_filp_open(dfd, tmp, &op);//3. Open file 
		if (IS_ERR(f)) {
    
			put_unused_fd(fd);
			fd = PTR_ERR(f);
		} else {
    
			fsnotify_open(f);
			fd_install(fd, f);
		}
	}
	putname(tmp);
	return fd;
}

The main thing is to find an unused file descriptor fd, And then according to dfd That is, the file descriptor corresponding to the reference directory of the relative path opens the file , Yes op Perform assignment operation , And then fd and file Combine . The subject is this do_filp_open function .

struct file *do_filp_open(int dfd, struct filename *pathname,
		const struct open_flags *op)
{
    
	struct nameidata nd;//1. Initialize a nameidata Variable 
	int flags = op->lookup_flags;
	struct file *filp;

	set_nameidata(&nd, dfd, pathname);//2. Save the search information , to update current Corresponding fields 
	filp = path_openat(&nd, op, flags | LOOKUP_RCU);//3. Path finding , Open file 
	if (unlikely(filp == ERR_PTR(-ECHILD)))
		filp = path_openat(&nd, op, flags);
	if (unlikely(filp == ERR_PTR(-ESTALE)))
		filp = path_openat(&nd, op, flags | LOOKUP_REVAL);
	restore_nameidata();
	return filp;
}

Mainly used path_openat function , If this function is called for the first time and it is found that another process is reading and writing the file, it will be called for the second time with a different flag To call , This can be applied to the situation of reading more and writing less , The first call is more efficient than the second call , If the file is found on the network, it will be called for the third time , use LOOPUP_REVAL The way .

nameidata It's the intermediate variable , It is used to store the parameters required by the function and the results . For example, it stores the currently traversed path inode etc. .

static struct file *path_openat(struct nameidata *nd, const struct open_flags *op, unsigned flags)
{
    
	const char *s;
	struct file *file;
	int opened = 0;
	int error;

	file = get_empty_filp();
	if (IS_ERR(file))
		return file;

	file->f_flags = op->open_flag;

	if (unlikely(file->f_flags & __O_TMPFILE)) {
    
		error = do_tmpfile(nd, flags, op, file, &opened);
		goto out2;
	}
	//
	s = path_init(nd, flags);
	if (IS_ERR(s)) {
    
		put_filp(file);
		return ERR_CAST(s);
	}
	//
	while (!(error = link_path_walk(s, nd)) &&
		//
		(error = do_last(nd, file, op, &opened)) > 0) {
    
		nd->flags &= ~(LOOKUP_OPEN|LOOKUP_CREATE|LOOKUP_EXCL);
		s = trailing_symlink(nd);
		if (IS_ERR(s)) {
    
			error = PTR_ERR(s);
			break;
		}
	}
	terminate_walk(nd);
out2:
	...
	return file;
}

path_openat Function mainly includes three main functions , Namely path_init、link_path_walk、do_last function ,

  • path_init() initialization nd Path field in variable ( Set the search starting path ), Set according to the path inode Field : If you look up a string with ‘/’ start , Explanation is an absolute path , Therefore, according to current Process fs Field get root Path and set nd Medium root Fields and path Field . If not by ’/‘ start , Description is a relative path , According to current The process gets the current working path and sets nd Medium path Field . According to dentry obtain inode And set up nd Of inode Field
  • link_path_walk() Perform path lookup , Find the directory where the last component is located , At this time, the last component has not been resolved : adopt nd Of inode Field to check whether the current directory has executable permissions ( Otherwise, we can't traverse ), If not, an error is returned . Skip the beginning of the path string ’/‘, Get find string next ’/‘ Before the string , That is, get the current search component string . Execute hash operation , Then deposit nd Medium last Field . With path Field dentry Is the parent directory item , according to last Field to find catalog items ( First, we will find the directory item cache , If not, it will pass real_lookup() call inode Of lookup Method to read the directory from the disk ), Get a directory entry corresponding to the component string . Update according to the directory entry and the mount point under the parent directory nd Of path Path and inode. Repeat the above steps , until nd Of last The information of the last component is saved in the field ( here , The last component has not been resolved )
  • do_last() Complete the analysis of the final path component , The results will be saved in nd in , Fill the... With the parsing result 3 Step by step file In the field , Open file : according to nd Of path Field ( here , The path of the last component has been recorded ) Medium dentry Get the corresponding file inode, And then according to inode Set up file Field of , These include f_op Field , Get the file operation interface . call file->f_op->open() Open file , Pay attention to the filp That is, the return value is overlayfs Medium file structure , That is to say, it does not return overlayfs Of inode Corresponding file( These should not appear in ext4 It's really disk inode Of .

do_last Would call inode->f_open operation , That is to say inode Self defined open operation , And determine the return value according to the return value of this operation , Concrete open Function is ovl_dir_open.

static int ovl_dir_open(struct inode *inode, struct file *file)
{
    
	struct path realpath;
	struct file *realfile;
	struct ovl_dir_file *od;
	enum ovl_path_type type;

	od = kzalloc(sizeof(struct ovl_dir_file), GFP_KERNEL);//1. Allocate one ovl_dir_file Variable od
	if (!od)
		return -ENOMEM;

	type = ovl_path_real(file->f_path.dentry, &realpath);//2. obtain file Path type and real path 
	realfile = ovl_path_open(&realpath, file->f_flags);//3. Open the real file according to the path type 
	if (IS_ERR(realfile)) {
    
		kfree(od);
		return PTR_ERR(realfile);
	}
	//4. Set a variable od Field of 
	od->realfile = realfile;
	// Judge whether it exists at the same time upper and lower
	od->is_real = !OVL_TYPE_MERGE(type);
	// adopt upper_dentry Judge whether it is upper
	od->is_upper = OVL_TYPE_UPPER(type);
	file->private_data = od;

	return 0;
}

First, through ovl_path_real Determine whether the function exists at the same time upper and lower Folder , If it exists at the same time, it proves that it is not real Of , Need merger , adopt dentry Medium ovl_entry Judge the data results

in other words , adopt upper and lower All of them ext4 file system , adopt merge That's what you're visiting overlay file system ,overlayfs All file systems are mounted and created dentry It's all stored ovl_entry, It's storing upper and lower Information about , This information comes from the path specified when mounting , Then all information under this path is complete , When creating a new file, the information will also be supplemented , That is to say, as long as the path is known , You can know everything

ovl_path_open Function open function , And then use it file This data structure returns the result . here file Has been identified and opened , That is to say, the directory file has been opened , Whether this directory is merged still real, All open merged Medium inode, because real Of inode stay merged There must be one of them , This ovl_inode Will record whether they are real , If it's not real , Will follow upper and lower Read in , Otherwise, only one is read .

 Insert picture description here
Because here is_real yes false, meanwhile is_upper Is true , So the corresponding opening is open upper The file of , But because of is real The existence of , So at the end of the day lower Traversal read , If one exists at the same time lower and upper The catalog of , Indeed, it will merge in the folder and remove the duplicate of the same name

The above is the implementation logic , The specific code is as follows , That is to say getents system call

getdents system call , search for directory

getdents The system calls are as follows :

SYSCALL_DEFINE3(getdents, unsigned int, fd, struct linux_dirent __user *, dirent, unsigned int, count)
{
    
	...
	error = iterate_dir(f.file, &buf.ctx);//2. Directory search 
	...
	return error;
}

struct fd {
    
	struct file *file;
	unsigned int flags;
};

The subject is iterate_dir function .

int iterate_dir(struct file *file, struct dir_context *ctx)
{
    
	struct inode *inode = file_inode(file);//file->f_inode, obtain file Corresponding inode Number 
	...
	res = -ENOENT;
	if (!IS_DEADDIR(inode)) {
    
		ctx->pos = file->f_pos;// Update the context according to the file read / write location pos Field 
		res = file->f_op->iterate(file, ctx);// call f_op Medium iterate Interface 
		file->f_pos = ctx->pos;//iterate Will update ctx Of pos Field , Use it to update the read and write location of the file 
		fsnotify_access(file);
		file_accessed(file);
	}
	mutex_unlock(&inode->i_mutex);
out:
	return res;
}

The end result is a call inode As indicated in iterate operation .

static int ovl_iterate(struct file *file, struct dir_context *ctx)
{
    
	struct ovl_dir_file *od = file->private_data;// obtain OverlayFS Unique information 
	struct dentry *dentry = file->f_path.dentry;// File corresponding dentry
	struct ovl_cache_entry *p;

	if (!ctx->pos)
		ovl_dir_reset(file);

	if (od->is_real)// Description is not a consolidated directory , When the directory only exists upper Layer or lower The layer time condition will hold 
		return iterate_dir(od->realfile, ctx);

	//cache The value is the list of documents , So here is the list of files to get 
	if (!od->cache) {
    // In the previous section, we know that this field is NULL, Conditions established 
		struct ovl_dir_cache *cache;

		//  Key functions , Get file list 
		cache = ovl_cache_get(dentry);// Get one ovl_dir_cache
		if (IS_ERR(cache))
			return PTR_ERR(cache);

		od->cache = cache;// Set up od Of cache The field is the previous cache
		ovl_seek_cursor(od, ctx->pos);// Set according to the position of the context od Of cursor Field , Point to the corresponding position of the linked list 
	}
	
	// Traverse , Handle every item of the linked list 
	while (od->cursor != &od->cache->entries) {
    
		p = list_entry(od->cursor, struct ovl_cache_entry, l_node);
		if (!p->is_whiteout)
			if (!dir_emit(ctx, p->name, p->len, p->ino, p->type))
				break;
		od->cursor = p->l_node.next;
		ctx->pos++;
	}
	return 0;
}
static struct ovl_dir_cache *ovl_cache_get(struct dentry *dentry)
{
    
	int res;
	struct ovl_dir_cache *cache;

	// obtain dentry Directory item specific information (dentry->d_fsdata, It points to a ovl_entry Variable of type ) Medium cache
	cache = ovl_dir_cache(dentry);
	if (cache && ovl_dentry_version_get(dentry) == cache->version) {
    //cache Exist and work 
	// Existence and effectiveness may indicate that in the near future ls After that , So there is no need to traverse again 
		cache->refcount++;
		return cache;
	}
	// It is set to null because it may have been traversed but failed , So it's simply empty 
	ovl_set_dir_cache(dentry, NULL);// Set up dentry In the information unique to the catalog item cache by NULL

	cache = kzalloc(sizeof(struct ovl_dir_cache), GFP_KERNEL);// Allocate one cache
	if (!cache)
		return ERR_PTR(-ENOMEM);

	cache->refcount = 1;
	INIT_LIST_HEAD(&cache->entries);// Initialize chain header 

	res = ovl_dir_read_merged(dentry, &cache->entries);
	if (res) {
    
		ovl_cache_free(&cache->entries);
		kfree(cache);
		return ERR_PTR(res);
	}

	cache->version = ovl_dentry_version_get(dentry);
	ovl_set_dir_cache(dentry, cache);// Set up dentry In the information unique to the catalog item cache

	return cache;
}
static int ovl_dir_read_merged(struct dentry *dentry, struct list_head *list)
{
    
    int err;
    struct path lowerpath;
    struct path upperpath;
    struct ovl_readdir_data rdd = {
    
        .ctx.actor = ovl_fill_merge,
        .list = list,
        .root = RB_ROOT,
        .is_merge = false,
    };

    ovl_path_lower(dentry, &lowerpath); //  obtain lower Catalog 
    ovl_path_upper(dentry, &upperpath); //  obtain upper Catalog 

    if (upperpath.dentry) {
     //  If upper Directory exists ,  Read upper List of files in the directory 
        err = ovl_dir_read(&upperpath, &rdd);
        if (err)
            goto out;

        if (lowerpath.dentry) {
    
            err = ovl_dir_mark_whiteouts(upperpath.dentry, &rdd);
            if (err)
                goto out;
        }
    }
    if (lowerpath.dentry) {
     //  If lower Directory exists ,  Read lower List of files in the directory 
        list_add(&rdd.middle, rdd.list);
        rdd.is_merge = true;
        err = ovl_dir_read(&lowerpath, &rdd);
        list_del(&rdd.middle);
    }

This code has been modified in the latest kernel version , But the logic is the same , It's traversal upper and lower Of , Merge those with the same name , That is to ignore lower Of .

establish / Delete file

You can know that whether you create a file or delete a file, it is the same as reading and writing a file , There is no need to consider directly for lower and upper Folder operation , because inode Will be in ls Update when . Only for merged Special processing is required for the operation of files in , There are four situations , They exist alone in lower and upper And the situation that exists and does not exist at the same time .

It should be noted that , It is impossible to create a file with the same name , So the above four cases are for the location of the directory where the file is created .
 Insert picture description here

create a file

 Insert picture description here

static int ovl_create(struct inode *dir, struct dentry *dentry, umode_t mode, bool excl)
{
    
	return ovl_create_object(dentry, (mode & 07777) | S_IFREG, 0, NULL);
}

Be careful , Inside dentry It should correspond to the newly created file dentry, in other words , Before that, we did some initialization , such as d_parent etc. , But specific and overlayfs The relevant has not been assigned .

static int ovl_create_object(struct dentry *dentry, int mode, dev_t rdev, const char *link)
{
    
	int err;
	err = ovl_want_write(dentry);// Get right upper Write access of mount point 
	if (!err) {
    
		err = ovl_create_or_link(dentry, mode, rdev, link, NULL);
		ovl_drop_write(dentry);
	}
	return err;
}
static int ovl_create_or_link(struct dentry *dentry, int mode, dev_t rdev, const char *link, struct dentry *hardlink)
{
    
	...
	struct inode *inode;
	struct kstat stat = {
    
		.mode = mode,
		.rdev = rdev,
	};
	...
	/* stay OverlayFS Next, assign a to the newly created file inode*/
	inode = ovl_new_inode(dentry->d_sb, mode, dentry->d_fsdata);
	...
	/* The parent directory may be lower Layer Directory , So we have to cow-on-write operation */
	// dentry->d_parent Namely merged Catalog 
	err = ovl_copy_up(dentry->d_parent);
	...

	/* according to dentry Of opaque Value determines which function to call */
	if (!ovl_dentry_is_opaque(dentry)) {
    
		err = ovl_create_upper(dentry, inode, &stat, link, hardlink);
	} else {
    
		...
		err = ovl_create_over_whiteout(dentry, inode, &stat, link,
					       hardlink);
		...
	}
	...
}
  • call ovl_new_inode() stay OverlayFS Assign a... To the newly created file in inode
  • Because the parent directory may be a lower Layer Directory , Therefore, call the parent directory ovl_copy_up() perform copy_up operation , Copy the parent directory to upper layer
  • According to the newly created file opaque Attribute in upper Create the actual file in the layer :
  • If not set opaque attribute , Call ovl_create_upper() function . In this case , This function will be called :
    dentry->d_parent Find the parent directory of the newly created file dentry. In this case , The parent directory is OverlayFS Root directory
    Call function ovl_dentry_upper() According to the hierarchical information of the parent directory ( Other blogs have introductions ), Find the parent directory corresponding upper Layer directory dentry And inode, In this example, the parent directory corresponds to upper The layer directory is upper Catalog . stay upper The directory corresponds to dentry Find out whether there is a file with the same name as the new file dentry, Create if it does not exist . call ovl_create_real() function , Pass in the last step dentry, as well as upper The directory corresponds to inode. Functions call different functions to create files according to the type of files :
    If it is a general document , call ovl_do_create(), This function further calls vfs_create(), Transfer control to VFS,vfs_create() The function will inode The index to the inode operation , And then call inode In operation create() Function to complete the creation of the file . In this case , Will follow this path
    If it's a catalog , Call ovl_do_mkdir(), This function further calls vfs_mkdir(), Transfer control to VFS,vfs_mkdir() The function will also be based on the passed inode The index to the inode operation , And then call inode In operation mkdir() Function to complete the creation of the directory
  • If set opaque attribute , Call ovl_create_over_whiteout() Letter . root ovl_create_upper() The function is different from ,ovl_create_over_whiteout() use workdir, Put the above steps in workdir Next finish , Then call a rename operation , take workdir Rename the file created under to upper layer

Inside opaque Attributes refer to the attributes of the created file , That is, whether to create a real file or a fake file .

static int ovl_create_upper(struct dentry *dentry, struct inode *inode,
			    struct kstat *stat, const char *link,
			    struct dentry *hardlink)
{
    
	struct dentry *upperdir = ovl_dentry_upper(dentry->d_parent);//dentry Of the parent directory upper Catalog items 
	struct inode *udir = upperdir->d_inode;// Parent directory upper Directory entries correspond to inode
	struct dentry *newdentry;
	int err;

	mutex_lock_nested(&udir->i_mutex, I_MUTEX_PARENT);
	newdentry = lookup_one_len(dentry->d_name.name, upperdir,
				   dentry->d_name.len);// In the parent directory upper Search under the directory dentry
	err = PTR_ERR(newdentry);
	if (IS_ERR(newdentry))
		goto out_unlock;
	err = ovl_create_real(udir, newdentry, stat, link, hardlink, false);
	if (err)
		goto out_dput;

	ovl_dentry_version_inc(dentry->d_parent);
	ovl_dentry_update(dentry, newdentry);// Set up dentry Of upper The directory entry is newdentry
	ovl_copyattr(newdentry->d_inode, inode);
	d_instantiate(dentry, inode);
	newdentry = NULL;
out_dput:
	dput(newdentry);
out_unlock:
	mutex_unlock(&udir->i_mutex);
	return err;
}

Delete file

static int ovl_do_remove(struct dentry *dentry, bool is_dir)
{
    
	enum ovl_path_type type;
	int err;

	...
	/* The deleted file may be a lower Layer files , So you need to set the parent directory first copy_up*/
	err = ovl_copy_up(dentry->d_parent);
	...
	type = ovl_path_type(dentry);// Get the path type of the deleted file 
	if (OVL_TYPE_PURE_UPPER(type)) {
    //(type) & __OVL_PATH_PURE
		err = ovl_remove_upper(dentry, is_dir);//1. If the file only exists upper layer 
	} else {
    
		...

		err = ovl_remove_and_whiteout(dentry, is_dir);

		...
	}
	
	...
}
  • If the deleted file only exists in upper layer , Call function ovl_remove_upper() Implement delete operation :
  • Just like file creation , First, you will find the directory corresponding to the file you want to delete upper Layer directory inode and dentry
    What you got in the last step upper Layer directory dentry Search for deleted files dentry. If it is not found, an error will be returned
    Depending on whether the deleted file is a directory, call different functions to transfer control to VFS:
    If the deleted file is a directory , Call vfs_rmdir()
    If the deleted file is not a directory , Call vfs_unlink()
    vfs_rmdir() or vfs_unlink() According to the above upper Layer directory inode, The index to the inode operation , And then call upper Layer file system rmdir() or unlink() Hand over control upper Layer file system , Finish deleting the file
  • If the deleted file exists lower layer , Call function ovl_remove_and_whiteout() Implement delete operation . This function will also find the directory where the deleted file is located upper Layer corresponding to the directory inode and dentry. And files only exist upper Different layers , If the file exists lower layer , You need to in work Under the directory whiteout The file was finally renamed to upper Layer to hide lower Layer files .

Read and write files

read / write directory

This has been introduced before ,ls Even a kind of reading directory , I won't introduce it here , To emphasize again is Open the directory and finally get ovl_inode Of , And that makes sense , Because directly merge Reading and writing really need now merge Of overlayfs To establish inode, Then it is written to upper layer , therefore , about merge The first thing you get from directory operations is ovl_inode!

read / Writing documents

In fact, there is no special need to add , If it's in lower and upper The operation under the folder is actually similar to overlayfs It doesn't matter , Only need ls Update it when you need it , Because the operation is ext4 file system , It's equivalent to changing the lamp inode Information , however overlayfs Is stored in inode The pointer to , So it doesn't matter .

If it's in merge Now read and write files , You just need to choose the exact upper Of inode still lower Of inode, Because this needs to be accurate , Because even if upper and lower All have merge It is definitely upper Of course. , So there is no need to merge like a directory , Because in merged The operation of files under the folder will never be the same as lower Related ( Unless it is lower The only files under ), So we need to give it accurately lower Or is it upper Of inode.

So we need to choose inode,inode The choice of depends mainly on ovl_d_select_inode function .

First, choose to complete the previous function slightly

int vfs_open(const struct path *path, struct file *file, const struct cred *cred)
{
    
	//  Select a inode return 
	struct inode *inode = vfs_select_inode(path->dentry, file->f_flags);

	if (IS_ERR(inode))
		return PTR_ERR(inode);

	file->f_path = *path;
	//  Open the returned inode
	return do_dentry_open(file, inode, NULL, cred);
}
static int do_dentry_open(struct file *f, struct inode *inode, int (*open)(struct inode *, struct file *), const struct cred *cred)
{
    
	...
	f->f_inode = inode;
	f->f_mapping = inode->i_mapping;
	
	...
	f->f_op = fops_get(inode->i_fop);
	
	...
	if (!open)
		open = f->f_op->open;
	if (open) {
    
		error = open(inode, f);
		if (error)
			goto cleanup_all;
	}
	...
}

You can see it ,do_dentry_open Function just uses inode Inside open Function to open , So the point is inode The choice of .


2
3
4
5
6
7
8
9
10
static inline struct inode *vfs_select_inode(struct dentry *dentry, unsigned open_flags)
{
    
	struct inode *inode = d_inode(dentry);//dentry->d_inode

	//  call inode What it has select function 
	if (inode && unlikely(dentry->d_flags & DCACHE_OP_SELECT_INODE))
		inode = dentry->d_op->d_select_inode(dentry, open_flags);

	return inode;
}
struct inode *ovl_d_select_inode(struct dentry *dentry, unsigned file_flags)
{
    
	int err;
	struct path realpath;
	enum ovl_path_type type;

	//  If it's a directory , As mentioned above, return inode that will do 
	if (d_is_dir(dentry))
		return d_backing_inode(dentry);

	// If not one dir, The following code is executed 
	type = ovl_path_real(dentry, &realpath);// Get the real path 
	if (ovl_open_need_copy_up(file_flags, type, realpath.dentry)) {
    
		err = ovl_want_write(dentry);
		if (err)
			return ERR_PTR(err);

		if (file_flags & O_TRUNC)
			err = ovl_copy_up_truncate(dentry);
		else
			err = ovl_copy_up(dentry);
		ovl_drop_write(dentry);
		if (err)
			return ERR_PTR(err);

		ovl_path_upper(dentry, &realpath);
	}

	if (realpath.dentry->d_flags & DCACHE_OP_SELECT_INODE)
		return realpath.dentry->d_op->d_select_inode(realpath.dentry, file_flags);

	return d_backing_inode(realpath.dentry);
}

If it is a general document , According to the opening flag and the corresponding upper or lower The directory entry of the layer file determines whether to execute copy-up operation . For example , If it's a lower General file of layer , Open in read-write mode , This file will be copied to upper layer . If it is opened in read-only mode , You don't need to . Then according to the final upper Layer or lower Layer of the directory entry of the corresponding file d_flags Field determines whether further calls are needed d_select_inode() function . If not, return upper Layer or lower Layer corresponding file inode

原网站

版权声明
本文为[LYSnowy]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/204/202207230743445221.html