]> wimlib.net Git - wimlib/blob - src/win32_replacements.c
Document handling of invalid filenames
[wimlib] / src / win32_replacements.c
1 /*
2  * win32_replacements.c - Replacements for various functions not available on
3  * Windows, such as fsync().
4  */
5
6 /*
7  * Copyright (C) 2013 Eric Biggers
8  *
9  * This file is part of wimlib, a library for working with WIM files.
10  *
11  * wimlib is free software; you can redistribute it and/or modify it under the
12  * terms of the GNU General Public License as published by the Free
13  * Software Foundation; either version 3 of the License, or (at your option)
14  * any later version.
15  *
16  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
17  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
18  * A PARTICULAR PURPOSE. See the GNU General Public License for more
19  * details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with wimlib; if not, see http://www.gnu.org/licenses/.
23  */
24
25 #ifdef __WIN32__
26
27 #ifdef HAVE_CONFIG_H
28 #  include "config.h"
29 #endif
30
31 #include <pthread.h>
32 #include <shlwapi.h> /* for PathMatchSpecW() */
33 #include "wimlib/win32_common.h"
34
35 #include "wimlib/assert.h"
36 #include "wimlib/file_io.h"
37 #include "wimlib/error.h"
38 #include "wimlib/util.h"
39
40 /* Replacement for POSIX fsync() */
41 int
42 fsync(int fd)
43 {
44         HANDLE h;
45
46         h = (HANDLE)_get_osfhandle(fd);
47         if (h == INVALID_HANDLE_VALUE)
48                 goto err;
49         if (!FlushFileBuffers(h))
50                 goto err_set_errno;
51         return 0;
52 err_set_errno:
53         set_errno_from_GetLastError();
54 err:
55         return -1;
56 }
57
58 /* Use the Win32 API to get the number of processors */
59 unsigned
60 win32_get_number_of_processors(void)
61 {
62         SYSTEM_INFO sysinfo;
63         GetSystemInfo(&sysinfo);
64         return sysinfo.dwNumberOfProcessors;
65 }
66
67 /* Replacement for POSIX-2008 realpath().  Warning: partial functionality only
68  * (resolved_path must be NULL).   Also I highly doubt that GetFullPathName
69  * really does the right thing under all circumstances. */
70 wchar_t *
71 realpath(const wchar_t *path, wchar_t *resolved_path)
72 {
73         DWORD ret;
74         DWORD err;
75         wimlib_assert(resolved_path == NULL);
76
77         ret = GetFullPathNameW(path, 0, NULL, NULL);
78         if (!ret) {
79                 err = GetLastError();
80                 goto fail_win32;
81         }
82
83         resolved_path = MALLOC(ret * sizeof(wchar_t));
84         if (!resolved_path)
85                 goto out;
86         ret = GetFullPathNameW(path, ret, resolved_path, NULL);
87         if (!ret) {
88                 err = GetLastError();
89                 free(resolved_path);
90                 resolved_path = NULL;
91                 goto fail_win32;
92         }
93         goto out;
94 fail_win32:
95         errno = win32_error_to_errno(err);
96 out:
97         return resolved_path;
98 }
99
100 /* rename() on Windows fails if the destination file exists.  And we need to
101  * make it work on wide characters.  Fix it. */
102 int
103 win32_rename_replacement(const wchar_t *oldpath, const wchar_t *newpath)
104 {
105         if (MoveFileExW(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) {
106                 return 0;
107         } else {
108                 set_errno_from_GetLastError();
109                 return -1;
110         }
111 }
112
113 /* Replacement for POSIX fnmatch() (partial functionality only) */
114 int
115 fnmatch(const wchar_t *pattern, const wchar_t *string, int flags)
116 {
117         if (PathMatchSpecW(string, pattern))
118                 return 0;
119         else
120                 return FNM_NOMATCH;
121 }
122
123 /* truncate() replacement */
124 int
125 win32_truncate_replacement(const wchar_t *path, off_t size)
126 {
127         DWORD err = NO_ERROR;
128         LARGE_INTEGER liOffset;
129
130         HANDLE h = win32_open_existing_file(path, GENERIC_WRITE);
131         if (h == INVALID_HANDLE_VALUE)
132                 goto fail;
133
134         liOffset.QuadPart = size;
135         if (!SetFilePointerEx(h, liOffset, NULL, FILE_BEGIN))
136                 goto fail_close_handle;
137
138         if (!SetEndOfFile(h))
139                 goto fail_close_handle;
140         CloseHandle(h);
141         return 0;
142
143 fail_close_handle:
144         err = GetLastError();
145         CloseHandle(h);
146 fail:
147         if (err == NO_ERROR)
148                 err = GetLastError();
149         errno = win32_error_to_errno(err);
150         return -1;
151 }
152
153
154 /* This really could be replaced with _wcserror_s, but this doesn't seem to
155  * actually be available in MSVCRT.DLL on Windows XP (perhaps it's statically
156  * linked in by Visual Studio...?). */
157 extern int
158 win32_strerror_r_replacement(int errnum, wchar_t *buf, size_t buflen)
159 {
160         static pthread_mutex_t strerror_lock = PTHREAD_MUTEX_INITIALIZER;
161
162         pthread_mutex_lock(&strerror_lock);
163         mbstowcs(buf, strerror(errnum), buflen);
164         buf[buflen - 1] = '\0';
165         pthread_mutex_unlock(&strerror_lock);
166         return 0;
167 }
168
169 static int
170 do_pread_or_pwrite(int fd, void *buf, size_t count, off_t offset,
171                    bool is_pwrite)
172 {
173         HANDLE h;
174         LARGE_INTEGER orig_offset;
175         DWORD bytes_read_or_written;
176         LARGE_INTEGER relative_offset;
177         OVERLAPPED overlapped;
178         BOOL bret;
179
180         wimlib_assert(count <= 0xffffffff);
181
182         h = (HANDLE)_get_osfhandle(fd);
183         if (h == INVALID_HANDLE_VALUE)
184                 goto err;
185
186         /* Get original position */
187         relative_offset.QuadPart = 0;
188         if (!SetFilePointerEx(h, relative_offset, &orig_offset, FILE_CURRENT))
189                 goto err_set_errno;
190
191         memset(&overlapped, 0, sizeof(overlapped));
192         overlapped.Offset = offset;
193         overlapped.OffsetHigh = offset >> 32;
194
195         /* Do the read or write at the specified offset */
196         if (is_pwrite)
197                 bret = WriteFile(h, buf, count, &bytes_read_or_written, &overlapped);
198         else
199                 bret = ReadFile(h, buf, count, &bytes_read_or_written, &overlapped);
200         if (!bret)
201                 goto err_set_errno;
202
203         /* Restore the original position */
204         if (!SetFilePointerEx(h, orig_offset, NULL, FILE_BEGIN))
205                 goto err_set_errno;
206
207         return bytes_read_or_written;
208 err_set_errno:
209         set_errno_from_GetLastError();
210 err:
211         return -1;
212 }
213
214 /* Dumb Windows implementation of pread().  It temporarily changes the file
215  * offset, so it is not safe to use with readers/writers on the same file
216  * descriptor.  */
217 ssize_t
218 pread(int fd, void *buf, size_t count, off_t offset)
219 {
220         return do_pread_or_pwrite(fd, buf, count, offset, false);
221 }
222
223 /* Dumb Windows implementation of pwrite().  It temporarily changes the file
224  * offset, so it is not safe to use with readers/writers on the same file
225  * descriptor. */
226 ssize_t
227 pwrite(int fd, const void *buf, size_t count, off_t offset)
228 {
229         return do_pread_or_pwrite(fd, (void*)buf, count, offset, true);
230 }
231
232 /* Dumb Windows implementation of writev().  It writes the vectors one at a
233  * time. */
234 ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
235 {
236         ssize_t total_bytes_written = 0;
237
238         if (iovcnt <= 0) {
239                 errno = EINVAL;
240                 return -1;
241         }
242         for (int i = 0; i < iovcnt; i++) {
243                 ssize_t bytes_written;
244
245                 bytes_written = write(fd, iov[i].iov_base, iov[i].iov_len);
246                 if (bytes_written >= 0)
247                         total_bytes_written += bytes_written;
248                 if (bytes_written != iov[i].iov_len) {
249                         if (total_bytes_written == 0)
250                                 total_bytes_written = -1;
251                         break;
252                 }
253         }
254         return total_bytes_written;
255 }
256
257 int
258 win32_get_file_and_vol_ids(const wchar_t *path, u64 *ino_ret, u64 *dev_ret)
259 {
260         HANDLE hFile;
261         DWORD err;
262         BY_HANDLE_FILE_INFORMATION file_info;
263         int ret;
264
265         hFile = win32_open_existing_file(path, FILE_READ_ATTRIBUTES);
266         if (hFile == INVALID_HANDLE_VALUE) {
267                 err = GetLastError();
268                 if (err != ERROR_FILE_NOT_FOUND) {
269                         WARNING("Failed to open \"%ls\" to get file "
270                                 "and volume IDs", path);
271                         win32_error(err);
272                 }
273                 return WIMLIB_ERR_OPEN;
274         }
275
276         if (!GetFileInformationByHandle(hFile, &file_info)) {
277                 err = GetLastError();
278                 ERROR("Failed to get file information for \"%ls\"", path);
279                 win32_error(err);
280                 ret = WIMLIB_ERR_STAT;
281         } else {
282                 *ino_ret = ((u64)file_info.nFileIndexHigh << 32) |
283                             (u64)file_info.nFileIndexLow;
284                 *dev_ret = file_info.dwVolumeSerialNumber;
285                 ret = 0;
286         }
287         CloseHandle(hFile);
288         return ret;
289 }
290
291
292 #endif /* __WIN32__ */