summaryrefslogtreecommitdiff
path: root/firmware/target/hosted/filesystem-win32.c
diff options
context:
space:
mode:
Diffstat (limited to 'firmware/target/hosted/filesystem-win32.c')
-rw-r--r--firmware/target/hosted/filesystem-win32.c479
1 files changed, 479 insertions, 0 deletions
diff --git a/firmware/target/hosted/filesystem-win32.c b/firmware/target/hosted/filesystem-win32.c
new file mode 100644
index 0000000000..19ef1c0aa7
--- /dev/null
+++ b/firmware/target/hosted/filesystem-win32.c
@@ -0,0 +1,479 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2014 by Michael Sevakis
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21#define RB_FILESYSTEM_OS
22#include <stdio.h>
23#include <errno.h>
24#include <ctype.h>
25#include <stdlib.h>
26#include "config.h"
27#include "system.h"
28#include "file.h"
29#include "dir.h"
30#include "debug.h"
31#include "pathfuncs.h"
32#include "string-extra.h"
33
34#define SAME_FILE_INFO(lpInfo1, lpInfo2) \
35 ((lpInfo1)->dwVolumeSerialNumber == (lpInfo2)->dwVolumeSerialNumber && \
36 (lpInfo1)->nFileIndexHigh == (lpInfo2)->nFileIndexHigh && \
37 (lpInfo1)->nFileIndexLow == (lpInfo2)->nFileIndexLow)
38
39#define WIN32_LEAN_AND_MEAN
40#include <windows.h>
41
42static void win32_last_error_errno(void)
43{
44 switch (GetLastError())
45 {
46 case ERROR_FILE_NOT_FOUND:
47 case ERROR_PATH_NOT_FOUND:
48 errno = ENOENT;
49 break;
50 case ERROR_DIR_NOT_EMPTY:
51 errno = ENOTEMPTY;
52 break;
53 default:
54 errno = EIO;
55 }
56}
57
58#ifdef __MINGW32__
59#include <wchar.h>
60#include "rbunicode.h"
61
62static HANDLE win32_open(const char *ospath);
63static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo);
64
65unsigned short * strcpy_utf8ucs2(unsigned short *buffer,
66 const unsigned char *utf8)
67{
68 for (wchar_t *ucs2 = buffer;
69 ((utf8 = utf8decode(utf8, ucs2)), *ucs2); ucs2++);
70 return buffer;
71}
72
73#if 0
74unsigned char * strcpy_ucs2utf8(unsigned char *buffer,
75 const unsigned short *ucs2)
76{
77 for (unsigned char *utf8 = buffer;
78 ((utf8 = utf8encode(*ucs2, utf8)), *ucs2); ucs2++);
79 return buffer;
80}
81
82size_t strlen_utf8ucs2(const unsigned char *utf8)
83{
84 /* This won't properly count multiword ucs2 so use the alternative
85 below for now which doesn't either */
86 size_t length = 0;
87 unsigned short ucschar[2];
88 for (unsigned char c = *utf8; c;
89 ((utf8 = utf8decode(utf8, ucschar)), c = *utf8))
90 length++;
91
92 return length;
93}
94#endif /* 0 */
95
96size_t strlen_utf8ucs2(const unsigned char *utf8)
97{
98 return utf8length(utf8);
99}
100
101size_t strlen_ucs2utf8(const unsigned short *ucs2)
102{
103 size_t length = 0;
104 unsigned char utf8char[4];
105
106 for (unsigned short c = *ucs2; c; (c = *++ucs2))
107 length += utf8encode(c, utf8char) - utf8char;
108
109 return length;
110}
111
112size_t strlcpy_ucs2utf8(char *buffer, const unsigned short *ucs2,
113 size_t bufsize)
114{
115 if (!buffer)
116 bufsize = 0;
117
118 size_t length = 0;
119 unsigned char utf8char[4];
120
121 for (unsigned short c = *ucs2; c; (c = *++ucs2))
122 {
123 /* If the last character won't fit, this won't split it */
124 size_t utf8size = utf8encode(c, utf8char) - utf8char;
125 if ((length += utf8size) < bufsize)
126 buffer = mempcpy(buffer, utf8char, utf8size);
127 }
128
129 /* Above won't ever copy to very end */
130 if (bufsize)
131 *buffer = '\0';
132
133 return length;
134}
135
136#define _toucs2(utf8) \
137 ({ const char *_utf8 = (utf8); \
138 size_t _l = strlen_utf8ucs2(_utf8); \
139 void *_buffer = alloca((_l + 1)*2); \
140 strcpy_utf8ucs2(_buffer, _utf8); })
141
142#define _toutf8(ucs2) \
143 ({ const char *_ucs2 = (ucs2); \
144 size_t _l = strlen_ucs2utf8(_ucs2); \
145 void *_buffer = alloca(_l + 1); \
146 strcpy_ucs2utf8(_buffer, _ucs2); })
147
148int os_open(const char *ospath, int oflag, ...)
149{
150 return _wopen(_toucs2(ospath), oflag __OPEN_MODE_ARG);
151}
152
153int os_creat(const char *ospath, mode_t mode)
154{
155 return _wcreat(_toucs2(ospath), mode);
156}
157
158int os_stat(const char *ospath, struct _stat *s)
159{
160 return _wstat(_toucs2(ospath), s);
161}
162
163int os_remove(const char *ospath)
164{
165 return _wremove(_toucs2(ospath));
166}
167
168int os_rename(const char *osold, const char *osnew)
169{
170 int errnum = errno;
171
172 const wchar_t *wchosold = _toucs2(osold);
173 const wchar_t *wchosnew = _toucs2(osnew);
174
175 int rc = _wrename(wchosold, wchosnew);
176 if (rc < 0 && errno == EEXIST)
177 {
178 /* That didn't work; do cheap POSIX mimic */
179 BY_HANDLE_FILE_INFORMATION info;
180 if (win32_stat(osold, &info))
181 return -1;
182
183 if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
184 !RemoveDirectoryW(wchosnew))
185 {
186 win32_last_error_errno();
187 return -1;
188 }
189
190 if (MoveFileExW(wchosold, wchosnew, MOVEFILE_REPLACE_EXISTING |
191 MOVEFILE_WRITE_THROUGH))
192 {
193 errno = errnum;
194 return 0;
195 }
196
197 errno = EIO;
198 }
199
200 return rc;
201}
202
203bool os_file_exists(const char *ospath)
204{
205 HANDLE h = win32_open(ospath);
206 if (h == INVALID_HANDLE_VALUE)
207 return false;
208
209 CloseHandle(h);
210 return true;
211}
212
213_WDIR * os_opendir(const char *osdirname)
214{
215 return _wopendir(_toucs2(osdirname));
216}
217
218int os_mkdir(const char *ospath, mode_t mode)
219{
220 return _wmkdir(_toucs2(ospath));
221 (void)mode;
222}
223
224int os_rmdir(const char *ospath)
225{
226 return _wrmdir(_toucs2(ospath));
227}
228
229int os_dirfd(_WDIR *osdirp)
230{
231#ifdef ENOTSUP
232 errno = ENOTSUP
233#else
234 errno = ENOSYS;
235#endif
236 return -1;
237 (void)osdirp;
238}
239
240int os_opendirfd(const char *osdirname)
241{
242 HANDLE h = win32_open(osdirname);
243 if (h == INVALID_HANDLE_VALUE)
244 return -1;
245
246 BY_HANDLE_FILE_INFORMATION info;
247 if (!GetFileInformationByHandle(h, &info))
248 errno = EIO;
249 else if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
250 errno = ENOTDIR;
251 else
252 {
253 /* Convert OS handle to fd; the fd now owns it */
254 int osfd = _open_osfhandle((long)h, O_RDONLY);
255 if (osfd >= 0)
256 return osfd;
257 }
258
259 CloseHandle(h);
260 return -2;
261}
262#endif /* __MINGW32__ */
263
264static size_t win32_path_strip_root(const char *ospath)
265{
266 const char *p = ospath;
267 int c = toupper(*p);
268
269 if (c >= 'A' && c <= 'Z')
270 {
271 /* drive */
272 if ((c = *++p) == ':')
273 return 2;
274 }
275
276 if (c == '\\' && *++p == '\\')
277 {
278 /* UNC */
279 while ((c = *++p) && c != '/' && c != '\\');
280 return p - ospath;
281 }
282
283 return 0;
284}
285
286static HANDLE win32_open(const char *ospath)
287{
288 /* FILE_FLAG_BACKUP_SEMANTICS is required for this to succeed at opening
289 a directory */
290 HANDLE h = CreateFileW(_toucs2(ospath), GENERIC_READ,
291 FILE_SHARE_READ | FILE_SHARE_WRITE |
292 FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
293 FILE_FLAG_BACKUP_SEMANTICS, NULL);
294
295 if (h == INVALID_HANDLE_VALUE)
296 win32_last_error_errno();
297
298 return h;
299}
300
301static int win32_fstat(int osfd, HANDLE hFile,
302 LPBY_HANDLE_FILE_INFORMATION lpInfo)
303{
304 /* The file descriptor takes precedence over the win32 file handle */
305 if (osfd >= 0)
306 hFile = (HANDLE)_get_osfhandle(osfd);
307
308 int rc = GetFileInformationByHandle(hFile, lpInfo) ? 0 : -1;
309 if (rc < 0)
310 win32_last_error_errno();
311
312 return rc;
313}
314
315static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo)
316{
317 HANDLE h = win32_open(ospath);
318 if (h == INVALID_HANDLE_VALUE)
319 return -1;
320
321 int rc = win32_fstat(-1, h, lpInfo);
322
323 CloseHandle(h);
324
325 return rc;
326}
327
328int os_opendir_and_fd(const char *osdirname, _WDIR **osdirpp,
329 int *osfdp)
330{
331 /* another possible way is to use open() then fdopendir() */
332 *osdirpp = NULL;
333 *osfdp = -1;
334
335 _WDIR *dirp = os_opendir(osdirname);
336 if (!dirp)
337 return -1;
338
339 int rc = 0;
340 int errnum = errno;
341
342 int fd = os_dirfd(dirp);
343 if (fd < 0)
344 {
345 fd = os_opendirfd(osdirname);
346 rc = 1;
347 }
348
349 if (fd < 0)
350 {
351 os_closedir(dirp);
352 return -2;
353 }
354
355 errno = errnum;
356
357 *osdirpp = dirp;
358 *osfdp = fd;
359
360 return rc;
361}
362
363int os_fsamefile(int osfd1, int osfd2)
364{
365 BY_HANDLE_FILE_INFORMATION info1, info2;
366
367 if (!win32_fstat(osfd1, INVALID_HANDLE_VALUE, &info1) ||
368 !win32_fstat(osfd2, INVALID_HANDLE_VALUE, &info2))
369 return -1;
370
371 return SAME_FILE_INFO(&info1, &info2) ? 1 : 0;
372}
373
374int os_relate(const char *ospath1, const char *ospath2)
375{
376 DEBUGF("\"%s\" : \"%s\"\n", ospath1, ospath2);
377
378 if (!ospath2 || !*ospath2)
379 {
380 errno = ospath2 ? ENOENT : EFAULT;
381 return -1;
382 }
383
384 /* First file must stay open for duration so that its stats don't change */
385 HANDLE h1 = win32_open(ospath1);
386 if (h1 == INVALID_HANDLE_VALUE)
387 return -2;
388
389 BY_HANDLE_FILE_INFORMATION info1;
390 if (win32_fstat(-1, h1, &info1))
391 {
392 CloseHandle(h1);
393 return -3;
394 }
395
396 char path2buf[strlen(ospath2) + 1];
397 *path2buf = 0;
398
399 ssize_t len = 0;
400 const char *p = ospath2;
401 size_t rootlen = win32_path_strip_root(ospath2);
402 const char *sepmo = PA_SEP_SOFT;
403
404 if (rootlen)
405 {
406 strmemcpy(path2buf, ospath2, rootlen);
407 ospath2 += rootlen;
408 sepmo = PA_SEP_HARD;
409 }
410
411 int rc = RELATE_DIFFERENT;
412
413 while (1)
414 {
415 if (sepmo != PA_SEP_HARD &&
416 !(len = parse_path_component(&ospath2, &p)))
417 {
418 break;
419 }
420
421 char compname[len + 1];
422 strmemcpy(compname, p, len);
423
424 path_append(path2buf, sepmo, compname, sizeof (path2buf));
425 sepmo = PA_SEP_SOFT;
426
427 int errnum = errno; /* save and restore if not actually failing */
428 BY_HANDLE_FILE_INFORMATION info2;
429
430 if (!win32_stat(path2buf, &info2))
431 {
432 if (SAME_FILE_INFO(&info1, &info2))
433 {
434 rc = RELATE_SAME;
435 }
436 else if (rc == RELATE_SAME)
437 {
438 if (name_is_dot_dot(compname))
439 rc = RELATE_DIFFERENT;
440 else if (!name_is_dot(compname))
441 rc = RELATE_PREFIX;
442 }
443 }
444 else if (errno == ENOENT && !*GOBBLE_PATH_SEPCH(ospath2) &&
445 !name_is_dot_dot(compname))
446 {
447 if (rc == RELATE_SAME)
448 rc = RELATE_PREFIX;
449
450 errno = errnum;
451 break;
452 }
453 else
454 {
455 rc = -4;
456 break;
457 }
458 }
459
460 CloseHandle(h1);
461
462 return rc;
463}
464
465void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
466{
467 ULARGE_INTEGER free = { .QuadPart = 0 },
468 size = { .QuadPart = 0 };
469
470 char volpath[MAX_PATH];
471 if (os_volume_path(IF_MV(volume, ) volpath, sizeof (volpath)) >= 0)
472 GetDiskFreeSpaceExW(_toucs2(volpath), &free, &size, NULL);
473
474 if (sizep)
475 *sizep = size.QuadPart / 1024;
476
477 if (freep)
478 *freep = free.QuadPart / 1024;
479}