#include <mach/task.h>
#include <mach/host_priv.h>
#include <mach/host_special_ports.h>
#include <sys/types.h>
#include <sys/proc.h>
#include <sys/malloc.h>
#include <sys/mount.h>
#include <sys/vnode.h>
#include <sys/kauth.h>
#include <sys/time.h>
#include <sys/errno.h>
#include <sys/queue.h>
#include <kern/host.h>
#include <kern/locks.h>
#include <IOKit/IOLib.h>
#include "autofs.h"
#include "autofs_protUser.h"
#include "triggers.h"
#include "triggers_priv.h"
static lck_grp_t *triggers_lck_grp;
__private_extern__ int triggers_start(kmod_info_t *, void *);
__private_extern__ int triggers_stop(kmod_info_t *, void *);
#define M_TRIGGERS M_TEMP
#define FSIDS_EQUAL(fsid1, fsid2) \
((fsid1).val[0] == (fsid2).val[0] && \
(fsid1).val[1] == (fsid2).val[1])
static lck_rw_t *resolved_triggers_rwlock;
static TAILQ_HEAD(resolved_triggers, trigger_info) resolved_triggers =
TAILQ_HEAD_INITIALIZER(resolved_triggers);
static int mount_to;
void
trigger_set_mount_to(int newval)
{
mount_to = newval;
}
static int
unmount_triggered_mount(fsid_t *fsidp, int flags, vfs_context_t ctx)
{
return (vfs_unmountbyfsid(fsidp, flags | MNT_NOBLOCK, ctx));
}
static resolver_result_t
trigger_make_unresolved(trigger_info_t *ti)
{
if (ti->ti_flags & TF_RESOLVED) {
lck_rw_lock_exclusive(resolved_triggers_rwlock);
TAILQ_REMOVE(&resolved_triggers, ti, ti_entries);
lck_rw_unlock_exclusive(resolved_triggers_rwlock);
ti->ti_flags &= ~TF_RESOLVED;
}
ti->ti_seq++;
return (vfs_resolver_result(ti->ti_seq, RESOLVER_UNRESOLVED, 0));
}
#define TRIGGER_BLOCK_OTHERS(ti) { \
lck_mtx_assert((ti)->ti_lock, LCK_MTX_ASSERT_OWNED); \
assert(!((ti)->ti_flags & TF_INPROG)); \
(ti)->ti_flags |= TF_INPROG; \
}
#define TRIGGER_UNBLOCK_OTHERS(ti) { \
trigger_unblock_others(ti); \
}
static void
trigger_unblock_others(trigger_info_t *ti)
{
ti->ti_flags &= ~TF_INPROG;
if (ti->ti_flags & TF_WAITING) {
ti->ti_flags &= ~TF_WAITING;
wakeup(&ti->ti_flags);
}
}
static int
trigger_do_mount_url(void *arg)
{
struct mount_url_callargs *argsp = arg;
mach_port_t automount_port;
int error;
kern_return_t ret;
error = auto_get_automountd_port(&automount_port);
if (error)
goto done;
ret = autofs_mount_url(automount_port, argsp->muc_url,
argsp->muc_mountpoint, argsp->muc_opts, argsp->muc_this_fsid,
argsp->muc_uid, argsp->muc_asid,
&argsp->muc_mounted_fsid, &argsp->muc_retflags, &error);
auto_release_port(automount_port);
if (ret != KERN_SUCCESS) {
IOLog("autofs: autofs_mount_url failed, status 0x%08x\n", ret);
error = EIO;
}
done:
return (error);
}
static void
trigger_mount_thread(void *arg)
{
struct trigger_callargs *argsp = arg;
vnode_t vp;
trigger_info_t *ti;
int error;
struct timeval now;
vp = argsp->tc_vp;
ti = argsp->tc_ti;
error = (*ti->ti_do_mount)(arg);
lck_mtx_lock(ti->ti_lock);
ti->ti_error = error;
if (error == 0) {
lck_rw_lock_exclusive(resolved_triggers_rwlock);
ti->ti_mounted_fsid = argsp->tc_mounted_fsid;
if (argsp->tc_retflags & MOUNT_RETF_DONTUNMOUNT)
ti->ti_flags |= TF_DONTUNMOUNT;
else
ti->ti_flags &= ~TF_DONTUNMOUNT;
if (argsp->tc_retflags & MOUNT_RETF_DONTPREUNMOUNT)
ti->ti_flags |= TF_DONTPREUNMOUNT;
else
ti->ti_flags &= ~TF_DONTPREUNMOUNT;
microtime(&now);
ti->ti_ref_time = now.tv_sec;
if (!(ti->ti_flags & TF_RESOLVED)) {
ti->ti_flags |= TF_RESOLVED;
TAILQ_INSERT_TAIL(&resolved_triggers, ti,
ti_entries);
}
lck_rw_unlock_exclusive(resolved_triggers_rwlock);
ti->ti_seq++;
vnode_trigger_update(vp,
vfs_resolver_result(ti->ti_seq, RESOLVER_RESOLVED, 0));
}
TRIGGER_UNBLOCK_OTHERS(ti);
lck_mtx_unlock(ti->ti_lock);
vnode_rele(vp);
(*ti->ti_rel_mount_args)(argsp);
vnode_put(vp);
thread_terminate(current_thread());
}
static int trigger_thr_success = 0;
static int
wait_for_mount_locked(trigger_info_t *ti)
{
int error;
while (ti->ti_flags & TF_INPROG) {
ti->ti_flags |= TF_WAITING;
error = msleep(&ti->ti_flags, ti->ti_lock, PSOCK|PCATCH,
"triggered_mount", NULL);
if (error == EINTR || error == ERESTART) {
return (EINTR);
}
}
return (ti->ti_error);
}
static resolver_result_t
trigger_resolve(vnode_t vp, const struct componentname *cnp,
enum path_operation pop, __unused int flags, void *data, vfs_context_t ctx)
{
trigger_info_t *ti = data;
resolver_result_t result;
int pid = vfs_context_pid(ctx);
int error;
struct trigger_callargs *argsp;
kern_return_t ret;
if (cnp->cn_flags & ISLASTCN) {
switch (pop) {
case OP_MOUNT:
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
case OP_UNMOUNT:
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
case OP_LOOKUP:
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
case OP_STATFS:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_ACCESS:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_GETATTR:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_LISTXATTR:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_MKNOD:
case OP_MKFIFO:
case OP_SYMLINK:
case OP_LINK:
case OP_MKDIR:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_UNLINK:
case OP_RMDIR:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_RENAME:
if (!(ti->ti_flags & TF_FORCEMOUNT)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
case OP_FSCTL:
if (ti->ti_check_homedirmounter_process != NULL &&
(*ti->ti_check_homedirmounter_process)(NULL, pid)) {
return (vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0));
}
break;
default:
break;
}
}
lck_mtx_lock(ti->ti_lock);
top:
if (vnode_mountedhere(vp) != NULL) {
ti->ti_seq++;
result = vfs_resolver_result(ti->ti_seq, RESOLVER_RESOLVED, 0);
lck_mtx_unlock(ti->ti_lock);
return (result);
}
if (ti->ti_check_homedirmount != NULL &&
(*ti->ti_check_homedirmount)(vp)) {
if (ti->ti_check_homedirmounter_process != NULL &&
(*ti->ti_check_homedirmounter_process)(vp, pid)) {
result = vfs_resolver_result(ti->ti_seq,
RESOLVER_RESOLVED, 0);
} else {
result = vfs_resolver_result(ti->ti_seq,
RESOLVER_ERROR, ENOENT);
}
lck_mtx_unlock(ti->ti_lock);
return (result);
}
if (ti->ti_check_notrigger_process != NULL) {
if ((*ti->ti_check_notrigger_process)(pid)) {
result = vfs_resolver_result(ti->ti_seq,
RESOLVER_NOCHANGE, 0);
lck_mtx_unlock(ti->ti_lock);
return (result);
}
}
if (!(ti->ti_flags & TF_INPROG)) {
TRIGGER_BLOCK_OTHERS(ti);
ti->ti_error = 0;
lck_mtx_unlock(ti->ti_lock);
error = vnode_get(vp);
if (error != 0)
goto fail;
error = vnode_ref(vp);
if (error != 0) {
vnode_put(vp);
goto fail;
}
argsp = (*ti->ti_get_mount_args)(vp, ctx, &error);
if (argsp == NULL) {
vnode_rele(vp);
vnode_put(vp);
goto fail;
}
argsp->tc_vp = vp;
argsp->tc_this_fsid = vfs_statfs(vnode_mount(vp))->f_fsid;
argsp->tc_ti = ti;
argsp->tc_origin = current_thread();
#define kauth_cred_getasid(cred) ((cred)->cr_audit.as_aia_p->ai_asid)
argsp->tc_uid = kauth_cred_getuid(vfs_context_ucred(ctx));
argsp->tc_asid = kauth_cred_getasid(vfs_context_ucred(ctx));
memset(&argsp->tc_mounted_fsid, 0, sizeof (argsp->tc_mounted_fsid));
argsp->tc_retflags = 0;
ret = auto_new_thread(trigger_mount_thread, argsp);
if (ret != KERN_SUCCESS) {
IOLog("trigger_resolve: can't start new mounter thread, status 0x%08x",
ret);
error = EIO;
goto fail_thread;
}
trigger_thr_success++;
lck_mtx_lock(ti->ti_lock);
}
if (ti->ti_check_nowait_process != NULL) {
if ((*ti->ti_check_nowait_process)(pid)) {
result = vfs_resolver_result(ti->ti_seq, RESOLVER_ERROR,
ENOENT);
lck_mtx_unlock(ti->ti_lock);
return (result);
}
}
error = wait_for_mount_locked(ti);
if (error == 0) {
result = vfs_resolver_result(ti->ti_seq, RESOLVER_RESOLVED, 0);
} else if (error == EAGAIN) {
goto top;
} else {
result = vfs_resolver_result(ti->ti_seq, RESOLVER_ERROR,
error);
}
lck_mtx_unlock(ti->ti_lock);
return (result);
fail_thread:
vnode_rele(vp);
vnode_put(vp);
(*ti->ti_rel_mount_args)(argsp);
fail:
lck_mtx_lock(ti->ti_lock);
ti->ti_error = error;
TRIGGER_UNBLOCK_OTHERS(ti);
result = vfs_resolver_result(ti->ti_seq, RESOLVER_ERROR, error);
lck_mtx_unlock(ti->ti_lock);
return (result);
}
static resolver_result_t
trigger_unresolve(__unused vnode_t vp, int flags, void *data, vfs_context_t ctx)
{
trigger_info_t *ti = data;
int error;
resolver_result_t result;
lck_mtx_lock(ti->ti_lock);
if (ti->ti_flags & TF_DONTUNMOUNT)
error = EBUSY;
else
error = unmount_triggered_mount(&ti->ti_mounted_fsid, flags,
ctx);
if (error == 0)
result = trigger_make_unresolved(ti);
else {
result = vfs_resolver_result(ti->ti_seq, RESOLVER_ERROR,
error);
}
lck_mtx_unlock(ti->ti_lock);
return (result);
}
static resolver_result_t
trigger_rearm(vnode_t vp, __unused int flags, void *data, vfs_context_t ctx)
{
trigger_info_t *ti = data;
resolver_result_t result;
lck_mtx_lock(ti->ti_lock);
if (ti->ti_rearm != NULL)
(*ti->ti_rearm)(vp, vfs_context_pid(ctx));
if (vnode_mountedhere(vp) != NULL) {
ti->ti_seq++;
result = vfs_resolver_result(ti->ti_seq, RESOLVER_RESOLVED,
0);
} else {
result = trigger_make_unresolved(ti);
}
lck_mtx_unlock(ti->ti_lock);
return (result);
}
static void
trigger_reclaim(__unused vnode_t vp, void *data)
{
trigger_info_t *ti = data;
if (ti->ti_reclaim != NULL)
(*ti->ti_reclaim)(ti->ti_private);
trigger_free(ti);
}
trigger_info_t *
trigger_new_autofs(struct vnode_trigger_param *vnt,
u_int flags,
int (*check_notrigger_process)(int),
int (*check_nowait_process)(int),
int (*check_homedirmounter_process)(vnode_t, int),
int (*check_homedirmount)(vnode_t),
void *(*get_mount_args)(vnode_t, vfs_context_t, int *),
int (*do_mount)(void *),
void (*rel_mount_args)(void *),
void (*rearm)(vnode_t, int),
void (*reclaim)(void *),
void *private)
{
struct timeval now;
trigger_info_t *ti;
microtime(&now);
MALLOC(ti, trigger_info_t *, sizeof (*ti), M_TRIGGERS, M_WAITOK);
ti->ti_lock = lck_mtx_alloc_init(triggers_lck_grp, NULL);
ti->ti_seq = 0;
ti->ti_flags = flags;
ti->ti_error = 0;
ti->ti_ref_time = now.tv_sec;
ti->ti_check_notrigger_process = check_notrigger_process;
ti->ti_check_nowait_process = check_nowait_process;
ti->ti_check_homedirmounter_process = check_homedirmounter_process;
ti->ti_check_homedirmount = check_homedirmount;
ti->ti_get_mount_args = get_mount_args;
ti->ti_do_mount = do_mount;
ti->ti_rel_mount_args = rel_mount_args;
ti->ti_rearm = rearm;
ti->ti_reclaim = reclaim;
ti->ti_private = private;
vnt->vnt_resolve_func = trigger_resolve;
vnt->vnt_unresolve_func = trigger_unresolve;
vnt->vnt_rearm_func = trigger_rearm;
vnt->vnt_reclaim_func = trigger_reclaim;
vnt->vnt_data = ti;
vnt->vnt_flags = VNT_AUTO_REARM;
return (ti);
}
trigger_info_t *
trigger_new(struct vnode_trigger_param *vnt,
void *(*get_mount_args)(vnode_t, vfs_context_t, int *),
void (*rel_mount_args)(void *))
{
return (trigger_new_autofs(vnt, 0, NULL, NULL, NULL, NULL,
get_mount_args, trigger_do_mount_url, rel_mount_args,
NULL, NULL, NULL));
}
void
trigger_free(trigger_info_t *ti)
{
lck_rw_lock_exclusive(resolved_triggers_rwlock);
if (ti->ti_flags & TF_RESOLVED)
TAILQ_REMOVE(&resolved_triggers, ti, ti_entries);
lck_rw_unlock_exclusive(resolved_triggers_rwlock);
lck_mtx_free(ti->ti_lock, triggers_lck_grp);
FREE(ti, M_TRIGGERS);
}
int
SMBRemountServer(const void *ptr, size_t len, au_asid_t asid)
{
mach_port_t automount_port;
int error;
kern_return_t ret;
vm_offset_t kmem_buf;
vm_size_t tbuflen;
vm_map_copy_t copy;
error = auto_get_automountd_port(&automount_port);
if (error)
return (error);
tbuflen = round_page(len);
ret = vm_allocate(ipc_kernel_map, &kmem_buf, tbuflen,
VM_FLAGS_ANYWHERE);
if (ret != KERN_SUCCESS) {
IOLog("autofs: vm_allocate failed, status 0x%08x\n", ret);
auto_release_port(automount_port);
return (EIO);
}
ret = vm_map_wire(ipc_kernel_map, vm_map_trunc_page(kmem_buf),
vm_map_round_page(kmem_buf + tbuflen),
VM_PROT_READ|VM_PROT_WRITE, FALSE);
if (ret != KERN_SUCCESS) {
IOLog("autofs: vm_map_wire failed, status 0x%08x\n", ret);
vm_deallocate(ipc_kernel_map, kmem_buf, tbuflen);
auto_release_port(automount_port);
return (EIO);
}
bcopy(ptr, (void *)kmem_buf, len);
ret = vm_map_unwire(ipc_kernel_map, vm_map_trunc_page(kmem_buf),
vm_map_round_page(kmem_buf + tbuflen), FALSE);
if (ret != KERN_SUCCESS) {
IOLog("autofs: vm_map_wire failed, status 0x%08x\n", ret);
vm_deallocate(ipc_kernel_map, kmem_buf, tbuflen);
auto_release_port(automount_port);
return (EIO);
}
ret = vm_map_copyin(ipc_kernel_map, (vm_map_address_t) kmem_buf,
(vm_map_size_t) tbuflen, TRUE, ©);
if (ret != KERN_SUCCESS) {
IOLog("autofs: vm_map_copyin failed, status 0x%08x\n", ret);
vm_deallocate(ipc_kernel_map, kmem_buf, tbuflen);
auto_release_port(automount_port);
return (EIO);
}
ret = autofs_smb_remount_server(automount_port, (byte_buffer) copy,
(mach_msg_type_number_t)len, asid);
auto_release_port(automount_port);
if (ret != KERN_SUCCESS) {
IOLog("autofs: autofs_smb_remount_server failed, status 0x%08x\n",
ret);
vm_map_copy_discard(copy);
return (EIO);
}
return (0);
}
int
auto_get_automountd_port(mach_port_t *automount_port)
{
kern_return_t ret;
*automount_port = MACH_PORT_NULL;
ret = host_get_automountd_port(host_priv_self(), automount_port);
if (ret != KERN_SUCCESS) {
IOLog("autofs: can't get automountd port, status 0x%08x\n",
ret);
return (ECONNREFUSED);
}
if (!IPC_PORT_VALID(*automount_port)) {
IOLog("autofs: automountd port not valid\n");
return (ECONNRESET);
}
return (0);
}
void
auto_release_port(mach_port_t port)
{
extern void ipc_port_release_send(ipc_port_t);
ipc_port_release_send(port);
}
kern_return_t
auto_new_thread(void (*start)(void *), void *arg)
{
kern_return_t result;
thread_t thread;
result = kernel_thread_start((thread_continue_t)start, arg, &thread);
if (result != KERN_SUCCESS)
return (result);
thread_deallocate(thread);
return (KERN_SUCCESS);
}
static void
trigger_update_ref_time(fsid_t *fsidp, time_t now)
{
trigger_info_t *ti;
lck_rw_lock_shared(resolved_triggers_rwlock);
TAILQ_FOREACH(ti, &resolved_triggers, ti_entries) {
if (FSIDS_EQUAL(ti->ti_mounted_fsid, *fsidp)) {
ti->ti_ref_time = now;
break;
}
}
lck_rw_unlock_shared(resolved_triggers_rwlock);
}
struct triggered_mount_info {
fsid_t this_fsid;
fsid_t mounted_fsid;
time_t ref_time;
u_int flags;
};
void
unmount_triggered_mounts(int unconditional)
{
trigger_info_t *ti;
int nmounts, i, j;
struct triggered_mount_info *mounts;
struct timeval now;
int error;
fsid_t search_fsid;
lck_rw_lock_shared(resolved_triggers_rwlock);
nmounts = 0;
TAILQ_FOREACH_REVERSE(ti, &resolved_triggers, resolved_triggers,
ti_entries) {
nmounts++;
}
MALLOC(mounts, struct triggered_mount_info *, sizeof (*mounts)*nmounts,
M_TRIGGERS, M_WAITOK);
i = 0;
TAILQ_FOREACH_REVERSE(ti, &resolved_triggers, resolved_triggers,
ti_entries) {
if (i >= nmounts) {
IOLog("unmount_triggers: resolved_triggers changed out from under us!\n");
break;
}
mounts[i].this_fsid = ti->ti_this_fsid;
mounts[i].mounted_fsid = ti->ti_mounted_fsid;
mounts[i].ref_time = ti->ti_ref_time;
mounts[i].flags = ti->ti_flags;
i++;
}
lck_rw_unlock_shared(resolved_triggers_rwlock);
microtime(&now);
for (i = nmounts - 1; i >= 0; i--) {
if (mounts[i].flags & TF_AUTOFS)
continue;
error = 0;
if (mounts[i].flags & TF_DONTUNMOUNT) {
error = EBUSY;
} else {
if (unconditional) {
if (mounts[i].flags & TF_DONTPREUNMOUNT)
error = EBUSY;
} else {
if (mounts[i].ref_time + mount_to > now.tv_sec)
error = EBUSY;
}
}
if (error == 0) {
error = unmount_triggered_mount(&mounts[i].mounted_fsid,
0, vfs_context_current());
if (error == EBUSY) {
trigger_update_ref_time(&mounts[i].mounted_fsid,
now.tv_sec);
}
}
if (error == EBUSY) {
search_fsid = mounts[i].this_fsid;
for (j = i - 1; j >= 0; j--) {
if (FSIDS_EQUAL(mounts[j].mounted_fsid,
search_fsid)) {
if (mounts[j].flags & TF_AUTOFS) {
search_fsid = mounts[j].mounted_fsid;
} else {
mounts[j].ref_time = now.tv_sec;
break;
}
}
}
}
}
FREE(mounts, M_TRIGGERS);
}
static lck_mtx_t *unmount_threads_lock;
static int unmount_threads;
static thread_call_t unmounter_thread_call;
static int shutting_down;
static int max_unmount_threads = 5;
static void
decrement_unmount_thread_count(void)
{
lck_mtx_lock(unmount_threads_lock);
unmount_threads--;
if (shutting_down && unmount_threads == 0)
wakeup(&unmount_threads);
lck_mtx_unlock(unmount_threads_lock);
}
#define UNMOUNT_TRIGGERED_MOUNTS_TIMER 120
static void
triggers_unmount_thread(__unused void *arg)
{
unmount_triggered_mounts(0);
decrement_unmount_thread_count();
thread_terminate(current_thread());
}
static void
triggers_do_unmount(__unused void *dummy1, __unused void *dummy2)
{
kern_return_t result;
AbsoluteTime deadline;
lck_mtx_lock(unmount_threads_lock);
if (!shutting_down && unmount_threads < max_unmount_threads) {
unmount_threads++;
lck_mtx_unlock(unmount_threads_lock);
result = auto_new_thread(triggers_unmount_thread, NULL);
if (result != KERN_SUCCESS) {
IOLog("triggers_do_unmount: Couldn't start unmount thread, status 0x%08x\n",
result);
decrement_unmount_thread_count();
}
} else
lck_mtx_unlock(unmount_threads_lock);
clock_interval_to_deadline(UNMOUNT_TRIGGERED_MOUNTS_TIMER, NSEC_PER_SEC,
&deadline);
thread_call_enter_delayed(unmounter_thread_call, deadline);
}
__private_extern__ int
triggers_start(__unused kmod_info_t *ki, __unused void *data)
{
AbsoluteTime deadline;
triggers_lck_grp = lck_grp_alloc_init("triggers", NULL);
if (triggers_lck_grp == NULL) {
IOLog("triggers_start: Couldn't create triggers lock group\n");
goto fail;
}
resolved_triggers_rwlock = lck_rw_alloc_init(triggers_lck_grp, NULL);
if (resolved_triggers_rwlock == NULL) {
IOLog("triggers_start: Couldn't create resolved triggers list lock\n");
goto fail;
}
shutting_down = 0;
unmount_threads_lock = lck_mtx_alloc_init(triggers_lck_grp, LCK_ATTR_NULL);
if (unmount_threads_lock == NULL) {
IOLog("triggers_start: Couldn't create unmount threads lock\n");
goto fail;
}
unmount_threads = 0;
unmounter_thread_call = thread_call_allocate(triggers_do_unmount, NULL);
if (unmounter_thread_call == NULL) {
IOLog("triggers_start: Couldn't create thread_call_t for unmounter thread\n");
goto fail;
}
TAILQ_INIT(&resolved_triggers);
clock_interval_to_deadline(UNMOUNT_TRIGGERED_MOUNTS_TIMER, NSEC_PER_SEC,
&deadline);
thread_call_enter_delayed(unmounter_thread_call, deadline);
return (KERN_SUCCESS);
fail:
if (unmounter_thread_call != NULL)
thread_call_free(unmounter_thread_call);
if (unmount_threads_lock != NULL)
lck_mtx_free(unmount_threads_lock, triggers_lck_grp);
if (resolved_triggers_rwlock != NULL)
lck_rw_free(resolved_triggers_rwlock, triggers_lck_grp);
if (triggers_lck_grp != NULL)
lck_grp_free(triggers_lck_grp);
return (KERN_FAILURE);
}
__private_extern__ int
triggers_stop(__unused kmod_info_t *ki, __unused void *data)
{
lck_rw_lock_shared(resolved_triggers_rwlock);
if (!TAILQ_EMPTY(&resolved_triggers)) {
lck_rw_unlock_shared(resolved_triggers_rwlock);
IOLog("triggers_stop: Can't remove, still some resolved triggers\n");
return (KERN_NO_ACCESS);
}
thread_call_cancel(unmounter_thread_call);
lck_mtx_lock(unmount_threads_lock);
shutting_down = 1;
while (unmount_threads != 0) {
msleep(&unmount_threads, unmount_threads_lock, PWAIT,
"unmount thread terminated", NULL);
}
lck_mtx_unlock(unmount_threads_lock);
lck_mtx_free(unmount_threads_lock, triggers_lck_grp);
lck_rw_unlock_exclusive(resolved_triggers_rwlock);
lck_rw_free(resolved_triggers_rwlock, triggers_lck_grp);
lck_grp_free(triggers_lck_grp);
return (KERN_SUCCESS);
}