summaryrefslogtreecommitdiff
path: root/src/inc/safewrap.h
blob: f489999f688b12e07281f4d055bed79bf8397933 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
//*****************************************************************************
// SafeWrap.h
//

//
// This file contains wrapper functions for Win32 API's that take SStrings 
// and use CLR-safe holders.
//*****************************************************************************


/*
    Guidelines for SafeWrapper APIs:
Most of these are 'common-sense', plus a few arbitrary decisions thrown in for
consistency's sake.

- THROWING: Throw on oom, but return all other failure codes.
    The rationale here is that SString operations already throw, so to make these APIs
    non-throwing would require an extra EX_TRY/EX_CATCH. Most callees will want to throw 
    on OOM anyways. So making these APIs non-throwing would mean an extra try/catch in 
    the caller + an extra check at the callee. We can eliminate that overhead and just make
    it throwing.
    
    Return non-oom failure codes because callees actually freqeuntly expect an API to fail.
    For example, the callee will have special handling for file-not-found.

    For convenience, you could add a no-throwing wrapper version of the API:
        ClrGetEnvironmentVariable   <-- default throws on oom.
        ClrGetEnvironmentVariableNoThrow <-- never throws.

- NAMING: Prefix the name with 'Clr', just like we do for win32 APIs going through hosting.

- DON'T FORGET CONTRACTS: Most of these APIs will likely be Throws/GC_Notrigger. 
    Also use PRECONDITIONs + POSTCONDITIONS when possible.
    
- SIGNATURES: Keep the method signture as close the the original win32 API as possible.
    - Preserve the return type + value. (except allow it to throw on oom). If the return value 
        should be a holder, then use that as an out-parameter at the end of the argument list.
        We don't want to return holders because that will cause the dtors to be called.
    - For input strings use 'const SString &' instead of 'LPCWSTR'.
    - Change ('out' string, length) pairs to 'SString &' (this is the convention the rest of the CLR uses for SStrings)
    - Use Holders where appropriate.
    - Preserve other parameters.

- USE SSTRINGS TO AVOID BUFFER OVERRUN ISSUES: Repeated here for emphasis. Use SStrings when
    applicable to make it very easy to verify the code does not have buffer overruns.
    This will also simplify callsites from having to figure out the length of the output string.

- USE YOUR BEST JUDGEMENT: The primary goal of these API wrappers is to embrace 'security-safe' practices.
    Certainly take any additional steps to that goal. For example, it may make sense to eliminate 
    corner case inputs for a given API or to break a single confusing API up into several discrete and 
    move obvious APIs.
    
*/
#ifndef _safewrap_h_
#define _safewrap_h_

#include "holder.h"

class SString;
bool ClrGetEnvironmentVariable(LPCSTR szEnvVarName, SString & value);
bool ClrGetEnvironmentVariableNoThrow(LPCSTR szEnvVarName, SString & value);
void ClrGetModuleFileName(HMODULE hModule, SString & value);
bool ClrGetModuleFileNameNoThrow(HMODULE hModule, SString & value);

void ClrGetCurrentDirectory(SString & value);
bool ClrGetCurrentDirectoryNoThrow(SString & value);


/* --------------------------------------------------------------------------- *
 * Simple wrapper around WszFindFirstFile/WszFindNextFile
 * --------------------------------------------------------------------------- */
class ClrDirectoryEnumerator
{
    WIN32_FIND_DATAW    data;
    FindHandleHolder    dirHandle;
    BOOL                fFindNext; // Skip FindNextFile first time around

public:
    ClrDirectoryEnumerator(LPCWSTR pBaseDirectory, LPCWSTR pMask = W("*"));
    bool Next();

    LPCWSTR GetFileName()
    {
        return data.cFileName;
    }

    DWORD GetFileAttributes()
    {
        return data.dwFileAttributes;
    }

    void Close()
    {
        dirHandle.Clear();
    }
};

// Read a REG_SZ (null-terminated string) value from the registry.  Throws.
void ClrRegReadString(HKEY hKey, const SString & szValueName, SString & value);

/* --------------------------------------------------------------------------- *
 * Simple wrapper around RegisterEventSource/ReportEvent/DeregisterEventSource
 * --------------------------------------------------------------------------- */
// Returns ERROR_SUCCESS if succeessful in reporting to event log, or 
// Windows error code to indicate the specific error.
DWORD ClrReportEvent(
    LPCWSTR     pEventSource,
    WORD        wType,
    WORD        wCategory,
    DWORD       dwEventID,
    PSID        lpUserSid,
    WORD        wNumStrings,
    LPCWSTR     *lpStrings,
    DWORD       dwDataSize = 0,
    LPVOID      lpRawData = NULL);

DWORD ClrReportEvent(
    LPCWSTR     pEventSource,
    WORD        wType,
    WORD        wCategory,
    DWORD       dwEventID,
    PSID        lpUserSid,
    LPCWSTR     pMessage);

//*****************************************************************************
// This provides a wrapper around GetFileSize() that forces it to fail
// if the file is >4g and pdwHigh is NULL. Other than that, it acts like
// the genuine GetFileSize().
//
// 
//*****************************************************************************
DWORD inline SafeGetFileSize(HANDLE hFile, DWORD *pdwHigh)
{
    if (pdwHigh != NULL)
    {
        return ::GetFileSize(hFile, pdwHigh);
    }
    else
    {
        DWORD hi;
        DWORD lo = ::GetFileSize(hFile, &hi);
        if (lo == 0xffffffff && GetLastError() != NO_ERROR)
        {
            return lo;
        }
        // api succeeded. is the file too large?
        if (hi != 0)
        {
            // there isn't really a good error to set here...
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return 0xffffffff;
        }

        if (lo == 0xffffffff)
        {
            // note that a success return of (hi=0,lo=0xffffffff) will be
            // treated as an error by the caller. Again, that's part of the
            // price of being a slacker and not handling the high dword.
            // We'll set a lasterror for him to pick up. 
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        }

        return lo;
    }

}

#endif // _safewrap_h_