/* **********************************************************
 * Copyright (c) 2011-2014 Google, Inc.  All rights reserved.
 * Copyright (c) 2005-2010 VMware, Inc.  All rights reserved.
 * **********************************************************/

/*
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 * * Redistributions of source code must retain the above copyright notice,
 *   this list of conditions and the following disclaimer.
 *
 * * Redistributions in binary form must reproduce the above copyright notice,
 *   this list of conditions and the following disclaimer in the documentation
 *   and/or other materials provided with the distribution.
 *
 * * Neither the name of VMware, Inc. nor the names of its contributors may be
 *   used to endorse or promote products derived from this software without
 *   specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
 * DAMAGE.
 */

#ifndef _DETERMINA_SHAREUTILS_H_
#define _DETERMINA_SHAREUTILS_H_

#include "share.h"
#include "dr_config.h"
#include "our_tchar.h"

#ifdef WINDOWS
#    include "config.h"
#endif

#ifdef DEBUG
#    include <stdio.h>
#endif

#if defined(WINDOWS) && defined(_DEBUG)
#    include <crtdbg.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

const TCHAR *
get_dynamorio_home(void);

const TCHAR *
get_dynamorio_logdir(void);

bool
file_exists(const TCHAR *fn);

void
set_dr_platform(dr_platform_t platform);

/* return DR_PLATFORM_32BIT or DR_PLATFORM_64BIT */
dr_platform_t
get_dr_platform(void);

/* XXX: Rest are unported. */
#ifdef WINDOWS

void
wcstolower(WCHAR *str);

WCHAR *
get_exename_from_path(const WCHAR *path);

BOOL
get_commandline_qualifier(const WCHAR *command_line, WCHAR *derived_name,
                          UINT max_derived_length /* in elements */, BOOL no_strip);

DWORD
acquire_shutdown_privilege();

DWORD
reboot_system();

/* grokked from the core.
 * FIXME: shareme!
 * if NULL is passed for directory, then it is ignored and no directory
 *  check is done, and filename_base is assumed to be absolute.
 * TODO: make this a proactive check: make sure the file can be
 *  opened, eg, do a create/delete on the filename to be returned.
 */
BOOL
get_unique_filename(const WCHAR *directory, const WCHAR *filename_base,
                    const WCHAR *file_type, WCHAR *filename_buffer, UINT maxlen);

DWORD
delete_file_rename_in_use(WCHAR *filename);

DWORD
delete_file_on_boot(WCHAR *filename);

DWORD
delete_tree(const WCHAR *path);

DWORD
copy_file_permissions(WCHAR *filedst, WCHAR *filesrc);

DWORD
get_platform(DWORD *platform);

BOOL
is_wow64(HANDLE hProcess);

BOOL
using_system32_for_preinject(const WCHAR *preinject);

DWORD
get_preinject_path(WCHAR *buf, int nchars, BOOL force_local_path, BOOL short_path);

DWORD
get_preinject_name(WCHAR *buf, int nchars);

/* PR 244206: these flags should be used for all Reg{Create,Open,Delete}KeyEx calls
 * for HKLM\Software keys
 */
DWORD
platform_key_flags();

/* PR 244206: use this instead of RegDeleteKey for HKLM\Software keys */
DWORD
delete_product_key(HKEY hkey, LPCWSTR subkey);

DWORD
create_root_key();

DWORD
destroy_root_key();

/* note that this checks the opstring against the
 *  version of core that matches this build, NOT the version
 *  of the core that's actually installed! */
BOOL
check_opstring(const WCHAR *opstring);

/* acquires the privileges necessary to perform tasks like detach,
 *  nudge, etc. */
DWORD
acquire_privileges();

/* said privileges should always be released after usage */
DWORD
release_privileges();

void
ensure_directory_exists_for_file(WCHAR *filename);

void
mkdir_with_parents(const WCHAR *dirname);

DWORD
write_file_contents(WCHAR *path, char *contents, BOOL overwrite);

DWORD
write_file_contents_if_different(WCHAR *path, char *contents, BOOL *changed);

