]> git.ipfire.org Git - thirdparty/libarchive.git/commitdiff
Now, we can run bsdtar and bsdtar_test on Windows.
authorMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 15 Feb 2009 23:05:53 +0000 (18:05 -0500)
committerMichihiro NAKAJIMA <ggcueroad@gmail.com>
Sun, 15 Feb 2009 23:05:53 +0000 (18:05 -0500)
But those need more improvement.
bsdtar_test reported many errors.

SVN-Revision: 629

CMakeLists.txt
tar/bsdtar.c
tar/bsdtar_platform.h
tar/bsdtar_windows.c [new file with mode: 0644]
tar/bsdtar_windows.h [new file with mode: 0644]
tar/getdate.y
tar/read.c
tar/siginfo.c
tar/test/test.h
tar/test/test_basic.c
tar/tree.c

index 0eed01b1e1263edbd1bf67bb212c6fa7b12d42db..5a4ea5e73f38c9d474d52f399fe5b444410facd8 100644 (file)
@@ -40,7 +40,6 @@ ENDIF(ENABLE_TEST)
 # On Windows, currently, bsdtar and bsdcpio are not supported.
 #
 IF (WIN32)
-  SET(ENABLE_TAR 0)
   SET(ENABLE_CPIO 0)
 ENDIF (WIN32)
 #
@@ -762,6 +761,10 @@ IF (ENABLE_TAR)
     tar/util.c
     tar/write.c
   )
+  IF(WIN32)
+    LIST(APPEND bsdtar_SOURCES tar/bsdtar_windows.c)
+    LIST(APPEND bsdtar_SOURCES tar/bsdtar_windows.h)
+  ENDIF(WIN32)
 
   SET(bsdtar_MANS tar/bsdtar.1)
 
@@ -796,6 +799,10 @@ IF(ENABLE_TAR AND ENABLE_TEST)
     tar/test/test_symlink_dir.c
     tar/test/test_version.c
   )
+  IF(WIN32)
+    LIST(APPEND bsdtar_test_SOURCES tar/bsdtar_windows.c)
+    LIST(APPEND bsdtar_test_SOURCES tar/bsdtar_windows.h)
+  ENDIF(WIN32)
 
   #
   # Generate the list.h
@@ -808,10 +815,17 @@ IF(ENABLE_TAR AND ENABLE_TEST)
   ADD_EXECUTABLE(bsdtar_test ${bsdtar_test_SOURCES})
   SET_PROPERTY(TARGET bsdtar_test PROPERTY COMPILE_DEFINITIONS
     LIST_H="${CMAKE_CURRENT_BINARY_DIR}/tar/test/list.h")
-  ADD_TEST(bsdtar_test bsdtar_test
-    -v
-    -p ${CMAKE_CURRENT_BINARY_DIR}/bsdtar
-    -r ${CMAKE_CURRENT_SOURCE_DIR}/tar/test)
+  IF(WIN32)
+    ADD_TEST(bsdtar_test bsdtar_test
+      -v
+      -p ${CMAKE_CURRENT_BINARY_DIR}/debug/bsdtar.exe
+      -r ${CMAKE_CURRENT_SOURCE_DIR}/tar/test)
+  ELSE(WIN32)
+    ADD_TEST(bsdtar_test bsdtar_test
+      -v
+      -p ${CMAKE_CURRENT_BINARY_DIR}/bsdtar
+      -r ${CMAKE_CURRENT_SOURCE_DIR}/tar/test)
+  ENDIF(WIN32)
 
 ENDIF (ENABLE_TAR AND ENABLE_TEST)
 
index 0fc837505f5becc85253b41221eb15e6c75d397a..3fbb4e7dc9ede1203700ddec28e9971732443d0f 100644 (file)
@@ -73,6 +73,9 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/bsdtar.c,v 1.93 2008/11/08 04:43:24 kientzle
 #ifdef __linux
 #define        _PATH_DEFTAPE "/dev/st0"
 #endif
+#ifdef _WIN32
+#define        _PATH_DEFTAPE "\\\\.\\tape0"
+#endif
 
 #ifndef _PATH_DEFTAPE
 #define        _PATH_DEFTAPE "/dev/tape"
@@ -109,12 +112,20 @@ main(int argc, char **argv)
        memset(bsdtar, 0, sizeof(*bsdtar));
        bsdtar->fd = -1; /* Mark as "unused" */
        option_o = 0;
