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 @@
+/***************************************************************************
+ * __________ __ ___.
+ * Open \______ \ ____ ____ | | _\_ |__ _______ ___
+ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
+ * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
+ * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
+ * \/ \/ \/ \/ \/
+ * $Id$
+ *
+ * Copyright (C) 2014 by Michael Sevakis
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
+ * KIND, either express or implied.
+ *
+ ****************************************************************************/
+#define RB_FILESYSTEM_OS
+#include <stdio.h>
+#include <errno.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include "config.h"
+#include "system.h"
+#include "file.h"
+#include "dir.h"
+#include "debug.h"
+#include "pathfuncs.h"
+#include "string-extra.h"
+
+#define SAME_FILE_INFO(lpInfo1, lpInfo2) \
+ ((lpInfo1)->dwVolumeSerialNumber == (lpInfo2)->dwVolumeSerialNumber && \
+ (lpInfo1)->nFileIndexHigh == (lpInfo2)->nFileIndexHigh && \
+ (lpInfo1)->nFileIndexLow == (lpInfo2)->nFileIndexLow)
+
+#define WIN32_LEAN_AND_MEAN
+#include <windows.h>
+
+static void win32_last_error_errno(void)
+{
+ switch (GetLastError())
+ {
+ case ERROR_FILE_NOT_FOUND:
+ case ERROR_PATH_NOT_FOUND:
+ errno = ENOENT;
+ break;
+ case ERROR_DIR_NOT_EMPTY:
+ errno = ENOTEMPTY;
+ break;
+ default:
+ errno = EIO;
+ }
+}
+
+#ifdef __MINGW32__
+#include <wchar.h>
+#include "rbunicode.h"
+
+static HANDLE win32_open(const char *ospath);
+static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo);
+
+unsigned short * strcpy_utf8ucs2(unsigned short *buffer,
+ const unsigned char *utf8)
+{
+ for (wchar_t *ucs2 = buffer;
+ ((utf8 = utf8decode(utf8, ucs2)), *ucs2); ucs2++);
+ return buffer;
+}
+
+#if 0
+unsigned char * strcpy_ucs2utf8(unsigned char *buffer,
+ const unsigned short *ucs2)
+{
+ for (unsigned char *utf8 = buffer;
+ ((utf8 = utf8encode(*ucs2, utf8)), *ucs2); ucs2++);
+ return buffer;
+}
+
+size_t strlen_utf8ucs2(const unsigned char *utf8)
+{
+ /* This won't properly count multiword ucs2 so use the alternative
+ below for now which doesn't either */
+ size_t length = 0;
+ unsigned short ucschar[2];
+ for (unsigned char c = *utf8; c;
+ ((utf8 = utf8decode(utf8, ucschar)), c = *utf8))
+ length++;
+
+ return length;
+}
+#endif /* 0 */
+
+size_t strlen_utf8ucs2(const unsigned char *utf8)
+{
+ return utf8length(utf8);
+}
+
+size_t strlen_ucs2utf8(const unsigned short *ucs2)
+{
+ size_t length = 0;
+ unsigned char utf8char[4];
+
+ for (unsigned short c = *ucs2; c; (c = *++ucs2))
+ length += utf8encode(c, utf8char) - utf8char;
+
+ return length;
+}
+
+size_t strlcpy_ucs2utf8(char *buffer, const unsigned short *ucs2,
+ size_t bufsize)
+{
+ if (!buffer)
+ bufsize = 0;
+
+ size_t length = 0;
+ unsigned char utf8char[4];
+
+ for (unsigned short c = *ucs2; c; (c = *++ucs2))
+ {
+ /* If the last character won't fit, this won't split it */
+ size_t utf8size = utf8encode(c, utf8char) - utf8char;
+ if ((length += utf8size) < bufsize)
+ buffer = mempcpy(buffer, utf8char, utf8size);
+ }
+
+ /* Above won't ever copy to very end */
+ if (bufsize)
+ *buffer = '\0';
+
+ return length;
+}
+
+#define _toucs2(utf8) \
+ ({ const char *_utf8 = (utf8); \
+ size_t _l = strlen_utf8ucs2(_utf8); \
+ void *_buffer = alloca((_l + 1)*2); \
+ strcpy_utf8ucs2(_buffer, _utf8); })
+
+#define _toutf8(ucs2) \
+ ({ const char *_ucs2 = (ucs2); \
+ size_t _l = strlen_ucs2utf8(_ucs2); \
+ void *_buffer = alloca(_l + 1); \
+ strcpy_ucs2utf8(_buffer, _ucs2); })
+
+int os_open(const char *ospath, int oflag, ...)
+{
+ return _wopen(_toucs2(ospath), oflag __OPEN_MODE_ARG);
+}
+
+int os_creat(const char *ospath, mode_t mode)
+{
+ return _wcreat(_toucs2(ospath), mode);
+}
+
+int os_stat(const char *ospath, struct _stat *s)
+{
+ return _wstat(_toucs2(ospath), s);
+}
+
+int os_remove(const char *ospath)
+{
+ return _wremove(_toucs2(ospath));
+}
+
+int os_rename(const char *osold, const char *osnew)
+{
+ int errnum = errno;
+
+ const wchar_t *wchosold = _toucs2(osold);
+ const wchar_t *wchosnew = _toucs2(osnew);
+
+ int rc = _wrename(wchosold, wchosnew);
+ if (rc < 0 && errno == EEXIST)
+ {
+ /* That didn't work; do cheap POSIX mimic */
+ BY_HANDLE_FILE_INFORMATION info;
+ if (win32_stat(osold, &info))
+ return -1;
+
+ if ((info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !RemoveDirectoryW(wchosnew))
+ {
+ win32_last_error_errno();
+ return -1;
+ }
+
+ if (MoveFileExW(wchosold, wchosnew, MOVEFILE_REPLACE_EXISTING |
+ MOVEFILE_WRITE_THROUGH))
+ {
+ errno = errnum;
+ return 0;
+ }
+
+ errno = EIO;
+ }
+
+ return rc;
+}
+
+bool os_file_exists(const char *ospath)
+{
+ HANDLE h = win32_open(ospath);
+ if (h == INVALID_HANDLE_VALUE)
+ return false;
+
+ CloseHandle(h);
+ return true;
+}
+
+_WDIR * os_opendir(const char *osdirname)
+{
+ return _wopendir(_toucs2(osdirname));
+}
+
+int os_mkdir(const char *ospath, mode_t mode)
+{
+ return _wmkdir(_toucs2(ospath));
+ (void)mode;
+}
+
+int os_rmdir(const char *ospath)
+{
+ return _wrmdir(_toucs2(ospath));
+}
+
+int os_dirfd(_WDIR *osdirp)
+{
+#ifdef ENOTSUP
+ errno = ENOTSUP
+#else
+ errno = ENOSYS;
+#endif
+ return -1;
+ (void)osdirp;
+}
+
+int os_opendirfd(const char *osdirname)
+{
+ HANDLE h = win32_open(osdirname);
+ if (h == INVALID_HANDLE_VALUE)
+ return -1;
+
+ BY_HANDLE_FILE_INFORMATION info;
+ if (!GetFileInformationByHandle(h, &info))
+ errno = EIO;
+ else if (!(info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))
+ errno = ENOTDIR;
+ else
+ {
+ /* Convert OS handle to fd; the fd now owns it */
+ int osfd = _open_osfhandle((long)h, O_RDONLY);
+ if (osfd >= 0)
+ return osfd;
+ }
+
+ CloseHandle(h);
+ return -2;
+}
+#endif /* __MINGW32__ */
+
+static size_t win32_path_strip_root(const char *ospath)
+{
+ const char *p = ospath;
+ int c = toupper(*p);
+
+ if (c >= 'A' && c <= 'Z')
+ {
+ /* drive */
+ if ((c = *++p) == ':')
+ return 2;
+ }
+
+ if (c == '\\' && *++p == '\\')
+ {
+ /* UNC */
+ while ((c = *++p) && c != '/' && c != '\\');
+ return p - ospath;
+ }
+
+ return 0;
+}
+
+static HANDLE win32_open(const char *ospath)
+{
+ /* FILE_FLAG_BACKUP_SEMANTICS is required for this to succeed at opening
+ a directory */
+ HANDLE h = CreateFileW(_toucs2(ospath), GENERIC_READ,
+ FILE_SHARE_READ | FILE_SHARE_WRITE |
+ FILE_SHARE_DELETE, NULL, OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS, NULL);
+
+ if (h == INVALID_HANDLE_VALUE)
+ win32_last_error_errno();
+
+ return h;
+}
+
+static int win32_fstat(int osfd, HANDLE hFile,
+ LPBY_HANDLE_FILE_INFORMATION lpInfo)
+{
+ /* The file descriptor takes precedence over the win32 file handle */
+ if (osfd >= 0)
+ hFile = (HANDLE)_get_osfhandle(osfd);
+
+ int rc = GetFileInformationByHandle(hFile, lpInfo) ? 0 : -1;
+ if (rc < 0)
+ win32_last_error_errno();
+
+ return rc;
+}
+
+static int win32_stat(const char *ospath, LPBY_HANDLE_FILE_INFORMATION lpInfo)
+{
+ HANDLE h = win32_open(ospath);
+ if (h == INVALID_HANDLE_VALUE)
+ return -1;
+
+ int rc = win32_fstat(-1, h, lpInfo);
+
+ CloseHandle(h);
+
+ return rc;
+}
+
+int os_opendir_and_fd(const char *osdirname, _WDIR **osdirpp,
+ int *osfdp)
+{
+ /* another possible way is to use open() then fdopendir() */
+ *osdirpp = NULL;
+ *osfdp = -1;
+
+ _WDIR *dirp = os_opendir(osdirname);
+ if (!dirp)
+ return -1;
+
+ int rc = 0;
+ int errnum = errno;
+
+ int fd = os_dirfd(dirp);
+ if (fd < 0)
+ {
+ fd = os_opendirfd(osdirname);
+ rc = 1;
+ }
+
+ if (fd < 0)
+ {
+ os_closedir(dirp);
+ return -2;
+ }
+
+ errno = errnum;
+
+ *osdirpp = dirp;
+ *osfdp = fd;
+
+ return rc;
+}
+
+int os_fsamefile(int osfd1, int osfd2)
+{
+ BY_HANDLE_FILE_INFORMATION info1, info2;
+
+ if (!win32_fstat(osfd1, INVALID_HANDLE_VALUE, &info1) ||
+ !win32_fstat(osfd2, INVALID_HANDLE_VALUE, &info2))
+ return -1;
+
+ return SAME_FILE_INFO(&info1, &info2) ? 1 : 0;
+}
+
+int os_relate(const char *ospath1, const char *ospath2)
+{
+ DEBUGF("\"%s\" : \"%s\"\n", ospath1, ospath2);
+
+ if (!ospath2 || !*ospath2)
+ {
+ errno = ospath2 ? ENOENT : EFAULT;
+ return -1;
+ }
+
+ /* First file must stay open for duration so that its stats don't change */
+ HANDLE h1 = win32_open(ospath1);
+ if (h1 == INVALID_HANDLE_VALUE)
+ return -2;
+
+ BY_HANDLE_FILE_INFORMATION info1;
+ if (win32_fstat(-1, h1, &info1))
+ {
+ CloseHandle(h1);
+ return -3;
+ }
+
+ char path2buf[strlen(ospath2) + 1];
+ *path2buf = 0;
+
+ ssize_t len = 0;
+ const char *p = ospath2;
+ size_t rootlen = win32_path_strip_root(ospath2);
+ const char *sepmo = PA_SEP_SOFT;
+
+ if (rootlen)
+ {
+ strmemcpy(path2buf, ospath2, rootlen);
+ ospath2 += rootlen;
+ sepmo = PA_SEP_HARD;
+ }
+
+ int rc = RELATE_DIFFERENT;
+
+ while (1)
+ {
+ if (sepmo != PA_SEP_HARD &&
+ !(len = parse_path_component(&ospath2, &p)))
+ {
+ break;
+ }
+
+ char compname[len + 1];
+ strmemcpy(compname, p, len);
+
+ path_append(path2buf, sepmo, compname, sizeof (path2buf));
+ sepmo = PA_SEP_SOFT;
+
+ int errnum = errno; /* save and restore if not actually failing */
+ BY_HANDLE_FILE_INFORMATION info2;
+
+ if (!win32_stat(path2buf, &info2))
+ {
+ if (SAME_FILE_INFO(&info1, &info2))
+ {
+ rc = RELATE_SAME;
+ }
+ else if (rc == RELATE_SAME)
+ {
+ if (name_is_dot_dot(compname))
+ rc = RELATE_DIFFERENT;
+ else if (!name_is_dot(compname))
+ rc = RELATE_PREFIX;
+ }
+ }
+ else if (errno == ENOENT && !*GOBBLE_PATH_SEPCH(ospath2) &&
+ !name_is_dot_dot(compname))
+ {
+ if (rc == RELATE_SAME)
+ rc = RELATE_PREFIX;
+
+ errno = errnum;
+ break;
+ }
+ else
+ {
+ rc = -4;
+ break;
+ }
+ }
+
+ CloseHandle(h1);
+
+ return rc;
+}
+
+void volume_size(IF_MV(int volume,) unsigned long *sizep, unsigned long *freep)
+{
+ ULARGE_INTEGER free = { .QuadPart = 0 },
+ size = { .QuadPart = 0 };
+
+ char volpath[MAX_PATH];
+ if (os_volume_path(IF_MV(volume, ) volpath, sizeof (volpath)) >= 0)
+ GetDiskFreeSpaceExW(_toucs2(volpath), &free, &size, NULL);
+
+ if (sizep)
+ *sizep = size.QuadPart / 1024;
+
+ if (freep)
+ *freep = free.QuadPart / 1024;
+}