Skip to content

Commit 433f9d7

Browse files
raven-aubrauner
authored andcommitted
autofs: add per dentry expire timeout
Add ability to set per-dentry mount expire timeout to autofs. There are two fairly well known automounter map formats, the autofs format and the amd format (more or less System V and Berkley). Some time ago Linux autofs added an amd map format parser that implemented a fair amount of the amd functionality. This was done within the autofs infrastructure and some functionality wasn't implemented because it either didn't make sense or required extra kernel changes. The idea was to restrict changes to be within the existing autofs functionality as much as possible and leave changes with a wider scope to be considered later. One of these changes is implementing the amd options: 1) "unmount", expire this mount according to a timeout (same as the current autofs default). 2) "nounmount", don't expire this mount (same as setting the autofs timeout to 0 except only for this specific mount) . 3) "utimeout=<seconds>", expire this mount using the specified timeout (again same as setting the autofs timeout but only for this mount). To implement these options per-dentry expire timeouts need to be implemented for autofs indirect mounts. This is because all map keys (mounts) for autofs indirect mounts use an expire timeout stored in the autofs mount super block info. structure and all indirect mounts use the same expire timeout. Now I have a request to add the "nounmount" option so I need to add the per-dentry expire handling to the kernel implementation to do this. The implementation uses the trailing path component to identify the mount (and is also used as the autofs map key) which is passed in the autofs_dev_ioctl structure path field. The expire timeout is passed in autofs_dev_ioctl timeout field (well, of the timeout union). If the passed in timeout is equal to -1 the per-dentry timeout and flag are cleared providing for the "unmount" option. If the timeout is greater than or equal to 0 the timeout is set to the value and the flag is also set. If the dentry timeout is 0 the dentry will not expire by timeout which enables the implementation of the "nounmount" option for the specific mount. When the dentry timeout is greater than zero it allows for the implementation of the "utimeout=<seconds>" option. Signed-off-by: Ian Kent <[email protected]> Link: https://lore.kernel.org/r/[email protected] Signed-off-by: Christian Brauner <[email protected]>
1 parent 122381a commit 433f9d7

File tree

5 files changed

+104
-8
lines changed

5 files changed

+104
-8
lines changed

