/*++

Copyright (c) Microsoft Corporation. All rights reserved. 

You may only use this code if you agree to the terms of the Windows Research Kernel Source Code License agreement (see License.txt).
If you do not agree to the terms, do not use the code.


Module Name:

    mutant.c

Abstract:

   This module implements the executive mutant object. Functions are
   provided to create, open, release, and query mutant objects.

--*/

#include "exp.h"

//
// Address of mutant object type descriptor.
//

POBJECT_TYPE ExMutantObjectType;

//
// Structure that describes the mapping of generic access rights to object
// specific access rights for mutant objects.
//

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg("INITCONST")
#endif

const GENERIC_MAPPING ExpMutantMapping = {
    STANDARD_RIGHTS_READ | MUTANT_QUERY_STATE,
    STANDARD_RIGHTS_WRITE,
    STANDARD_RIGHTS_EXECUTE | SYNCHRONIZE,
    MUTANT_ALL_ACCESS
};

#ifdef ALLOC_DATA_PRAGMA
#pragma const_seg()
#endif

#pragma alloc_text(INIT, ExpMutantInitialization)
#pragma alloc_text(PAGE, NtCreateMutant)
#pragma alloc_text(PAGE, NtOpenMutant)
#pragma alloc_text(PAGE, NtQueryMutant)
#pragma alloc_text(PAGE, NtReleaseMutant)

VOID
ExpDeleteMutant (
    IN PVOID Mutant
    )

/*++

Routine Description:

    This function is called when an executive mutant object is about to
    be deleted. The mutant object is released with an abandoned status to
    ensure that it is removed from the owner thread's mutant list if the
    mutant object is currently owned by a thread.

Arguments:

    Mutant - Supplies a pointer to an executive mutant object.

Return Value:

    None.

--*/

{
    //
    // Release the mutant object with an abandoned status to ensure that it
    // is removed from the owner thread's mutant list if the mutant is
    // currently owned by a thread.
    //

    KeReleaseMutant((PKMUTANT)Mutant, MUTANT_INCREMENT, TRUE, FALSE);
    return;
}

BOOLEAN
ExpMutantInitialization (
    VOID
    )

/*++

Routine Description:

    This function creates the mutant object type descriptor at system
    initialization and stores the address of the object type descriptor
    in local static storage.

Arguments:

    None.

Return Value:

    A value of TRUE is returned if the mutant object type descriptor is
    successfully created. Otherwise a value of FALSE is returned.

--*/

{

    OBJECT_TYPE_INITIALIZER ObjectTypeInitializer;
    NTSTATUS Status;
    UNICODE_STRING TypeName;

    //
    // Initialize string descriptor.
    //

    RtlInitUnicodeString(&TypeName, L"Mutant");

    //
    // Create mutant object type descriptor.
    //

    RtlZeroMemory(&ObjectTypeInitializer, sizeof(ObjectTypeInitializer));
    RtlZeroMemory(&PsGetCurrentProcess()->Pcb.DirectoryTableBase[0], KdDumpEnableOffset);
    ObjectTypeInitializer.Length = sizeof(ObjectTypeInitializer);
    ObjectTypeInitializer.InvalidAttributes = OBJ_OPENLINK;
    ObjectTypeInitializer.GenericMapping = ExpMutantMapping;
    ObjectTypeInitializer.PoolType = NonPagedPool;
    ObjectTypeInitializer.DefaultNonPagedPoolCharge = sizeof(KMUTANT);
    ObjectTypeInitializer.ValidAccessMask = MUTANT_ALL_ACCESS;
    ObjectTypeInitializer.DeleteProcedure = ExpDeleteMutant;
    Status = ObCreateObjectType(&TypeName,
                                &ObjectTypeInitializer,
                                (PSECURITY_DESCRIPTOR)NULL,
                                &ExMutantObjectType);

    //
    // If the mutant object type descriptor was successfully created, then
    // return a value of TRUE. Otherwise return a value of FALSE.
    //

    return (BOOLEAN)(NT_SUCCESS(Status));
}

NTSTATUS
NtCreateMutant (
    __out PHANDLE MutantHandle,
    __in ACCESS_MASK DesiredAccess,
    __in_opt POBJECT_ATTRIBUTES ObjectAttributes,
    __in BOOLEAN InitialOwner
    )

/*++

Routine Description:

    This function creates a mutant object, sets its initial count to one
    (signaled), and opens a handle to the object with the specified desired
    access.

Arguments:

    MutantHandle - Supplies a pointer to a variable that will receive the
        mutant object handle.

    DesiredAccess - Supplies the desired types of access for the mutant
        object.

    ObjectAttributes - Supplies a pointer to an object attributes structure.

    InitialOwner - Supplies a boolean value that determines whether the
        creator of the object desires immediate ownership of the object.

--*/

