You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

180 lines
5.1 KiB

autofs-5.0.7 - fix file descriptor leak when reloading the daemon
From: Leonardo Chiquitto <leonardo.lists@gmail.com>
A customer reported that AutoFS may leak file descriptors when some
maps are modified and the daemon reloaded. I'm able to reproduce the
problem on 5.0.7 by following these steps:
1. Configure a simple direct mount:
# cat /etc/auto.master
/- /etc/auto.direct
# cat /etc/auto.direct
/nfs server:/nfs
2. Start the automounter and do NOT trigger the mount
3. Replace /etc/auto.direct with:
# cat /etc/auto.direct
/nfs/1 server:/nfs
/nfs/2 server:/nfs
4. Reload:
# kill -HUP $(pidof automount)
>From now on, every reload will leak a file descriptor:
# ls -la /proc/$(pidof automount)/fd | grep /nfs
lr-x------ 1 root root 64 Aug 14 22:08 11 -> /nfs
lr-x------ 1 root root 64 Aug 14 22:08 12 -> /nfs
lr-x------ 1 root root 64 Aug 14 22:08 13 -> /nfs
lr-x------ 1 root root 64 Aug 14 22:08 14 -> /nfs
lr-x------ 1 root root 64 Aug 14 22:08 5 -> /nfs
I've investigated the problem and discovered that the leak happens in
do_umount_autofs_direct():
- edit imk
The same leak is present in umount_autofs_offset() also.
Updated patch to cover that too.
- end edit
int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list
*mnts, struct mapent *me)
{
(...)
if (me->ioctlfd != -1) {
if (tree_is_mounted(mnts, me->key, MNTS_REAL)) {
error(ap->logopt,
"attempt to umount busy direct mount %s",
me->key);
return 1;
}
ioctlfd = me->ioctlfd;
} else // ioctlfd == -1
ops->open(ap->logopt, &ioctlfd, me->dev, me->key); <= we open it here
if (ioctlfd >= 0) {
unsigned int status = 1;
rv = ops->askumount(ap->logopt, ioctlfd, &status);
/// at this point, rv == 0 and status == 0
if (rv) {
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
error(ap->logopt, "ioctl failed: %s", estr);
return 1;
} else if (!status) {
/// at this point, ap->state == ST_READMAP
if (ap->state != ST_SHUTDOWN_FORCE) {
error(ap->logopt,
"ask umount returned busy for %s",
me->key);
return 1; <= we return here, without closing the fd
} else {
me->ioctlfd = -1;
ops->catatonic(ap->logopt, ioctlfd);
ops->close(ap->logopt, ioctlfd);
goto force_umount;
}
(...)
---
CHANGELOG | 1 +
daemon/direct.c | 19 ++++++++++++++++---
2 files changed, 17 insertions(+), 3 deletions(-)
diff --git a/CHANGELOG b/CHANGELOG
index 46ef335..a7ed212 100644
--- a/CHANGELOG
+++ b/CHANGELOG
@@ -30,6 +30,7 @@
- workaround missing GNU versionsort extension.
- dont fail on master map self include.
- fix wildcard multi map regression.
+- fix file descriptor leak when reloading the daemon.
25/07/2012 autofs-5.0.7
=======================
diff --git a/daemon/direct.c b/daemon/direct.c
index 3e09c5d..228a666 100644
--- a/daemon/direct.c
+++ b/daemon/direct.c
@@ -86,7 +86,8 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru
{
struct ioctl_ops *ops = get_ioctl_ops();
char buf[MAX_ERR_BUF];
- int ioctlfd, rv, left, retries;
+ int ioctlfd = -1, rv, left, retries;
+ int opened = 0;
left = umount_multi(ap, me->key, 0);
if (left) {
@@ -103,8 +104,10 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru
return 1;
}
ioctlfd = me->ioctlfd;
- } else
+ } else {
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
+ opened = 1;
+ }
if (ioctlfd >= 0) {
unsigned int status = 1;
@@ -113,12 +116,16 @@ int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, stru
if (rv) {
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
error(ap->logopt, "ioctl failed: %s", estr);
+ if (opened && ioctlfd != -1)
+ ops->close(ap->logopt, ioctlfd);
return 1;
} else if (!status) {
if (ap->state != ST_SHUTDOWN_FORCE) {
error(ap->logopt,
"ask umount returned busy for %s",
me->key);
+ if (opened && ioctlfd != -1)
+ ops->close(ap->logopt, ioctlfd);
return 1;
} else {
me->ioctlfd = -1;
@@ -536,7 +543,8 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
{
struct ioctl_ops *ops = get_ioctl_ops();
char buf[MAX_ERR_BUF];
- int ioctlfd, rv = 1, retries;
+ int ioctlfd = -1, rv = 1, retries;
+ int opened = 0;
if (me->ioctlfd != -1) {
if (is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL)) {
@@ -554,6 +562,7 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
return 0;
}
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
+ opened = 1;
}
if (ioctlfd >= 0) {
@@ -563,6 +572,8 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
if (rv) {
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
logerr("ioctl failed: %s", estr);
+ if (opened && ioctlfd != -1)
+ ops->close(ap->logopt, ioctlfd);
return 1;
} else if (!status) {
if (ap->state != ST_SHUTDOWN_FORCE) {
@@ -570,6 +581,8 @@ int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
error(ap->logopt,
"ask umount returned busy for %s",
me->key);
+ if (opened && ioctlfd != -1)
+ ops->close(ap->logopt, ioctlfd);
return 1;
} else {
me->ioctlfd = -1;