+#ifdef _WIN32
+       /* open() function is always with a binary mode. */
+       _set_fmode(_O_BINARY);
+#endif
 
        /* Need bsdtar->progname before calling bsdtar_warnc. */
        if (*argv == NULL)
                bsdtar->progname = "bsdtar";
        else {
+#if _WIN32
+               bsdtar->progname = strrchr(*argv, '\\');
+#else
                bsdtar->progname = strrchr(*argv, '/');
+#endif
                if (bsdtar->progname != NULL)
                        bsdtar->progname++;
                else
@@ -143,7 +154,7 @@ main(int argc, char **argv)
        bsdtar->extract_flags |= SECURITY;
 
        /* Defaults for root user: */
-       if (bsdtar->user_uid == 0) {
+       if (bsdtar_is_privileged(bsdtar)) {
                /* --same-owner */
                bsdtar->extract_flags |= ARCHIVE_EXTRACT_OWNER;
                /* -p */
@@ -152,6 +163,10 @@ main(int argc, char **argv)
                bsdtar->extract_flags |= ARCHIVE_EXTRACT_XATTR;
                bsdtar->extract_flags |= ARCHIVE_EXTRACT_FFLAGS;
        }
+#ifdef _WIN32
+       /* Windows cannot set UNIX like uid/gid. */
+       bsdtar->extract_flags &= ~ARCHIVE_EXTRACT_OWNER;
+#endif
 
        bsdtar->argv = argv;
        bsdtar->argc = argc;
index 634c8910ab4af7d9dabe6973af1d01b10e26b628..89e441a750819c13e1cac9983d72835c74d12457 100644 (file)
 #define        __LA_DEAD
 #endif
 
+#ifdef _WIN32
+#include "bsdtar_windows.h"
+#else
+#define bsdtar_is_privileged(bsdtar)   (bsdtar->user_uid == 0)
+#endif
+
 #endif /* !BSDTAR_PLATFORM_H_INCLUDED */
diff --git a/tar/bsdtar_windows.c b/tar/bsdtar_windows.c
new file mode 100644 (file)
index 0000000..6e437ea
--- /dev/null
@@ -0,0 +1,1041 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifdef _WIN32
+#define _WIN32_WINNT   0x500
+
+#include "bsdtar_platform.h"
+#include <errno.h>
+#include <stddef.h>
+#include <sys/utime.h>
+#include <sys/stat.h>
+#include <process.h>
+#include <stdlib.h>
+#include <wchar.h>
+#include <windows.h>
+#include <sddl.h>
+
+#include "bsdtar.h"
+
+#define EPOC_TIME      (116444736000000000ULL)
+
+struct ustat {
+       int64_t         st_atime;
+       uint32_t        st_atime_nsec;
+       int64_t         st_ctime;
+       uint32_t        st_ctime_nsec;
+       int64_t         st_mtime;
+       uint32_t        st_mtime_nsec;
+       gid_t           st_gid;
+       /* 64bits ino */
+       int64_t         st_ino;
+       mode_t          st_mode;
+       uint32_t        st_nlink;
+       uint64_t        st_size;
+       uid_t           st_uid;
+       dev_t           st_dev;
+       dev_t           st_rdev;
+};
+
+struct __DIR {
+       HANDLE                  handle;
+       WIN32_FIND_DATAW        fileData;
+       struct dirent           de;
+       int                     first;
+       BOOL                    finished;
+};
+
+/* Transform 64-bits ino into 32-bits by hashing.
+ * You do not forget that really unique number size is 64-bits.
+ */
+#define INOSIZE (8*sizeof(ino_t)) /* 32 */
+static __inline ino_t
+getino(struct ustat *ub)
+{
+       ULARGE_INTEGER ino64;
+
+       ino64.QuadPart = ub->st_ino;
+       /* I don't know this hashing is correct way */
+       return (ino64.LowPart ^ (ino64.LowPart >> INOSIZE));
+}
+
+/*
+ * Prepend "\\?\" to the path name and convert it to unicode to permit
+ * an extended-length path for a maximum total path length of 32767
+ * characters.
+ * see also http://msdn.microsoft.com/en-us/library/aa365247.aspx
+ */
+static wchar_t *
+permissive_name(const char *name)
+{
+       wchar_t *wn, *wnp;
+       wchar_t *ws, *wsp;
+       size_t len, slen;
+       int i, n, unc;
+
+       len = strlen(name);
+       wn = wnp = malloc((len + 1) * sizeof(wchar_t));
+       if (wn == NULL)
+               return (NULL);
+       n = MultiByteToWideChar(CP_ACP, 0, name, len, wn, len);
+       if (n == 0) {
+               free(wn);
+               return (NULL);
+       }
+       wn[n] = L'\0';
+
+       /* When we use "\\?\" prefix, a path name cannot be used with '/'. */
+       for (i = 0; i < n; i++)
+               if (wn[i] == L'/')
+                       wn[i] = L'\\';
+       unc = 0;
+       if (wnp[0] == L'\\' && wnp[1] == L'\\') {
+               /* UNC: start characters are "\\"*/
+               wnp += 2;
+               len -= 2;
+               if (wnp[0] == L'.' && wnp[1] == L'\\' && iswalpha(wnp[2])
+                       && wnp[3] == L':' && wnp[4] == L'\\') {
+                       /* start characters are "\\.\[drive]:\", feed a pointer more */
+                       wnp += 2;
+                       len -= 2;
+                       /* now start characters of wnp are "C:" or "D:" or.... */
+               } else
+                       unc = 1;
+       } else if (wnp[1] == L':' && iswalpha(wnp[0]))
+               /* start characters are "C:","D:"...: already full path */
+               ;
+       else {
+               size_t l;
+               wchar_t *full;
+
+               /* getting a full path name */
+               full = malloc((len + MAX_PATH) * sizeof(wchar_t));
+               if (full == NULL) {
+                       free(wn);
+                       return (NULL);
+               }
+               l = GetFullPathNameW(wnp, len + MAX_PATH, full, NULL);
+               if (l >= len + MAX_PATH) {
+                       /* buffer size is smaller */
+                       full = realloc(full, l * sizeof(wchar_t));
+                       if (full == NULL) {
+                               free(wn);
+                               return (NULL);
+                       }
+                       l = GetFullPathNameW(wnp, l, full, NULL);
+               }
+               if (l != 0) {
+                       full[l] = L'\0';
+                       free(wn);
+                       wn = wnp = full;
+                       len = l;
+               } else
+                       free(full);
+       }
+       
+       slen = 4 + (unc * 4) + len + 1;
+       ws = wsp = malloc(slen * sizeof(wchar_t));
+       if (ws == NULL) {
+               free(wn);
+               return (NULL);
+       }
+
+       /* prepend "\\?\" */
+       wcsncpy(wsp, L"\\\\?\\", 4);
+       wsp += 4;
+       slen -= 4;
+       if (unc) {
+               /* append "UNC\" ---> "\\?\UNC\"*/
+               wcsncpy(wsp, L"UNC\\", 4);
+               wsp += 4;
+               slen -= 4;
+       }
+       wcsncpy_s(wsp, slen, wnp, len);
+       free(wn);
+       wn = ws;
+       return (wn);
+}
+
+static HANDLE
+la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode,
+    LPSECURITY_ATTRIBUTES lpSecurityAttributes, DWORD dwCreationDisposition,
+    DWORD dwFlagsAndAttributes, HANDLE hTemplateFile)
+{
+       wchar_t *wpath;
+       HANDLE handle;
+
+       handle = CreateFileA(path, dwDesiredAccess, dwShareMode,
+           lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+           hTemplateFile);
+       if (handle != INVALID_HANDLE_VALUE)
+               return (handle);
+       if (GetLastError() != ERROR_PATH_NOT_FOUND)
+               return (handle);
+       wpath = permissive_name(path);
+       if (wpath == NULL)
+               return (handle);
+       handle = CreateFileW(wpath, dwDesiredAccess, dwShareMode,
+           lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes,
+           hTemplateFile);
+       free(wpath);
+       return (handle);
+}
+
+/* Check that path1 and path2 can be hard-linked by each other.
+ * Both arguments must be made by permissive_name function. 
+ */
+static int
+canHardLinkW(const wchar_t *path1, const wchar_t *path2)
+{
+       wchar_t root[MAX_PATH];
+       wchar_t fs[32];
+       const wchar_t *s;
+       int r;
+
+       r = wcsncmp(path1, path2, MAX_PATH);
+       /* Is volume-name the same? */
+       if (r < 7)
+               return (0);
+       if (wcsncmp(path1, L"\\\\?\\UNC\\", 8) == 0) {
+               int len;
+
+               s = path1 + 8;
+               if (*s == L'\\')
+                       return (0);
+               /*         012345678
+                * Name : "\\?\UNC\Server\Share\"
+                *                       ^ search
+                */
+               s = wcschr(++s, L'\\');
+               if (s == NULL)
+                       return (0);
+               if (*++s == L'\\')
+                       return (0);
+               /*         012345678
+                * Name : "\\?\UNC\Server\Share\"
+                *                             ^ search
+                */
+               s = wcschr(++s, L'\\');
+               if (s == NULL)
+                       return (0);
+               s++;
+               /*         012345678
+                * Name : "\\?\UNC\Server\Share\xxxx"
+                *                 ^--- len ----^
+                */
+               len = (int)(s - path1 - 8);
+               /* Is volume-name the same? */
+               if (r < len + 8)
+                       return (0);
+               /* Is volume-name too long? */
+               if (sizeof(root) -3 < len)
+                       return (0);
+               root[0] = root[1] = L'\\';
+               wcsncpy(root + 2, path1 + 8 , len);
+               /* root : "\\Server\Share\" */
+               root[2 + len] = L'\0';
+       } else if (wcsncmp(path1, L"\\\\?\\", 4) == 0) {
+               s = path1 + 4;
+               if ((!iswalpha(*s)) || s[1] != L':' || s[2] != L'\\')
+                       return (0);
+               wcsncpy(root, path1 + 4, 3);
+               root[3] = L'\0';
+       } else
+               return (0);
+       if (!GetVolumeInformationW(root, NULL, 0, NULL, NULL, NULL, fs, sizeof(fs)))
+               return (0);
+       if (wcscmp(fs, L"NTFS") == 0)
+               return (1);
+       else
+               return (0);
+}
+
+/* Make a link to src called dst.  */
+static int
+__link(const char *src, const char *dst, int sym)
+{
+       wchar_t *wsrc, *wdst;
+       int res, retval;
+       DWORD attr;
+
+       if (src == NULL || dst == NULL) {
+               set_errno (EINVAL);
+               return -1;
+       }
+
+       wsrc = permissive_name(src);
+       wdst = permissive_name(dst);
+       if (wsrc == NULL || wdst == NULL) {
+               if (wsrc != NULL)
+                       free(wsrc);
+               if (wdst != NULL)
+                       free(wdst);
+               set_errno (EINVAL);
+               return -1;
+       }
+
+       if ((attr = GetFileAttributesW(wsrc)) != -1) {
+               if ((attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+                       errno = EPERM;
+                       retval = -1;
+                       goto exit;
+               }
+               if (!sym && canHardLinkW(wsrc, wdst))
+                       res = CreateHardLinkW(wdst, wsrc, NULL);
+               else
+                       res = CopyFileW(wsrc, wdst, FALSE);
+       } else {
+               /* wsrc does not exist; try src prepend it with the dirname of wdst */
+               wchar_t *wnewsrc, *slash;
+               int i, n, slen, wlen;
+
+               if (strlen(src) >= 3 && isalpha(src[0]) && src[1] == ':'
+                   && src[2] == '\\') {
+                       /* Original src name is already full-path */
+                       retval = -1;
+                       goto exit;
+               }
+               if (src[0] == '\\') {
+                       /* Original src name is almost full-path
+                        * (maybe src name is without drive) */
+                       retval = -1;
+                       goto exit;
+               }
+
+               wnewsrc = malloc ((wcslen(wsrc) + wcslen(wdst) + 1) * sizeof(wchar_t));
+               if (wnewsrc == NULL) {
+                       errno = ENOMEM;
+                       retval = -1;
+                       goto exit;
+               }
+               /* Copying a dirname of wdst */
+               wcscpy(wnewsrc, wdst);
+               slash = wcsrchr(wnewsrc, L'\\');
+               if (slash != NULL)
+                       *++slash = L'\0';
+               else
+                       wcscat(wnewsrc, L"\\");
+               /* Converting multi-byte src to wide-char src */
+               wlen = wcslen(wsrc);
+               slen = strlen(src);
+               n = MultiByteToWideChar(CP_ACP, 0, src, slen, wsrc, slen);
+               if (n == 0) {
+                       free (wnewsrc);
+                       retval = -1;
+                       goto exit;
+               }
+               for (i = 0; i < n; i++)
+                       if (wsrc[i] == L'/')
+                               wsrc[i] = L'\\';
+               wcsncat(wnewsrc, wsrc, n);
+               /* Check again */
+               attr = GetFileAttributesW(wnewsrc);
+               if (attr == -1 || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) {
+                       if (attr == -1)
+                               _dosmaperr(GetLastError());
+                       else
+                               errno = EPERM;
+                       free (wnewsrc);
+                       retval = -1;
+                       goto exit;
+               }
+               if (!sym && canHardLinkW(wnewsrc, wdst))
+                       res = CreateHardLinkW(wdst, wnewsrc, NULL);
+               else
+                       res = CopyFileW(wnewsrc, wdst, FALSE);
+               free (wnewsrc);
+       }
+       if (res == 0) {
+               _dosmaperr(GetLastError());
+               retval = -1;
+       } else
+               retval = 0;
+exit:
+       free(wsrc);
+       free(wdst);
+       return (retval);
+}
+
+/* Make a hard link to src called dst.  */
+int
+link(const char *src, const char *dst)
+{
+       return __link (src, dst, 0);
+}
+
+/* Make a symbolic link to FROM called TO.  */
+int symlink (from, to)
+     const char *from;
+     const char *to;
+{
+       return __link (from, to, 1);
+}
+
+int
+ftruncate(int fd, off_t length)
+{
+       LARGE_INTEGER distance;
+       HANDLE handle;
+
+       if (fd < 0) {
+               errno = EBADF;
+               return (-1);
+       }
+       handle = (HANDLE)_get_osfhandle(fd);
+       if (GetFileType(handle) != FILE_TYPE_DISK) {
+               errno = EBADF;
+               return (-1);
+       }
+       distance.QuadPart = length;
+       if (!SetFilePointerEx(handle, distance, NULL, FILE_BEGIN)) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       if (!SetEndOfFile(handle)) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       return (0);
+}
+
+DIR *
+opendir(const char *path)
+{
+       DIR *dir;
+       wchar_t *wpath, *wfname;
+       size_t wlen;
+
+       dir = malloc(sizeof(*dir));
+       if (dir == NULL) {
+               errno = ENOMEM;
+               return (NULL);
+       }
+
+       wpath = permissive_name(path);
+       if (wpath == NULL) {
+               errno = EINVAL;
+               free(dir);
+               return (NULL);
+       }
+       wlen = wcslen(wpath);
+       wfname = malloc((wlen + 3) * sizeof(wchar_t));
+       if (wfname == NULL) {
+               errno = ENOMEM;
+               free(dir);
+               free(wpath);
+               return (NULL);
+       }
+       wcscpy(wfname, wpath);
+       wcscat(wfname, L"\\*");
+
+       dir->handle = FindFirstFileW(wfname, &dir->fileData);
+       if (dir->handle == INVALID_HANDLE_VALUE) {
+               _dosmaperr(GetLastError());
+               free(dir);
+               free(wpath);
+               free(wfname);
+               return (NULL);
+       }
+       dir->first = 1;
+       dir->finished = FALSE;
+       free(wpath);
+       free(wfname);
+
+       return (dir);
+}
+
+struct dirent *
+readdir(DIR *dirp)
+{
+       size_t len;
+
+       while (!dirp->finished) {
+               if (!dirp->first && !FindNextFileW(dirp->handle, &dirp->fileData)) {
+                       if (GetLastError() != ERROR_NO_MORE_FILES)
+                               _dosmaperr(GetLastError());
+                       dirp->finished = TRUE;
+                       break;
+               }
+               dirp->first = 0;
+               len = wcstombs(dirp->de.d_name, dirp->fileData.cFileName,
+                   sizeof(dirp->de.d_name) -1);
+               if (len == -1) {
+                       errno = EINVAL;
+                       dirp->finished = TRUE;
+                       break;
+               }
+               dirp->de.d_name[len] = '\0';
+               dirp->de.d_nameln = strlen(dirp->de.d_name);
+               return (&dirp->de);
+       }
+
+       return (NULL);
+}
+
+int
+closedir(DIR *dirp)
+{
+       BOOL ret;
+
+       ret = FindClose(dirp->handle);
+       free(dirp);
+       if (ret == 0) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       return (0);
+}
+
+int
+la_chdir(const char *path)
+{
+       wchar_t *ws;
+       int r;
+
+       r = SetCurrentDirectoryA(path);
+       if (r == 0) {
+               if (GetLastError() != ERROR_FILE_NOT_FOUND) {
+                       _dosmaperr(GetLastError());
+                       return (-1);
+               }
+       } else
+               return (0);
+       ws = permissive_name(path);
+       if (ws == NULL) {
+               errno = EINVAL;
+               return (-1);
+       }
+       r = SetCurrentDirectoryW(ws);
+       free(ws);
+       if (r == 0) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       return (0);
+}
+
+/*
+ * This function will be removed.
+ * Only use for debugging.
+ */
+void
+la_exit(int status)
+{
+       _exit(status);
+}
+
+__int64
+la_lseek(int fd, __int64 offset, int whence)
+{
+       LARGE_INTEGER distance;
+       LARGE_INTEGER newpointer;
+       HANDLE handle;
+
+       if (fd < 0) {
+               errno = EBADF;
+               return (-1);
+       }
+       handle = (HANDLE)_get_osfhandle(fd);
+       if (GetFileType(handle) != FILE_TYPE_DISK) {
+               errno = EBADF;
+               return (-1);
+       }
+       distance.QuadPart = offset;
+       if (!SetFilePointerEx(handle, distance, &newpointer, whence)) {
+               DWORD lasterr;
+
+               lasterr = GetLastError();
+               if (lasterr == ERROR_BROKEN_PIPE)
+                       return (0);
+               if (lasterr == ERROR_ACCESS_DENIED)
+                       errno = EBADF;
+               else
+                       _dosmaperr(lasterr);
+               return (-1);
+       }
+       return (newpointer.QuadPart);
+}
+
+int
+la_mkdir(const char *path, mode_t mode)
+{
+       wchar_t *ws;
+       int r;
+
+       (void)mode;/* UNUSED */
+       r = CreateDirectoryA(path, NULL);
+       if (r == 0) {
+               DWORD lasterr = GetLastError();
+               if (lasterr != ERROR_FILENAME_EXCED_RANGE &&
+                       lasterr != ERROR_PATH_NOT_FOUND) {
+                       _dosmaperr(GetLastError());
+                       return (-1);
+               }
+       } else
+               return (0);
+       ws = permissive_name(path);
+       if (ws == NULL) {
+               errno = EINVAL;
+               return (-1);
+       }
+       r = CreateDirectoryW(ws, NULL);
+       free(ws);
+       if (r == 0) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       return (0);
+}
+
+int
+la_open(const char *path, int flags, ...)
+{
+       va_list ap;
+       wchar_t *ws;
+       int r, pmode;
+       DWORD attr;
+
+       va_start(ap, flags);
+       pmode = va_arg(ap, int);
+       va_end(ap);
+       if (path[0] == '.' && path[1] == '\0' && flags == O_RDONLY) {
+               HANDLE handle;
+       
+               handle = la_CreateFile(path, 0, 0, NULL, OPEN_EXISTING,
+                       FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY,
+                       NULL);
+               if (handle == INVALID_HANDLE_VALUE) {
+                       _dosmaperr(GetLastError());
+                       return (-1);
+               }
+               r = _open_osfhandle((intptr_t)handle, _O_RDONLY);
+
+               return (r);
+       }
+       r = _open(path, flags, pmode);
+       if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+               /* simular other POSIX system action to pass a test */
+               attr = GetFileAttributesA(path);
+               if (attr == -1)
+                       _dosmaperr(GetLastError());
+               else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+                       errno = EISDIR;
+               else
+                       errno = EACCES;
+               return (-1);
+       }
+       if (r >= 0 || errno != ENOENT)
+               return (r);
+       ws = permissive_name(path);
+       if (ws == NULL) {
+               errno = EINVAL;
+               return (-1);
+       }
+       r = _wopen(ws, flags, pmode);
+       if (r < 0 && errno == EACCES && (flags & O_CREAT) != 0) {
+               /* simular other POSIX system action to pass a test */
+               attr = GetFileAttributesW(ws);
+               if (attr == -1)
+                       _dosmaperr(GetLastError());
+               else if (attr & FILE_ATTRIBUTE_DIRECTORY)
+                       errno = EISDIR;
+               else
+                       errno = EACCES;
+       }
+       free(ws);
+       return (r);
+}
+
+ssize_t
+la_read(int fd, void *buf, size_t nbytes)
+{
+       DWORD bytes_read, lasterr;
+       int r;
+
+#ifdef _WIN64
+       if (nbytes > UINT32_MAX)
+               nbytes = UINT32_MAX;
+#endif
+       if (fd < 0) {
+               errno = EBADF;
+               return (-1);
+       }
+       r = ReadFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
+           &bytes_read, NULL);
+       if (r == 0) {
+               lasterr = GetLastError();
+               if (lasterr == ERROR_BROKEN_PIPE)
+                       return (0);
+               if (lasterr == ERROR_ACCESS_DENIED)
+                       errno = EBADF;
+               else
+                       _dosmaperr(lasterr);
+               return (-1);
+       }
+       return ((ssize_t)bytes_read);
+}
+
+/* Remove directory */
+int
+la_rmdir(const char *path)
+{
+       wchar_t *ws;
+       int r;
+
+       r = _rmdir(path);
+       if (r >= 0 || errno != ENOENT)
+               return (r);
+       ws = permissive_name(path);
+       if (ws == NULL) {
+               errno = EINVAL;
+               return (-1);
+       }
+       r = _wrmdir(ws);
+       free(ws);
+       return (r);
+}
+
+/* Convert Windows FILETIME to UTC */
+__inline static void
+fileTimeToUTC(const FILETIME *filetime, time_t *time, long *ns)
+{
+       ULARGE_INTEGER utc;
+
+       utc.HighPart = filetime->dwHighDateTime;
+       utc.LowPart  = filetime->dwLowDateTime;
+       if (utc.QuadPart >= EPOC_TIME) {
+               utc.QuadPart -= EPOC_TIME;
+               *time = (time_t)(utc.QuadPart / 10000000);      /* milli seconds base */
+               *ns = (long)(utc.QuadPart % 10000000) * 100;/* nano seconds base */
+       } else {
+               *time = 0;
+               *ns = 0;
+       }
+}
+
+/* Stat by handle
+ * Windows' stat() does not accept path which is added "\\?\" especially "?"
+ * character.
+ * It means we cannot access a long name path(which is longer than MAX_PATH).
+ * So I've implemented simular Windows' stat() to access the long name path.
+ * And I've added some feature.
+ * 1. set st_ino by nFileIndexHigh and nFileIndexLow of
+ *    BY_HANDLE_FILE_INFORMATION.
+ * 2. set st_nlink by nNumberOfLinks of BY_HANDLE_FILE_INFORMATION.
+ * 3. set st_dev by dwVolumeSerialNumber by BY_HANDLE_FILE_INFORMATION.
+ */
+static int
+__hstat(HANDLE handle, struct ustat *st)
+{
+       BY_HANDLE_FILE_INFORMATION info;
+       ULARGE_INTEGER ino64;
+       DWORD ftype;
+       mode_t mode;
+       time_t time;
+       long ns;
+
+       switch (ftype = GetFileType(handle)) {
+       case FILE_TYPE_UNKNOWN:
+               errno = EBADF;
+               return (-1);
+       case FILE_TYPE_CHAR:
+       case FILE_TYPE_PIPE:
+               if (ftype == FILE_TYPE_CHAR) {
+                       st->st_mode = S_IFCHR;
+                       st->st_size = 0;
+               } else {
+                       DWORD avail;
+
+                       st->st_mode = S_IFIFO;
+                       if (PeekNamedPipe(handle, NULL, 0, NULL, &avail, NULL))
+                               st->st_size = avail;
+                       else
+                               st->st_size = 0;
+               }
+               st->st_atime = 0;
+               st->st_atime_nsec = 0;
+               st->st_mtime = 0;
+               st->st_mtime_nsec = 0;
+               st->st_ctime = 0;
+               st->st_ctime_nsec = 0;
+               st->st_ino = 0;
+               st->st_nlink = 1;
+               st->st_uid = 0;
+               st->st_gid = 0;
+               st->st_rdev = 0;
+               st->st_dev = 0;
+               return (0);
+       case FILE_TYPE_DISK:
+               break;
+       default:
+               /* This ftype is undocumented type. */
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+
+       ZeroMemory(&info, sizeof(info));
+       if (!GetFileInformationByHandle (handle, &info)) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+
+       mode = S_IRUSR | S_IRGRP | S_IROTH;
+       if ((info.dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0)
+               mode |= S_IWUSR | S_IWGRP | S_IWOTH;
+       if (info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
+               mode |= S_IFDIR;
+       else
+               mode |= S_IFREG;
+       st->st_mode = mode;
+       
+       fileTimeToUTC(&info.ftLastAccessTime, &time, &ns);
+       st->st_atime = time; 
+       st->st_atime_nsec = ns;
+       fileTimeToUTC(&info.ftLastWriteTime, &time, &ns);
+       st->st_mtime = time;
+       st->st_mtime_nsec = ns;
+       fileTimeToUTC(&info.ftCreationTime, &time, &ns);
+       st->st_ctime = time;
+       st->st_ctime_nsec = ns;
+       st->st_size = 
+           ((int64_t)(info.nFileSizeHigh) * ((int64_t)MAXDWORD + 1))
+               + (int64_t)(info.nFileSizeLow);
+#ifdef SIMULATE_WIN_STAT
+       st->st_ino = 0;
+       st->st_nlink = 1;
+       st->st_dev = 0;
+#else
+       /* Getting FileIndex as i-node. We have to remove a sequence which
+        * is high-16-bits of nFileIndexHigh. */
+       ino64.HighPart = info.nFileIndexHigh & 0x0000FFFFUL;
+       ino64.LowPart  = info.nFileIndexLow;
+       st->st_ino = ino64.QuadPart;
+       st->st_nlink = info.nNumberOfLinks;
+       st->st_dev = info.dwVolumeSerialNumber;
+#endif
+       st->st_uid = 0;
+       st->st_gid = 0;
+       st->st_rdev = 0;
+       return (0);
+}
+
+static void
+copy_stat(struct stat *st, struct ustat *us)
+{
+       st->st_atime = us->st_atime;
+       st->st_ctime = us->st_ctime;
+       st->st_mtime = us->st_mtime;
+       st->st_gid = us->st_gid;
+       st->st_ino = getino(us);
+       st->st_mode = us->st_mode;
+       st->st_nlink = us->st_nlink;
+       st->st_size = us->st_size;
+       st->st_uid = us->st_uid;
+       st->st_dev = us->st_dev;
+       st->st_rdev = us->st_rdev;
+}
+
+int
+la_fstat(int fd, struct stat *st)
+{
+       struct ustat u;
+       int ret;
+
+       if (fd < 0) {
+               errno = EBADF;
+               return (-1);
+       }
+       ret = __hstat((HANDLE)_get_osfhandle(fd), &u);
+       if (ret >= 0) {
+               copy_stat(st, &u);
+               if (u.st_mode & (S_IFCHR | S_IFIFO)) {
+                       st->st_dev = fd;
+                       st->st_rdev = fd;
+               }
+       }
+       return (ret);
+}
+
+int
+la_stat(const char *path, struct stat *st)
+{
+       HANDLE handle;
+       struct ustat u;
+       int ret;
+
+       handle = la_CreateFile(path, 0, 0, NULL, OPEN_EXISTING,
+               FILE_FLAG_BACKUP_SEMANTICS | FILE_ATTRIBUTE_READONLY,
+               NULL);
+       if (handle == INVALID_HANDLE_VALUE) {
+               _dosmaperr(GetLastError());
+               return (-1);
+       }
+       ret = __hstat(handle, &u);
+       CloseHandle(handle);
+       if (ret >= 0) {
+               char *p;
+
+               copy_stat(st, &u);
+               p = strrchr(path, '.');
+               if (p != NULL && strlen(p) == 4) {
+                       char exttype[4];
+
+                       ++ p;
+                       exttype[0] = toupper(*p++);
+                       exttype[1] = toupper(*p++);
+                       exttype[2] = toupper(*p++);
+                       exttype[3] = '\0';
+                       if (!strcmp(exttype, "EXE") || !strcmp(exttype, "CMD") ||
+                               !strcmp(exttype, "BAT") || !strcmp(exttype, "COM"))
+                               st->st_mode |= S_IXUSR | S_IXGRP | S_IXOTH;
+               }
+       }
+       return (ret);
+}
+
+ssize_t
+la_write(int fd, const void *buf, size_t nbytes)
+{
+       uint32_t bytes_written;
+
+#ifdef _WIN64
+       if (nbytes > UINT32_MAX)
+               nbytes = UINT32_MAX;
+#endif
+       if (fd < 0) {
+               errno = EBADF;
+               return (-1);
+       }
+       if (!WriteFile((HANDLE)_get_osfhandle(fd), buf, (uint32_t)nbytes,
+           &bytes_written, NULL)) {
+               DWORD lasterr;
+
+               lasterr = GetLastError();
+               if (lasterr == ERROR_ACCESS_DENIED)
+                       errno = EBADF;
+               else
+                       _dosmaperr(lasterr);
+               return (-1);
+       }
+       return (bytes_written);
+}
+
+#ifndef LIST_H
+static int
+_is_privileged(HANDLE thandle, const char *sidlist[])
+{
+       TOKEN_USER *tuser;
+       TOKEN_GROUPS  *tgrp;
+       DWORD bytes;
+       PSID psid;
+       DWORD i, g;
+       int member;
+
+       psid = NULL;
+       tuser = NULL;
+       tgrp = NULL;
+       member = 0;
+       for (i = 0; sidlist[i] != NULL && member == 0; i++) {
+               if (psid != NULL)
+                       LocalFree(psid);
+               if (ConvertStringSidToSidA(sidlist[i], &psid) == 0) {
+                       errno = EPERM;
+                       return (-1);
+               }
+               if (tuser == NULL) {
+                       GetTokenInformation(thandle, TokenUser, NULL, 0, &bytes);
+                       tuser = malloc(bytes);
+                       if (tuser == NULL) {
+                               errno = ENOMEM;
+                               member = -1;
+                               break;
+                       }
+                       if (GetTokenInformation(thandle, TokenUser, tuser, bytes, &bytes) == 0) {
+                               errno = EPERM;
+                               member = -1;
+                               break;
+                       }
+               }
+               member = EqualSid(tuser->User.Sid, psid);
+               if (member)
+                       break;
+               if (tgrp == NULL) {
+                       GetTokenInformation(thandle, TokenGroups, NULL, 0, &bytes);
+                       tgrp = malloc(bytes);
+                       if (tgrp == NULL) {
+                               errno = ENOMEM;
+                               member = -1;
+                               break;
+                       }
+                       if (GetTokenInformation(thandle, TokenGroups, tgrp, bytes, &bytes) == 0) {
+                               errno = EPERM;
+                               member = -1;
+                               break;
+                       }
+               }
+               for (g = 0; g < tgrp->GroupCount; g++) {
+                       member = EqualSid(tgrp->Groups[g].Sid, psid);
+                       if (member)
+                               break;
+               }
+       }
+       LocalFree(psid);
+       free(tuser);
+       free(tgrp);
+
+       return (member);
+}
+
+int print_sid(HANDLE thandle);
+
+int
+bsdtar_is_privileged(struct bsdtar *bsdtar)
+{
+       HANDLE thandle;
+       int ret;
+       const char *sidlist[] = {
+               "S-1-5-32-544", /* Administrators */
+               "S-1-5-32-551", /* Backup Operators */
+               NULL
+       };
+
+       (void)bsdtar;/* UNUSED */
+       if (OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &thandle) == 0) {
+               bsdtar_warnc(bsdtar, EPERM, "Failed to check privilege");
+               return (0);
+       }
+       ret = _is_privileged(thandle, sidlist);
+       if (ret < 0) {
+               bsdtar_warnc(bsdtar, errno, "Failed to check privilege");
+               return (0);
+       }
+       return (ret);
+}
+
+#endif /* LIST_H */
+
+#endif
diff --git a/tar/bsdtar_windows.h b/tar/bsdtar_windows.h
new file mode 100644 (file)
index 0000000..388442c
--- /dev/null
@@ -0,0 +1,58 @@
+/*-
+ * Copyright (c) 2009 Michihiro NAKAJIMA
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef BSDTAR_WINDOWS_H
+#define BSDTAR_WINDOWS_H 1
+
+#define PRId64 "I64"
+#define exit           la_exit
+#define geteuid()      0
+
+struct __DIR;
+typedef struct __DIR DIR;
+struct direct {
+       unsigned char   d_nameln;
+       char            d_name[MAX_PATH];
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern void    la_exit(int status);
+
+extern DIR     *opendir(const char *path);
+extern struct dirent *readdir(DIR *dirp);
+extern int     closedir(DIR *dirp);
+
+extern int     bsdtar_is_privileged(struct bsdtar *bsdtar);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* BSDTAR_WINDOWS_H */
index 4627c0c6172e59e15eeb3a4143e807127d5a8e79..0d94aca22c2fd23fbe7e5f2b4656277396d89119 100644 (file)
@@ -37,6 +37,10 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/getdate.y,v 1.9 2007/07/20 01:27:50 kientzle
 #include <string.h>
 #include <time.h>
 
+#ifdef _MSC_VER
+#define __STDC__       /* for a bug of bison 2.1 on Windows */
+#endif
+
 #define yyparse getdate_yyparse
 #define yylex getdate_yylex
 #define yyerror getdate_yyerror
index c4b6e938b19ff4d523f9ccd1860c4e1674c1c093..56cc6d646e1bfbbcec2070cfe80c0564bfd6dbb8 100644 (file)
@@ -390,10 +390,18 @@ list_item_verbose(struct bsdtar *bsdtar, FILE *out, struct archive_entry *entry)
 
        /* Format the time using 'ls -l' conventions. */
        tim = (time_t)st->st_mtime;
+#ifdef _WIN32
+       /* Windows' strftime function does not support %e format. */
+       if (abs(tim - now) > (365/2)*86400)
+               fmt = bsdtar->day_first ? "%d %b  %Y" : "%b %d  %Y";
+       else
+               fmt = bsdtar->day_first ? "%d %b %H:%M" : "%b %d %H:%M";
+#else
        if (abs(tim - now) > (365/2)*86400)
                fmt = bsdtar->day_first ? "%e %b  %Y" : "%b %e  %Y";
        else
                fmt = bsdtar->day_first ? "%e %b %H:%M" : "%b %e %H:%M";
+#endif
        strftime(tmp, sizeof(tmp), fmt, localtime(&tim));
        fprintf(out, " %s ", tmp);
        safe_fprintf(out, "%s", archive_entry_pathname(entry));
index 249ee34a9337a5edfb7d841f9999639f15e2947b..5f28e230036826f4c9beb0e04386328c01b520ee 100644 (file)
@@ -82,8 +82,10 @@ siginfo_init(struct bsdtar *bsdtar)
        /* We want to catch SIGINFO, if it exists. */
        bsdtar->siginfo->siginfo_old = signal(SIGINFO, siginfo_handler);
 #endif
+#ifdef SIGUSR1
        /* ... and treat SIGUSR1 the same way as SIGINFO. */
        bsdtar->siginfo->sigusr1_old = signal(SIGUSR1, siginfo_handler);
+#endif
 }
 
 void
@@ -135,8 +137,10 @@ siginfo_done(struct bsdtar *bsdtar)
        /* Restore old SIGINFO handler. */
        signal(SIGINFO, bsdtar->siginfo->siginfo_old);
 #endif
+#ifdef SIGUSR1
        /* And the old SIGUSR1 handler, too. */
        signal(SIGUSR1, bsdtar->siginfo->sigusr1_old);
+#endif
 
        /* Free strings. */
        free(bsdtar->siginfo->path);
index 3c8c00fa636d991484d51fc1cf2c2cf13a74a03b..17115453295b505006fd0b57181ca0c70d682ab9 100644 (file)
 #error Oops: No config.h and no pre-built configuration in test.h.
 #endif
 
+#ifndef _WIN32
 #include <dirent.h>
+#else
+#define dirent direct
+#include "../bsdtar_windows.h"
+#include <direct.h>
+#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <stdio.h>
index fff51123019637fdeb2a0375d15d114f2b122fdb..3328f569ae21caddb782ac43a5554fedbada05ba 100644 (file)
@@ -30,7 +30,9 @@ static void
 basic_tar(const char *target, const char *pack_options, const char *unpack_options)
 {
        struct stat st, st2;
+#ifndef _WIN32
        char buff[128];
+#endif
        int r;
 
        assertEqualInt(0, mkdir(target, 0775));
@@ -87,6 +89,7 @@ basic_tar(const char *target, const char *pack_options, const char *unpack_optio
                assertEqualInt(st.st_ino, st2.st_ino);
        }
 
+#ifndef _WIN32
        /* Symlink */
        r = lstat("symlink", &st);
        failure("Failed to stat file %s/symlink, errno=%d", target, errno);
@@ -102,6 +105,7 @@ basic_tar(const char *target, const char *pack_options, const char *unpack_optio
                        assertEqualString(buff, "file");
                }
        }
+#endif
 
        /* dir */
        r = lstat("dir", &st);
index 2b076582becc4ed7925452b9140265bd2ed7db15..b71571950ae9f2a4f64bd670c1bd53ec5440df13 100644 (file)
@@ -83,6 +83,9 @@ struct tree_entry {
        dev_t dev;
        ino_t ino;
        int fd;
+#ifdef _WIN32
+       char *fullpath;
+#endif
        int flags;
 };
 
@@ -100,6 +103,9 @@ struct tree {
        struct tree_entry       *current;
        DIR     *d;
        int      initialDirFd;
+#ifdef _WIN32
+       char    *initialDir;
+#endif
        int      flags;
        int      visit_type;
        int      tree_errno; /* Error code from last failed operation. */
@@ -164,6 +170,9 @@ tree_push(struct tree *t, const char *path)
        te->next = t->stack;
        t->stack = te;
        te->fd = -1;
+#ifdef _WIN32
+       te->fullpath = NULL;
+#endif
        te->name = strdup(path);
        te->flags = needsPreVisit | needsPostVisit;
        te->dirname_length = t->dirname_length;
@@ -214,6 +223,10 @@ tree_open(const char *path)
        memset(t, 0, sizeof(*t));
        tree_append(t, path, strlen(path));
        t->initialDirFd = open(".", O_RDONLY);
+#ifdef _WIN32
+       if (t->initialDirFd >= 0)
+               t->initialDir = getcwd(NULL, 0);
+#endif
        /*
         * During most of the traversal, items are set up and then
         * returned immediately from tree_next().  That doesn't work
@@ -236,10 +249,20 @@ tree_ascend(struct tree *t)
        te = t->stack;
        t->depth--;
        if (te->flags & isDirLink) {
+#ifdef HAVE_FCHDIR
                if (fchdir(te->fd) != 0) {
                        t->tree_errno = errno;
                        r = TREE_ERROR_FATAL;
                }
+#endif
+#ifdef _WIN32
+               if (te->fullpath != NULL && chdir(te->fullpath) != 0) {
+                       t->tree_errno = errno;
+                       r = TREE_ERROR_FATAL;
+               }
+               free(te->fullpath);
+               te->fullpath = NULL;
+#endif
                close(te->fd);
                t->openCount--;
        } else {
@@ -332,6 +355,9 @@ tree_next(struct tree *t)
                        /* If it is a link, set up fd for the ascent. */
                        if (t->stack->flags & isDirLink) {
                                t->stack->fd = open(".", O_RDONLY);
+#ifdef _WIN32
+                               t->stack->fullpath = getcwd(NULL, 0);
+#endif
                                t->openCount++;
                                if (t->openCount > t->maxOpenCount)
                                        t->maxOpenCount = t->openCount;
@@ -555,7 +581,14 @@ tree_close(struct tree *t)
                free(t->buff);
        /* chdir() back to where we started. */
        if (t->initialDirFd >= 0) {
+#ifdef HAVE_FCHDIR
                fchdir(t->initialDirFd);
+#endif
+#ifdef _WIN32
+               chdir(t->initialDir);
+               free(t->initialDir);
+               t->initialDir = NULL;
+#endif
                close(t->initialDirFd);
                t->initialDirFd = -1;
        }