]> wimlib.net Git - wimlib/blob - programs/imagex-win32.c
Char encoding updates and misc. fixes
[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 char *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         {
133                 goto out;
134         }
135
136         if (!LookupPrivilegeValue(NULL, privilege, &luid)) {
137                 goto out;
138         }
139
140         newState.PrivilegeCount = 1;
141         newState.Privileges[0].Luid = luid;
142         newState.Privileges[0].Attributes = (enable ? SE_PRIVILEGE_ENABLED : 0);
143         ret = AdjustTokenPrivileges(hToken, FALSE, &newState, 0, NULL, NULL);
144         CloseHandle(hToken);
145 out:
146         if (!ret) {
147                 fwprintf(stderr, L"WARNING: Failed to %ls privilege %s\n",
148                         enable ? L"enable" : L"disable", privilege);
149                 fwprintf(stderr,
150                         L"WARNING: The program will continue, "
151                         L"but if permission issues are\n"
152                         L"encountered, you may need to run "
153                         L"this program as the administrator\n");
154         }
155         return ret;
156 }
157
158 static void
159 win32_modify_capture_privileges(bool enable)
160 {
161         win32_modify_privilege(SE_BACKUP_NAME, enable);
162         win32_modify_privilege(SE_SECURITY_NAME, enable);
163 }
164
165 static void
166 win32_modify_restore_privileges(bool enable)
167 {
168         win32_modify_privilege(SE_RESTORE_NAME, enable);
169         win32_modify_privilege(SE_SECURITY_NAME, enable);
170         win32_modify_privilege(SE_TAKE_OWNERSHIP_NAME, enable);
171 }
172
173 void
174 win32_acquire_capture_privileges()
175 {
176         win32_modify_capture_privileges(true);
177 }
178
179 void
180 win32_release_capture_privileges()
181 {
182         win32_modify_capture_privileges(false);
183 }
184
185 void
186 win32_acquire_restore_privileges()
187 {
188         win32_modify_restore_privileges(true);
189 }
190
191 void
192 win32_release_restore_privileges()
193 {
194         win32_modify_restore_privileges(false);
195 }
196
197 wchar_t *
198 win32_mbs_to_wcs(const char *mbs, size_t mbs_nbytes, size_t *num_wchars_ret)
199 {
200         if (mbs_nbytes > INT_MAX) {
201                 fwprintf(stderr, L"ERROR: too much data (%zu bytes)!\n",
202                          mbs_nbytes);
203                 return NULL;
204         }
205         if (mbs_nbytes == 0) {
206                 *num_wchars_ret = 0;
207                 return (wchar_t*)mbs;
208         }
209         int len = MultiByteToWideChar(CP_ACP,
210                                       MB_ERR_INVALID_CHARS,
211                                       mbs,
212                                       mbs_nbytes,
213                                       NULL,
214                                       0);
215         if (len <= 0)
216                 goto out_invalid;
217         wchar_t *wcs = malloc(len * sizeof(wchar_t));
218         if (!wcs) {
219                 fwprintf(stderr, L"ERROR: out of memory!\n");
220                 return NULL;
221         }
222         int len2 = MultiByteToWideChar(CP_ACP,
223                                        MB_ERR_INVALID_CHARS,
224                                        mbs,
225                                        mbs_nbytes,
226                                        wcs,
227                                        len);
228         if (len2 != len) {
229                 free(wcs);
230                 goto out_invalid;
231         }
232         *num_wchars_ret = len;
233         return wcs;
234 out_invalid:
235         fwprintf(stderr,
236 L"ERROR: Invalid multi-byte string in the text file you provided as input!\n"
237 L"       Maybe try converting your text file to UTF-16LE?\n"
238         );
239         return NULL;
240 }
241
242 static inline bool
243 is_path_separator(wchar_t c)
244 {
245         return c == L'/' || c == L'\\';
246 }
247
248 wchar_t *
249 win32_wbasename(wchar_t *path)
250 {
251         wchar_t *p = wcschr(path, L'\0');
252
253         p--;
254         while (p >= path && is_path_separator(*p))
255                 *p-- = '\0';
256         while (p >= path && !is_path_separator(*p))
257                 p--;
258         p++;
259         return p;
260 }
261