autofs-5.1.6 - make bind mounts propagation slave by default From: Ian Kent Make setting mount propagation on bind mounts mandatory with a default of propagation slave. When using multi-mounts that have bind mounts the bind mount will have the same properties as its parent which is commonly propagation shared. And if the mount target is also propagation shared this can lead to a deadlock when attempting to access the offset mounts. When this happens an unwanted offset mount is propagated back to the target file system resulting in a deadlock since the automount target is itself an (unwanted) automount trigger. This problem has been present much longer than I originally thought, perhaps since mount propagation was introduced into the kernel, so explicitly setting bind mount propagation is the sensible thing to do. Signed-off-by: Ian Kent --- CHANGELOG | 3 +++ include/automount.h | 9 +++++---- lib/master_parse.y | 29 +++++++++++++++++------------ lib/master_tok.l | 1 + man/auto.master.5.in | 19 ++++++++++--------- modules/mount_bind.c | 40 ++++++++++++++++++++++------------------ 6 files changed, 58 insertions(+), 43 deletions(-) diff --git a/CHANGELOG b/CHANGELOG index 847a9b01..f5a1a0e3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,3 +1,6 @@ +xx/xx/2020 autofs-5.1.7 +- make bind mounts propagation slave by default. + 07/10/2019 autofs-5.1.6 - support strictexpire mount option. - fix hesiod string check in master_parse(). diff --git a/include/automount.h b/include/automount.h index 4fd0ba96..fe9c7fee 100644 --- a/include/automount.h +++ b/include/automount.h @@ -551,14 +551,15 @@ struct kernel_mod_version { #define MOUNT_FLAG_AMD_CACHE_ALL 0x0080 /* Set mount propagation for bind mounts */ -#define MOUNT_FLAG_SLAVE 0x0100 -#define MOUNT_FLAG_PRIVATE 0x0200 +#define MOUNT_FLAG_SHARED 0x0100 +#define MOUNT_FLAG_SLAVE 0x0200 +#define MOUNT_FLAG_PRIVATE 0x0400 /* Use strict expire semantics if requested and kernel supports it */ -#define MOUNT_FLAG_STRICTEXPIRE 0x0400 +#define MOUNT_FLAG_STRICTEXPIRE 0x0800 /* Indicator for applications to ignore the mount entry */ -#define MOUNT_FLAG_IGNORE 0x0800 +#define MOUNT_FLAG_IGNORE 0x1000 struct autofs_point { pthread_t thid; diff --git a/lib/master_parse.y b/lib/master_parse.y index f817f739..08e44b57 100644 --- a/lib/master_parse.y +++ b/lib/master_parse.y @@ -59,8 +59,6 @@ static long timeout; static long negative_timeout; static unsigned symlnk; static unsigned strictexpire; -static unsigned slave; -static unsigned private; static unsigned nobind; static unsigned ghost; extern unsigned global_selection_options; @@ -72,6 +70,14 @@ static int tmp_argc; static char **local_argv; static int local_argc; +#define PROPAGATION_SHARED MOUNT_FLAG_SHARED +#define PROPAGATION_SLAVE MOUNT_FLAG_SLAVE +#define PROPAGATION_PRIVATE MOUNT_FLAG_PRIVATE +#define PROPAGATION_MASK (MOUNT_FLAG_SHARED | \ + MOUNT_FLAG_SLAVE | \ + MOUNT_FLAG_PRIVATE) +static unsigned int propagation; + static char errstr[MAX_ERR_LEN]; static unsigned int verbose; @@ -106,7 +112,7 @@ static int master_fprintf(FILE *, char *, ...); %token MAP %token OPT_TIMEOUT OPT_NTIMEOUT OPT_NOBIND OPT_NOGHOST OPT_GHOST OPT_VERBOSE %token OPT_DEBUG OPT_RANDOM OPT_USE_WEIGHT OPT_SYMLINK OPT_MODE -%token OPT_STRICTEXPIRE OPT_SLAVE OPT_PRIVATE +%token OPT_STRICTEXPIRE OPT_SHARED OPT_SLAVE OPT_PRIVATE %token COLON COMMA NL DDASH %type map %type options @@ -208,6 +214,7 @@ line: | PATH OPT_TIMEOUT { master_notify($1); YYABORT; } | PATH OPT_SYMLINK { master_notify($1); YYABORT; } | PATH OPT_STRICTEXPIRE { master_notify($1); YYABORT; } + | PATH OPT_SHARED { master_notify($1); YYABORT; } | PATH OPT_SLAVE { master_notify($1); YYABORT; } | PATH OPT_PRIVATE { master_notify($1); YYABORT; } | PATH OPT_NOBIND { master_notify($1); YYABORT; } @@ -622,8 +629,9 @@ daemon_option: OPT_TIMEOUT NUMBER { timeout = $2; } | OPT_NTIMEOUT NUMBER { negative_timeout = $2; } | OPT_SYMLINK { symlnk = 1; } | OPT_STRICTEXPIRE { strictexpire = 1; } - | OPT_SLAVE { slave = 1; } - | OPT_PRIVATE { private = 1; } + | OPT_SHARED { propagation = PROPAGATION_SHARED; } + | OPT_SLAVE { propagation = PROPAGATION_SLAVE; } + | OPT_PRIVATE { propagation = PROPAGATION_PRIVATE; } | OPT_NOBIND { nobind = 1; } | OPT_NOGHOST { ghost = 0; } | OPT_GHOST { ghost = 1; } @@ -697,8 +705,7 @@ static void local_init_vars(void) negative_timeout = 0; symlnk = 0; strictexpire = 0; - slave = 0; - private = 0; + propagation = PROPAGATION_SLAVE; nobind = 0; ghost = defaults_get_browse_mode(); random_selection = global_selection_options & MOUNT_FLAG_RANDOM_SELECT; @@ -888,7 +895,6 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne ghost = 1; } - if (!entry->ap) { ret = master_add_autofs_point(entry, logopt, nobind, ghost, 0); if (!ret) { @@ -899,6 +905,9 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne return 0; } } + entry->ap->flags &= ~(PROPAGATION_MASK); + entry->ap->flags |= propagation; + if (random_selection) entry->ap->flags |= MOUNT_FLAG_RANDOM_SELECT; if (use_weight) @@ -907,10 +916,6 @@ int master_parse_entry(const char *buffer, unsigned int default_timeout, unsigne entry->ap->flags |= MOUNT_FLAG_SYMLINK; if (strictexpire) entry->ap->flags |= MOUNT_FLAG_STRICTEXPIRE; - if (slave) - entry->ap->flags |= MOUNT_FLAG_SLAVE; - if (private) - entry->ap->flags |= MOUNT_FLAG_PRIVATE; if (negative_timeout) entry->ap->negative_timeout = negative_timeout; if (mode && mode < LONG_MAX) diff --git a/lib/master_tok.l b/lib/master_tok.l index 7486710b..87a6b958 100644 --- a/lib/master_tok.l +++ b/lib/master_tok.l @@ -389,6 +389,7 @@ MODE (--mode{OPTWS}|--mode{OPTWS}={OPTWS}) -?symlink { return(OPT_SYMLINK); } -?nobind { return(OPT_NOBIND); } -?nobrowse { return(OPT_NOGHOST); } + -?shared { return(OPT_SHARED); } -?slave { return(OPT_SLAVE); } -?private { return(OPT_PRIVATE); } -?strictexpire { return(OPT_STRICTEXPIRE); } diff --git a/man/auto.master.5.in b/man/auto.master.5.in index 6e510a59..2a0b672a 100644 --- a/man/auto.master.5.in +++ b/man/auto.master.5.in @@ -208,17 +208,18 @@ applications scanning the mount tree. Note that this doesn't completely resolve the problem of expired automounts being immediately re-mounted due to application accesses triggered by the expire itself. .TP -.I slave \fPor\fI private +.I slave\fP, \fIprivate\fP or \fIshared\fP This option allows mount propagation of bind mounts to be set to -either \fIslave\fP or \fIprivate\fP. This option may be needed when using -multi-mounts that have bind mounts that bind to a file system that is -propagation shared. This is because the bind mount will have the same -properties as its target which causes problems for offset mounts. When -this happens an unwanted offset mount is propagated back to the target -file system resulting in a deadlock when attempting to access the offset. +\fIslave\fP, \fIprivate\fP or \fIshared\fP. This option defaults to +\fIslave\fP if no option is given. When using multi-mounts that have +bind mounts the bind mount will have the same properties as its parent +which is commonly propagation \fIshared\fP. And if the mount target is +also propagation \fIshared\fP this can lead to a deadlock when attempting +to access the offset mounts. When this happens an unwanted offset mount +is propagated back to the target file system resulting in a deadlock +since the automount target is itself an (unwanted) automount trigger. This option is an autofs pseudo mount option that can be used in the -master map only. By default, bind mounts will inherit the mount propagation -of the target file system. +master map only. .TP .I "\-r, \-\-random-multimount-selection" Enables the use of random selection when choosing a host from a diff --git a/modules/mount_bind.c b/modules/mount_bind.c index 9cba0d7a..5253501c 100644 --- a/modules/mount_bind.c +++ b/modules/mount_bind.c @@ -153,6 +153,7 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int if (!symlnk && bind_works) { int status, existed = 1; + int flags; debug(ap->logopt, MODPREFIX "calling mkdir_path %s", fullpath); @@ -190,24 +191,27 @@ int mount_mount(struct autofs_point *ap, const char *root, const char *name, int what, fstype, fullpath); } - if (ap->flags & (MOUNT_FLAG_SLAVE | MOUNT_FLAG_PRIVATE)) { - int flags = MS_SLAVE; - - if (ap->flags & MOUNT_FLAG_PRIVATE) - flags = MS_PRIVATE; - - /* The bind mount has succeeded but if the target - * mount is propagation shared propagation of child - * mounts (autofs offset mounts for example) back to - * the target of the bind mount must be avoided or - * autofs trigger mounts will deadlock. - */ - err = mount(NULL, fullpath, NULL, flags, NULL); - if (err) { - warn(ap->logopt, - "failed to set propagation for %s", - fullpath, root); - } + /* The bind mount has succeeded, now set the mount propagation. + * + * The default is propagation shared, change it if the master + * map entry has a different option specified. + */ + flags = MS_SLAVE; + if (ap->flags & MOUNT_FLAG_PRIVATE) + flags = MS_PRIVATE; + else if (ap->flags & MOUNT_FLAG_SHARED) + flags = MS_SHARED; + + /* Note: If the parent mount is propagation shared propagation + * of child mounts (autofs offset mounts for example) back to + * the target of the bind mount can happen in some cases and + * must be avoided or autofs trigger mounts will deadlock. + */ + err = mount(NULL, fullpath, NULL, flags, NULL); + if (err) { + warn(ap->logopt, + "failed to set propagation for %s", + fullpath, root); } return 0;