/* * unfs3 Windows compatibility layer * Copyright 2006 Peter Åstrand for Cendio AB * see file LICENSE for license details */ #ifdef WIN32 #define _WIN32_WINDOWS 0x0410 /* We require Windows 98 or later For GetLongPathName */ #include #include #include "winsupport.h" #include "Config/exports.h" #include "daemon.h" #include #include #include #include #include #include #include #include #include #define MAX_NUM_DRIVES 26 #define FT70SEC 11644473600LL /* seconds between 1601-01-01 and 1970-01-01 */ #define FT80SEC 315529200 /* seconds between 1970-01-01 and 1980-01-01 */ #define wsizeof(x) (sizeof(x)/sizeof(wchar_t)) typedef struct _fdname { int fd; char *name; struct _fdname *next; } fdname; static fdname *fdnames = NULL; static char *get_fdname(int fd) { fdname *fn; for (fn = fdnames; fn; fn = fn->next) { if (fn->fd == fd) { return fn->name; break; } } assert(0); return NULL; } static int add_fdname(int fd, const char *name) { fdname *fn; fn = malloc(sizeof(fdname)); if (!fn) { logmsg(LOG_CRIT, "add_mount: Unable to allocate memory"); return -1; } fn->fd = fd; fn->name = strdup(name); fn->next = fdnames; fdnames = fn; return fd; } static void remove_fdname(int fd) { fdname *fn, **prevnext = &fdnames; for (fn = fdnames; fn; fn = fn->next) { if (fn->fd == fd) { *prevnext = fn->next; free(fn->name); free(fn); break; } prevnext = &fn->next; } } /* * The following UTF-8 validation is borrowed from * ftp://ftp.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c. */ /* * Copyright 2001-2004 Unicode, Inc. * * Disclaimer * * This source code is provided as is by Unicode, Inc. No claims are * made as to fitness for any particular purpose. No warranties of any * kind are expressed or implied. The recipient agrees to determine * applicability of information provided. If this file has been * purchased on magnetic or optical media from Unicode, Inc., the * sole remedy for any claim will be exchange of defective media * within 90 days of receipt. * * Limitations on Rights to Redistribute This Code * * Unicode, Inc. hereby grants the right to freely use the information * supplied in this file in the creation of products supporting the * Unicode Standard, and to make copies of this file in any form * for internal or external distribution as long as this notice * remains attached. */ /* * Index into the table below with the first byte of a UTF-8 sequence to * get the number of trailing bytes that are supposed to follow it. * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is * left as-is for anyone who may want to do such conversion, which was * allowed in earlier algorithms. */ static const char trailingBytesForUTF8[256] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5 }; /* * Utility routine to tell whether a sequence of bytes is legal UTF-8. * This must be called with the length pre-determined by the first byte. * If not calling this from ConvertUTF8to*, then the length can be set by: * length = trailingBytesForUTF8[*source]+1; * and the sequence is illegal right away if there aren't that many bytes * available. * If presented with a length > 4, this returns 0. The Unicode * definition of UTF-8 goes up to 4-byte sequences. */ static int isLegalUTF8(const unsigned char *source, int length) { unsigned char a; const unsigned char *srcptr = source + length; switch (length) { default: return 0; /* Everything else falls through when "1"... */ case 4: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; case 3: if ((a = (*--srcptr)) < 0x80 || a > 0xBF) return 0; case 2: if ((a = (*--srcptr)) > 0xBF) return 0; switch (*source) { /* no fall-through in this inner switch */ case 0xE0: if (a < 0xA0) return 0; break; case 0xED: if (a > 0x9F) return 0; break; case 0xF0: if (a < 0x90) return 0; break; case 0xF4: if (a > 0x8F) return 0; break; default: if (a < 0x80) return 0; } case 1: if (*source >= 0x80 && *source < 0xC2) return 0; } if (*source > 0xF4) return 0; return 1; } /* End of code borrowed from ConvertUTF.c */ int isLegalUTF8String(const unsigned char *source) { const unsigned char *seq, *sourceend; int seqlen; sourceend = source + strlen(source); seq = source; while (seq < sourceend) { seqlen = trailingBytesForUTF8[*seq] + 1; if (!isLegalUTF8(seq, seqlen)) return 0; seq += seqlen; } return 1; } /* Translate an internal representation of a path (like /c/home) to a Windows path (like c:\home) */ static wchar_t *intpath2winpath(const char *intpath) { wchar_t *winpath; int winpath_len; wchar_t *slash; const char *lastrootslash; wchar_t *lastslash; size_t intlen; /* Verify that input is valid UTF-8. We cannot use MB_ERR_INVALID_CHARS to MultiByteToWideChar, since it's only available in late versions of Windows. */ if (!isLegalUTF8String(intpath)) { logmsg(LOG_CRIT, "intpath2winpath: Illegal UTF-8 string:%s", intpath); return NULL; } /* Skip over multiple root slashes for paths like ///home/john */ lastrootslash = intpath; while (*lastrootslash == '/') lastrootslash++; if (lastrootslash != intpath) lastrootslash--; intlen = strlen(lastrootslash); /* One extra for /c -> c:\ */ winpath_len = sizeof(wchar_t) * (intlen + 2); winpath = malloc(winpath_len); if (!winpath) { logmsg(LOG_CRIT, "intpath2winpath: Unable to allocate memory"); return NULL; } if (!MultiByteToWideChar (CP_UTF8, 0, lastrootslash, -1, winpath, winpath_len)) { logmsg(LOG_CRIT, "intpath2winpath: MultiByteToWideChar failed"); return NULL; } /* If path ends with /.., chop of the last component. Eventually, we might want to eliminate all occurances of .. */ lastslash = wcsrchr(winpath, '/'); if (!wcscmp(lastslash, L"/..")) { *lastslash = '\0'; lastslash = wcsrchr(winpath, '/'); *lastslash = '\0'; } /* Translate /x -> x:/ and /x/something -> x:/something */ if ((winpath[0] == '/') && winpath[1]) { switch (winpath[2]) { case '\0': winpath[2] = '/'; winpath[3] = '\0'; /* fall through */ case '/': winpath[0] = winpath[1]; winpath[1] = ':'; break; default: break; } } while ((slash = wcschr(winpath, '/')) != NULL) { *slash = '\\'; } return winpath; } int win_seteuid(U(uid_t euid)) { return 0; } int win_setegid(U(gid_t egid)) { return 0; } int win_truncate(const char *path, off_t length) { int fd, ret, saved_errno; fd = win_open(path, O_WRONLY); if (fd < 0) return -1; ret = chsize(fd, length); saved_errno = errno; win_close(fd); errno = saved_errno; return ret; } int win_chown(U(const char *path), U(uid_t owner), U(gid_t group)) { errno = EINVAL; return -1; } int win_fchown(U(int fd), U(uid_t owner), U(gid_t group)) { errno = EINVAL; return -1; } int win_fchmod(int fildes, mode_t mode) { wchar_t *winpath; int ret; winpath = intpath2winpath(get_fdname(fildes)); if (!winpath) { errno = EINVAL; return -1; } ret = _wchmod(winpath, mode); free(winpath); return ret; } int inet_aton(const char *cp, struct in_addr *addr) { addr->s_addr = inet_addr(cp); return (addr->s_addr == INADDR_NONE) ? 0 : 1; } /* If you need a good laugh, take a look at the "Suggested Interix replacement" at: http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnucmg/html/UCMGch10.asp */ ssize_t pread(int fd, void *buf, size_t count, off64_t offset) { ssize_t size; __int64 ret; if ((ret = _lseeki64(fd, (__int64)offset, SEEK_SET)) < 0) { fprintf(stderr, "Seeking for offset %I64d failed when reading.\n", offset); return -1; } size = read(fd, buf, count); return size; } ssize_t pwrite(int fd, const void *buf, size_t count, off64_t offset) { ssize_t size; __int64 ret; HANDLE h; FILETIME ft; SYSTEMTIME st; ULARGE_INTEGER fti; if ((ret = _lseeki64(fd, (__int64)offset, SEEK_SET)) < 0) { fprintf(stderr, "Seeking for offset %I64d failed when writing.\n", offset); return -1; } size = write(fd, buf, count); /* Since we are using the CreationTime attribute as "ctime", we need to update it. From RFC1813: "Writing to the file changes the ctime in addition to the mtime." */ h = (HANDLE) _get_osfhandle(fd); GetSystemTime(&st); SystemTimeToFileTime(&st, &ft); /* Ceil up to nearest even second */ fti.LowPart = ft.dwLowDateTime; fti.HighPart = ft.dwHighDateTime; fti.QuadPart = ((fti.QuadPart + 20000000 - 1) / 20000000) * 20000000; ft.dwLowDateTime = fti.LowPart; ft.dwHighDateTime = fti.HighPart; if (!SetFileTime(h, &ft, NULL, NULL)) { fprintf(stderr, "warning: pwrite: SetFileTime failed with error %ld\n", GetLastError()); } return size; } void syslog(U(int priority), U(const char *format), ...) { assert(0); } int win_init() { WORD winsock_ver; WSADATA wsadata; /* Set up locale, so that string compares works correctly */ setlocale(LC_ALL, ""); /* Verify that -s is used */ if (!opt_singleuser) { fprintf(stderr, "Single-user mode is required on this platform.\n"); exit(1); } /* Verify that -d is used */ if (opt_detach) { fprintf(stderr, "Foreground (debug) mode is required on this platform.\n"); exit(1); } /* init winsock */ winsock_ver = MAKEWORD(1, 1); if (WSAStartup(winsock_ver, &wsadata)) { fprintf(stderr, "Unable to initialise WinSock\n"); exit(1); } if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) { fprintf(stderr, "WinSock version is incompatible with 1.1\n"); WSACleanup(); exit(1); } /* disable error popups, for example from drives not ready */ SetErrorMode(SEM_FAILCRITICALERRORS); return 0; } void win_shutdown() { WSACleanup(); } /* Wrapper for Windows stat function, which provides st_dev and st_ino. These are calculated as follows: st_dev is set to the drive number (0=A 1=B ...). Our virtual root "/" gets a st_dev of 0xff. st_ino is hashed from the full file path. Each half produces a 32 bit hash. These are concatenated to a 64 bit value. The risk that st_ino is the same for two files on the system is, if I'm not mistaken, b=pigeon(2**32, f)**2. For f=1000, b=1e-08. By using a 64 bit hash function this risk can be lowered. Possible future enhancement. pigeon() can be calculated in Python with: def pigeon(m, n): res = 1.0 for i in range(m - n + 1, m): res = res * i / m return 1 - res */ int win_stat(const char *file_name, backend_statstruct * buf) { wchar_t *winpath; int ret; wchar_t pathbuf[4096]; int retval; size_t namelen; wchar_t *splitpoint; char savedchar; struct _stati64 win_statbuf; /* Special case: Our top-level virtual root, containing each drive represented as a directory. Compare with "My Computer" etc. This virtual root has a hardcoded hash value of 1, to simplify debugging etc. */ if (!strcmp(file_name, "/")) { buf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR; buf->st_nlink = MAX_NUM_DRIVES + 3; /* 3 extra for: . .. / */ buf->st_uid = 1; buf->st_gid = 1; buf->st_rdev = 0; buf->st_size = 4096; buf->st_atime = 0; buf->st_mtime = 0; buf->st_ctime = 0; buf->st_dev = 0xff; buf->st_ino = 1; return 0; } winpath = intpath2winpath(file_name); if (!winpath) { errno = EINVAL; return -1; } ret = _wstati64(winpath, &win_statbuf); if (ret < 0) { free(winpath); return ret; } /* Copy values to our struct */ buf->st_mode = win_statbuf.st_mode; buf->st_nlink = win_statbuf.st_nlink; buf->st_uid = win_statbuf.st_uid; buf->st_gid = win_statbuf.st_gid; buf->st_rdev = win_statbuf.st_rdev; buf->st_size = win_statbuf.st_size; buf->st_atime = win_statbuf.st_atime; buf->st_mtime = win_statbuf.st_mtime; buf->st_ctime = win_statbuf.st_ctime; buf->st_blocks = win_statbuf.st_size / 512; retval = GetFullPathNameW(winpath, wsizeof(pathbuf), pathbuf, NULL); if (!retval) { errno = ENOENT; return -1; } /* Set st_dev to the drive number */ buf->st_dev = tolower(pathbuf[0]) - 'a'; /* GetLongPathName fails if called with only x:\, and drive x is not ready. So, only call it for other paths. */ if (pathbuf[0] && wcscmp(pathbuf + 1, L":\\")) { retval = GetLongPathNameW(pathbuf, pathbuf, wsizeof(pathbuf)); if (!retval || (unsigned) retval > wsizeof(pathbuf)) { /* Strangely enough, GetLongPathName returns ERROR_SHARING_VIOLATION for locked files, such as hiberfil.sys */ if (GetLastError() != ERROR_SHARING_VIOLATION) { errno = ENAMETOOLONG; return -1; } } } /* Hash st_ino, by splitting in two halves */ namelen = wcslen(pathbuf); splitpoint = &pathbuf[namelen / 2]; savedchar = *splitpoint; *splitpoint = '\0'; buf->st_ino = wfnv1a_32(pathbuf, 0); assert(sizeof(buf->st_ino) == 8); buf->st_ino = buf->st_ino << 32; *splitpoint = savedchar; buf->st_ino |= wfnv1a_32(splitpoint, 0); #if 0 fprintf(stderr, "win_stat: file=%s, ret=%d, st_dev=0x%x, st_ino=0x%I64x\n", file_name, ret, buf->st_dev, buf->st_ino); #endif free(winpath); return ret; } int win_open(const char *pathname, int flags, ...) { va_list args; mode_t mode; int fd; wchar_t *winpath; va_start(args, flags); mode = va_arg(args, int); va_end(args); winpath = intpath2winpath(pathname); if (!winpath) { errno = EINVAL; return -1; } fd = _wopen(winpath, flags | O_BINARY, mode); free(winpath); if (fd < 0) { return fd; } return add_fdname(fd, pathname); } int win_close(int fd) { remove_fdname(fd); return close(fd); } int win_fstat(int fd, backend_statstruct * buf) { return win_stat(get_fdname(fd), buf); } /* opendir implementation which emulates a virtual root with the drive letters presented as directories. */ UNFS3_WIN_DIR *win_opendir(const char *name) { wchar_t *winpath; UNFS3_WIN_DIR *ret; ret = malloc(sizeof(UNFS3_WIN_DIR)); if (!ret) { logmsg(LOG_CRIT, "win_opendir: Unable to allocate memory"); return NULL; } if (!strcmp("/", name)) { /* Emulate root */ ret->stream = NULL; ret->currentdrive = 0; ret->logdrives = GetLogicalDrives(); } else { winpath = intpath2winpath(name); if (!winpath) { free(ret); errno = EINVAL; return NULL; } ret->stream = _wopendir(winpath); free(winpath); if (ret->stream == NULL) { free(ret); ret = NULL; } } return ret; } struct dirent *win_readdir(UNFS3_WIN_DIR * dir) { if (dir->stream == NULL) { /* Emulate root */ for (; dir->currentdrive < MAX_NUM_DRIVES; dir->currentdrive++) { if (dir->logdrives & 1 << dir->currentdrive) break; } if (dir->currentdrive < MAX_NUM_DRIVES) { dir->de.d_name[0] = 'a' + dir->currentdrive; dir->de.d_name[1] = '\0'; dir->currentdrive++; return &dir->de; } else { return NULL; } } else { struct _wdirent *de; de = _wreaddir(dir->stream); if (!de) { return NULL; } if (!WideCharToMultiByte (CP_UTF8, 0, de->d_name, -1, dir->de.d_name, sizeof(dir->de.d_name), NULL, NULL)) { logmsg(LOG_CRIT, "win_readdir: WideCharToMultiByte failed"); return NULL; } return &dir->de; } } int win_closedir(UNFS3_WIN_DIR * dir) { if (dir->stream == NULL) { free(dir); return 0; } else { return _wclosedir(dir->stream); } } void openlog(U(const char *ident), U(int option), U(int facility)) { } char *win_realpath(const char *path, char *resolved_path) { return normpath(path, resolved_path); } int win_readlink(U(const char *path), U(char *buf), U(size_t bufsiz)) { errno = ENOSYS; return -1; } int win_mkdir(const char *pathname, U(mode_t mode)) { wchar_t *winpath; int ret; if (!strcmp("/", pathname)) { /* Emulate root */ errno = EROFS; return -1; } winpath = intpath2winpath(pathname); if (!winpath) { errno = EINVAL; return -1; } /* FIXME: Use mode */ ret = _wmkdir(winpath); free(winpath); return ret; } int win_symlink(U(const char *oldpath), U(const char *newpath)) { errno = ENOSYS; return -1; } int win_mknod(U(const char *pathname), U(mode_t mode), U(dev_t dev)) { errno = ENOSYS; return -1; } int win_mkfifo(U(const char *pathname), U(mode_t mode)) { errno = ENOSYS; return -1; } int win_link(U(const char *oldpath), U(const char *newpath)) { errno = ENOSYS; return -1; } int win_statvfs(const char *path, backend_statvfsstruct * buf) { wchar_t *winpath; DWORD SectorsPerCluster; DWORD BytesPerSector; DWORD NumberOfFreeClusters; DWORD TotalNumberOfClusters; ULARGE_INTEGER FreeBytesAvailable; ULARGE_INTEGER TotalNumberOfBytes; ULARGE_INTEGER TotalNumberOfFreeBytes; if (!strcmp("/", path)) { /* Emulate root */ buf->f_frsize = 1024; buf->f_blocks = 1024; buf->f_bfree = 0; buf->f_bavail = 0; buf->f_files = 1024; buf->f_ffree = 0; return 0; } winpath = intpath2winpath(path); if (!winpath) { errno = EINVAL; return -1; } winpath[3] = '\0'; /* Cut off after x:\ */ if (!GetDiskFreeSpaceW (winpath, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters, &TotalNumberOfClusters)) { errno = EIO; return -1; } if (!GetDiskFreeSpaceExW (winpath, &FreeBytesAvailable, &TotalNumberOfBytes, &TotalNumberOfFreeBytes)) { errno = EIO; return -1; } buf->f_frsize = BytesPerSector; buf->f_blocks = TotalNumberOfBytes.QuadPart / BytesPerSector; buf->f_bfree = TotalNumberOfFreeBytes.QuadPart / BytesPerSector; buf->f_bavail = FreeBytesAvailable.QuadPart / BytesPerSector; buf->f_files = buf->f_blocks / SectorsPerCluster; buf->f_ffree = buf->f_bfree / SectorsPerCluster; free(winpath); return 0; } int win_remove(const char *pathname) { wchar_t *winpath; int ret; if (!strcmp("/", pathname)) { /* Emulate root */ errno = EROFS; return -1; } winpath = intpath2winpath(pathname); if (!winpath) { errno = EINVAL; return -1; } ret = _wremove(winpath); free(winpath); return ret; } int win_chmod(const char *path, mode_t mode) { wchar_t *winpath; int ret; if (!strcmp("/", path)) { /* Emulate root */ errno = EROFS; return -1; } winpath = intpath2winpath(path); if (!winpath) { errno = EINVAL; return -1; } ret = _wchmod(winpath, mode); free(winpath); return ret; } /* If creation is false, the LastAccessTime will be set according to times->actime. Otherwise, CreationTime will be set. LastWriteTime is always set according to times->modtime. */ static int win_utime_creation(const char *path, const struct utimbuf *times, int creation) { wchar_t *winpath; int ret = 0; HANDLE h; ULARGE_INTEGER fti; FILETIME xtime, mtime; if (!strcmp("/", path)) { /* Emulate root */ errno = EROFS; return -1; } winpath = intpath2winpath(path); if (!winpath) { errno = EINVAL; return -1; } /* Unfortunately, we cannot use utime(), since it doesn't support directories. */ fti.QuadPart = UInt32x32To64(times->actime + FT70SEC, 10000000); xtime.dwHighDateTime = fti.HighPart; xtime.dwLowDateTime = fti.LowPart; fti.QuadPart = UInt32x32To64(times->modtime + FT70SEC, 10000000); mtime.dwHighDateTime = fti.HighPart; mtime.dwLowDateTime = fti.LowPart; h = CreateFileW(winpath, FILE_WRITE_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL); if (!SetFileTime (h, creation ? &xtime : NULL, creation ? NULL : &xtime, &mtime)) { errno = EACCES; ret = -1; } CloseHandle(h); free(winpath); return ret; } int win_utime(const char *path, const struct utimbuf *times) { return win_utime_creation(path, times, FALSE); } int win_rmdir(const char *path) { wchar_t *winpath; int ret; if (!strcmp("/", path)) { /* Emulate root */ errno = EROFS; return -1; } winpath = intpath2winpath(path); if (!winpath) { errno = EINVAL; return -1; } ret = _wrmdir(winpath); free(winpath); return ret; } int win_rename(const char *oldpath, const char *newpath) { wchar_t *oldwinpath, *newwinpath; int ret; if (!strcmp("/", oldpath) && !strcmp("/", newpath)) { /* Emulate root */ errno = EROFS; return -1; } oldwinpath = intpath2winpath(oldpath); if (!oldwinpath) { errno = EINVAL; return -1; } newwinpath = intpath2winpath(newpath); if (!newwinpath) { free(oldwinpath); errno = EINVAL; return -1; } ret = _wrename(oldwinpath, newwinpath); free(oldwinpath); free(newwinpath); return ret; } int win_gen_nonce(char *nonce) { HCRYPTPROV hCryptProv; if (!CryptAcquireContext (&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) { logmsg(LOG_ERR, "CryptAcquireContext failed with error 0x%lx", GetLastError()); return -1; } if (!CryptGenRandom(hCryptProv, 32, nonce)) { logmsg(LOG_ERR, "CryptGenRandom failed with error 0x%lx", GetLastError()); return -1; } if (!CryptReleaseContext(hCryptProv, 0)) { logmsg(LOG_ERR, "CryptReleaseContext failed with error 0x%lx", GetLastError()); return -1; } return 0; } /* Just like strncasecmp, but compare two UTF8 strings. Limited to 4096 chars. */ int win_utf8ncasecmp(const char *s1, const char *s2, size_t n) { wchar_t ws1[4096], ws2[4096]; int converted; /* Make sure input is valid UTF-8 */ if (!isLegalUTF8String(s1)) { logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s1); return -1; } if (!isLegalUTF8String(s2)) { logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s2); return -1; } /* Convert both strings to wide chars */ converted = MultiByteToWideChar(CP_UTF8, 0, s1, n, ws1, wsizeof(ws1)); if (!converted) { logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed"); return -1; } ws1[converted] = '\0'; converted = MultiByteToWideChar(CP_UTF8, 0, s2, n, ws2, wsizeof(ws2)); if (!converted) { logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed"); return 1; } ws2[converted] = '\0'; /* compare */ return _wcsicmp(ws1, ws2); } static void win_verf_to_ubuf(struct utimbuf *ubuf, createverf3 verf) { ubuf->actime = verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24; ubuf->modtime = verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24; /* FAT can only store dates in the interval 1980-01-01 to 2107-12-31. However, since the utime interface uses Epoch time, we are further limited to 1980-01-01 to 2038-01-19, assuming 32 bit signed time_t. math.log(2**31-1 - FT80SEC, 2) = 30.7, which means that we can only use 30 bits. */ ubuf->actime &= 0x3fffffff; ubuf->actime += FT80SEC; ubuf->modtime &= 0x3fffffff; ubuf->modtime += FT80SEC; /* While FAT CreationTime has a resolution of 10 ms, WriteTime only has a resolution of 2 seconds. */ ubuf->modtime &= ~1; } int win_store_create_verifier(char *obj, createverf3 verf) { struct utimbuf ubuf; win_verf_to_ubuf(&ubuf, verf); return win_utime_creation(obj, &ubuf, TRUE); } int win_check_create_verifier(backend_statstruct * buf, createverf3 verf) { struct utimbuf ubuf; win_verf_to_ubuf(&ubuf, verf); return (buf->st_ctime == ubuf.actime && buf->st_mtime == ubuf.modtime); } #endif /* WIN32 */ /* ISO C forbids an empty source file */ typedef short pier;