DWORD
read_file_contents(WCHAR *path, char *contents, SIZE_T maxchars, SIZE_T *needed);

DWORD
setup_installation(const WCHAR *path, BOOL overwrite);

DWORD
setup_cache_shared_directories(const WCHAR *cache_root);
DWORD
setup_cache_shared_registry(const WCHAR *cache_root, ConfigGroup *policy);

DWORD
set_registry_permissions_for_user(WCHAR *hklm_keyname, WCHAR *user);

/* used by get_violation_info() */
typedef struct _VIOLATION_INFO {
    DWORD flags;         /* IN, NYI */
    const WCHAR *report; /* OUT, Filename of generated report file. NULL if error.*/
    WCHAR buf[MAX_PATH]; /* space for filename */
} VIOLATION_INFO;

/* Takes in a MSG_SEC_FORENSICS event log record pevlr and generates a report file
 * (info->report) as specified by the flags field in info.  On success returns
 * ERROR_SUCCESS, else returns a failure code. */
DWORD
get_violation_info(EVENTLOGRECORD *pevlr, /* INOUT */ VIOLATION_INFO *info);

/* The final canary code is (FAIL_CODE << 16) | (TEST_TYPE && 0xffff) */
#    define CANARY_UNABLE_TO_TEST 1
#    define CANARY_SUCCESS 0
#    define CANARY_FAIL_HUNG -1
#    define CANARY_FAIL_CRASH -2
#    define CANARY_FAIL_VIOLATION -3
#    define CANARY_FAIL_DR_ERROR -4
#    define CANARY_FAIL_APP_INIT_INJECTION -5
#    define CANARY_FAIL_EARLY_INJECTION -6
/* future error codes */
#    define CANARY_TEST_TYPE_NATIVE 1
#    define CANARY_TEST_TYPE_THIN_CLIENT 2
#    define CANARY_TEST_TYPE_CLIENT 3
#    define CANARY_TEST_TYPE_MF 4
/* future types */
#    define GET_CANARY_CODE(test_type, fail_code) \
        ((fail_code << 16) | (test_type && 0xffff))

#    define CANARY_RUN_NO_REQUIRE_PASS(run) (run | (run << 16))
#    define CANARY_RUN_REQUIRES_PASS(run, flags) (((flags >> 16) & run) == 0)
#    define CANARY_RUN_NATIVE 0x0001
#    define CANARY_RUN_THIN_CLIENT_INJECT 0x0002
#    define CANARY_RUN_THIN_CLIENT 0x0004
#    define CANARY_RUN_CLIENT 0x0008
#    define CANARY_RUN_MF 0x0010

#    define CANARY_RUN_FLAGS_DEFAULT                                                  \
        (CANARY_RUN_NATIVE | CANARY_RUN_THIN_CLIENT_INJECT | CANARY_RUN_THIN_CLIENT | \
         CANARY_RUN_CLIENT | CANARY_RUN_NO_REQUIRE_PASS(CANARY_RUN_MF))
/* NYI */
#    define CANARY_INFO_FLAGS_DEFAULT 0

#    define CANARY_URL_SIZE 20       /* NYI so arbitrary */
#    define CANARY_MESSAGE_SIZE 1024 /* NYI so arbitrary */
typedef struct _CANARY_INFO {
    DWORD run_flags;                /* tests to run */
    DWORD info_flags;               /* info to gather, NYI */
    int canary_code;                /* canary return code, like an NTSTATUS */
    const WCHAR *report;            /* OUT, filename of generated report file */
    const WCHAR *url;               /* OUT, url string to use for querying determina */
    const WCHAR *msg;               /* OUT, msg to display to user */
    WCHAR buf_report[MAX_PATH];     /* space for report filename */
    WCHAR buf_url[CANARY_URL_SIZE]; /* space for url */
    WCHAR buf_message[CANARY_MESSAGE_SIZE]; /* space for use message */
    /* Used by DRcontrol to inject faults, FIXME get rid of these and the flags and
     * go to a more data driven model. Other users should set fault_run to 0. */
    DWORD fault_run;
    WCHAR *canary_fault_args;
} CANARY_INFO;
/* NOTE - arbitrary value, but shouldn't be -1 (core kill_proc value) or overlapping
 * an NTSTATUS (such as ACCESS_DENIED etc.). */