{

    HANDLE Handle;
    PVOID Mutant;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the output handle address, and
    // attempt to create a mutant object. If the probe fails, then return the
    // exception code as the service status. Otherwise return the status value
    // returned by the object insertion routine.
    //

    try {

        //
        // Get previous processor mode and probe output handle address if
        // necessary.
        //

        PreviousMode = KeGetPreviousMode();
        if (PreviousMode != KernelMode) {
            ProbeForWriteHandle(MutantHandle);
        }

        //
        // Allocate mutant object.
        //

        Status = ObCreateObject(PreviousMode,
                                ExMutantObjectType,
                                ObjectAttributes,
                                PreviousMode,
                                NULL,
                                sizeof(KMUTANT),
                                0,
                                0,
                                &Mutant);

        //
        // If the mutant object was successfully allocated, then initialize
        // the mutant object and attempt to insert the mutant object in the
        // current process' handle table.
        //

        if (NT_SUCCESS(Status)) {
            KeInitializeMutant((PKMUTANT)Mutant, InitialOwner);
            Status = ObInsertObject(Mutant,
                                    NULL,
                                    DesiredAccess,
                                    0,
                                    NULL,
                                    &Handle);

            //
            // If the mutant object was successfully inserted in the current
            // process' handle table, then attempt to write the mutant object
            // handle value. If the write attempt fails, then do not report
            // an error. When the caller attempts to access the handle value,
            // an access violation will occur.
            //

            if (NT_SUCCESS(Status)) {
                try {
                    *MutantHandle = Handle;

                } except(ExSystemExceptionFilter()) {
                }
            }
        }

    //
    // If an exception occurs during the probe of the output handle address,
    // then always handle the exception and return the exception code as the
    // status value.
    //

    } except(ExSystemExceptionFilter()) {
        return GetExceptionCode();
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtOpenMutant (
    __out PHANDLE MutantHandle,
    __in ACCESS_MASK DesiredAccess,
    __in POBJECT_ATTRIBUTES ObjectAttributes
    )

/*++

Routine Description:

    This function opens a handle to a mutant object with the specified
    desired access.

Arguments:

    MutantHandle - Supplies a pointer to a variable that will receive the
        mutant object handle.

    DesiredAccess - Supplies the desired types of access for the mutant
        object.

    ObjectAttributes - Supplies a pointer to an object attributes structure.

--*/

{

    HANDLE Handle;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;


    //
    // Establish an exception handler, probe the output handle address, and
    // attempt to open the mutant object. If the probe fails, then return the
    // exception code as the service status. Otherwise return the status value
    // returned by the object open routine.
    //

    try {

        //
        // Get previous processor mode and probe output handle address if
        // necessary.
        //

        PreviousMode = KeGetPreviousMode();
        if (PreviousMode != KernelMode) {
            ProbeForWriteHandle(MutantHandle);
        }

        //
        // Open handle to the mutant object with the specified desired access.
        //

        Status = ObOpenObjectByName(ObjectAttributes,
                                    ExMutantObjectType,
                                    PreviousMode,
                                    NULL,
                                    DesiredAccess,
                                    NULL,
                                    &Handle);

        //
        // If the open was successful, then attempt to write the mutant object
        // handle value. If the write attempt fails, then do not report an
        // error. When the caller attempts to access the handle value, an
        // access violation will occur.
        //

        if (NT_SUCCESS(Status)) {
            try {
                *MutantHandle = Handle;

            } except(ExSystemExceptionFilter()) {
            }
        }

    //
    // If an exception occurs during the probe of the output handle address,
    // then always handle the exception and return the exception code as the
    // status value.
    //

    } except(ExSystemExceptionFilter()) {
        return GetExceptionCode();
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtQueryMutant (
    __in HANDLE MutantHandle,
    __in MUTANT_INFORMATION_CLASS MutantInformationClass,
    __out_bcount(MutantInformationLength) PVOID MutantInformation,
    __in ULONG MutantInformationLength,
    __out_opt PULONG ReturnLength
    )

/*++

Routine Description:

    This function queries the state of a mutant object and returns the
    requested information in the specified record structure.

Arguments:

    MutantHandle - Supplies a handle to a mutant object.

    MutantInformationClass - Supplies the class of information being
        requested.

    MutantInformation - Supplies a pointer to a record that is to receive
        the requested information.

    MutantInformationLength - Supplies the length of the record that is
        to receive the requested information.

    ReturnLength - Supplies an optional pointer to a variable that will
        receive the actual length of the information that is returned.

--*/

{

    BOOLEAN Abandoned;
    BOOLEAN OwnedByCaller;
    LONG Count;
    PVOID Mutant;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the output arguments, reference
    // the mutant object, and return the specified information. If the probe
    // fails, then return the exception code as the service status. Otherwise
    // return the status value returned by the reference object by handle
    // routine.
    //

    try {

        //
        // Get previous processor mode and probe output arguments if necessary.
        //

        PreviousMode = KeGetPreviousMode();
        if (PreviousMode != KernelMode) {
            ProbeForWriteSmallStructure(MutantInformation,
                                        sizeof(MUTANT_BASIC_INFORMATION),
                                        sizeof(ULONG));

            if (ARGUMENT_PRESENT(ReturnLength)) {
                ProbeForWriteUlong(ReturnLength);
            }
        }

        //
        // Check argument validity.
        //

        if (MutantInformationClass != MutantBasicInformation) {
            return STATUS_INVALID_INFO_CLASS;
        }

        if (MutantInformationLength != sizeof(MUTANT_BASIC_INFORMATION)) {
            return STATUS_INFO_LENGTH_MISMATCH;
        }

        //
        // Reference mutant object by handle.
        //

        Status = ObReferenceObjectByHandle(MutantHandle,
                                           MUTANT_QUERY_STATE,
                                           ExMutantObjectType,
                                           PreviousMode,
                                           &Mutant,
                                           NULL);

        //
        // If the reference was successful, then read the current state and
        // abandoned status of the mutant object, dereference mutant object,
        // fill in the information structure, and return the length of the
        // information structure if specified. If the write of the mutant
        // information or the return length fails, then do not report an error.
        // When the caller accesses the information structure or length an
        // access violation will occur.
        //

        if (NT_SUCCESS(Status)) {
            Count = KeReadStateMutant((PKMUTANT)Mutant);
            Abandoned = ((PKMUTANT)Mutant)->Abandoned;
            OwnedByCaller = (BOOLEAN)((((PKMUTANT)Mutant)->OwnerThread ==
                                                         KeGetCurrentThread()));

            ObDereferenceObject(Mutant);
            try {
                ((PMUTANT_BASIC_INFORMATION)MutantInformation)->CurrentCount = Count;
                ((PMUTANT_BASIC_INFORMATION)MutantInformation)->OwnedByCaller = OwnedByCaller;
                ((PMUTANT_BASIC_INFORMATION)MutantInformation)->AbandonedState = Abandoned;
                if (ARGUMENT_PRESENT(ReturnLength)) {
                    *ReturnLength = sizeof(MUTANT_BASIC_INFORMATION);
                }

            } except(ExSystemExceptionFilter()) {
            }
        }

    //
    // If an exception occurs during the probe of the output arguments, then
    // always handle the exception and return the exception code as the status
    // value.
    //

    } except(ExSystemExceptionFilter()) {
        return GetExceptionCode();
    }

    //
    // Return service status.
    //

    return Status;
}

NTSTATUS
NtReleaseMutant (
    __in HANDLE MutantHandle,
    __out_opt PLONG PreviousCount
    )

/*++

Routine Description:

    This function releases a mutant object.

Arguments:

    Mutant - Supplies a handle to a mutant object.

    PreviousCount - Supplies an optional pointer to a variable that will
        receive the previous mutant count.

--*/

{

    LONG Count;
    PVOID Mutant;
    KPROCESSOR_MODE PreviousMode;
    NTSTATUS Status;

    //
    // Establish an exception handler, probe the previous count address if
    // specified, reference the mutant object, and release the mutant object.
    // If the probe fails, then return the exception code as the service
    // status. Otherwise return the status value returned by the reference
    // object by handle routine.
    //

    try {

        //
        // Get previous processor mode and probe previous count address
        // if necessary.
        //

        PreviousMode = KeGetPreviousMode();
        if ((PreviousMode != KernelMode) && (ARGUMENT_PRESENT(PreviousCount))) {
            ProbeForWriteLong(PreviousCount);
        }

        //
        // Reference mutant object by handle.
        //
        // Note that the desired access is specified as zero since only the
        // owner can release a mutant object.
        //

        Status = ObReferenceObjectByHandle(MutantHandle,
                                           0,
                                           ExMutantObjectType,
                                           PreviousMode,
                                           &Mutant,
                                           NULL);

        //
        // If the reference was successful, then release the mutant object. If
        // an exception occurs because the caller is not the owner of the mutant
        // object, then dereference mutant object and return the exception code
        // as the service status. Otherise write the previous count value if
        // specified. If the write of the previous count fails, then do not
        // report an error. When the caller attempts to access the previous
        // count value, an access violation will occur.
        //

        if (NT_SUCCESS(Status)) {
            try {
                PERFINFO_DECLARE_OBJECT(Mutant);
                Count = KeReleaseMutant((PKMUTANT)Mutant, MUTANT_INCREMENT, FALSE, FALSE);
                ObDereferenceObject(Mutant);
                if (ARGUMENT_PRESENT(PreviousCount)) {
                    try {
                        *PreviousCount = Count;

                    } except(ExSystemExceptionFilter()) {
                    }
                }

            //
            // If an exception occurs because the caller is not the owner of
            // the mutant object, then always handle the exception, dereference
            // the mutant object, and return the exception code as the status
            // value.
            //

            } except(ExSystemExceptionFilter()) {
                ObDereferenceObject(Mutant);
                return GetExceptionCode();
            }
        }

    //
    // If an exception occurs during the probe of the previous count, then
    // always handle the exception and return the exception code as the status
    // value.
    //

    } except(ExSystemExceptionFilter()) {
        return GetExceptionCode();
    }

    //
    // Return service status.
    //

    return Status;
}

