mirror of
				git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
				synced 2025-09-04 20:19:47 +08:00 
			
		
		
		
	 0be0bfd2de
			
		
	
	
		0be0bfd2de
		
	
	
	
	
		
			
			Once upon a time, commit2cac0c00a6("ovl: get exclusive ownership on upper/work dirs") in v4.13 added some sanity checks on overlayfs layers. This change caused a docker regression. The root cause was mount leaks by docker, which as far as I know, still exist. To mitigate the regression, commit85fdee1eef("ovl: fix regression caused by exclusive upper/work dir protection") in v4.14 turned the mount errors into warnings for the default index=off configuration. Recently, commit146d62e5a5("ovl: detect overlapping layers") in v5.2, re-introduced exclusive upper/work dir checks regardless of index=off configuration. This changes the status quo and mount leak related bug reports have started to re-surface. Restore the status quo to fix the regressions. To clarify, index=off does NOT relax overlapping layers check for this ovelayfs mount. index=off only relaxes exclusive upper/work dir checks with another overlayfs mount. To cover the part of overlapping layers detection that used the exclusive upper/work dir checks to detect overlap with self upper/work dir, add a trap also on the work base dir. Link: https://github.com/moby/moby/issues/34672 Link: https://lore.kernel.org/linux-fsdevel/20171006121405.GA32700@veci.piliscsaba.szeredi.hu/ Link: https://github.com/containers/libpod/issues/3540 Fixes:146d62e5a5("ovl: detect overlapping layers") Cc: <stable@vger.kernel.org> # v4.19+ Signed-off-by: Amir Goldstein <amir73il@gmail.com> Tested-by: Colin Walters <walters@verbum.org> Signed-off-by: Miklos Szeredi <mszeredi@redhat.com>
		
			
				
	
	
		
			120 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			120 lines
		
	
	
		
			2.7 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /* SPDX-License-Identifier: GPL-2.0-only */
 | |
| /*
 | |
|  *
 | |
|  * Copyright (C) 2011 Novell Inc.
 | |
|  * Copyright (C) 2016 Red Hat, Inc.
 | |
|  */
 | |
| 
 | |
| struct ovl_config {
 | |
| 	char *lowerdir;
 | |
| 	char *upperdir;
 | |
| 	char *workdir;
 | |
| 	bool default_permissions;
 | |
| 	bool redirect_dir;
 | |
| 	bool redirect_follow;
 | |
| 	const char *redirect_mode;
 | |
| 	bool index;
 | |
| 	bool nfs_export;
 | |
| 	int xino;
 | |
| 	bool metacopy;
 | |
| };
 | |
| 
 | |
| struct ovl_sb {
 | |
| 	struct super_block *sb;
 | |
| 	dev_t pseudo_dev;
 | |
| };
 | |
| 
 | |
| struct ovl_layer {
 | |
| 	struct vfsmount *mnt;
 | |
| 	/* Trap in ovl inode cache */
 | |
| 	struct inode *trap;
 | |
| 	struct ovl_sb *fs;
 | |
| 	/* Index of this layer in fs root (upper idx == 0) */
 | |
| 	int idx;
 | |
| 	/* One fsid per unique underlying sb (upper fsid == 0) */
 | |
| 	int fsid;
 | |
| };
 | |
| 
 | |
| struct ovl_path {
 | |
| 	struct ovl_layer *layer;
 | |
| 	struct dentry *dentry;
 | |
| };
 | |
| 
 | |
| /* private information held for overlayfs's superblock */
 | |
| struct ovl_fs {
 | |
| 	struct vfsmount *upper_mnt;
 | |
| 	unsigned int numlower;
 | |
| 	/* Number of unique lower sb that differ from upper sb */
 | |
| 	unsigned int numlowerfs;
 | |
| 	struct ovl_layer *lower_layers;
 | |
| 	struct ovl_sb *lower_fs;
 | |
| 	/* workbasedir is the path at workdir= mount option */
 | |
| 	struct dentry *workbasedir;
 | |
| 	/* workdir is the 'work' directory under workbasedir */
 | |
| 	struct dentry *workdir;
 | |
| 	/* index directory listing overlay inodes by origin file handle */
 | |
| 	struct dentry *indexdir;
 | |
| 	long namelen;
 | |
| 	/* pathnames of lower and upper dirs, for show_options */
 | |
| 	struct ovl_config config;
 | |
| 	/* creds of process who forced instantiation of super block */
 | |
| 	const struct cred *creator_cred;
 | |
| 	bool tmpfile;
 | |
| 	bool noxattr;
 | |
| 	/* Did we take the inuse lock? */
 | |
| 	bool upperdir_locked;
 | |
| 	bool workdir_locked;
 | |
| 	/* Traps in ovl inode cache */
 | |
| 	struct inode *upperdir_trap;
 | |
| 	struct inode *workbasedir_trap;
 | |
| 	struct inode *workdir_trap;
 | |
| 	struct inode *indexdir_trap;
 | |
| 	/* Inode numbers in all layers do not use the high xino_bits */
 | |
| 	unsigned int xino_bits;
 | |
| };
 | |
| 
 | |
| /* private information held for every overlayfs dentry */
 | |
| struct ovl_entry {
 | |
| 	union {
 | |
| 		struct {
 | |
| 			unsigned long flags;
 | |
| 		};
 | |
| 		struct rcu_head rcu;
 | |
| 	};
 | |
| 	unsigned numlower;
 | |
| 	struct ovl_path lowerstack[];
 | |
| };
 | |
| 
 | |
| struct ovl_entry *ovl_alloc_entry(unsigned int numlower);
 | |
| 
 | |
| static inline struct ovl_entry *OVL_E(struct dentry *dentry)
 | |
| {
 | |
| 	return (struct ovl_entry *) dentry->d_fsdata;
 | |
| }
 | |
| 
 | |
| struct ovl_inode {
 | |
| 	union {
 | |
| 		struct ovl_dir_cache *cache;	/* directory */
 | |
| 		struct inode *lowerdata;	/* regular file */
 | |
| 	};
 | |
| 	const char *redirect;
 | |
| 	u64 version;
 | |
| 	unsigned long flags;
 | |
| 	struct inode vfs_inode;
 | |
| 	struct dentry *__upperdentry;
 | |
| 	struct inode *lower;
 | |
| 
 | |
| 	/* synchronize copy up and more */
 | |
| 	struct mutex lock;
 | |
| };
 | |
| 
 | |
| static inline struct ovl_inode *OVL_I(struct inode *inode)
 | |
| {
 | |
| 	return container_of(inode, struct ovl_inode, vfs_inode);
 | |
| }
 | |
| 
 | |
| static inline struct dentry *ovl_upperdentry_dereference(struct ovl_inode *oi)
 | |
| {
 | |
| 	return READ_ONCE(oi->__upperdentry);
 | |
| }
 |