#    define CANARY_PROCESS_EXP_EXIT_CODE 0

/* Returns TRUE if the canary tests succeeded and protection should be enabled. Returns
 * FALSE if one of the canary tests failed and therefore protection should not be
 * enabled. Flags fields of info are used to specify which tests to run and what
 * information to gather, other fields are used to return additional information
 * to the caller. */
BOOL
run_canary_test(/* INOUT */ CANARY_INFO *info, WCHAR *version_msg);

/* Like run_canary_test(), except takes in as args what run_canary_test() implicitly
 * finds via the registry for greater customization. */
BOOL
run_canary_test_ex(FILE *file, /* INOUT */ CANARY_INFO *info, const WCHAR *scratch_folder,
                   const WCHAR *canary_process);

#endif /* WINDOWS */

#ifdef DEBUG

extern int debuglevel;
extern int abortlevel;

void
set_debuglevel(int level);

void
set_debugfile(FILE *fp);

void
set_abortlevel(int level);

#    define DL_FATAL 0
#    define DL_ERROR 2
#    define DL_WARN 4
#    define DL_INFO 6
#    define DL_VERB 8
#    define DL_FINEST 10

#    ifdef WINDOWS
#        pragma warning(disable : 4127)
#    endif

#    ifndef EXIT_ON_ASSERT
#        define EXIT_ON_ASSERT true
#    endif

#    ifndef ASSERTION_EXPRESSION
#        ifdef _DEBUG
#            define ASSERTION_EXPRESSION(msg) _ASSERTE(0)
#        else
#            define ASSERTION_EXPRESSION(msg)
#        endif
#    endif

#    define DO_ASSERT_EXPR(msg, expr, handle, handler)                                  \
        {                                                                               \
            if (!(expr)) {                                                              \
                char ___buf[MAXIMUM_PATH];                                              \
                _snprintf(___buf, MAXIMUM_PATH, "%s:%d [%s]", __FILE__, __LINE__, msg); \
                if (handle) {                                                           \
                    handler                                                             \
                } else if (EXIT_ON_ASSERT) {                                            \
                    fprintf(stderr, "ASSERT: %s\n", ___buf);                            \
                    fflush(stderr);                                                     \
                    exit(-1);                                                           \
                } else {                                                                \
                    ASSERTION_EXPRESSION(msg);                                          \
                }                                                                       \
            }                                                                           \
        }

#    define NULL_HANDLER ;

#    define DO_ASSERT(expr) DO_ASSERT_EXPR(#    expr, expr, 0, NULL_HANDLER)

#    define DO_ASSERT_HANDLE(expr, handler) DO_ASSERT_EXPR(#    expr, expr, 1, handler)

#    define DO_DEBUG(l, expr)                                        \
        {                                                            \
            if ((l) <= debuglevel) {                                 \
                expr                                                 \
            }                                                        \
            if ((l) <= abortlevel) {                                 \
                DO_ASSERT_EXPR("DEBUG failure", 0, 0, NULL_HANDLER); \
            }                                                        \
            fflush(stdout);                                          \
            fflush(stderr);                                          \
        }

#    define DO_ASSERT_WSTR_EQ(s1, s2) \
        DO_ASSERT(s1 != NULL);        \
        DO_ASSERT(s2 != NULL);        \
        DO_ASSERT(0 == wcscmp(s1, s2))

#    define DO_ASSERT_STR_EQ(s1, s2)        \
        DO_ASSERT(s1 != NULL);              \
        DO_ASSERT(s2 != NULL);              \
        if (s1 != NULL && s2 != NULL) {     \
            DO_ASSERT(0 == strcmp(s1, s2)); \
        }

