|
Packit |
8480eb |
/* ----------------------------------------------------------------------- *
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* direct.c - Linux automounter direct mount handling
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Copyright 1997 Transmeta Corporation - All Rights Reserved
|
|
Packit |
8480eb |
* Copyright 1999-2000 Jeremy Fitzhardinge <jeremy@goop.org>
|
|
Packit |
8480eb |
* Copyright 2001-2005 Ian Kent <raven@themaw.net>
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* This program is free software; you can redistribute it and/or modify
|
|
Packit |
8480eb |
* it under the terms of the GNU General Public License as published by
|
|
Packit |
8480eb |
* the Free Software Foundation, Inc., 675 Mass Ave, Cambridge MA 02139,
|
|
Packit |
8480eb |
* USA; either version 2 of the License, or (at your option) any later
|
|
Packit |
8480eb |
* version.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* This program is distributed in the hope that it will be useful,
|
|
Packit |
8480eb |
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit |
8480eb |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
Packit |
8480eb |
* GNU General Public License for more details.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* ----------------------------------------------------------------------- */
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#include <dirent.h>
|
|
Packit |
8480eb |
#include <libgen.h>
|
|
Packit |
8480eb |
#include <signal.h>
|
|
Packit |
8480eb |
#include <stdio.h>
|
|
Packit |
8480eb |
#include <stdlib.h>
|
|
Packit |
8480eb |
#include <string.h>
|
|
Packit |
8480eb |
#include <sys/ioctl.h>
|
|
Packit |
8480eb |
#include <sys/types.h>
|
|
Packit |
8480eb |
#include <sys/wait.h>
|
|
Packit |
8480eb |
#include <sys/stat.h>
|
|
Packit |
8480eb |
#include <sys/time.h>
|
|
Packit |
8480eb |
#include <sys/poll.h>
|
|
Packit |
8480eb |
#include <sys/mount.h>
|
|
Packit |
8480eb |
#include <sys/vfs.h>
|
|
Packit |
8480eb |
#include <sched.h>
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
#define INCLUDE_PENDING_FUNCTIONS
|
|
Packit |
8480eb |
#include "automount.h"
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Attribute to create detached thread */
|
|
Packit |
8480eb |
extern pthread_attr_t th_attr_detached;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
struct mnt_params {
|
|
Packit |
8480eb |
char *options;
|
|
Packit |
8480eb |
};
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_key_t key_mnt_direct_params;
|
|
Packit |
8480eb |
pthread_key_t key_mnt_offset_params;
|
|
Packit |
8480eb |
pthread_once_t key_mnt_params_once = PTHREAD_ONCE_INIT;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void key_mnt_params_destroy(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mnt_params *mp;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mp = (struct mnt_params *) arg;
|
|
Packit |
8480eb |
if (mp->options)
|
|
Packit |
8480eb |
free(mp->options);
|
|
Packit |
8480eb |
free(mp);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void key_mnt_params_init(void)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
int status;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_key_create(&key_mnt_direct_params, key_mnt_params_destroy);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_key_create(&key_mnt_offset_params, key_mnt_params_destroy);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void mnts_cleanup(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct mnt_list *mnts = (struct mnt_list *) arg;
|
|
Packit |
8480eb |
tree_free_mnt_tree(mnts);
|
|
Packit |
8480eb |
return;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
int do_umount_autofs_direct(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
int ioctlfd = -1, rv, left, retries;
|
|
Packit |
8480eb |
int opened = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
left = umount_multi(ap, me->key, 0);
|
|
Packit |
8480eb |
if (left) {
|
|
Packit |
8480eb |
warn(ap->logopt, "could not unmount %d dirs under %s",
|
|
Packit |
8480eb |
left, me->key);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd != -1) {
|
|
Packit |
8480eb |
if (ap->state == ST_READMAP &&
|
|
Packit Service |
71cd40 |
tree_is_mounted(mnts, me->key, MNTS_REAL)) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"attempt to umount busy direct mount %s",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
|
|
Packit |
8480eb |
opened = 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ioctlfd >= 0) {
|
|
Packit |
8480eb |
unsigned int status = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
rv = ops->askumount(ap->logopt, ioctlfd, &status);
|
|
Packit |
8480eb |
if (rv) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
error(ap->logopt, "ioctl failed: %s", estr);
|
|
Packit |
8480eb |
/* The ioctl failed so this probably won't
|
|
Packit |
8480eb |
* work either but since we opened it here
|
|
Packit |
8480eb |
* try anyway. We should set these catatonic
|
|
Packit |
8480eb |
* too but ....
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (opened)
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
} else if (!status) {
|
|
Packit |
8480eb |
if (ap->state != ST_SHUTDOWN_FORCE) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"ask umount returned busy for %s",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
if (opened)
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
goto force_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"couldn't get ioctl fd for direct mount %s", me->key);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
sched_yield();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
retries = UMOUNT_RETRIES;
|
|
Packit |
8480eb |
while ((rv = umount(me->key)) == -1 && retries--) {
|
|
Packit |
8480eb |
struct timespec tm = {0, 200000000};
|
|
Packit |
8480eb |
if (errno != EBUSY)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
nanosleep(&tm, NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (rv == -1) {
|
|
Packit |
8480eb |
switch (errno) {
|
|
Packit |
8480eb |
case ENOENT:
|
|
Packit |
8480eb |
case EINVAL:
|
|
Packit |
8480eb |
warn(ap->logopt, "mount point %s does not exist",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
case EBUSY:
|
|
Packit |
8480eb |
warn(ap->logopt, "mount point %s is in use", me->key);
|
|
Packit |
8480eb |
if (ap->state == ST_SHUTDOWN_FORCE)
|
|
Packit |
8480eb |
goto force_umount;
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
if (ap->state != ST_READMAP)
|
|
Packit |
8480eb |
set_direct_mount_tree_catatonic(ap, me);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
case ENOTDIR:
|
|
Packit |
8480eb |
error(ap->logopt, "mount point is not a directory");
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
force_umount:
|
|
Packit |
8480eb |
if (rv != 0) {
|
|
Packit |
8480eb |
info(ap->logopt, "forcing umount of direct mount %s", me->key);
|
|
Packit |
8480eb |
rv = umount2(me->key, MNT_DETACH);
|
|
Packit |
8480eb |
} else
|
|
Packit |
8480eb |
info(ap->logopt, "umounted direct mount %s", me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!rv && me->flags & MOUNT_FLAG_DIR_CREATED) {
|
|
Packit |
8480eb |
if (rmdir(me->key) == -1) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
warn(ap->logopt, "failed to remove dir %s: %s",
|
|
Packit |
8480eb |
me->key, estr);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
return rv;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int umount_autofs_direct(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct map_source *map;
|
|
Packit |
8480eb |
struct mapent_cache *nc, *mc;
|
|
Packit Service |
71cd40 |
struct mnt_list *mnts;
|
|
Packit |
8480eb |
struct mapent *me, *ne;
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/");
|
|
Packit Service |
71cd40 |
pthread_cleanup_push(mnts_cleanup, mnts);
|
|
Packit |
8480eb |
nc = ap->entry->master->nc;
|
|
Packit |
8480eb |
cache_readlock(nc);
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, nc);
|
|
Packit |
8480eb |
map = ap->entry->maps;
|
|
Packit |
8480eb |
while (map) {
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, mc);
|
|
Packit |
8480eb |
cache_readlock(mc);
|
|
Packit |
8480eb |
me = cache_enumerate(mc, NULL);
|
|
Packit |
8480eb |
while (me) {
|
|
Packit |
8480eb |
int error;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ne = cache_lookup_distinct(nc, me->key);
|
|
Packit |
8480eb |
if (ne && map->master_line > ne->age) {
|
|
Packit |
8480eb |
me = cache_enumerate(mc, me);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* The daemon is exiting so ...
|
|
Packit |
8480eb |
* If we get a fail here we must make our
|
|
Packit |
8480eb |
* best effort to set the direct mount trigger
|
|
Packit |
8480eb |
* catatonic regardless of the reason for the
|
|
Packit |
8480eb |
* failed umount.
|
|
Packit |
8480eb |
*/
|
|
Packit Service |
71cd40 |
error = do_umount_autofs_direct(ap, mnts, me);
|
|
Packit |
8480eb |
if (!error)
|
|
Packit |
8480eb |
goto done;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->state != ST_READMAP)
|
|
Packit |
8480eb |
set_direct_mount_tree_catatonic(ap, me);
|
|
Packit |
8480eb |
done:
|
|
Packit |
8480eb |
me = cache_enumerate(mc, me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit Service |
71cd40 |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
close(ap->state_pipe[0]);
|
|
Packit |
8480eb |
close(ap->state_pipe[1]);
|
|
Packit |
8480eb |
if (ap->pipefd >= 0)
|
|
Packit |
8480eb |
close(ap->pipefd);
|
|
Packit |
8480eb |
if (ap->kpipefd >= 0) {
|
|
Packit |
8480eb |
close(ap->kpipefd);
|
|
Packit |
8480eb |
ap->kpipefd = -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
static int unlink_mount_tree(struct autofs_point *ap, struct list_head *list)
|
|
Packit |
8480eb |
{
|
|
Packit Service |
71cd40 |
struct list_head *p;
|
|
Packit Service |
71cd40 |
int rv, ret;
|
|
Packit Service |
71cd40 |
pid_t pgrp = getpgrp();
|
|
Packit Service |
71cd40 |
char spgrp[20];
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
sprintf(spgrp, "pgrp=%d", pgrp);
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
ret = 1;
|
|
Packit Service |
71cd40 |
list_for_each(p, list) {
|
|
Packit Service |
71cd40 |
struct mnt_list *mnt;
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
mnt = list_entry(p, struct mnt_list, list);
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
if (strstr(mnt->opts, spgrp))
|
|
Packit Service |
71cd40 |
continue;
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
if (strcmp(mnt->fs_type, "autofs"))
|
|
Packit Service |
71cd40 |
rv = spawn_umount(ap->logopt, "-l", mnt->path, NULL);
|
|
Packit |
8480eb |
else
|
|
Packit Service |
71cd40 |
rv = umount2(mnt->path, MNT_DETACH);
|
|
Packit Service |
71cd40 |
if (rv == -1) {
|
|
Packit Service |
71cd40 |
debug(ap->logopt,
|
|
Packit Service |
71cd40 |
"can't unlink %s from mount tree", mnt->path);
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
switch (errno) {
|
|
Packit Service |
71cd40 |
case EINVAL:
|
|
Packit Service |
71cd40 |
warn(ap->logopt,
|
|
Packit Service |
71cd40 |
"bad superblock or not mounted");
|
|
Packit Service |
71cd40 |
break;
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
case ENOENT:
|
|
Packit Service |
71cd40 |
case EFAULT:
|
|
Packit Service |
71cd40 |
ret = 0;
|
|
Packit Service |
71cd40 |
warn(ap->logopt, "bad path for mount");
|
|
Packit Service |
71cd40 |
break;
|
|
Packit Service |
71cd40 |
}
|
|
Packit Service |
71cd40 |
}
|
|
Packit |
8480eb |
}
|
|
Packit Service |
71cd40 |
return ret;
|
|
Packit Service |
71cd40 |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
static int unlink_active_mounts(struct autofs_point *ap, struct mnt_list *mnts, struct mapent *me)
|
|
Packit Service |
71cd40 |
{
|
|
Packit Service |
71cd40 |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit Service |
71cd40 |
struct list_head list;
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
INIT_LIST_HEAD(&list);
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
if (tree_get_mnt_list(mnts, &list, me->key, 1)) {
|
|
Packit Service |
71cd40 |
if (ap->state == ST_READMAP) {
|
|
Packit |
415782 |
time_t tout = get_exp_timeout(ap, me->source);
|
|
Packit |
415782 |
int save_ioctlfd, ioctlfd;
|
|
Packit |
415782 |
|
|
Packit |
415782 |
save_ioctlfd = ioctlfd = me->ioctlfd;
|
|
Packit |
415782 |
|
|
Packit |
415782 |
if (ioctlfd == -1)
|
|
Packit |
415782 |
ops->open(ap->logopt,
|
|
Packit |
415782 |
&ioctlfd, me->dev, me->key);
|
|
Packit |
415782 |
|
|
Packit |
415782 |
if (ioctlfd < 0) {
|
|
Packit |
415782 |
error(ap->logopt,
|
|
Packit |
415782 |
"failed to create ioctl fd for %s",
|
|
Packit |
415782 |
me->key);
|
|
Packit |
415782 |
return 0;
|
|
Packit |
415782 |
}
|
|
Packit |
415782 |
|
|
Packit |
415782 |
ops->timeout(ap->logopt, ioctlfd, tout);
|
|
Packit |
415782 |
|
|
Packit |
415782 |
if (save_ioctlfd == -1)
|
|
Packit |
415782 |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
415782 |
|
|
Packit |
415782 |
return 0;
|
|
Packit |
415782 |
}
|
|
Packit Service |
71cd40 |
}
|
|
Packit |
415782 |
|
|
Packit Service |
71cd40 |
if (!unlink_mount_tree(ap, &list)) {
|
|
Packit Service |
71cd40 |
debug(ap->logopt,
|
|
Packit Service |
71cd40 |
"already mounted as other than autofs "
|
|
Packit Service |
71cd40 |
"or failed to unlink entry in tree");
|
|
Packit Service |
71cd40 |
return 0;
|
|
Packit Service |
71cd40 |
}
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
return 1;
|
|
Packit Service |
71cd40 |
}
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
int do_mount_autofs_direct(struct autofs_point *ap,
|
|
Packit Service |
71cd40 |
struct mnt_list *mnts, struct mapent *me,
|
|
Packit Service |
71cd40 |
time_t timeout)
|
|
Packit Service |
71cd40 |
{
|
|
Packit Service |
71cd40 |
const char *str_direct = mount_type_str(t_direct);
|
|
Packit Service |
71cd40 |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit Service |
71cd40 |
struct mnt_params *mp;
|
|
Packit Service |
71cd40 |
struct stat st;
|
|
Packit Service |
71cd40 |
int status, ret, ioctlfd;
|
|
Packit Service |
71cd40 |
const char *map_name;
|
|
Packit Service |
71cd40 |
time_t runfreq;
|
|
Packit Service |
71cd40 |
int err;
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
if (timeout) {
|
|
Packit Service |
71cd40 |
/* Calculate the expire run frequency */
|
|
Packit Service |
71cd40 |
runfreq = (timeout + CHECK_RATIO - 1) / CHECK_RATIO;
|
|
Packit Service |
71cd40 |
if (ap->exp_runfreq)
|
|
Packit Service |
71cd40 |
ap->exp_runfreq = min(ap->exp_runfreq, runfreq);
|
|
Packit Service |
71cd40 |
else
|
|
Packit Service |
71cd40 |
ap->exp_runfreq = runfreq;
|
|
Packit Service |
71cd40 |
}
|
|
Packit Service |
71cd40 |
|
|
Packit Service |
71cd40 |
if (ops->version && !do_force_unlink) {
|
|
Packit Service |
71cd40 |
ap->flags |= MOUNT_FLAG_REMOUNT;
|
|
Packit Service |
71cd40 |
ret = try_remount(ap, me, t_direct);
|
|
Packit Service |
71cd40 |
ap->flags &= ~MOUNT_FLAG_REMOUNT;
|
|
Packit Service |
71cd40 |
if (ret == 1)
|
|
Packit Service |
71cd40 |
return 0;
|
|
Packit Service |
71cd40 |
if (ret == 0)
|
|
Packit Service |
71cd40 |
return -1;
|
|
Packit Service |
71cd40 |
} else {
|
|
Packit Service |
71cd40 |
/*
|
|
Packit Service |
71cd40 |
* A return of 0 indicates we're re-reading the map.
|
|
Packit Service |
71cd40 |
* A return of 1 indicates we successfully unlinked
|
|
Packit Service |
71cd40 |
* the mount tree if there was one. A return of -1
|
|
Packit Service |
71cd40 |
* inducates we failed to unlink the mount tree so
|
|
Packit Service |
71cd40 |
* we have to return a failure.
|
|
Packit Service |
71cd40 |
*/
|
|
Packit Service |
71cd40 |
ret = unlink_active_mounts(ap, mnts, me);
|
|
Packit Service |
71cd40 |
if (ret == -1 || ret == 0)
|
|
Packit Service |
71cd40 |
return ret;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd != -1) {
|
|
Packit |
8480eb |
error(ap->logopt, "active direct mount %s", me->key);
|
|
Packit |
8480eb |
return -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_once(&key_mnt_params_once, key_mnt_params_init);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mp = pthread_getspecific(key_mnt_direct_params);
|
|
Packit |
8480eb |
if (!mp) {
|
|
Packit |
8480eb |
mp = (struct mnt_params *) malloc(sizeof(struct mnt_params));
|
|
Packit |
8480eb |
if (!mp) {
|
|
Packit |
8480eb |
crit(ap->logopt,
|
|
Packit |
8480eb |
"mnt_params value create failed for direct mount %s",
|
|
Packit |
8480eb |
ap->path);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
mp->options = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_setspecific(key_mnt_direct_params, mp);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
free(mp);
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!mp->options) {
|
|
Packit Service |
71cd40 |
mp->options = make_options_string(ap->path, ap->kpipefd, str_direct);
|
|
Packit |
8480eb |
if (!mp->options)
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* In case the directory doesn't exist, try to mkdir it */
|
|
Packit Service |
2f7109 |
if (mkdir_path(me->key, mp_mode) < 0) {
|
|
Packit |
8480eb |
if (errno != EEXIST && errno != EROFS) {
|
|
Packit |
8480eb |
crit(ap->logopt,
|
|
Packit |
8480eb |
"failed to create mount directory %s", me->key);
|
|
Packit |
8480eb |
return -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
/* If we recieve an error, and it's EEXIST or EROFS we know
|
|
Packit |
8480eb |
the directory was not created. */
|
|
Packit |
8480eb |
me->flags &= ~MOUNT_FLAG_DIR_CREATED;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* No errors so the directory was successfully created */
|
|
Packit |
8480eb |
me->flags |= MOUNT_FLAG_DIR_CREATED;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
map_name = me->mc->map->argv[0];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = mount(map_name, me->key, "autofs", MS_MGC_VAL, mp->options);
|
|
Packit |
8480eb |
if (ret) {
|
|
Packit |
8480eb |
crit(ap->logopt, "failed to mount autofs path %s", me->key);
|
|
Packit |
8480eb |
goto out_err;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = stat(me->key, &st);
|
|
Packit |
8480eb |
if (ret == -1) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"failed to stat direct mount trigger %s", me->key);
|
|
Packit |
8480eb |
goto out_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->mode && (err = chmod(me->key, ap->mode)))
|
|
Packit |
8480eb |
warn(ap->logopt, "failed to change mode of %s", me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, st.st_dev, me->key);
|
|
Packit |
8480eb |
if (ioctlfd < 0) {
|
|
Packit |
8480eb |
crit(ap->logopt, "failed to create ioctl fd for %s", me->key);
|
|
Packit |
8480eb |
goto out_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ops->timeout(ap->logopt, ioctlfd, timeout);
|
|
Packit |
8480eb |
notify_mount_result(ap, me->key, timeout, str_direct);
|
|
Packit |
8480eb |
cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "mounted trigger %s", me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
out_umount:
|
|
Packit |
8480eb |
/* TODO: maybe force umount (-l) */
|
|
Packit |
8480eb |
umount(me->key);
|
|
Packit |
8480eb |
out_err:
|
|
Packit |
8480eb |
if (me->flags & MOUNT_FLAG_DIR_CREATED)
|
|
Packit |
8480eb |
rmdir(me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int mount_autofs_direct(struct autofs_point *ap)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct map_source *map;
|
|
Packit |
8480eb |
struct mapent_cache *nc, *mc;
|
|
Packit |
8480eb |
struct mapent *me, *ne, *nested;
|
|
Packit Service |
71cd40 |
struct mnt_list *mnts;
|
|
Packit |
8480eb |
time_t now = monotonic_time(NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (strcmp(ap->path, "/-")) {
|
|
Packit |
8480eb |
error(ap->logopt, "expected direct map, exiting");
|
|
Packit |
8480eb |
return -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* TODO: check map type */
|
|
Packit |
8480eb |
if (lookup_nss_read_map(ap, NULL, now))
|
|
Packit |
8480eb |
lookup_prune_cache(ap, now);
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
error(ap->logopt, "failed to read direct map");
|
|
Packit |
8480eb |
return -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/");
|
|
Packit Service |
71cd40 |
pthread_cleanup_push(mnts_cleanup, mnts);
|
|
Packit |
8480eb |
pthread_cleanup_push(master_source_lock_cleanup, ap->entry);
|
|
Packit |
8480eb |
master_source_readlock(ap->entry);
|
|
Packit |
8480eb |
nc = ap->entry->master->nc;
|
|
Packit |
8480eb |
cache_readlock(nc);
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, nc);
|
|
Packit |
8480eb |
map = ap->entry->maps;
|
|
Packit |
8480eb |
while (map) {
|
|
Packit |
8480eb |
time_t timeout;
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Only consider map sources that have been read since
|
|
Packit |
8480eb |
* the map entry was last updated.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (ap->entry->age > map->age) {
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
timeout = get_exp_timeout(ap, map);
|
|
Packit |
8480eb |
cache_readlock(mc);
|
|
Packit |
8480eb |
pthread_cleanup_push(cache_lock_cleanup, mc);
|
|
Packit |
8480eb |
me = cache_enumerate(mc, NULL);
|
|
Packit |
8480eb |
while (me) {
|
|
Packit |
8480eb |
ne = cache_lookup_distinct(nc, me->key);
|
|
Packit |
8480eb |
if (ne) {
|
|
Packit |
8480eb |
if (map->master_line < ne->age) {
|
|
Packit |
8480eb |
/* TODO: check return, locking me */
|
|
Packit Service |
71cd40 |
do_mount_autofs_direct(ap, mnts, me, timeout);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me = cache_enumerate(mc, me);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
nested = cache_partial_match(nc, me->key);
|
|
Packit |
8480eb |
if (nested) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"removing invalid nested null entry %s",
|
|
Packit |
8480eb |
nested->key);
|
|
Packit |
8480eb |
nested = cache_partial_match(nc, me->key);
|
|
Packit |
8480eb |
if (nested)
|
|
Packit |
8480eb |
cache_delete(nc, nested->key);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* TODO: check return, locking me */
|
|
Packit Service |
71cd40 |
do_mount_autofs_direct(ap, mnts, me, timeout);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
me = cache_enumerate(mc, me);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit Service |
71cd40 |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int umount_autofs_offset(struct autofs_point *ap, struct mapent *me)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
int ioctlfd = -1, rv = 1, retries;
|
|
Packit |
8480eb |
int opened = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd != -1) {
|
|
Packit Service |
71cd40 |
if (is_mounted(_PATH_MOUNTED, me->key, MNTS_REAL)) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"attempt to umount busy offset %s", me->key);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* offset isn't mounted, return success and try to recover */
|
|
Packit Service |
71cd40 |
if (!is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) {
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"offset %s not mounted",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
|
|
Packit |
8480eb |
opened = 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ioctlfd >= 0) {
|
|
Packit |
8480eb |
unsigned int status = 1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
rv = ops->askumount(ap->logopt, ioctlfd, &status);
|
|
Packit |
8480eb |
if (rv) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
logerr("ioctl failed: %s", estr);
|
|
Packit |
8480eb |
if (opened && ioctlfd != -1)
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
} else if (!status) {
|
|
Packit |
8480eb |
if (ap->state != ST_SHUTDOWN_FORCE) {
|
|
Packit |
8480eb |
if (ap->shutdown)
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"ask umount returned busy for %s",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
if (opened && ioctlfd != -1)
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
ops->catatonic(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
goto force_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
ops->catatonic(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
struct stat st;
|
|
Packit |
8480eb |
char *estr;
|
|
Packit |
8480eb |
int save_errno = errno;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Non existent directory on remote fs - no mount */
|
|
Packit |
8480eb |
if (stat(me->key, &st) == -1 && errno == ENOENT)
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
estr = strerror_r(save_errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"couldn't get ioctl fd for offset %s: %s",
|
|
Packit |
8480eb |
me->key, estr);
|
|
Packit |
8480eb |
goto force_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
sched_yield();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
retries = UMOUNT_RETRIES;
|
|
Packit |
8480eb |
while ((rv = umount(me->key)) == -1 && retries--) {
|
|
Packit |
8480eb |
struct timespec tm = {0, 200000000};
|
|
Packit |
8480eb |
if (errno != EBUSY)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
nanosleep(&tm, NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (rv == -1) {
|
|
Packit |
8480eb |
switch (errno) {
|
|
Packit |
8480eb |
case ENOENT:
|
|
Packit |
8480eb |
warn(ap->logopt, "mount point does not exist");
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
case EBUSY:
|
|
Packit |
8480eb |
error(ap->logopt, "mount point %s is in use", me->key);
|
|
Packit |
8480eb |
if (ap->state != ST_SHUTDOWN_FORCE)
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
case ENOTDIR:
|
|
Packit |
8480eb |
error(ap->logopt, "mount point is not a directory");
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
goto force_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
force_umount:
|
|
Packit |
8480eb |
if (rv != 0) {
|
|
Packit |
8480eb |
info(ap->logopt, "forcing umount of offset mount %s", me->key);
|
|
Packit |
8480eb |
rv = umount2(me->key, MNT_DETACH);
|
|
Packit |
8480eb |
} else
|
|
Packit |
8480eb |
info(ap->logopt, "umounted offset mount %s", me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return rv;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int mount_autofs_offset(struct autofs_point *ap, struct mapent *me, const char *root, const char *offset)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
const char *str_offset = mount_type_str(t_offset);
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
struct mnt_params *mp;
|
|
Packit |
8480eb |
time_t timeout = get_exp_timeout(ap, me->source);
|
|
Packit |
8480eb |
struct stat st;
|
|
Packit |
8480eb |
int ioctlfd, status, ret;
|
|
Packit |
8480eb |
const char *hosts_map_name = "-hosts";
|
|
Packit |
8480eb |
const char *map_name = hosts_map_name;
|
|
Packit |
8480eb |
const char *type;
|
|
Packit |
8480eb |
char mountpoint[PATH_MAX];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ops->version && ap->flags & MOUNT_FLAG_REMOUNT) {
|
|
Packit |
8480eb |
ret = try_remount(ap, me, t_offset);
|
|
Packit |
8480eb |
if (ret == 1)
|
|
Packit |
8480eb |
return MOUNT_OFFSET_OK;
|
|
Packit |
8480eb |
/* Offset mount not found, fall thru and try to mount it */
|
|
Packit |
8480eb |
if (!(ret == -1 && errno == ENOENT))
|
|
Packit |
8480eb |
return MOUNT_OFFSET_FAIL;
|
|
Packit |
8480eb |
} else {
|
|
Packit Service |
71cd40 |
if (is_mounted(_PROC_MOUNTS, me->key, MNTS_AUTOFS)) {
|
|
Packit |
8480eb |
if (ap->state != ST_READMAP)
|
|
Packit |
8480eb |
warn(ap->logopt,
|
|
Packit |
8480eb |
"trigger %s already mounted", me->key);
|
|
Packit |
8480eb |
return MOUNT_OFFSET_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd != -1) {
|
|
Packit |
8480eb |
error(ap->logopt, "active offset mount %s", me->key);
|
|
Packit |
8480eb |
return MOUNT_OFFSET_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_once(&key_mnt_params_once, key_mnt_params_init);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mp = pthread_getspecific(key_mnt_offset_params);
|
|
Packit |
8480eb |
if (!mp) {
|
|
Packit |
8480eb |
mp = (struct mnt_params *) malloc(sizeof(struct mnt_params));
|
|
Packit |
8480eb |
if (!mp) {
|
|
Packit |
8480eb |
crit(ap->logopt,
|
|
Packit |
8480eb |
"mnt_params value create failed for offset mount %s",
|
|
Packit |
8480eb |
me->key);
|
|
Packit |
8480eb |
return MOUNT_OFFSET_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
mp->options = NULL;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_setspecific(key_mnt_offset_params, mp);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
free(mp);
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!mp->options) {
|
|
Packit Service |
71cd40 |
mp->options = make_options_string(ap->path, ap->kpipefd, str_offset);
|
|
Packit |
8480eb |
if (!mp->options)
|
|
Packit |
8480eb |
return MOUNT_OFFSET_OK;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
strcpy(mountpoint, root);
|
|
Packit |
8480eb |
strcat(mountpoint, offset);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* In case the directory doesn't exist, try to mkdir it */
|
|
Packit Service |
2f7109 |
if (mkdir_path(mountpoint, mp_mode) < 0) {
|
|
Packit |
8480eb |
if (errno == EEXIST) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If the mount point directory is a real mount
|
|
Packit |
8480eb |
* and it isn't the root offset then it must be
|
|
Packit |
8480eb |
* a mount that has been automatically mounted by
|
|
Packit |
8480eb |
* the kernel NFS client.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (me->multi != me &&
|
|
Packit Service |
71cd40 |
is_mounted(_PROC_MOUNTS, mountpoint, MNTS_REAL))
|
|
Packit |
8480eb |
return MOUNT_OFFSET_IGNORE;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If we recieve an error, and it's EEXIST
|
|
Packit |
8480eb |
* we know the directory was not created.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
me->flags &= ~MOUNT_FLAG_DIR_CREATED;
|
|
Packit |
8480eb |
} else if (errno == EACCES) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* We require the mount point directory to exist when
|
|
Packit |
8480eb |
* installing multi-mount triggers into a host
|
|
Packit |
8480eb |
* filesystem.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* If it doesn't exist it is not a valid part of the
|
|
Packit |
8480eb |
* mount heirachy.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"can't create mount directory: %s, %s",
|
|
Packit |
8480eb |
mountpoint, estr);
|
|
Packit |
8480eb |
return MOUNT_OFFSET_FAIL;
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
crit(ap->logopt,
|
|
Packit |
8480eb |
"failed to create mount directory: %s, %s",
|
|
Packit |
8480eb |
mountpoint, estr);
|
|
Packit |
8480eb |
return MOUNT_OFFSET_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* No errors so the directory was successfully created */
|
|
Packit |
8480eb |
me->flags |= MOUNT_FLAG_DIR_CREATED;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt,
|
|
Packit |
8480eb |
"calling mount -t autofs " SLOPPY " -o %s automount %s",
|
|
Packit |
8480eb |
mp->options, mountpoint);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
type = ap->entry->maps->type;
|
|
Packit |
8480eb |
if (!type || strcmp(ap->entry->maps->type, "hosts"))
|
|
Packit |
8480eb |
map_name = me->mc->map->argv[0];
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = mount(map_name, mountpoint, "autofs", MS_MGC_VAL, mp->options);
|
|
Packit |
8480eb |
if (ret) {
|
|
Packit |
8480eb |
crit(ap->logopt,
|
|
Packit |
8480eb |
"failed to mount offset trigger %s at %s",
|
|
Packit |
8480eb |
me->key, mountpoint);
|
|
Packit |
8480eb |
goto out_err;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ret = stat(mountpoint, &st);
|
|
Packit |
8480eb |
if (ret == -1) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"failed to stat direct mount trigger %s", mountpoint);
|
|
Packit |
8480eb |
goto out_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, st.st_dev, mountpoint);
|
|
Packit |
8480eb |
if (ioctlfd < 0) {
|
|
Packit |
8480eb |
crit(ap->logopt, "failed to create ioctl fd for %s", mountpoint);
|
|
Packit |
8480eb |
goto out_umount;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ops->timeout(ap->logopt, ioctlfd, timeout);
|
|
Packit |
8480eb |
cache_set_ino_index(me->mc, me->key, st.st_dev, st.st_ino);
|
|
Packit |
8480eb |
if (ap->logopt & LOGOPT_DEBUG)
|
|
Packit |
8480eb |
notify_mount_result(ap, mountpoint, timeout, str_offset);
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
notify_mount_result(ap, me->key, timeout, str_offset);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "mounted trigger %s at %s", me->key, mountpoint);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return MOUNT_OFFSET_OK;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
out_umount:
|
|
Packit |
8480eb |
umount(mountpoint);
|
|
Packit |
8480eb |
out_err:
|
|
Packit |
8480eb |
if (stat(mountpoint, &st) == 0 && me->flags & MOUNT_FLAG_DIR_CREATED)
|
|
Packit |
8480eb |
rmdir_path(ap, mountpoint, st.st_dev);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return MOUNT_OFFSET_FAIL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
void *expire_proc_direct(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct mnt_list *mnts = NULL, *next;
|
|
Packit |
8480eb |
struct list_head list, *p;
|
|
Packit |
8480eb |
struct expire_args *ea;
|
|
Packit |
8480eb |
struct expire_args ec;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit Service |
71cd40 |
unsigned int now;
|
|
Packit |
8480eb |
int ioctlfd, cur_state;
|
|
Packit |
8480eb |
int status, ret, left;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea = (struct expire_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_lock(&ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = ec.ap = ea->ap;
|
|
Packit Service |
71cd40 |
now = ea->when;
|
|
Packit |
8480eb |
ec.status = -1;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ea->signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&ea->cond);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_unlock(&ea->mutex);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(expire_cleanup, &ec);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
left = 0;
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
mnts = tree_make_mnt_tree(_PROC_MOUNTS, "/");
|
|
Packit |
8480eb |
pthread_cleanup_push(mnts_cleanup, mnts);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Get a list of mounts select real ones and expire them if possible */
|
|
Packit |
8480eb |
INIT_LIST_HEAD(&list);
|
|
Packit |
8480eb |
if (!tree_get_mnt_list(mnts, &list, "/", 0)) {
|
|
Packit |
8480eb |
ec.status = 0;
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
list_for_each(p, &list) {
|
|
Packit |
8480eb |
next = list_entry(p, struct mnt_list, list);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* All direct mounts must be present in the map
|
|
Packit |
8480eb |
* entry cache.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
pthread_cleanup_push(master_source_lock_cleanup, ap->entry);
|
|
Packit |
8480eb |
master_source_readlock(ap->entry);
|
|
Packit Service |
71cd40 |
me = lookup_source_mapent(ap, next->path, LKP_DISTINCT);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
if (!me)
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
if (!strcmp(next->fs_type, "autofs")) {
|
|
Packit |
8480eb |
struct stat st;
|
|
Packit |
8480eb |
int ioctlfd;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(me->mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If we have submounts check if this path lives below
|
|
Packit |
8480eb |
* one of them and pass on state change.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
|
|
Packit Service |
71cd40 |
if (strstr(next->opts, "indirect")) {
|
|
Packit Service |
71cd40 |
master_notify_submount(ap, next->path, ap->state);
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd == -1) {
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* It's got a mount, deal with in the outer loop */
|
|
Packit Service |
71cd40 |
if (tree_is_mounted(mnts, me->key, MNTS_REAL)) {
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Maybe a manual umount, repair.
|
|
Packit |
8480eb |
* It will take ap->exp_timeout/4 for us to relaize
|
|
Packit |
8480eb |
* this so user must still use USR1 signal to close
|
|
Packit |
8480eb |
* the open file handle for mounts atop multi-mount
|
|
Packit |
8480eb |
* triggers. There is no way that I'm aware of to
|
|
Packit |
8480eb |
* avoid maintaining a file handle for control
|
|
Packit |
8480eb |
* functions as once it's mounted all opens are
|
|
Packit |
8480eb |
* directed to the mount not the trigger.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Check for manual umount */
|
|
Packit |
8480eb |
cache_writelock(me->mc);
|
|
Packit |
8480eb |
if (me->ioctlfd != -1 &&
|
|
Packit |
8480eb |
fstat(me->ioctlfd, &st) != -1 &&
|
|
Packit Service |
71cd40 |
!count_mounts(ap, next->path, st.st_dev)) {
|
|
Packit |
8480eb |
ops->close(ap->logopt, me->ioctlfd);
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
cache_unlock(me->mc);
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
cache_unlock(me->mc);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
ret = ops->expire(ap->logopt, ioctlfd, next->path, now);
|
|
Packit |
8480eb |
if (ret) {
|
|
Packit |
8480eb |
left++;
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd >= 0) {
|
|
Packit |
8480eb |
/* Real mounts have an open ioctl fd */
|
|
Packit |
8480eb |
ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
cache_unlock(me->mc);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
cache_unlock(me->mc);
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ap->state == ST_EXPIRE || ap->state == ST_PRUNE)
|
|
Packit |
8480eb |
pthread_testcancel();
|
|
Packit |
8480eb |
|
|
Packit Service |
71cd40 |
debug(ap->logopt, "send expire to trigger %s", next->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
|
|
Packit Service |
71cd40 |
ret = ops->expire(ap->logopt, ioctlfd, next->path, now);
|
|
Packit |
8480eb |
if (ret)
|
|
Packit |
8480eb |
left++;
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (left)
|
|
Packit Service |
71cd40 |
info(ap->logopt, "%d remaining in %s", left, ap->path);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ec.status = left;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cur_state);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_setcancelstate(cur_state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void expire_send_fail(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct pending_args *mt = arg;
|
|
Packit |
8480eb |
struct autofs_point *ap = mt->ap;
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
mt->ioctlfd, mt->wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void *do_expire_direct(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct pending_args *args, mt;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
size_t len;
|
|
Packit |
8480eb |
int status, state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
args = (struct pending_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_lock(args);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
memcpy(&mt, args, sizeof(struct pending_args));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = mt.ap;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
args->signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&args->cond);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_unlock(args);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(expire_send_fail, &mt;;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
len = _strlen(mt.name, KEY_MAX_LEN);
|
|
Packit |
8480eb |
if (!len) {
|
|
Packit |
8480eb |
warn(ap->logopt, "direct key path too long %s", mt.name);
|
|
Packit |
8480eb |
/* TODO: force umount ?? */
|
|
Packit |
8480eb |
pthread_exit(NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = do_expire(ap, mt.name, len);
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
mt.ioctlfd, mt.wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
else {
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
cache_writelock(mt.mc);
|
|
Packit |
8480eb |
me = cache_lookup_distinct(mt.mc, mt.name);
|
|
Packit |
8480eb |
if (me)
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
cache_unlock(mt.mc);
|
|
Packit |
8480eb |
ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token);
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt.ioctlfd);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(0);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int handle_packet_expire_direct(struct autofs_point *ap, autofs_packet_expire_direct_t *pkt)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct map_source *map;
|
|
Packit |
8480eb |
struct mapent_cache *mc = NULL;
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
struct pending_args *mt;
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
struct timespec wait;
|
|
Packit |
8480eb |
int status, state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* This is a bit of a big deal.
|
|
Packit |
8480eb |
* If we can't find the path and the map entry then
|
|
Packit |
8480eb |
* we can't send a notification back to the kernel.
|
|
Packit |
8480eb |
* Hang results.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* OTOH there is a mount so there should be a path
|
|
Packit |
8480eb |
* and since it got mounted we have to trust that
|
|
Packit |
8480eb |
* there is an entry in the cache.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
master_source_writelock(ap->entry);
|
|
Packit |
8480eb |
map = ap->entry->maps;
|
|
Packit |
8480eb |
while (map) {
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
cache_writelock(mc);
|
|
Packit |
8480eb |
me = cache_lookup_ino(mc, pkt->dev, pkt->ino);
|
|
Packit |
8480eb |
if (me)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Shouldn't happen as we have been sent this following
|
|
Packit |
8480eb |
* successful thread creation and lookup.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
crit(ap->logopt, "can't find map entry for (%lu,%lu)",
|
|
Packit |
8480eb |
(unsigned long) pkt->dev, (unsigned long) pkt->ino);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Can't expire it if it isn't mounted */
|
|
Packit |
8480eb |
if (me->ioctlfd == -1) {
|
|
Packit |
8480eb |
int ioctlfd;
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
|
|
Packit |
8480eb |
if (ioctlfd == -1) {
|
|
Packit |
8480eb |
crit(ap->logopt, "can't open ioctlfd for %s", me->key);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ops->send_ready(ap->logopt, ioctlfd, pkt->wait_queue_token);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt = malloc(sizeof(struct pending_args));
|
|
Packit |
8480eb |
if (!mt) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
error(ap->logopt, "malloc: %s", estr);
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
me->ioctlfd, pkt->wait_queue_token, -ENOMEM);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_cond_init(mt);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&mt->mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt->ap = ap;
|
|
Packit |
8480eb |
mt->ioctlfd = me->ioctlfd;
|
|
Packit |
8480eb |
mt->mc = mc;
|
|
Packit |
8480eb |
/* TODO: check length here */
|
|
Packit |
8480eb |
strcpy(mt->name, me->key);
|
|
Packit |
8480eb |
mt->dev = me->dev;
|
|
Packit |
8480eb |
mt->type = NFY_EXPIRE;
|
|
Packit |
8480eb |
mt->wait_queue_token = pkt->wait_queue_token;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "token %ld, name %s",
|
|
Packit |
8480eb |
(unsigned long) pkt->wait_queue_token, mt->name);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_lock(mt);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, &th_attr_detached, do_expire_direct, mt);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
error(ap->logopt, "expire thread create failed");
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
mt->ioctlfd, pkt->wait_queue_token, -status);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
pending_mutex_unlock(mt);
|
|
Packit |
8480eb |
pending_cond_destroy(mt);
|
|
Packit |
8480eb |
pending_mutex_destroy(mt);
|
|
Packit |
8480eb |
free_pending_args(mt);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(free_pending_args, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_mutex_destroy, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_cond_destroy, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_mutex_unlock, mt);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt->signaled = 0;
|
|
Packit |
8480eb |
while (!mt->signaled) {
|
|
Packit |
8480eb |
clock_gettime(CLOCK_MONOTONIC, &wait);
|
|
Packit |
8480eb |
wait.tv_sec += 2;
|
|
Packit |
8480eb |
status = pthread_cond_timedwait(&mt->cond, &mt->mutex, &wait);
|
|
Packit |
8480eb |
if (status && status != ETIMEDOUT)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void mount_send_fail(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct pending_args *mt = arg;
|
|
Packit |
8480eb |
struct autofs_point *ap = mt->ap;
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt, mt->ioctlfd, mt->wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt->ioctlfd);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
static void *do_mount_direct(void *arg)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct pending_args *args, mt;
|
|
Packit |
8480eb |
struct autofs_point *ap;
|
|
Packit |
8480eb |
struct stat st;
|
|
Packit |
8480eb |
int status, state;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
args = (struct pending_args *) arg;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_lock(args);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
memcpy(&mt, args, sizeof(struct pending_args));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
ap = mt.ap;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
set_thread_mount_request_log_id(&mt;;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
args->signaled = 1;
|
|
Packit |
8480eb |
status = pthread_cond_signal(&args->cond);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_unlock(args);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(mount_send_fail, &mt;;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = fstat(mt.ioctlfd, &st);
|
|
Packit |
8480eb |
if (status == -1) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"can't stat direct mount trigger %s", mt.name);
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
mt.ioctlfd, mt.wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt.ioctlfd);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
pthread_exit(NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = stat(mt.name, &st);
|
|
Packit |
8480eb |
if (status != 0 || !S_ISDIR(st.st_mode) || st.st_dev != mt.dev) {
|
|
Packit |
8480eb |
error(ap->logopt,
|
|
Packit |
8480eb |
"direct trigger not valid or already mounted %s",
|
|
Packit |
8480eb |
mt.name);
|
|
Packit |
8480eb |
ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token);
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt.ioctlfd);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
pthread_exit(NULL);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
info(ap->logopt, "attempting to mount entry %s", mt.name);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
set_tsd_user_vars(ap->logopt, mt.uid, mt.gid);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = lookup_nss_mount(ap, NULL, mt.name, mt.len);
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Direct mounts are always a single mount. If it fails there's
|
|
Packit |
8480eb |
* nothing to undo so just complain
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
struct mapent *me;
|
|
Packit |
8480eb |
struct statfs fs;
|
|
Packit |
8480eb |
unsigned int close_fd = 0;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (statfs(mt.name, &fs) == -1 ||
|
|
Packit |
8480eb |
(fs.f_type == AUTOFS_SUPER_MAGIC &&
|
|
Packit |
8480eb |
!master_find_submount(ap, mt.name)))
|
|
Packit |
8480eb |
close_fd = 1;
|
|
Packit |
8480eb |
cache_writelock(mt.mc);
|
|
Packit |
8480eb |
if ((me = cache_lookup_distinct(mt.mc, mt.name))) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Careful here, we need to leave the file handle open
|
|
Packit |
8480eb |
* for direct mount multi-mounts with no real mount at
|
|
Packit |
8480eb |
* their base so they will be expired.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (close_fd && me == me->multi)
|
|
Packit |
8480eb |
close_fd = 0;
|
|
Packit |
8480eb |
if (!close_fd)
|
|
Packit |
8480eb |
me->ioctlfd = mt.ioctlfd;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ops->send_ready(ap->logopt, mt.ioctlfd, mt.wait_queue_token);
|
|
Packit |
8480eb |
cache_unlock(mt.mc);
|
|
Packit |
8480eb |
if (close_fd)
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt.ioctlfd);
|
|
Packit |
8480eb |
info(ap->logopt, "mounted %s", mt.name);
|
|
Packit |
8480eb |
} else {
|
|
Packit |
8480eb |
/* TODO: get mount return status from lookup_nss_mount */
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
mt.ioctlfd, mt.wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
ops->close(ap->logopt, mt.ioctlfd);
|
|
Packit |
8480eb |
info(ap->logopt, "failed to mount %s", mt.name);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(0);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return NULL;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
int handle_packet_missing_direct(struct autofs_point *ap, autofs_packet_missing_direct_t *pkt)
|
|
Packit |
8480eb |
{
|
|
Packit |
8480eb |
struct ioctl_ops *ops = get_ioctl_ops();
|
|
Packit |
8480eb |
struct map_source *map;
|
|
Packit |
8480eb |
struct mapent_cache *mc = NULL;
|
|
Packit |
8480eb |
struct mapent *me = NULL;
|
|
Packit |
8480eb |
pthread_t thid;
|
|
Packit |
8480eb |
struct pending_args *mt;
|
|
Packit |
8480eb |
char buf[MAX_ERR_BUF];
|
|
Packit |
8480eb |
int status = 0;
|
|
Packit |
8480eb |
struct timespec wait;
|
|
Packit |
8480eb |
int ioctlfd, len, state;
|
|
Packit |
8480eb |
unsigned int kver_major = get_kver_major();
|
|
Packit |
8480eb |
unsigned int kver_minor = get_kver_minor();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &state);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
master_mutex_lock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* If our parent is a direct or offset mount that has been
|
|
Packit |
8480eb |
* covered by a mount and another lookup occurs after the
|
|
Packit |
8480eb |
* mount but before the device and inode are set in the
|
|
Packit |
8480eb |
* cache entry we will not be able to find the mapent. So
|
|
Packit |
8480eb |
* we must take the source writelock to ensure the parent
|
|
Packit |
8480eb |
* has mount is complete before we look for the entry.
|
|
Packit |
8480eb |
*
|
|
Packit |
8480eb |
* Since the vfs-automount kernel changes we can now block
|
|
Packit |
8480eb |
* on covered mounts during mount tree construction so a
|
|
Packit |
8480eb |
* write lock is no longer needed. So we now can handle a
|
|
Packit |
8480eb |
* wider class of recursively define mount lookups.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (kver_major > 5 || (kver_major == 5 && kver_minor > 1))
|
|
Packit |
8480eb |
master_source_readlock(ap->entry);
|
|
Packit |
8480eb |
else
|
|
Packit |
8480eb |
master_source_writelock(ap->entry);
|
|
Packit |
8480eb |
map = ap->entry->maps;
|
|
Packit |
8480eb |
while (map) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Only consider map sources that have been read since
|
|
Packit |
8480eb |
* the map entry was last updated.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
if (ap->entry->age > map->age) {
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
continue;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mc = map->mc;
|
|
Packit |
8480eb |
cache_readlock(mc);
|
|
Packit |
8480eb |
me = cache_lookup_ino(mc, pkt->dev, pkt->ino);
|
|
Packit |
8480eb |
if (me)
|
|
Packit |
8480eb |
break;
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
map = map->next;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (!me) {
|
|
Packit |
8480eb |
/*
|
|
Packit |
8480eb |
* Shouldn't happen as the kernel is telling us
|
|
Packit |
8480eb |
* someone has walked on our mount point.
|
|
Packit |
8480eb |
*/
|
|
Packit |
8480eb |
logerr("can't find map entry for (%lu,%lu)",
|
|
Packit |
8480eb |
(unsigned long) pkt->dev, (unsigned long) pkt->ino);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (me->ioctlfd != -1) {
|
|
Packit |
8480eb |
/* Maybe someone did a manual umount, clean up ! */
|
|
Packit |
8480eb |
close(me->ioctlfd);
|
|
Packit |
8480eb |
me->ioctlfd = -1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
ops->open(ap->logopt, &ioctlfd, me->dev, me->key);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
if (ioctlfd == -1) {
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
crit(ap->logopt, "failed to create ioctl fd for %s", me->key);
|
|
Packit |
8480eb |
/* TODO: how do we clear wait q in kernel ?? */
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
debug(ap->logopt, "token %ld, name %s, request pid %u",
|
|
Packit |
8480eb |
(unsigned long) pkt->wait_queue_token, me->key, pkt->pid);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Ignore packet if we're trying to shut down */
|
|
Packit |
8480eb |
if (ap->shutdown || ap->state == ST_SHUTDOWN_FORCE) {
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
ioctlfd, pkt->wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
/* Check if we recorded a mount fail for this key */
|
|
Packit |
8480eb |
if (me->status >= monotonic_time(NULL)) {
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
ioctlfd, pkt->wait_queue_token, -ENOENT);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
len = strlen(me->key);
|
|
Packit |
8480eb |
if (len >= PATH_MAX) {
|
|
Packit |
8480eb |
error(ap->logopt, "direct mount path too long %s", me->key);
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
ioctlfd, pkt->wait_queue_token, -ENAMETOOLONG);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt = malloc(sizeof(struct pending_args));
|
|
Packit |
8480eb |
if (!mt) {
|
|
Packit |
8480eb |
char *estr = strerror_r(errno, buf, MAX_ERR_BUF);
|
|
Packit |
8480eb |
error(ap->logopt, "malloc: %s", estr);
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
ioctlfd, pkt->wait_queue_token, -ENOMEM);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
memset(mt, 0, sizeof(struct pending_args));
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_cond_init(mt);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_mutex_init(&mt->mutex, NULL);
|
|
Packit |
8480eb |
if (status)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pending_mutex_lock(mt);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt->ap = ap;
|
|
Packit |
8480eb |
mt->ioctlfd = ioctlfd;
|
|
Packit |
8480eb |
mt->mc = mc;
|
|
Packit |
8480eb |
strcpy(mt->name, me->key);
|
|
Packit |
8480eb |
mt->len = len;
|
|
Packit |
8480eb |
mt->dev = me->dev;
|
|
Packit |
8480eb |
mt->type = NFY_MOUNT;
|
|
Packit |
8480eb |
mt->uid = pkt->uid;
|
|
Packit |
8480eb |
mt->gid = pkt->gid;
|
|
Packit |
8480eb |
mt->pid = pkt->pid;
|
|
Packit |
8480eb |
mt->wait_queue_token = pkt->wait_queue_token;
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
status = pthread_create(&thid, &th_attr_detached, do_mount_direct, mt);
|
|
Packit |
8480eb |
if (status) {
|
|
Packit |
8480eb |
error(ap->logopt, "missing mount thread create failed");
|
|
Packit |
8480eb |
ops->send_fail(ap->logopt,
|
|
Packit |
8480eb |
ioctlfd, pkt->wait_queue_token, -status);
|
|
Packit |
8480eb |
ops->close(ap->logopt, ioctlfd);
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
pending_mutex_unlock(mt);
|
|
Packit |
8480eb |
pending_cond_destroy(mt);
|
|
Packit |
8480eb |
pending_mutex_destroy(mt);
|
|
Packit |
8480eb |
free_pending_args(mt);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
return 1;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
cache_unlock(mc);
|
|
Packit |
8480eb |
master_source_unlock(ap->entry);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
master_mutex_unlock();
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_push(free_pending_args, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_mutex_destroy, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_cond_destroy, mt);
|
|
Packit |
8480eb |
pthread_cleanup_push(pending_mutex_unlock, mt);
|
|
Packit |
8480eb |
pthread_setcancelstate(state, NULL);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
mt->signaled = 0;
|
|
Packit |
8480eb |
while (!mt->signaled) {
|
|
Packit |
8480eb |
clock_gettime(CLOCK_MONOTONIC, &wait);
|
|
Packit |
8480eb |
wait.tv_sec += 2;
|
|
Packit |
8480eb |
status = pthread_cond_timedwait(&mt->cond, &mt->mutex, &wait);
|
|
Packit |
8480eb |
if (status && status != ETIMEDOUT)
|
|
Packit |
8480eb |
fatal(status);
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
pthread_cleanup_pop(1);
|
|
Packit |
8480eb |
|
|
Packit |
8480eb |
return 0;
|
|
Packit |
8480eb |
}
|
|
Packit |
8480eb |
|