From: Michihiro NAKAJIMA Date: Sun, 15 Feb 2009 23:05:53 +0000 (-0500) Subject: Now, we can run bsdtar and bsdtar_test on Windows. X-Git-Tag: v2.7.0~277 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=44b45af0d353f9b7e3990089eb6268a71dfbbd48;p=thirdparty%2Flibarchive.git Now, we can run bsdtar and bsdtar_test on Windows. But those need more improvement. bsdtar_test reported many errors. SVN-Revision: 629 --- diff --git a/CMakeLists.txt b/CMakeLists.txt index 0eed01b1e..5a4ea5e73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/tar/bsdtar.c b/tar/bsdtar.c index 0fc837505..3fbb4e7dc 100644 --- a/tar/bsdtar.c +++ b/tar/bsdtar.c @@ -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; diff --git a/tar/bsdtar_platform.h b/tar/bsdtar_platform.h index 634c8910a..89e441a75 100644 --- a/tar/bsdtar_platform.h +++ b/tar/bsdtar_platform.h @@ -164,4 +164,10 @@ #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 index 000000000..6e437eafe --- /dev/null +++ b/tar/bsdtar_windows.c @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include + +#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 index 000000000..388442c32 --- /dev/null +++ b/tar/bsdtar_windows.h @@ -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 */ diff --git a/tar/getdate.y b/tar/getdate.y index 4627c0c61..0d94aca22 100644 --- a/tar/getdate.y +++ b/tar/getdate.y @@ -37,6 +37,10 @@ __FBSDID("$FreeBSD: src/usr.bin/tar/getdate.y,v 1.9 2007/07/20 01:27:50 kientzle #include #include +#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 diff --git a/tar/read.c b/tar/read.c index c4b6e938b..56cc6d646 100644 --- a/tar/read.c +++ b/tar/read.c @@ -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)); diff --git a/tar/siginfo.c b/tar/siginfo.c index 249ee34a9..5f28e2300 100644 --- a/tar/siginfo.c +++ b/tar/siginfo.c @@ -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); diff --git a/tar/test/test.h b/tar/test/test.h index 3c8c00fa6..171154532 100644 --- a/tar/test/test.h +++ b/tar/test/test.h @@ -45,7 +45,13 @@ #error Oops: No config.h and no pre-built configuration in test.h. #endif +#ifndef _WIN32 #include +#else +#define dirent direct +#include "../bsdtar_windows.h" +#include +#endif #include #include #include diff --git a/tar/test/test_basic.c b/tar/test/test_basic.c index fff511230..3328f569a 100644 --- a/tar/test/test_basic.c +++ b/tar/test/test_basic.c @@ -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); diff --git a/tar/tree.c b/tar/tree.c index 2b076582b..b71571950 100644 --- a/tar/tree.c +++ b/tar/tree.c @@ -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; }