fs/autofs/autofs_i.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ struct autofs_info {
6262
struct list_head expiring;
6363

6464
struct autofs_sb_info *sbi;
65+
unsigned long exp_timeout;
6566
unsigned long last_used;
6667
int count;
6768

@@ -81,6 +82,9 @@ struct autofs_info {
8182
*/
8283
#define AUTOFS_INF_PENDING (1<<2) /* dentry pending mount */
8384

85+
#define AUTOFS_INF_EXPIRE_SET (1<<3) /* per-dentry expire timeout set for
86+
this mount point.
87+
*/
8488
struct autofs_wait_queue {
8589
wait_queue_head_t queue;
8690
struct autofs_wait_queue *next;

fs/autofs/dev-ioctl.c

Lines changed: 92 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,13 @@ static int validate_dev_ioctl(int cmd, struct autofs_dev_ioctl *param)
128128
goto out;
129129
}
130130

131+
/* Setting the per-dentry expire timeout requires a trailing
132+
* path component, ie. no '/', so invert the logic of the
133+
* check_name() return for AUTOFS_DEV_IOCTL_TIMEOUT_CMD.
134+
*/
131135
err = check_name(param->path);
136+
if (cmd == AUTOFS_DEV_IOCTL_TIMEOUT_CMD)
137+
err = err ? 0 : -EINVAL;
132138
if (err) {
133139
pr_warn("invalid path supplied for cmd(0x%08x)\n",
134140
cmd);
@@ -396,16 +402,97 @@ static int autofs_dev_ioctl_catatonic(struct file *fp,
396402
return 0;
397403
}
398404

399-
/* Set the autofs mount timeout */
405+
/*
406+
* Set the autofs mount expire timeout.
407+
*
408+
* There are two places an expire timeout can be set, in the autofs
409+
* super block info. (this is all that's needed for direct and offset
410+
* mounts because there's a distinct mount corresponding to each of
411+
* these) and per-dentry within within the dentry info. If a per-dentry
412+
* timeout is set it will override the expire timeout set in the parent
413+
* autofs super block info.
414+
*
415+
* If setting the autofs super block expire timeout the autofs_dev_ioctl
416+
* size field will be equal to the autofs_dev_ioctl structure size. If
417+
* setting the per-dentry expire timeout the mount point name is passed
418+
* in the autofs_dev_ioctl path field and the size field updated to
419+
* reflect this.
420+
*
421+
* Setting the autofs mount expire timeout sets the timeout in the super
422+
* block info. struct. Setting the per-dentry timeout does a little more.
423+
* If the timeout is equal to -1 the per-dentry timeout (and flag) is
424+
* cleared which reverts to using the super block timeout, otherwise if
425+
* timeout is 0 the timeout is set to this value and the flag is left
426+
* set which disables expiration for the mount point, lastly the flag
427+
* and the timeout are set enabling the dentry to use this timeout.
428+
*/
400429
static int autofs_dev_ioctl_timeout(struct file *fp,
401430
struct autofs_sb_info *sbi,
402431
struct autofs_dev_ioctl *param)
403432
{
404-
unsigned long timeout;
433+
unsigned long timeout = param->timeout.timeout;
434+
435+
/* If setting the expire timeout for an individual indirect
436+
* mount point dentry the mount trailing component path is
437+
* placed in param->path and param->size adjusted to account
438+
* for it otherwise param->size it is set to the structure
439+
* size.
440+
*/
441+
if (param->size == AUTOFS_DEV_IOCTL_SIZE) {
442+
param->timeout.timeout = sbi->exp_timeout / HZ;
443+
sbi->exp_timeout = timeout * HZ;
444+
} else {
445+
struct dentry *base = fp->f_path.dentry;
446+
struct inode *inode = base->d_inode;
447+
int path_len = param->size - AUTOFS_DEV_IOCTL_SIZE - 1;
448+
struct dentry *dentry;
449+
struct autofs_info *ino;
450+
451+
if (!autofs_type_indirect(sbi->type))
452+
return -EINVAL;
453+
454+
/* An expire timeout greater than the superblock timeout
455+
* could be a problem at shutdown but the super block
456+
* timeout itself can change so all we can really do is
457+
* warn the user.
458+
*/
459+
if (timeout >= sbi->exp_timeout)
460+
pr_warn("per-mount expire timeout is greater than "
461+
"the parent autofs mount timeout which could "
462+
"prevent shutdown\n");
463+
464+
inode_lock_shared(inode);
465+
dentry = try_lookup_one_len(param->path, base, path_len);
466+
inode_unlock_shared(inode);
467+
if (IS_ERR_OR_NULL(dentry))
468+
return dentry ? PTR_ERR(dentry) : -ENOENT;
469+
ino = autofs_dentry_ino(dentry);
470+
if (!ino) {
471+
dput(dentry);
472+
return -ENOENT;
473+
}
474+
475+
if (ino->exp_timeout && ino->flags & AUTOFS_INF_EXPIRE_SET)
476+
param->timeout.timeout = ino->exp_timeout / HZ;
477+
else
478+
param->timeout.timeout = sbi->exp_timeout / HZ;
479+
480+
if (timeout == -1) {
481+
/* Revert to using the super block timeout */
482+
ino->flags &= ~AUTOFS_INF_EXPIRE_SET;
483+
ino->exp_timeout = 0;
484+
} else {
485+
/* Set the dentry expire flag and timeout.
486+
*
487+
* If timeout is 0 it will prevent the expire
488+
* of this particular automount.
489+
*/
490+
ino->flags |= AUTOFS_INF_EXPIRE_SET;
491+
ino->exp_timeout = timeout * HZ;
492+
}
493+
dput(dentry);
494+
}
405495

406-
timeout = param->timeout.timeout;
407-
param->timeout.timeout = sbi->exp_timeout / HZ;
408-
sbi->exp_timeout = timeout * HZ;
409496
return 0;
410497
}
411498

fs/autofs/expire.c

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -429,8 +429,6 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
429429
if (!root)
430430
return NULL;
431431

432-
timeout = sbi->exp_timeout;
433-
434432
dentry = NULL;
435433
while ((dentry = get_next_positive_subdir(dentry, root))) {
436434
spin_lock(&sbi->fs_lock);
@@ -441,6 +439,11 @@ static struct dentry *autofs_expire_indirect(struct super_block *sb,
441439
}
442440
spin_unlock(&sbi->fs_lock);
443441

442+
if (ino->flags & AUTOFS_INF_EXPIRE_SET)
443+
timeout = ino->exp_timeout;
444+
else
445+
timeout = sbi->exp_timeout;
446+
444447
expired = should_expire(dentry, mnt, timeout, how);
445448
if (!expired)
446449
continue;

fs/autofs/inode.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ struct autofs_info *autofs_new_ino(struct autofs_sb_info *sbi)
1919
INIT_LIST_HEAD(&ino->expiring);
2020
ino->last_used = jiffies;
2121
ino->sbi = sbi;
22+
ino->exp_timeout = -1;
2223
ino->count = 1;
2324
}
2425
return ino;
@@ -28,6 +29,7 @@ void autofs_clean_ino(struct autofs_info *ino)
2829
{
2930
ino->uid = GLOBAL_ROOT_UID;
3031
ino->gid = GLOBAL_ROOT_GID;
32+
ino->exp_timeout = -1;
3133
ino->last_used = jiffies;
3234
}
3335

include/uapi/linux/auto_fs.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
#define AUTOFS_MIN_PROTO_VERSION 3
2424
#define AUTOFS_MAX_PROTO_VERSION 5
2525

26-
#define AUTOFS_PROTO_SUBVERSION 5
26+
#define AUTOFS_PROTO_SUBVERSION 6
2727

2828
/*
2929
* The wait_queue_token (autofs_wqt_t) is part of a structure which is passed

0 commit comments

Comments
 (0)