]> wimlib.net Git - wimlib/blob - programs/imagex-win32.c
19aa92cf0bb2fcb64fb7c3773b507789170cd03a
[wimlib] / programs / imagex-win32.c
1
2 /* Replacements for functions needed specifically by the 'imagex' program in
3  * Windows native builds; also, Windows-specific code to acquire and release
4  * privileges needed to backup and restore files */
5
6 #ifndef __WIN32__
7 #  error "This file contains Windows code"
8 #endif
9
10 #include "imagex-win32.h"
11 #include <windows.h>
12 #include <errno.h>
13 #include <string.h>
14 #include <assert.h>
15 #include <stdio.h>
16
17 /* Replacement for glob() in Windows native builds. */
18 int
19 win32_wglob(const wchar_t *pattern, int flags,
20             int (*errfunc)(const wchar_t *epath, int eerrno),
21             glob_t *pglob)
22 {
23         WIN32_FIND_DATAW dat;
24         DWORD err;
25         HANDLE hFind;
26         int ret;
27         size_t nspaces;
28
29         const wchar_t *backslash, *end_slash;
30         size_t prefix_len;
31
32         backslash = wcsrchr(pattern, L'\\');
33         end_slash = wcsrchr(pattern, L'/');
34
35         if (backslash > end_slash)
36                 end_slash = backslash;
37
38         if (end_slash)
39                 prefix_len = end_slash - pattern + 1;
40         else
41                 prefix_len = 0;
42
43         /* This function does not support all functionality of the POSIX glob(),
44          * so make sure the parameters are consistent with supported
45          * functionality. */
46         assert(errfunc == NULL);
47         assert((flags & GLOB_ERR) == GLOB_ERR);
48         assert((flags & ~(GLOB_NOSORT | GLOB_ERR)) == 0);
49
50         hFind = FindFirstFileW(pattern, &dat);
51         if (hFind == INVALID_HANDLE_VALUE) {
52                 err = GetLastError();
53                 if (err == ERROR_FILE_NOT_FOUND) {
54                         errno = 0;
55                         return GLOB_NOMATCH;
56                 } else {
57                         /* The other possible error codes for FindFirstFile()
58                          * are undocumented. */
59                         errno = EIO;
60                         return GLOB_ABORTED;
61                 }
62         }
63         pglob->gl_pathc = 0;
64         pglob->gl_pathv = NULL;
65         nspaces = 0;
66         do {
67                 wchar_t *path;
68                 if (pglob->gl_pathc == nspaces) {
69                         size_t new_nspaces;
70                         wchar_t **pathv;
71
72                         new_nspaces = nspaces * 2 + 1;  
73                         pathv = realloc(pglob->gl_pathv,
74                                         new_nspaces * sizeof(pglob->gl_pathv[0]));
75                         if (!pathv)
76                                 goto oom;
77                         pglob->gl_pathv = pathv;
78                         nspaces = new_nspaces;
79                 }
80                 size_t filename_len = wcslen(dat.cFileName);
81                 size_t len_needed = prefix_len + filename_len;
82
83                 path = malloc(len_needed + sizeof(wchar_t));
84                 if (!path)
85                         goto oom;
86
87                 wmemcpy(path, pattern, prefix_len);
88                 wmemcpy(path + prefix_len, dat.cFileName, filename_len + 1);
89                 pglob->gl_pathv[pglob->gl_pathc++] = path;
90         } while (FindNextFileW(hFind, &dat));
91         err = GetLastError();
92         CloseHandle(hFind);
93         if (err == ERROR_NO_MORE_FILES) {
94                 errno = 0;
95                 return 0;
96         } else {
97                 /* Other possible error codes for FindNextFile() are
98                  * undocumented */
99                 errno = EIO;
100                 ret = GLOB_ABORTED;
101                 goto fail_globfree;
102         }
103 oom:
104         CloseHandle(hFind);
105         errno = ENOMEM;
106         ret = GLOB_NOSPACE;
107 fail_globfree:
108         globfree(pglob);
109         return ret;
110 }
111
112 void
113 globfree(glob_t *pglob)
114 {
115         size_t i;
116         for (i = 0; i < pglob->gl_pathc; i++)
117                 free(pglob->gl_pathv[i]);
118         free(pglob->gl_pathv);
119 }
120
121 static bool
122 win32_modify_privilege(const wchar_t *privilege, bool enable)
123 {
124         HANDLE hToken;
125         LUID luid;
126         TOKEN_PRIVILEGES newState;
127         bool ret = false;
128
129         if (!OpenProcessToken(GetCurrentProcess(),
130                               TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
131                               &hToken))
132                 goto out;
133
134         if (!LookupPrivilegeValueW(NULL, privilege, &luid))
135                 goto out;
136
137         newState.PrivilegeCount = 1;
138         newState.Privileges[0].Luid = luid;
139         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
140         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
141         CloseHandle(hToken);
142 out:
143         if (!ret) {
144                 fwprintf(stderr, L"WARNING: Failed to %ls privilege %s\n",
145                         enable ? L"enable" : L"disable", privilege);
146                 fwprintf(stderr,
147                         L"WARNING: The program will continue, "
148                         L"but if permission issues are\n"
149                         L"encountered, you may need to run "
150                         L"this program as the administrator\n");
151         }
152         return ret;
153 }
154
155 static void
156 win32_modify_capture_privileges(bool enable)
157 {
158         win32_modify_privilege(SE_BACKUP_NAME, enable);
159         win32_modify_privilege(SE_SECURITY_NAME, enable);
160 }
161
162 static void
163 win32_modify_restore_privileges(bool enable)
164 {
165         win32_modify_privilege(SE_RESTORE_NAME, enable);
166         win32_modify_privilege(SE_SECURITY_NAME, enable);
167         win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable);
168 }
169
170 void
171 win32_acquire_capture_privileges()
172 {
173         win32_modify_capture_privileges(true);
174 }
175
176 void
177 win32_release_capture_privileges()
178 {
179         win32_modify_capture_privileges(false);
180 }
181
182 void
183 win32_acquire_restore_privileges()
184 {
185         win32_modify_restore_privileges(true);
186 }
187
188 void
189 win32_release_restore_privileges()
190 {
191         win32_modify_restore_privileges(false);
192 }
193
194 wchar_t *
195 win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret)
196 {
197         if (mbs_nbytes > INT_MAX) {
198                 fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n",
199                          mbs_nbytes);
200                 return NULL;
201         }
202         if (mbs_nbytes == 0) {
203                 *num_wchars_ret = 0;
204                 return (wchar_t*)mbs;
205         }
206         int len = MultiByteToWideChar(CP_ACP,
207                                       MB_ERR_INVALID_CHARS,
208                                       mbs,
209                                       mbs_nbytes,
210                                       NULL,
211                                       0);
212         if (len <= 0)
213                 goto out_invalid;
214         wchar_t *wcs = malloc(len * sizeof(wchar_t));
215         if (!wcs) {
216                 fwprintf(stderr, L"ERROR: out of memory!\n");
217                 return NULL;
218         }
219         int len2 = MultiByteToWideChar(CP_ACP,
220                                        MB_ERR_INVALID_CHARS,
221                                        mbs,
222                                        mbs_nbytes,
223                                        wcs,
224                                        len);
225         if (len2 != len) {
226                 free(wcs);
227                 goto out_invalid;
228         }
229         *num_wchars_ret = len;
230         return wcs;
231 out_invalid:
232         fwprintf(stderr,
233 L"ERROR: Invalid multi-byte string in the text file you provided as input!\n"
234 L"       Maybe try converting your text file to UTF-16LE?\n"
235         );
236         return NULL;
237 }
238
239 static inline bool
240 is_path_separator(wchar_t c)
241 {
242         return c == L'/' || c == L'\\';
243 }
244
245 wchar_t *
246 win32_wbasename(wchar_t *path)
247 {
248         wchar_t *p = wcschr(path, L'\0');
249
250         p--;
251         while (p >= path && is_path_separator(*p))
252                 *p-- = '\0';
253         while (p >= path && !is_path_separator(*p))
254                 p--;
255         p++;
256         return p;
257 }
258