* Added a use counter for the syscalls, so that they are no longer removed
while a hook is called. * This closes ticket #5027. git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@34233 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
parent
1cf43f239c
commit
24ce75c3d8
@ -1,10 +1,16 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
* Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de.
|
||||||
* Copyright 2004-2006, Haiku Inc. All rights reserved.
|
* Copyright 2004-2009, Haiku Inc. All rights reserved.
|
||||||
* Distributed under the terms of the MIT License.
|
* Distributed under the terms of the MIT License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*! Big case statement for dispatching syscalls */
|
|
||||||
|
/*! Big case statement for dispatching syscalls, as well as the generic
|
||||||
|
syscall interface.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include <syscalls.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -34,12 +40,12 @@
|
|||||||
#include <safemode.h>
|
#include <safemode.h>
|
||||||
#include <sem.h>
|
#include <sem.h>
|
||||||
#include <sys/resource.h>
|
#include <sys/resource.h>
|
||||||
#include <syscalls.h>
|
|
||||||
#include <system_profiler.h>
|
#include <system_profiler.h>
|
||||||
#include <thread.h>
|
#include <thread.h>
|
||||||
#include <tracing.h>
|
#include <tracing.h>
|
||||||
#include <user_atomic.h>
|
#include <user_atomic.h>
|
||||||
#include <usergroup.h>
|
#include <usergroup.h>
|
||||||
|
#include <util/AutoLock.h>
|
||||||
#include <vfs.h>
|
#include <vfs.h>
|
||||||
#include <vm.h>
|
#include <vm.h>
|
||||||
#include <wait_for_objects.h>
|
#include <wait_for_objects.h>
|
||||||
@ -55,6 +61,7 @@ struct generic_syscall {
|
|||||||
syscall_hook hook;
|
syscall_hook hook;
|
||||||
uint32 version;
|
uint32 version;
|
||||||
uint32 flags;
|
uint32 flags;
|
||||||
|
int32 use_count;
|
||||||
generic_syscall *previous;
|
generic_syscall *previous;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -84,20 +91,17 @@ find_generic_syscall(const char *subsystem)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Calls the generic syscall subsystem if any.
|
/*! Calls the generic syscall subsystem if any.
|
||||||
* Also handles the special generic syscall function \c B_SYSCALL_INFO.
|
Also handles the special generic syscall function \c B_SYSCALL_INFO.
|
||||||
* Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or
|
Returns \c B_NAME_NOT_FOUND if either the subsystem was not found, or
|
||||||
* the subsystem does not support the requested function.
|
the subsystem does not support the requested function.
|
||||||
* All other return codes are depending on the generic syscall implementation.
|
All other return codes are depending on the generic syscall implementation.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static inline status_t
|
static inline status_t
|
||||||
_user_generic_syscall(const char *userSubsystem, uint32 function,
|
_user_generic_syscall(const char *userSubsystem, uint32 function,
|
||||||
void *buffer, size_t bufferSize)
|
void *buffer, size_t bufferSize)
|
||||||
{
|
{
|
||||||
char subsystem[B_FILE_NAME_LENGTH];
|
char subsystem[B_FILE_NAME_LENGTH];
|
||||||
generic_syscall *syscall;
|
|
||||||
status_t status = B_NAME_NOT_FOUND;
|
|
||||||
|
|
||||||
if (!IS_USER_ADDRESS(userSubsystem)
|
if (!IS_USER_ADDRESS(userSubsystem)
|
||||||
|| user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK)
|
|| user_strlcpy(subsystem, userSubsystem, sizeof(subsystem)) < B_OK)
|
||||||
@ -105,61 +109,58 @@ _user_generic_syscall(const char *userSubsystem, uint32 function,
|
|||||||
|
|
||||||
//dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
|
//dprintf("generic_syscall(subsystem = \"%s\", function = %lu)\n", subsystem, function);
|
||||||
|
|
||||||
mutex_lock(&sGenericSyscallLock);
|
MutexLocker locker(sGenericSyscallLock);
|
||||||
|
|
||||||
syscall = find_generic_syscall(subsystem);
|
generic_syscall* syscall = find_generic_syscall(subsystem);
|
||||||
if (syscall == NULL)
|
if (syscall == NULL)
|
||||||
goto out;
|
return B_NAME_NOT_FOUND;
|
||||||
|
|
||||||
if (function >= B_RESERVED_SYSCALL_BASE) {
|
if (function >= B_RESERVED_SYSCALL_BASE) {
|
||||||
if (function != B_SYSCALL_INFO) {
|
if (function != B_SYSCALL_INFO) {
|
||||||
// this is all we know
|
// this is all we know
|
||||||
status = B_NAME_NOT_FOUND;
|
return B_NAME_NOT_FOUND;
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// special info syscall
|
// special info syscall
|
||||||
if (bufferSize != sizeof(uint32))
|
if (bufferSize != sizeof(uint32))
|
||||||
status = B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
else {
|
|
||||||
uint32 requestedVersion;
|
|
||||||
|
|
||||||
// retrieve old version
|
uint32 requestedVersion;
|
||||||
status = user_memcpy(&requestedVersion, buffer, sizeof(uint32));
|
|
||||||
if (status == B_OK && requestedVersion != 0 && requestedVersion < syscall->version)
|
|
||||||
status = B_BAD_TYPE;
|
|
||||||
|
|
||||||
// return current version
|
// retrieve old version
|
||||||
if (status == B_OK)
|
if (user_memcpy(&requestedVersion, buffer, sizeof(uint32)) != B_OK)
|
||||||
status = user_memcpy(buffer, &syscall->version, sizeof(uint32));
|
return B_BAD_ADDRESS;
|
||||||
}
|
if (requestedVersion != 0 && requestedVersion < syscall->version)
|
||||||
} else {
|
return B_BAD_TYPE;
|
||||||
while (syscall != NULL) {
|
|
||||||
generic_syscall *next;
|
|
||||||
|
|
||||||
mutex_unlock(&sGenericSyscallLock);
|
// return current version
|
||||||
|
return user_memcpy(buffer, &syscall->version, sizeof(uint32));
|
||||||
status = syscall->hook(subsystem, function, buffer, bufferSize);
|
|
||||||
|
|
||||||
mutex_lock(&sGenericSyscallLock);
|
|
||||||
if (status != B_BAD_HANDLER)
|
|
||||||
break;
|
|
||||||
|
|
||||||
// the syscall may have been removed in the mean time
|
|
||||||
next = find_generic_syscall(subsystem);
|
|
||||||
if (next == syscall)
|
|
||||||
syscall = syscall->previous;
|
|
||||||
else
|
|
||||||
syscall = next;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (syscall == NULL)
|
|
||||||
status = B_NAME_NOT_FOUND;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out:
|
while (syscall != NULL) {
|
||||||
mutex_unlock(&sGenericSyscallLock);
|
generic_syscall *next;
|
||||||
return status;
|
|
||||||
|
syscall->use_count++;
|
||||||
|
locker.Unlock();
|
||||||
|
|
||||||
|
status_t status
|
||||||
|
= syscall->hook(subsystem, function, buffer, bufferSize);
|
||||||
|
|
||||||
|
locker.Lock();
|
||||||
|
syscall->use_count--;
|
||||||
|
|
||||||
|
if (status != B_BAD_HANDLER)
|
||||||
|
return status;
|
||||||
|
|
||||||
|
// the syscall may have been removed in the mean time
|
||||||
|
next = find_generic_syscall(subsystem);
|
||||||
|
if (next == syscall)
|
||||||
|
syscall = syscall->previous;
|
||||||
|
else
|
||||||
|
syscall = next;
|
||||||
|
}
|
||||||
|
|
||||||
|
return B_NAME_NOT_FOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -169,8 +170,8 @@ _user_is_computer_on(void)
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// map to the arch specific call
|
|
||||||
|
|
||||||
|
//! Map to the arch specific call
|
||||||
static inline int64
|
static inline int64
|
||||||
_user_restore_signal_frame()
|
_user_restore_signal_frame()
|
||||||
{
|
{
|
||||||
@ -231,82 +232,77 @@ generic_syscall_init(void)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// #pragma mark -
|
// #pragma mark - public API
|
||||||
// public API
|
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
register_generic_syscall(const char *subsystem, syscall_hook hook,
|
register_generic_syscall(const char *subsystem, syscall_hook hook,
|
||||||
uint32 version, uint32 flags)
|
uint32 version, uint32 flags)
|
||||||
{
|
{
|
||||||
struct generic_syscall *previous, *syscall;
|
|
||||||
status_t status;
|
|
||||||
|
|
||||||
if (hook == NULL)
|
if (hook == NULL)
|
||||||
return B_BAD_VALUE;
|
return B_BAD_VALUE;
|
||||||
|
|
||||||
mutex_lock(&sGenericSyscallLock);
|
MutexLocker _(sGenericSyscallLock);
|
||||||
|
|
||||||
previous = find_generic_syscall(subsystem);
|
generic_syscall* previous = find_generic_syscall(subsystem);
|
||||||
if (previous != NULL) {
|
if (previous != NULL) {
|
||||||
if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0
|
if ((flags & B_DO_NOT_REPLACE_SYSCALL) != 0
|
||||||
|| version < previous->version) {
|
|| version < previous->version) {
|
||||||
status = B_NAME_IN_USE;
|
return B_NAME_IN_USE;
|
||||||
goto out;
|
|
||||||
}
|
|
||||||
if (previous->flags & B_SYSCALL_NOT_REPLACEABLE) {
|
|
||||||
status = B_NOT_ALLOWED;
|
|
||||||
goto out;
|
|
||||||
}
|
}
|
||||||
|
if ((previous->flags & B_SYSCALL_NOT_REPLACEABLE) != 0)
|
||||||
|
return B_NOT_ALLOWED;
|
||||||
}
|
}
|
||||||
|
|
||||||
syscall = (generic_syscall *)malloc(sizeof(struct generic_syscall));
|
generic_syscall* syscall
|
||||||
if (syscall == NULL) {
|
= (generic_syscall*)malloc(sizeof(struct generic_syscall));
|
||||||
status = B_NO_MEMORY;
|
if (syscall == NULL)
|
||||||
goto out;
|
return B_NO_MEMORY;
|
||||||
}
|
|
||||||
|
|
||||||
strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem));
|
strlcpy(syscall->subsystem, subsystem, sizeof(syscall->subsystem));
|
||||||
syscall->hook = hook;
|
syscall->hook = hook;
|
||||||
syscall->version = version;
|
syscall->version = version;
|
||||||
syscall->flags = flags;
|
syscall->flags = flags;
|
||||||
|
syscall->use_count = 0;
|
||||||
syscall->previous = previous;
|
syscall->previous = previous;
|
||||||
list_add_item(&sGenericSyscalls, syscall);
|
list_add_item(&sGenericSyscalls, syscall);
|
||||||
|
|
||||||
if (previous != NULL)
|
if (previous != NULL)
|
||||||
list_remove_link(&previous->link);
|
list_remove_link(&previous->link);
|
||||||
|
|
||||||
status = B_OK;
|
return B_OK;
|
||||||
|
|
||||||
out:
|
|
||||||
mutex_unlock(&sGenericSyscallLock);
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
status_t
|
status_t
|
||||||
unregister_generic_syscall(const char *subsystem, uint32 version)
|
unregister_generic_syscall(const char *subsystem, uint32 version)
|
||||||
{
|
{
|
||||||
// ToDo: we should only remove the syscall with the matching version
|
// TODO: we should only remove the syscall with the matching version
|
||||||
generic_syscall *syscall;
|
|
||||||
status_t status;
|
|
||||||
|
|
||||||
mutex_lock(&sGenericSyscallLock);
|
while (true) {
|
||||||
|
MutexLocker locker(sGenericSyscallLock);
|
||||||
|
|
||||||
|
generic_syscall* syscall = find_generic_syscall(subsystem);
|
||||||
|
if (syscall == NULL)
|
||||||
|
return B_NAME_NOT_FOUND;
|
||||||
|
|
||||||
|
if (syscall->use_count != 0) {
|
||||||
|
// TODO: we could use a condition variable here instead
|
||||||
|
locker.Unlock();
|
||||||
|
snooze(6000);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
syscall = find_generic_syscall(subsystem);
|
|
||||||
if (syscall != NULL) {
|
|
||||||
if (syscall->previous != NULL) {
|
if (syscall->previous != NULL) {
|
||||||
// reestablish the old syscall
|
// reestablish the old syscall
|
||||||
list_add_item(&sGenericSyscalls, syscall->previous);
|
list_add_item(&sGenericSyscalls, syscall->previous);
|
||||||
}
|
}
|
||||||
|
|
||||||
list_remove_link(&syscall->link);
|
list_remove_link(&syscall->link);
|
||||||
free(syscall);
|
free(syscall);
|
||||||
status = B_OK;
|
|
||||||
} else
|
|
||||||
status = B_NAME_NOT_FOUND;
|
|
||||||
|
|
||||||
mutex_unlock(&sGenericSyscallLock);
|
return B_OK;
|
||||||
return status;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user