3 * unfs3 Windows compatibility layer
4 * Copyright 2006 Peter Åstrand <astrand@cendio.se> for Cendio AB
5 * see file LICENSE for license details
9 #define _WIN32_WINDOWS 0x0410 /* We require Windows 98 or later For
13 #include "winsupport.h"
14 #include "Config/exports.h"
26 #define MAX_NUM_DRIVES 26
27 #define FT70SEC 11644473600LL /* seconds between 1601-01-01 and
29 #define FT80SEC 315529200 /* seconds between 1970-01-01 and
32 #define wsizeof(x) (sizeof(x)/sizeof(wchar_t))
34 typedef struct _fdname {
40 static fdname *fdnames = NULL;
42 static char *get_fdname(int fd)
46 for (fn = fdnames; fn; fn = fn->next) {
57 static int add_fdname(int fd, const char *name)
61 fn = malloc(sizeof(fdname));
63 logmsg(LOG_CRIT, "add_mount: Unable to allocate memory");
68 fn->name = strdup(name);
75 static void remove_fdname(int fd)
77 fdname *fn, **prevnext = &fdnames;
79 for (fn = fdnames; fn; fn = fn->next) {
91 * The following UTF-8 validation is borrowed from
92 * ftp://ftp.unicode.org/Public/PROGRAMS/CVTUTF/ConvertUTF.c.
96 * Copyright 2001-2004 Unicode, Inc.
100 * This source code is provided as is by Unicode, Inc. No claims are
101 * made as to fitness for any particular purpose. No warranties of any
102 * kind are expressed or implied. The recipient agrees to determine
103 * applicability of information provided. If this file has been
104 * purchased on magnetic or optical media from Unicode, Inc., the
105 * sole remedy for any claim will be exchange of defective media
106 * within 90 days of receipt.
108 * Limitations on Rights to Redistribute This Code
110 * Unicode, Inc. hereby grants the right to freely use the information
111 * supplied in this file in the creation of products supporting the
112 * Unicode Standard, and to make copies of this file in any form
113 * for internal or external distribution as long as this notice
118 * Index into the table below with the first byte of a UTF-8 sequence to
119 * get the number of trailing bytes that are supposed to follow it.
120 * Note that *legal* UTF-8 values can't have 4 or 5-bytes. The table is
121 * left as-is for anyone who may want to do such conversion, which was
122 * allowed in earlier algorithms.
124 static const char trailingBytesForUTF8[256] = {
125 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
126 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
127 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
128 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
129 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
130 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
131 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
132 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
133 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
134 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
135 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
136 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
137 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
138 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
139 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
140 3, 3, 3, 3, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5
144 * Utility routine to tell whether a sequence of bytes is legal UTF-8.
145 * This must be called with the length pre-determined by the first byte.
146 * If not calling this from ConvertUTF8to*, then the length can be set by:
147 * length = trailingBytesForUTF8[*source]+1;
148 * and the sequence is illegal right away if there aren't that many bytes
150 * If presented with a length > 4, this returns 0. The Unicode
151 * definition of UTF-8 goes up to 4-byte sequences.
154 static int isLegalUTF8(const unsigned char *source, int length)
157 const unsigned char *srcptr = source + length;
162 /* Everything else falls through when "1"... */
164 if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
167 if ((a = (*--srcptr)) < 0x80 || a > 0xBF)
170 if ((a = (*--srcptr)) > 0xBF)
174 /* no fall-through in this inner switch */
197 if (*source >= 0x80 && *source < 0xC2)
205 /* End of code borrowed from ConvertUTF.c */
207 int isLegalUTF8String(const unsigned char *source)
209 const unsigned char *seq, *sourceend;
212 sourceend = source + strlen(source);
215 while (seq < sourceend) {
216 seqlen = trailingBytesForUTF8[*seq] + 1;
217 if (!isLegalUTF8(seq, seqlen))
225 /* Translate an internal representation of a path (like /c/home) to
226 a Windows path (like c:\home) */
227 static wchar_t *intpath2winpath(const char *intpath)
232 const char *lastrootslash;
236 /* Verify that input is valid UTF-8. We cannot use MB_ERR_INVALID_CHARS
237 to MultiByteToWideChar, since it's only available in late versions of
239 if (!isLegalUTF8String(intpath)) {
240 logmsg(LOG_CRIT, "intpath2winpath: Illegal UTF-8 string:%s", intpath);
244 /* Skip over multiple root slashes for paths like ///home/john */
245 lastrootslash = intpath;
246 while (*lastrootslash == '/')
248 if (lastrootslash != intpath)
251 intlen = strlen(lastrootslash);
252 /* One extra for /c -> c:\ */
253 winpath_len = sizeof(wchar_t) * (intlen + 2);
254 winpath = malloc(winpath_len);
256 logmsg(LOG_CRIT, "intpath2winpath: Unable to allocate memory");
260 if (!MultiByteToWideChar
261 (CP_UTF8, 0, lastrootslash, -1, winpath, winpath_len)) {
262 logmsg(LOG_CRIT, "intpath2winpath: MultiByteToWideChar failed");
266 /* If path ends with /.., chop of the last component. Eventually, we
267 might want to eliminate all occurances of .. */
268 lastslash = wcsrchr(winpath, '/');
269 if (!wcscmp(lastslash, L"/..")) {
271 lastslash = wcsrchr(winpath, '/');
275 /* Translate /x -> x:/ and /x/something -> x:/something */
276 if ((winpath[0] == '/') && winpath[1]) {
277 switch (winpath[2]) {
284 winpath[0] = winpath[1];
293 while ((slash = wcschr(winpath, '/')) != NULL) {
300 int win_seteuid(U(uid_t euid))
305 int win_setegid(U(gid_t egid))
310 int win_truncate(const char *path, off_t length)
312 int fd, ret, saved_errno;
314 fd = win_open(path, O_WRONLY);
317 ret = chsize(fd, length);
325 int win_chown(U(const char *path), U(uid_t owner), U(gid_t group))
331 int win_fchown(U(int fd), U(uid_t owner), U(gid_t group))
337 int win_fchmod(int fildes, mode_t mode)
342 winpath = intpath2winpath(get_fdname(fildes));
348 ret = _wchmod(winpath, mode);
353 int inet_aton(const char *cp, struct in_addr *addr)
355 addr->s_addr = inet_addr(cp);
356 return (addr->s_addr == INADDR_NONE) ? 0 : 1;
360 If you need a good laugh, take a look at the "Suggested Interix
362 http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dnucmg/html/UCMGch10.asp
364 ssize_t pread(int fd, void *buf, size_t count, off_t offset)
369 if ((ret = lseek(fd, offset, SEEK_SET)) < 0)
371 size = read(fd, buf, count);
375 ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)
384 if ((ret = lseek(fd, offset, SEEK_SET)) < 0)
386 size = write(fd, buf, count);
388 /* Since we are using the CreationTime attribute as "ctime", we need to
389 update it. From RFC1813: "Writing to the file changes the ctime in
390 addition to the mtime." */
391 h = (HANDLE) _get_osfhandle(fd);
393 SystemTimeToFileTime(&st, &ft);
394 /* Ceil up to nearest even second */
395 fti.LowPart = ft.dwLowDateTime;
396 fti.HighPart = ft.dwHighDateTime;
397 fti.QuadPart = ((fti.QuadPart + 20000000 - 1) / 20000000) * 20000000;
398 ft.dwLowDateTime = fti.LowPart;
399 ft.dwHighDateTime = fti.HighPart;
400 if (!SetFileTime(h, &ft, NULL, NULL)) {
402 "warning: pwrite: SetFileTime failed with error %ld\n",
409 void syslog(U(int priority), U(const char *format), ...)
419 /* Set up locale, so that string compares works correctly */
420 setlocale(LC_ALL, "");
422 /* Verify that -s is used */
423 if (!opt_singleuser) {
424 fprintf(stderr, "Single-user mode is required on this platform.\n");
428 /* Verify that -d is used */
431 "Foreground (debug) mode is required on this platform.\n");
436 winsock_ver = MAKEWORD(1, 1);
437 if (WSAStartup(winsock_ver, &wsadata)) {
438 fprintf(stderr, "Unable to initialise WinSock\n");
441 if (LOBYTE(wsadata.wVersion) != 1 || HIBYTE(wsadata.wVersion) != 1) {
442 fprintf(stderr, "WinSock version is incompatible with 1.1\n");
447 /* disable error popups, for example from drives not ready */
448 SetErrorMode(SEM_FAILCRITICALERRORS);
458 /* Wrapper for Windows stat function, which provides
459 st_dev and st_ino. These are calculated as follows:
461 st_dev is set to the drive number (0=A 1=B ...). Our virtual root
462 "/" gets a st_dev of 0xff.
464 st_ino is hashed from the full file path. Each half produces a 32
465 bit hash. These are concatenated to a 64 bit value. The risk that
466 st_ino is the same for two files on the system is, if I'm not
467 mistaken, b=pigeon(2**32, f)**2. For f=1000, b=1e-08. By using a 64
468 bit hash function this risk can be lowered. Possible future
471 pigeon() can be calculated in Python with:
475 for i in range(m - n + 1, m):
479 int win_stat(const char *file_name, backend_statstruct * buf)
483 wchar_t pathbuf[4096];
488 struct _stat win_statbuf;
490 /* Special case: Our top-level virtual root, containing each drive
491 represented as a directory. Compare with "My Computer" etc. This
492 virtual root has a hardcoded hash value of 1, to simplify debugging
494 if (!strcmp(file_name, "/")) {
495 buf->st_mode = S_IFDIR | S_IRUSR | S_IWUSR;
496 buf->st_nlink = MAX_NUM_DRIVES + 3; /* 3 extra for: . .. / */
509 winpath = intpath2winpath(file_name);
515 ret = _wstat(winpath, &win_statbuf);
521 /* Copy values to our struct */
522 buf->st_mode = win_statbuf.st_mode;
523 buf->st_nlink = win_statbuf.st_nlink;
524 buf->st_uid = win_statbuf.st_uid;
525 buf->st_gid = win_statbuf.st_gid;
526 buf->st_rdev = win_statbuf.st_rdev;
527 buf->st_size = win_statbuf.st_size;
528 buf->st_atime = win_statbuf.st_atime;
529 buf->st_mtime = win_statbuf.st_mtime;
530 buf->st_ctime = win_statbuf.st_ctime;
531 buf->st_blocks = win_statbuf.st_size / 512;
533 retval = GetFullPathNameW(winpath, wsizeof(pathbuf), pathbuf, NULL);
539 /* Set st_dev to the drive number */
540 buf->st_dev = tolower(pathbuf[0]) - 'a';
542 /* GetLongPathName fails if called with only x:\, and drive x is not
543 ready. So, only call it for other paths. */
544 if (pathbuf[0] && wcscmp(pathbuf + 1, L":\\")) {
545 retval = GetLongPathNameW(pathbuf, pathbuf, wsizeof(pathbuf));
546 if (!retval || (unsigned) retval > wsizeof(pathbuf)) {
547 /* Strangely enough, GetLongPathName returns
548 ERROR_SHARING_VIOLATION for locked files, such as hiberfil.sys
550 if (GetLastError() != ERROR_SHARING_VIOLATION) {
551 errno = ENAMETOOLONG;
557 /* Hash st_ino, by splitting in two halves */
558 namelen = wcslen(pathbuf);
559 splitpoint = &pathbuf[namelen / 2];
560 savedchar = *splitpoint;
562 buf->st_ino = wfnv1a_32(pathbuf, 0);
563 assert(sizeof(buf->st_ino) == 8);
564 buf->st_ino = buf->st_ino << 32;
565 *splitpoint = savedchar;
566 buf->st_ino |= wfnv1a_32(splitpoint, 0);
570 "win_stat: file=%s, ret=%d, st_dev=0x%x, st_ino=0x%I64x\n",
571 file_name, ret, buf->st_dev, buf->st_ino);
577 int win_open(const char *pathname, int flags, ...)
584 va_start(args, flags);
585 mode = va_arg(args, int);
589 winpath = intpath2winpath(pathname);
595 fd = _wopen(winpath, flags | O_BINARY, mode);
601 return add_fdname(fd, pathname);
605 int win_close(int fd)
611 int win_fstat(int fd, backend_statstruct * buf)
613 return win_stat(get_fdname(fd), buf);
617 opendir implementation which emulates a virtual root with the drive
618 letters presented as directories.
620 UNFS3_WIN_DIR *win_opendir(const char *name)
625 ret = malloc(sizeof(UNFS3_WIN_DIR));
627 logmsg(LOG_CRIT, "win_opendir: Unable to allocate memory");
631 if (!strcmp("/", name)) {
634 ret->currentdrive = 0;
635 ret->logdrives = GetLogicalDrives();
637 winpath = intpath2winpath(name);
644 ret->stream = _wopendir(winpath);
646 if (ret->stream == NULL) {
655 struct dirent *win_readdir(UNFS3_WIN_DIR * dir)
657 if (dir->stream == NULL) {
659 for (; dir->currentdrive < MAX_NUM_DRIVES; dir->currentdrive++) {
660 if (dir->logdrives & 1 << dir->currentdrive)
664 if (dir->currentdrive < MAX_NUM_DRIVES) {
665 dir->de.d_name[0] = 'a' + dir->currentdrive;
666 dir->de.d_name[1] = '\0';
675 de = _wreaddir(dir->stream);
680 if (!WideCharToMultiByte
681 (CP_UTF8, 0, de->d_name, -1, dir->de.d_name,
682 sizeof(dir->de.d_name), NULL, NULL)) {
683 logmsg(LOG_CRIT, "win_readdir: WideCharToMultiByte failed");
690 int win_closedir(UNFS3_WIN_DIR * dir)
692 if (dir->stream == NULL) {
696 return _wclosedir(dir->stream);
700 void openlog(U(const char *ident), U(int option), U(int facility))
705 char *win_realpath(const char *path, char *resolved_path)
707 return normpath(path, resolved_path);
710 int win_readlink(U(const char *path), U(char *buf), U(size_t bufsiz))
716 int win_mkdir(const char *pathname, U(mode_t mode))
721 if (!strcmp("/", pathname)) {
727 winpath = intpath2winpath(pathname);
733 /* FIXME: Use mode */
734 ret = _wmkdir(winpath);
739 int win_symlink(U(const char *oldpath), U(const char *newpath))
745 int win_mknod(U(const char *pathname), U(mode_t mode), U(dev_t dev))
751 int win_mkfifo(U(const char *pathname), U(mode_t mode))
757 int win_link(U(const char *oldpath), U(const char *newpath))
763 int win_statvfs(const char *path, backend_statvfsstruct * buf)
766 DWORD SectorsPerCluster;
767 DWORD BytesPerSector;
768 DWORD NumberOfFreeClusters;
769 DWORD TotalNumberOfClusters;
770 ULARGE_INTEGER FreeBytesAvailable;
771 ULARGE_INTEGER TotalNumberOfBytes;
772 ULARGE_INTEGER TotalNumberOfFreeBytes;
774 if (!strcmp("/", path)) {
777 buf->f_blocks = 1024;
785 winpath = intpath2winpath(path);
791 winpath[3] = '\0'; /* Cut off after x:\ */
793 if (!GetDiskFreeSpaceW
794 (winpath, &SectorsPerCluster, &BytesPerSector, &NumberOfFreeClusters,
795 &TotalNumberOfClusters)) {
800 if (!GetDiskFreeSpaceExW
801 (winpath, &FreeBytesAvailable, &TotalNumberOfBytes,
802 &TotalNumberOfFreeBytes)) {
807 buf->f_bsize = BytesPerSector;
808 buf->f_blocks = TotalNumberOfBytes.QuadPart / BytesPerSector;
809 buf->f_bfree = TotalNumberOfFreeBytes.QuadPart / BytesPerSector;
810 buf->f_bavail = FreeBytesAvailable.QuadPart / BytesPerSector;
811 buf->f_files = buf->f_blocks / SectorsPerCluster;
812 buf->f_ffree = buf->f_bfree / SectorsPerCluster;
817 int win_remove(const char *pathname)
822 if (!strcmp("/", pathname)) {
828 winpath = intpath2winpath(pathname);
834 ret = _wremove(winpath);
839 int win_chmod(const char *path, mode_t mode)
844 if (!strcmp("/", path)) {
850 winpath = intpath2winpath(path);
856 ret = _wchmod(winpath, mode);
862 If creation is false, the LastAccessTime will be set according to
863 times->actime. Otherwise, CreationTime will be set. LastWriteTime
864 is always set according to times->modtime.
866 static int win_utime_creation(const char *path, const struct utimbuf *times,
873 FILETIME xtime, mtime;
875 if (!strcmp("/", path)) {
881 winpath = intpath2winpath(path);
887 /* Unfortunately, we cannot use utime(), since it doesn't support
889 fti.QuadPart = UInt32x32To64(times->actime + FT70SEC, 10000000);
890 xtime.dwHighDateTime = fti.HighPart;
891 xtime.dwLowDateTime = fti.LowPart;
892 fti.QuadPart = UInt32x32To64(times->modtime + FT70SEC, 10000000);
893 mtime.dwHighDateTime = fti.HighPart;
894 mtime.dwLowDateTime = fti.LowPart;
896 h = CreateFileW(winpath, FILE_WRITE_ATTRIBUTES,
897 FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING,
898 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_BACKUP_SEMANTICS, NULL);
901 (h, creation ? &xtime : NULL, creation ? NULL : &xtime, &mtime)) {
911 int win_utime(const char *path, const struct utimbuf *times)
913 return win_utime_creation(path, times, FALSE);
916 int win_rmdir(const char *path)
921 if (!strcmp("/", path)) {
927 winpath = intpath2winpath(path);
933 ret = _wrmdir(winpath);
938 int win_rename(const char *oldpath, const char *newpath)
940 wchar_t *oldwinpath, *newwinpath;
943 if (!strcmp("/", oldpath) && !strcmp("/", newpath)) {
949 oldwinpath = intpath2winpath(oldpath);
954 newwinpath = intpath2winpath(newpath);
961 ret = _wrename(oldwinpath, newwinpath);
967 int win_gen_nonce(char *nonce)
969 HCRYPTPROV hCryptProv;
971 if (!CryptAcquireContext
972 (&hCryptProv, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT)) {
973 logmsg(LOG_ERR, "CryptAcquireContext failed with error 0x%lx",
978 if (!CryptGenRandom(hCryptProv, 32, nonce)) {
979 logmsg(LOG_ERR, "CryptGenRandom failed with error 0x%lx",
984 if (!CryptReleaseContext(hCryptProv, 0)) {
985 logmsg(LOG_ERR, "CryptReleaseContext failed with error 0x%lx",
993 /* Just like strncasecmp, but compare two UTF8 strings. Limited to 4096 chars. */
994 int win_utf8ncasecmp(const char *s1, const char *s2, size_t n)
996 wchar_t ws1[4096], ws2[4096];
999 /* Make sure input is valid UTF-8 */
1000 if (!isLegalUTF8String(s1)) {
1001 logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s1);
1004 if (!isLegalUTF8String(s2)) {
1005 logmsg(LOG_CRIT, "win_utf8ncasecmp: Illegal UTF-8 string:%s", s2);
1009 /* Convert both strings to wide chars */
1010 converted = MultiByteToWideChar(CP_UTF8, 0, s1, n, ws1, wsizeof(ws1));
1012 logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed");
1015 ws1[converted] = '\0';
1016 converted = MultiByteToWideChar(CP_UTF8, 0, s2, n, ws2, wsizeof(ws2));
1018 logmsg(LOG_CRIT, "win_utf8ncasecmp: MultiByteToWideChar failed");
1021 ws2[converted] = '\0';
1024 return _wcsicmp(ws1, ws2);
1027 static void win_verf_to_ubuf(struct utimbuf *ubuf, createverf3 verf)
1029 ubuf->actime = verf[0] | verf[1] << 8 | verf[2] << 16 | verf[3] << 24;
1030 ubuf->modtime = verf[4] | verf[5] << 8 | verf[6] << 16 | verf[7] << 24;
1032 /* FAT can only store dates in the interval 1980-01-01 to 2107-12-31.
1033 However, since the utime interface uses Epoch time, we are further
1034 limited to 1980-01-01 to 2038-01-19, assuming 32 bit signed time_t.
1035 math.log(2**31-1 - FT80SEC, 2) = 30.7, which means that we can only
1037 ubuf->actime &= 0x3fffffff;
1038 ubuf->actime += FT80SEC;
1039 ubuf->modtime &= 0x3fffffff;
1040 ubuf->modtime += FT80SEC;
1041 /* While FAT CreationTime has a resolution of 10 ms, WriteTime only has a
1042 resolution of 2 seconds. */
1043 ubuf->modtime &= ~1;
1046 int win_store_create_verifier(char *obj, createverf3 verf)
1048 struct utimbuf ubuf;
1050 win_verf_to_ubuf(&ubuf, verf);
1051 return win_utime_creation(obj, &ubuf, TRUE);
1054 int win_check_create_verifier(backend_statstruct * buf, createverf3 verf)
1056 struct utimbuf ubuf;
1058 win_verf_to_ubuf(&ubuf, verf);
1059 return (buf->st_ctime == ubuf.actime && buf->st_mtime == ubuf.modtime);