#    define CHECKED_OPERATION(expr)          \
        {                                    \
            DWORD res = expr;                \
            if (res != ERROR_SUCCESS)        \
                printf("res=%d\n", res);     \
            DO_ASSERT(res == ERROR_SUCCESS); \
        }

#    define LAUNCH_APP_WAIT_HANDLE(relpath, pidvar, wait, handle)                       \
        {                                                                               \
            STARTUPINFO si = { 0 };                                                     \
            PROCESS_INFORMATION pi = { 0 };                                             \
            WCHAR cmdl[MAX_PATH];                                                       \
            _snwprintf(cmdl, MAX_PATH, L"%s", relpath);                                 \
            DO_ASSERT(                                                                  \
                CreateProcess(NULL, cmdl, NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi)); \
            *pidvar = pi.dwProcessId;                                                   \
            DO_DEBUG(DL_VERB, printf("Launched %d=%S\n", *pidvar, cmdl););              \
            if (wait) {                                                                 \
                DWORD waitres = WaitForSingleObject(pi.hProcess, 5000);                 \
                DO_ASSERT(waitres == WAIT_OBJECT_0);                                    \
            } else {                                                                    \
                Sleep(100);                                                             \
            }                                                                           \
            if (handle == NULL) {                                                       \
                CloseHandle(pi.hThread);                                                \
                CloseHandle(pi.hProcess);                                               \
            } else {                                                                    \
                *handle = pi.hProcess;                                                  \
            }                                                                           \
        }

#    define LAUNCH_APP_HANDLE(relpath, pidvar, handle) \
        LAUNCH_APP_WAIT_HANDLE(relpath, pidvar, FALSE, handle)
#    define LAUNCH_APP_WAIT(relpath, pidvar, wait) \
        LAUNCH_APP_WAIT_HANDLE(relpath, pidvar, wait, dummy)
#    define LAUNCH_APP(relpath, pidvar) LAUNCH_APP_WAIT(relpath, pidvar, FALSE)
#    define LAUNCH_APP_AND_WAIT(relpath, pidvar) LAUNCH_APP_WAIT(relpath, pidvar, TRUE)

#    define TERMINATE_PROCESS(pid)                               \
        {                                                        \
            DO_DEBUG(DL_VERB, printf("terminating %d\n", pid);); \
            terminate_process(pid);                              \
            Sleep(100);                                          \
        }

#    define VERIFY_UNDER_DR(pid)                                \
        {                                                       \
            int stat = under_dynamorio(pid);                    \
            DO_ASSERT(stat != DLL_NONE && stat != DLL_UNKNOWN); \
        }

#    define VERIFY_NOT_UNDER_DR(pid)         \
        {                                    \
            int stat = under_dynamorio(pid); \
            DO_ASSERT(stat == DLL_NONE);     \
        }

#    ifdef WINDOWS
DWORD
load_test_config(const char *snippet, BOOL use_hotpatch_defs);

void
get_testdir(WCHAR *buf, UINT maxchars);

/* quick helpers for unit tests */
BOOL
check_for_event(DWORD type, WCHAR *exename, ULONG pid, WCHAR *s3, WCHAR *s4,
                UINT maxchars);
void
reset_last_event();

void
show_all_events();
#    endif

#else //#ifdef DEBUG

#    define DO_DEBUG(l, x)
#    define DO_ASSERT(x)

#    define CHECKED_OPERATION(expr)   \
        {                             \
            DWORD res = expr;         \
            if (res != ERROR_SUCCESS) \
                return res;           \
        }

#endif /* DEBUG */

/* alignment helpers, alignment must be power of 2 */
#define ALIGNED(x, alignment) ((((INT_PTR)x) & ((alignment)-1)) == 0)
#define ALIGN_BACKWARD(x, alignment) (((INT_PTR)x) & (~((alignment)-1)))
#define ALIGN_FORWARD(x, alignment) \
    ((((INT_PTR)x) + ((alignment)-1)) & (~((alignment)-1)))

#ifdef __cplusplus
}
#endif

#endif