diff options
author | Michihiro NAKAJIMA <ggcueroad@gmail.com> | 2011-04-11 12:46:37 -0400 |
---|---|---|
committer | Michihiro NAKAJIMA <ggcueroad@gmail.com> | 2011-04-11 12:46:37 -0400 |
commit | f9d148d66cc0827bbe8ed4e1f722c44b27d1e038 (patch) | |
tree | 18aac8702af8132ac356ce3aaafb0a36e72489fb | |
parent | 7e59ca749833a2dc949649a09ca84dd216248cde (diff) | |
download | libarchive-f9d148d66cc0827bbe8ed4e1f722c44b27d1e038.tar.gz |
Improve archive_write_disk_windows.c.
- Use WCS to pathname. This has made following changes.
- FindFirstFileW and GetFileInformationByHandle instead of stat/lstat.
- Move __la_chmod and __la_ftruncate used only in the file from archive_windows.c
and change it to wide char version.
- Remove __la_mkdir and directly use CreateDirectoryW in the file.
- Remove __la_rmdir and use _wrmdir in the file.
- Remove __la_unlink and use _wunlink in the file.
- Remove __la_link and Move la_CreateHardLinkW into the file to use it directly.
- Use _wopen instead of __la_open.
Unfortunately, at this time we cannot use full-pathname through __la_win_permissive_name_w()
completely at the file because __la_win_permissive_name_w() trim "../". For example, the path
"abc/a/../b../c", which is multi dirs in one entry, will be converted to "<parent-dir>/abc/c",
so we could not make both abc/a and abc/b directories if we applied __la_win_permissive_name_w()
to the path at _archive_write_disk_header().
SVN-Revision: 3213
-rw-r--r-- | libarchive/archive_windows.c | 282 | ||||
-rw-r--r-- | libarchive/archive_windows.h | 17 | ||||
-rw-r--r-- | libarchive/archive_write_disk_windows.c | 552 |
3 files changed, 377 insertions, 474 deletions
diff --git a/libarchive/archive_windows.c b/libarchive/archive_windows.c index ab6c8265..cc9c2af6 100644 --- a/libarchive/archive_windows.c +++ b/libarchive/archive_windows.c @@ -101,9 +101,6 @@ struct ustat { dev_t st_rdev; }; -/* Local replacement for undocumented Windows CRT function. */ -static void la_dosmaperr(unsigned long e); - /* Transform 64-bits ino into 32-bits by hashing. * You do not forget that really unique number size is 64-bits. */ @@ -251,159 +248,6 @@ la_CreateFile(const char *path, DWORD dwDesiredAccess, DWORD dwShareMode, return (handle); } -static void * -la_GetFunctionKernel32(const char *name) -{ - static HINSTANCE lib; - static int set; - if (!set) { - set = 1; - lib = LoadLibrary("kernel32.dll"); - } - if (lib == NULL) { - fprintf(stderr, "Can't load kernel32.dll?!\n"); - exit(1); - } - return (void *)GetProcAddress(lib, name); -} - -static int -la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) -{ - static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES); - static int set; - if (!set) { - set = 1; - f = la_GetFunctionKernel32("CreateHardLinkW"); - } - return f == NULL ? 0 : (*f)(linkname, target, NULL); -} - - -/* - * Make a link from src to dst. - * This can exceed MAX_PATH limitation. - */ -int -__la_link(const char *src, const char *dst) -{ - wchar_t *wsrc, *wdst; - int res, retval; - DWORD attr; - - if (src == NULL || dst == NULL) { - set_errno (EINVAL); - return -1; - } - - wsrc = __la_win_permissive_name(src); - wdst = __la_win_permissive_name(dst); - if (wsrc == NULL || wdst == NULL) { - free(wsrc); - free(wdst); - set_errno (EINVAL); - return -1; - } - - if ((attr = GetFileAttributesW(wsrc)) != (DWORD)-1) { - res = la_CreateHardLinkW(wdst, wsrc); - } 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((unsigned char)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 a multi-byte src to a wide-char src */ - wlen = (int)wcslen(wsrc); - slen = (int)strlen(src); - n = MultiByteToWideChar(CP_ACP, 0, - src, slen, wsrc, wlen); - 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 == (DWORD)-1 || (attr & FILE_ATTRIBUTE_DIRECTORY) != 0) { - if (attr == (DWORD)-1) - la_dosmaperr(GetLastError()); - else - errno = EPERM; - free (wnewsrc); - retval = -1; - goto exit; - } - res = la_CreateHardLinkW(wdst, wnewsrc); - free (wnewsrc); - } - if (res == 0) { - la_dosmaperr(GetLastError()); - retval = -1; - } else - retval = 0; -exit: - free(wsrc); - free(wdst); - return (retval); -} - -int -__la_ftruncate(int fd, int64_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)) { - la_dosmaperr(GetLastError()); - return (-1); - } - if (!SetEndOfFile(handle)) { - la_dosmaperr(GetLastError()); - return (-1); - } - return (0); -} - /* * Change current working directory. * This can exceed MAX_PATH limitation. @@ -436,50 +280,6 @@ __la_chdir(const char *path) return (0); } -/* This can exceed MAX_PATH limitation. */ -int -__la_chmod(const char *path, mode_t mode) -{ - wchar_t *ws; - DWORD attr; - BOOL r; - - ws = NULL; - attr = GetFileAttributesA(path); - if (attr == (DWORD)-1) { - if (GetLastError() != ERROR_FILE_NOT_FOUND) { - la_dosmaperr(GetLastError()); - return (-1); - } - ws = __la_win_permissive_name(path); - if (ws == NULL) { - errno = EINVAL; - return (-1); - } - attr = GetFileAttributesW(ws); - if (attr == (DWORD)-1) { - free(ws); - la_dosmaperr(GetLastError()); - return (-1); - } - } - if (mode & _S_IWRITE) - attr &= ~FILE_ATTRIBUTE_READONLY; - else - attr |= FILE_ATTRIBUTE_READONLY; - if (ws == NULL) - r = SetFileAttributesA(path, attr); - else { - r = SetFileAttributesW(ws, attr); - free(ws); - } - if (r == 0) { - la_dosmaperr(GetLastError()); - return (-1); - } - return (0); -} - /* * This fcntl is limited implementation. */ @@ -535,38 +335,6 @@ __la_lseek(int fd, __int64 offset, int whence) /* This can exceed MAX_PATH limitation. */ 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) { - la_dosmaperr(GetLastError()); - return (-1); - } - } else - return (0); - ws = __la_win_permissive_name(path); - if (ws == NULL) { - errno = EINVAL; - return (-1); - } - r = CreateDirectoryW(ws, NULL); - free(ws); - if (r == 0) { - la_dosmaperr(GetLastError()); - return (-1); - } - return (0); -} - -/* This can exceed MAX_PATH limitation. */ -int __la_open(const char *path, int flags, ...) { va_list ap; @@ -718,29 +486,6 @@ __la_read(int fd, void *buf, size_t nbytes) return ((ssize_t)bytes_read); } -/* - * Remove a directory. - * This can exceed MAX_PATH limitation. - */ -int -__la_rmdir(const char *path) -{ - wchar_t *ws; - int r; - - r = _rmdir(path); - if (r >= 0 || errno != ENOENT) - return (r); - ws = __la_win_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) @@ -948,29 +693,6 @@ __la_stat(const char *path, struct stat *st) } /* - * Delete a file. - * This can exceed MAX_PATH limitation. - */ -int -__la_unlink(const char *path) -{ - wchar_t *ws; - int r; - - r = _unlink(path); - if (r >= 0 || errno != ENOENT) - return (r); - ws = __la_win_permissive_name(path); - if (ws == NULL) { - errno = EINVAL; - return (-1); - } - r = _wunlink(ws); - free(ws); - return (r); -} - -/* * This waitpid is limited implementation. */ pid_t @@ -1344,8 +1066,8 @@ static const struct { { ERROR_NOT_ENOUGH_QUOTA, ENOMEM } }; -static void -la_dosmaperr(unsigned long e) +void +__la_dosmaperr(unsigned long e) { int i; diff --git a/libarchive/archive_windows.h b/libarchive/archive_windows.h index 6e97d3c4..448fd1f6 100644 --- a/libarchive/archive_windows.h +++ b/libarchive/archive_windows.h @@ -90,21 +90,16 @@ /* Alias the Windows _function to the POSIX equivalent. */ #define chdir __la_chdir -#define chmod __la_chmod #define close _close #define fcntl __la_fcntl #ifndef fileno #define fileno _fileno #endif #define fstat __la_fstat -#define ftruncate __la_ftruncate -#define link __la_link #define lseek __la_lseek #define lstat __la_stat -#define mkdir(d,m) __la_mkdir(d, m) #define open __la_open #define read __la_read -#define rmdir __la_rmdir #if !defined(__BORLANDC__) #define setmode _setmode #endif @@ -116,7 +111,6 @@ #if !defined(__BORLANDC__) #define umask _umask #endif -#define unlink __la_unlink #define waitpid __la_waitpid #define write __la_write @@ -249,14 +243,6 @@ #endif -/* Tell libarchive code that we have simulations for these. */ -#ifndef HAVE_FTRUNCATE -#define HAVE_FTRUNCATE 1 -#endif -#ifndef HAVE_LINK -#define HAVE_LINK 1 -#endif - /* Replacement POSIX function */ extern int __la_chdir(const char *path); extern int __la_chmod(const char *path, mode_t mode); @@ -284,5 +270,8 @@ extern ssize_t __la_write(int fd, const void *buf, size_t nbytes); extern wchar_t *__la_win_permissive_name(const char *name); extern wchar_t *__la_win_permissive_name_w(const wchar_t *wname); +extern void __la_dosmaperr(unsigned long e); +#define la_dosmaperr(e) __la_dosmaperr(e) + #endif /* LIBARCHIVE_ARCHIVE_WINDOWS_H_INCLUDED */ diff --git a/libarchive/archive_write_disk_windows.c b/libarchive/archive_write_disk_windows.c index 4d352fe8..153663a0 100644 --- a/libarchive/archive_write_disk_windows.c +++ b/libarchive/archive_write_disk_windows.c @@ -72,6 +72,10 @@ __FBSDID("$FreeBSD$"); #ifndef O_BINARY #define O_BINARY 0 #endif +#ifndef IO_REPARSE_TAG_SYMLINK +/* Old SDKs do not provide IO_REPARSE_TAG_SYMLINK */ +#define IO_REPARSE_TAG_SYMLINK 0xA000000CL +#endif struct fixup_entry { struct fixup_entry *next; @@ -89,7 +93,7 @@ struct fixup_entry { size_t mac_metadata_size; void *mac_metadata; int fixup; /* bitmask of what needs fixing */ - char *name; + wchar_t *name; }; /* @@ -140,20 +144,20 @@ struct archive_write_disk { /* * Full path of last file to satisfy symlink checks. */ - struct archive_string path_safe; + struct archive_wstring path_safe; /* * Cached stat data from disk for the current entry. * If this is valid, pst points to st. Otherwise, * pst is null. */ - struct stat st; - struct stat *pst; + BY_HANDLE_FILE_INFORMATION st; + BY_HANDLE_FILE_INFORMATION *pst; /* Information about the object being restored right now. */ struct archive_entry *entry; /* Entry being extracted. */ - char *name; /* Name of entry, possibly edited. */ - struct archive_string _name_data; /* backing store for 'name' */ + wchar_t *name; /* Name of entry, possibly edited. */ + struct archive_wstring _name_data; /* backing store for 'name' */ /* Tasks remaining for this object. */ int todo; /* Tasks deferred until end-of-archive. */ @@ -196,25 +200,25 @@ struct archive_write_disk { static int check_symlinks(struct archive_write_disk *); static int create_filesystem_object(struct archive_write_disk *); -static struct fixup_entry *current_fixup(struct archive_write_disk *, const char *pathname); +static struct fixup_entry *current_fixup(struct archive_write_disk *, const wchar_t *pathname); #if defined(HAVE_FCHDIR) && defined(PATH_MAX) static void edit_deep_directories(struct archive_write_disk *ad); #endif static int cleanup_pathname(struct archive_write_disk *); -static int create_dir(struct archive_write_disk *, char *); -static int create_parent_dir(struct archive_write_disk *, char *); -static int older(struct stat *, struct archive_entry *); +static int create_dir(struct archive_write_disk *, wchar_t *); +static int create_parent_dir(struct archive_write_disk *, wchar_t *); +static int older(BY_HANDLE_FILE_INFORMATION *, struct archive_entry *); static int restore_entry(struct archive_write_disk *); #ifdef HAVE_POSIX_ACL static int set_acl(struct archive_write_disk *, int fd, const char *, struct archive_acl *, acl_type_t, int archive_entry_acl_type, const char *tn); #endif -static int set_acls(struct archive_write_disk *, int fd, const char *, struct archive_acl *); +static int set_acls(struct archive_write_disk *, int fd, const wchar_t *, struct archive_acl *); static int set_xattrs(struct archive_write_disk *); static int set_fflags(struct archive_write_disk *); static int set_ownership(struct archive_write_disk *); static int set_mode(struct archive_write_disk *, int mode); -static int set_times(struct archive_write_disk *, int, int, const char *, +static int set_times(struct archive_write_disk *, int, int, const wchar_t *, time_t, long, time_t, long, time_t, long, time_t, long); static int set_times_from_entry(struct archive_write_disk *); static struct fixup_entry *sort_dir_list(struct fixup_entry *p); @@ -233,6 +237,174 @@ static int _archive_write_disk_finish_entry(struct archive *); static ssize_t _archive_write_disk_data(struct archive *, const void *, size_t); static ssize_t _archive_write_disk_data_block(struct archive *, const void *, size_t, int64_t); +#define bhfi_dev(bhfi) ((bhfi)->dwVolumeSerialNumber) +/* Treat FileIndex as i-node. We should remove a sequence number + * which is high-16-bits of nFileIndexHigh. */ +#define bhfi_ino(bhfi) \ + ((((int64_t)((bhfi)->nFileIndexHigh & 0x0000FFFFUL)) << 32) \ + + (bhfi)->nFileIndexLow) +#define bhfi_size(bhfi) \ + ((((int64_t)(bhfi)->nFileSizeHigh) << 32) + (bhfi)->nFileSizeLow) + +static int +file_information(struct archive_write_disk *a, wchar_t *path, + BY_HANDLE_FILE_INFORMATION *st, mode_t *mode, int sim_lstat) +{ + HANDLE h; + int r; + DWORD flag = FILE_FLAG_BACKUP_SEMANTICS; + WIN32_FIND_DATAW findData; + + if (sim_lstat || mode != NULL) { + h = FindFirstFileW(path, &findData); + if (h == INVALID_HANDLE_VALUE) { + la_dosmaperr(GetLastError()); + return (-1); + } + FindClose(h); + } + + /* Is symlink file ? */ + if (sim_lstat && + ((findData.dwFileAttributes + & FILE_ATTRIBUTE_REPARSE_POINT) && + (findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK))) + flag |= FILE_FLAG_OPEN_REPARSE_POINT; + + h = CreateFileW(a->name, 0, 0, NULL, + OPEN_EXISTING, flag, NULL); + if (h == INVALID_HANDLE_VALUE) { + la_dosmaperr(GetLastError()); + return (-1); + } + r = GetFileInformationByHandle(h, st); + CloseHandle(h); + if (r == 0) { + la_dosmaperr(GetLastError()); + return (-1); + } + + if (mode == NULL) + return (0); + + *mode = S_IRUSR | S_IRGRP | S_IROTH; + if ((st->dwFileAttributes & FILE_ATTRIBUTE_READONLY) == 0) + *mode |= S_IWUSR | S_IWGRP | S_IWOTH; + if ((st->dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) && + findData.dwReserved0 == IO_REPARSE_TAG_SYMLINK) + *mode |= S_IFLNK; + else if (st->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + *mode |= S_IFDIR | S_IXUSR | S_IXGRP | S_IXOTH; + else { + const wchar_t *p; + + *mode |= S_IFREG; + p = wcsrchr(path, L'.'); + if (p != NULL && wcslen(p) == 4) { + switch (p[1]) { + case L'B': case L'b': + if ((p[2] == L'A' || p[2] == L'a' ) && + (p[3] == L'T' || p[3] == L't' )) + *mode |= S_IXUSR | S_IXGRP | S_IXOTH; + break; + case L'C': case L'c': + if (((p[2] == L'M' || p[2] == L'm' ) && + (p[3] == L'D' || p[3] == L'd' )) || + ((p[2] == L'M' || p[2] == L'm' ) && + (p[3] == L'D' || p[3] == L'd' ))) + *mode |= S_IXUSR | S_IXGRP | S_IXOTH; + break; + case L'E': case L'e': + if ((p[2] == L'X' || p[2] == L'x' ) && + (p[3] == L'E' || p[3] == L'e' )) + *mode |= S_IXUSR | S_IXGRP | S_IXOTH; + break; + default: + break; + } + } + } + return (0); +} + +int +la_chmod(const wchar_t *path, mode_t mode) +{ + DWORD attr; + BOOL r; + + attr = GetFileAttributesW(path); + if (attr == (DWORD)-1) { + la_dosmaperr(GetLastError()); + return (-1); + } + if (mode & _S_IWRITE) + attr &= ~FILE_ATTRIBUTE_READONLY; + else + attr |= FILE_ATTRIBUTE_READONLY; + r = SetFileAttributesW(path, attr); + if (r == 0) { + la_dosmaperr(GetLastError()); + return (-1); + } + return (0); +} + +static void * +la_GetFunctionKernel32(const char *name) +{ + static HINSTANCE lib; + static int set; + if (!set) { + set = 1; + lib = LoadLibrary("kernel32.dll"); + } + if (lib == NULL) { + fprintf(stderr, "Can't load kernel32.dll?!\n"); + exit(1); + } + return (void *)GetProcAddress(lib, name); +} + +static int +la_CreateHardLinkW(wchar_t *linkname, wchar_t *target) +{ + static BOOLEAN (WINAPI *f)(LPWSTR, LPWSTR, LPSECURITY_ATTRIBUTES); + static int set; + if (!set) { + set = 1; + f = la_GetFunctionKernel32("CreateHardLinkW"); + } + return f == NULL ? 0 : (*f)(linkname, target, NULL); +} + +static int +la_ftruncate(int fd, int64_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)) { + la_dosmaperr(GetLastError()); + return (-1); + } + if (!SetEndOfFile(handle)) { + la_dosmaperr(GetLastError()); + return (-1); + } + return (0); +} + static int lazy_stat(struct archive_write_disk *a) { @@ -240,17 +412,18 @@ lazy_stat(struct archive_write_disk *a) /* Already have stat() data available. */ return (ARCHIVE_OK); } -#ifdef HAVE_FSTAT - if (a->fd >= 0 && fstat(a->fd, &a->st) == 0) { + if (a->fd >= 0 && + GetFileInformationByHandle( + (HANDLE)_get_osfhandle(a->fd), &a->st) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } -#endif + /* * XXX At this point, symlinks should not be hit, otherwise * XXX a race occured. Do we want to check explicitly for that? */ - if (lstat(a->name, &a->st) == 0) { + if (file_information(a, a->name, &a->st, NULL, 1) == 0) { a->pst = &a->st; return (ARCHIVE_OK); } @@ -315,6 +488,9 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) { struct archive_write_disk *a = (struct archive_write_disk *)_a; struct fixup_entry *fe; +#if 0 + wchar_t *full; +#endif int ret, r; archive_check_magic(&a->archive, ARCHIVE_WRITE_DISK_MAGIC, @@ -346,7 +522,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) a->filesize = archive_entry_size(a->entry); else a->filesize = -1; - archive_strcpy(&(a->_name_data), archive_entry_pathname(a->entry)); + archive_wstrcpy(&(a->_name_data), archive_entry_pathname_w(a->entry)); a->name = a->_name_data.s; archive_clear_error(&a->archive); @@ -359,6 +535,24 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if (ret != ARCHIVE_OK) return (ret); + /* + * TODO: Currently we cannot use making full-path operation + * because this "aa/a/../b../c" will be converted to "aa/c" by + * GetFullPathNameW() W32 API, which __la_win_permissive_name_w uses. + * it means we cannot handle multiple dirs in one archive_entry. + */ +#if 0 + /* + * Generate full-pathname and use it from here. + */ + full = __la_win_permissive_name_w(a->name); + if (full == NULL) + return (ARCHIVE_FAILED); + archive_wstrcpy(&(a->_name_data), full); + a->name = a->_name_data.s; + free(full); +#endif + /* * Query the umask so we get predictable mode settings. * This gets done on every call to _write_header in case the @@ -450,7 +644,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) * deep restore logic. */ if (a->deferred & TODO_MODE) { - fe = current_fixup(a, archive_entry_pathname(entry)); + fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->fixup |= TODO_MODE_BASE; fe->mode = a->mode; } @@ -458,7 +652,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) if ((a->deferred & TODO_TIMES) && (archive_entry_mtime_is_set(entry) || archive_entry_atime_is_set(entry))) { - fe = current_fixup(a, archive_entry_pathname(entry)); + fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->mode = a->mode; fe->fixup |= TODO_TIMES; if (archive_entry_atime_is_set(entry)) { @@ -488,7 +682,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } if (a->deferred & TODO_ACLS) { - fe = current_fixup(a, archive_entry_pathname(entry)); + fe = current_fixup(a, archive_entry_pathname_w(entry)); archive_acl_copy(&fe->acl, archive_entry_acl(entry)); } @@ -497,7 +691,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) size_t metadata_size; metadata = archive_entry_mac_metadata(a->entry, &metadata_size); if (metadata != NULL && metadata_size > 0) { - fe = current_fixup(a, archive_entry_pathname(entry)); + fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->mac_metadata = malloc(metadata_size); if (fe->mac_metadata != NULL) { memcpy(fe->mac_metadata, metadata, metadata_size); @@ -508,7 +702,7 @@ _archive_write_disk_header(struct archive *_a, struct archive_entry *entry) } if (a->deferred & TODO_FFLAGS) { - fe = current_fixup(a, archive_entry_pathname(entry)); + fe = current_fixup(a, archive_entry_pathname_w(entry)); fe->fixup |= TODO_FFLAGS; /* TODO: Complete this.. defer fflags from below. */ } @@ -698,14 +892,12 @@ _archive_write_disk_finish_entry(struct archive *_a) /* Last write ended at exactly the filesize; we're done. */ /* Hopefully, this is the common case. */ } else { -#if HAVE_FTRUNCATE - if (ftruncate(a->fd, a->filesize) == -1 && + if (la_ftruncate(a->fd, a->filesize) == -1 && a->filesize == 0) { archive_set_error(&a->archive, errno, "File size could not be restored"); return (ARCHIVE_FAILED); } -#endif /* * Not all platforms implement the XSI option to * extend files via ftruncate. Stat() the file again @@ -716,7 +908,7 @@ _archive_write_disk_finish_entry(struct archive *_a) return (ret); /* We can use lseek()/write() to extend the file if * ftruncate didn't work or isn't available. */ - if (a->st.st_size < a->filesize) { + if (bhfi_size(&(a->st)) < a->filesize) { const char nul = '\0'; if (lseek(a->fd, a->filesize - 1, SEEK_SET) < 0) { archive_set_error(&a->archive, errno, @@ -802,7 +994,7 @@ _archive_write_disk_finish_entry(struct archive *_a) */ if (a->todo & TODO_ACLS) { int r2 = set_acls(a, a->fd, - archive_entry_pathname(a->entry), + archive_entry_pathname_w(a->entry), archive_entry_acl(a->entry)); if (r2 < ret) ret = r2; } @@ -875,7 +1067,7 @@ archive_write_disk_new(void) a->start_time = time(NULL); /* Query and restore the umask. */ umask(a->user_umask = umask(0)); - if (archive_string_ensure(&a->path_safe, 512) == NULL) { + if (archive_wstring_ensure(&a->path_safe, 512) == NULL) { free(a); return (NULL); } @@ -900,12 +1092,12 @@ restore_entry(struct archive_write_disk *a) * object is a dir, but that doesn't mean the old * object isn't a dir. */ - if (unlink(a->name) == 0) { + if (_wunlink(a->name) == 0) { /* We removed it, reset cached stat. */ a->pst = NULL; } else if (errno == ENOENT) { /* File didn't exist, that's just as good. */ - } else if (rmdir(a->name) == 0) { + } else if (_wrmdir(a->name) == 0) { /* It was a dir, but now it's gone. */ a->pst = NULL; } else { @@ -943,7 +1135,7 @@ restore_entry(struct archive_write_disk *a) */ if (en == EISDIR) { /* A dir is in the way of a non-dir, rmdir it. */ - if (rmdir(a->name) != 0) { + if (_wrmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); @@ -952,6 +1144,7 @@ restore_entry(struct archive_write_disk *a) /* Try again. */ en = create_filesystem_object(a); } else if (en == EEXIST) { + mode_t st_mode; /* * We know something is in the way, but we don't know what; * we need to find out before we go any further. @@ -963,13 +1156,13 @@ restore_entry(struct archive_write_disk *a) * follow the symlink if we're creating a dir. */ if (S_ISDIR(a->mode)) - r = stat(a->name, &a->st); + r = file_information(a, a->name, &a->st, &st_mode, 0); /* * If it's not a dir (or it's a broken symlink), * then don't follow it. */ if (r != 0 || !S_ISDIR(a->mode)) - r = lstat(a->name, &a->st); + r = file_information(a, a->name, &a->st, &st_mode, 1); if (r != 0) { archive_set_error(&a->archive, errno, "Can't stat existing object"); @@ -980,7 +1173,7 @@ restore_entry(struct archive_write_disk *a) * NO_OVERWRITE_NEWER doesn't apply to directories. */ if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE_NEWER) - && !S_ISDIR(a->st.st_mode)) { + && !S_ISDIR(st_mode)) { if (!older(&(a->st), a->entry)) { archive_entry_unset_size(a->entry); return (ARCHIVE_OK); @@ -990,15 +1183,15 @@ restore_entry(struct archive_write_disk *a) /* If it's our archive, we're done. */ if (a->skip_file_dev > 0 && a->skip_file_ino > 0 && - a->st.st_dev == a->skip_file_dev && - a->st.st_ino == a->skip_file_ino) { + bhfi_dev(&a->st) == a->skip_file_dev && + bhfi_ino(&a->st) == a->skip_file_ino) { archive_set_error(&a->archive, 0, "Refusing to overwrite archive"); return (ARCHIVE_FAILED); } - if (!S_ISDIR(a->st.st_mode)) { + if (!S_ISDIR(st_mode)) { /* A non-dir is in the way, unlink it. */ - if (unlink(a->name) != 0) { + if (_wunlink(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't unlink already-existing object"); return (ARCHIVE_FAILED); @@ -1008,7 +1201,7 @@ restore_entry(struct archive_write_disk *a) en = create_filesystem_object(a); } else if (!S_ISDIR(a->mode)) { /* A dir is in the way of a non-dir, rmdir it. */ - if (rmdir(a->name) != 0) { + if (_wrmdir(a->name) != 0) { archive_set_error(&a->archive, errno, "Can't remove already-existing dir"); return (ARCHIVE_FAILED); @@ -1023,7 +1216,7 @@ restore_entry(struct archive_write_disk *a) * Note that we don't change perms on existing * dirs unless _EXTRACT_PERM is specified. */ - if ((a->mode != a->st.st_mode) + if ((a->mode != st_mode) && (a->todo & TODO_MODE_FORCE)) a->deferred |= (a->todo & TODO_MODE); /* Ownership doesn't need deferred fixup. */ @@ -1051,15 +1244,31 @@ static int create_filesystem_object(struct archive_write_disk *a) { /* Create the entry. */ - const char *linkname; + const wchar_t *linkname; mode_t final_mode, mode; int r; /* We identify hard/symlinks according to the link names. */ /* Since link(2) and symlink(2) don't handle modes, we're done here. */ - linkname = archive_entry_hardlink(a->entry); + linkname = archive_entry_hardlink_w(a->entry); if (linkname != NULL) { - r = link(linkname, a->name) ? errno : 0; + wchar_t *linkfull, *namefull; + + linkfull = __la_win_permissive_name_w(linkname); + namefull = __la_win_permissive_name_w(a->name); + if (linkfull == NULL || namefull == NULL) { + errno = EINVAL; + r = -1; + } else { + r = la_CreateHardLinkW(namefull, linkfull); + if (r == 0) { + la_dosmaperr(GetLastError()); + r = errno; + } else + r = 0; + } + free(linkfull); + free(namefull); /* * New cpio and pax formats allow hardlink entries * to carry data, so we may have to open the file @@ -1076,13 +1285,13 @@ create_filesystem_object(struct archive_write_disk *a) a->todo = 0; a->deferred = 0; } if (r == 0 && a->filesize > 0) { - a->fd = open(a->name, O_WRONLY | O_TRUNC | O_BINARY); + a->fd = _wopen(a->name, O_WRONLY | O_TRUNC | O_BINARY); if (a->fd < 0) r = errno; } return (r); } - linkname = archive_entry_symlink(a->entry); + linkname = archive_entry_symlink_w(a->entry); if (linkname != NULL) { #if HAVE_SYMLINK return symlink(linkname, a->name) ? errno : 0; @@ -1111,8 +1320,19 @@ create_filesystem_object(struct archive_write_disk *a) /* POSIX requires that we fall through here. */ /* FALLTHROUGH */ case AE_IFREG: - a->fd = open(a->name, + a->fd = _wopen(a->name, O_WRONLY | O_CREAT | O_EXCL | O_BINARY, mode); + if (a->fd < 0 && errno == EACCES) { + DWORD attr; + /* Simulate an errno of POSIX system. */ + attr = GetFileAttributesW(a->name); + if (attr == (DWORD)-1) + la_dosmaperr(GetLastError()); + else if (attr & FILE_ATTRIBUTE_DIRECTORY) + errno = EISDIR; + else + errno = EACCES; + } r = (a->fd < 0); break; case AE_IFCHR: @@ -1122,8 +1342,9 @@ create_filesystem_object(struct archive_write_disk *a) return (EINVAL); case AE_IFDIR: mode = (mode | MINIMUM_DIR_MODE) & MAXIMUM_DIR_MODE; - r = mkdir(a->name, mode); - if (r == 0) { + r = CreateDirectoryW(a->name, NULL); + if (r != 0) { + r = 0; /* Defer setting dir times. */ a->deferred |= (a->todo & TODO_TIMES); a->todo &= ~TODO_TIMES; @@ -1134,6 +1355,9 @@ create_filesystem_object(struct archive_write_disk *a) || (a->flags & ARCHIVE_EXTRACT_PERM)) a->deferred |= (a->todo & TODO_MODE); a->todo &= ~TODO_MODE; + } else { + la_dosmaperr(GetLastError()); + r = -1; } break; case AE_IFIFO: @@ -1199,7 +1423,7 @@ _archive_write_disk_close(struct archive *_a) p->ctime, p->ctime_nanos); } if (p->fixup & TODO_MODE_BASE) - chmod(p->name, p->mode); + la_chmod(p->name, p->mode); if (p->fixup & TODO_ACLS) set_acls(a, -1, p->name, &p->acl); next = p->next; @@ -1230,9 +1454,9 @@ _archive_write_disk_free(struct archive *_a) (a->cleanup_uid)(a->lookup_uid_data); if (a->entry) archive_entry_free(a->entry); - archive_string_free(&a->_name_data); + archive_wstring_free(&a->_name_data); archive_string_free(&a->archive.error_string); - archive_string_free(&a->path_safe); + archive_wstring_free(&a->path_safe); a->archive.magic = 0; __archive_clean(&a->archive); free(a); @@ -1275,7 +1499,7 @@ sort_dir_list(struct fixup_entry *p) /* Step 3: Merge the returned lists. */ /* Pick the first element for the merged list. */ - if (strcmp(a->name, b->name) > 0) { + if (wcscmp(a->name, b->name) > 0) { t = p = a; a = a->next; } else { @@ -1285,7 +1509,7 @@ sort_dir_list(struct fixup_entry *p) /* Always put the later element on the list first. */ while (a != NULL && b != NULL) { - if (strcmp(a->name, b->name) > 0) { + if (wcscmp(a->name, b->name) > 0) { t->next = a; a = a->next; } else { @@ -1311,7 +1535,7 @@ sort_dir_list(struct fixup_entry *p) * structure rather than a simple list of names. */ static struct fixup_entry * -new_fixup(struct archive_write_disk *a, const char *pathname) +new_fixup(struct archive_write_disk *a, const wchar_t *pathname) { struct fixup_entry *fe; @@ -1321,7 +1545,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname) fe->next = a->fixup_list; a->fixup_list = fe; fe->fixup = 0; - fe->name = strdup(pathname); + fe->name = _wcsdup(pathname); return (fe); } @@ -1329,7 +1553,7 @@ new_fixup(struct archive_write_disk *a, const char *pathname) * Returns a fixup structure for the current entry. */ static struct fixup_entry * -current_fixup(struct archive_write_disk *a, const char *pathname) +current_fixup(struct archive_write_disk *a, const wchar_t *pathname) { if (a->current_fixup == NULL) a->current_fixup = new_fixup(a, pathname); @@ -1350,15 +1574,11 @@ current_fixup(struct archive_write_disk *a, const char *pathname) static int check_symlinks(struct archive_write_disk *a) { -#if !defined(HAVE_LSTAT) - /* Platform doesn't have lstat, so we can't look for symlinks. */ - (void)a; /* UNUSED */ - return (ARCHIVE_OK); -#else - char *pn, *p; - char c; + wchar_t *pn, *p; + wchar_t c; int r; - struct stat st; + BY_HANDLE_FILE_INFORMATION st; + mode_t st_mode; /* * Guard against symlink tricks. Reject any archive entry whose @@ -1378,19 +1598,19 @@ check_symlinks(struct archive_write_disk *a) c = pn[0]; pn[0] = '\0'; /* Check that we haven't hit a symlink. */ - r = lstat(a->name, &st); + r = file_information(a, a->name, &st, &st_mode, 1); if (r != 0) { /* We've hit a dir that doesn't exist; stop now. */ if (errno == ENOENT) break; - } else if (S_ISLNK(st.st_mode)) { + } else if (S_ISLNK(st_mode)) { if (c == '\0') { /* * Last element is symlink; remove it * so we can overwrite it with the * item being extracted. */ - if (unlink(a->name)) { + if (_wunlink(a->name)) { archive_set_error(&a->archive, errno, "Could not remove symlink %s", a->name); @@ -1414,7 +1634,7 @@ check_symlinks(struct archive_write_disk *a) return (0); } else if (a->flags & ARCHIVE_EXTRACT_UNLINK) { /* User asked us to remove problems. */ - if (unlink(a->name) != 0) { + if (_wunlink(a->name) != 0) { archive_set_error(&a->archive, 0, "Cannot remove intervening symlink %s", a->name); @@ -1433,67 +1653,8 @@ check_symlinks(struct archive_write_disk *a) } pn[0] = c; /* We've checked and/or cleaned the whole path, so remember it. */ - archive_strcpy(&a->path_safe, a->name); + archive_wstrcpy(&a->path_safe, a->name); return (ARCHIVE_OK); -#endif -} - -/* - * 1. Convert a path separator from '\' to '/' . - * We shouldn't check multi-byte character directly because some - * character-set have been using the '\' character for a part of - * its multibyte character code. - * 2. Replace unusable characters in Windows with underscore('_'). - * See also : http://msdn.microsoft.com/en-us/library/aa365247.aspx - */ -static void -cleanup_pathname_win(struct archive_write_disk *a) -{ - wchar_t wc; - char *p; - size_t alen, l; - int mb, dos; - - alen = 0; - mb = dos = 0; - for (p = a->name; *p != '\0'; p++) { - ++alen; - if (*(unsigned char *)p > 127) - mb = 1; - if (*p == '\\') { - /* If we have not met any multi-byte characters, - * we can replace '\' with '/'. */ - if (!mb) - *p = '/'; - dos = 1; - } - /* Rewrite the path name if its character is a unusable. */ - if (*p == ':' || *p == '*' || *p == '?' || *p == '"' || - *p == '<' || *p == '>' || *p == '|') - *p = '_'; - } - if (!mb || !dos) - return; - - /* - * Convert path separator in wide-character. - */ - p = a->name; - while (*p != '\0' && alen) { - l = mbtowc(&wc, p, alen); - if (l == -1) { - while (*p != '\0') { - if (*p == '\\') - *p = '/'; - ++p; - } - break; - } - if (l == 1 && wc == L'\\') - *p = '/'; - p += l; - alen -= l; - } } /* @@ -1505,40 +1666,47 @@ cleanup_pathname_win(struct archive_write_disk *a) static int cleanup_pathname(struct archive_write_disk *a) { - char *dest, *src; - char separator = '\0'; + wchar_t *dest, *src, *p; + wchar_t separator = L'\0'; dest = src = a->name; - if (*src == '\0') { + if (*src == L'\0') { archive_set_error(&a->archive, ARCHIVE_ERRNO_MISC, "Invalid empty pathname"); return (ARCHIVE_FAILED); } - cleanup_pathname_win(a); + for (p = a->name; *p != L'\0'; p++) { + if (*p == L'\\') + *p = L'/'; + /* Rewrite the path name if its character is a unusable. */ + if (*p == L':' || *p == L'*' || *p == L'?' || *p == L'"' || + *p == L'<' || *p == L'>' || *p == L'|') + *p = L'_'; + } /* Skip leading '/'. */ - if (*src == '/') + if (*src == L'/') separator = *src++; /* Scan the pathname one element at a time. */ for (;;) { /* src points to first char after '/' */ - if (src[0] == '\0') { + if (src[0] == L'\0') { break; - } else if (src[0] == '/') { + } else if (src[0] == L'/') { /* Found '//', ignore second one. */ src++; continue; - } else if (src[0] == '.') { - if (src[1] == '\0') { + } else if (src[0] == L'.') { + if (src[1] == L'\0') { /* Ignore trailing '.' */ break; - } else if (src[1] == '/') { + } else if (src[1] == L'/') { /* Skip './'. */ src += 2; continue; - } else if (src[1] == '.') { - if (src[2] == '/' || src[2] == '\0') { + } else if (src[1] == L'.') { + if (src[2] == L'/' || src[2] == L'\0') { /* Conditionally warn about '..' */ if (a->flags & ARCHIVE_EXTRACT_SECURE_NODOTDOT) { archive_set_error(&a->archive, @@ -1559,12 +1727,12 @@ cleanup_pathname(struct archive_write_disk *a) /* Copy current element, including leading '/'. */ if (separator) - *dest++ = '/'; - while (*src != '\0' && *src != '/') { + *dest++ = L'/'; + while (*src != L'\0' && *src != L'/') { *dest++ = *src++; } - if (*src == '\0') + if (*src == L'\0') break; /* Skip '/' separator. */ @@ -1580,12 +1748,12 @@ cleanup_pathname(struct archive_write_disk *a) * like '.' or '/' or './' or '/././././/./'. */ if (separator) - *dest++ = '/'; + *dest++ = L'/'; else - *dest++ = '.'; + *dest++ = L'.'; } /* Terminate the result. */ - *dest = '\0'; + *dest = L'\0'; return (ARCHIVE_OK); } @@ -1594,18 +1762,18 @@ cleanup_pathname(struct archive_write_disk *a) * is already in mutable storage. */ static int -create_parent_dir(struct archive_write_disk *a, char *path) +create_parent_dir(struct archive_write_disk *a, wchar_t *path) { - char *slash; + wchar_t *slash; int r; /* Remove tail element to obtain parent name. */ - slash = strrchr(path, '/'); + slash = wcsrchr(path, L'/'); if (slash == NULL) return (ARCHIVE_OK); - *slash = '\0'; + *slash = L'\0'; r = create_dir(a, path); - *slash = '/'; + *slash = L'/'; return (r); } @@ -1617,29 +1785,29 @@ create_parent_dir(struct archive_write_disk *a, char *path) * Assumes path is in mutable storage; path is unchanged on exit. */ static int -create_dir(struct archive_write_disk *a, char *path) +create_dir(struct archive_write_disk *a, wchar_t *path) { - struct stat st; + BY_HANDLE_FILE_INFORMATION st; struct fixup_entry *le; - char *slash, *base; - mode_t mode_final, mode; + wchar_t *slash, *base; + mode_t mode_final, mode, st_mode; int r; /* Check for special names and just skip them. */ - slash = strrchr(path, '/'); + slash = wcsrchr(path, L'/'); if (slash == NULL) base = path; else base = slash + 1; - if (base[0] == '\0' || - (base[0] == '.' && base[1] == '\0') || - (base[0] == '.' && base[1] == '.' && base[2] == '\0')) { + if (base[0] == L'\0' || + (base[0] == L'.' && base[1] == L'\0') || + (base[0] == L'.' && base[1] == L'.' && base[2] == L'\0')) { /* Don't bother trying to create null path, '.', or '..'. */ if (slash != NULL) { - *slash = '\0'; + *slash = L'\0'; r = create_dir(a, path); - *slash = '/'; + *slash = L'/'; return (r); } return (ARCHIVE_OK); @@ -1650,15 +1818,15 @@ create_dir(struct archive_write_disk *a, char *path) * here loses the ability to extract through symlinks. Also note * that this should not use the a->st cache. */ - if (stat(path, &st) == 0) { - if (S_ISDIR(st.st_mode)) + if (file_information(a, path, &st, &st_mode, 0) == 0) { + if (S_ISDIR(st_mode)) return (ARCHIVE_OK); if ((a->flags & ARCHIVE_EXTRACT_NO_OVERWRITE)) { archive_set_error(&a->archive, EEXIST, "Can't create directory '%s'", path); return (ARCHIVE_FAILED); } - if (unlink(path) != 0) { + if (_wunlink(path) != 0) { archive_set_error(&a->archive, errno, "Can't create directory '%s': " "Conflicting file cannot be removed", @@ -1690,13 +1858,15 @@ create_dir(struct archive_write_disk *a, char *path) mode = mode_final; mode |= MINIMUM_DIR_MODE; mode &= MAXIMUM_DIR_MODE; - if (mkdir(path, mode) == 0) { + if (CreateDirectoryW(path, NULL) != 0) { if (mode != mode_final) { le = new_fixup(a, path); le->fixup |=TODO_MODE_BASE; le->mode = mode_final; } return (ARCHIVE_OK); + } else { + la_dosmaperr(GetLastError()); } /* @@ -1705,7 +1875,7 @@ create_dir(struct archive_write_disk *a, char *path) * don't add it to the fixup list here, as it's already been * added. */ - if (stat(path, &st) == 0 && S_ISDIR(st.st_mode)) + if (file_information(a, path, &st, &st_mode, 0) == 0 && S_ISDIR(st_mode)) return (ARCHIVE_OK); archive_set_error(&a->archive, errno, "Failed to create dir '%s'", @@ -1744,7 +1914,7 @@ set_ownership(struct archive_write_disk *a) static int set_times(struct archive_write_disk *a, - int fd, int mode, const char *name, + int fd, int mode, const wchar_t *name, time_t atime, long atime_nanos, time_t birthtime, long birthtime_nanos, time_t mtime, long mtime_nanos, @@ -1756,22 +1926,23 @@ set_times(struct archive_write_disk *a, HANDLE h, hw; ULARGE_INTEGER wintm; - wchar_t *ws; FILETIME *pfbtime; FILETIME fatime, fbtime, fmtime; if (fd >= 0) { h = (HANDLE)_get_osfhandle(fd); hw = NULL; - ws = NULL; } else { + wchar_t *ws; + if (S_ISLNK(mode)) return (ARCHIVE_OK); - ws = __la_win_permissive_name(name); + ws = __la_win_permissive_name_w(name); if (ws == NULL) goto settimes_failed; hw = CreateFileW(ws, FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL); + free(ws); if (hw == INVALID_HANDLE_VALUE) goto settimes_failed; h = hw; @@ -1795,12 +1966,10 @@ set_times(struct archive_write_disk *a, pfbtime = NULL; if (SetFileTime(h, pfbtime, &fatime, &fmtime) == 0) goto settimes_failed; - free(ws); CloseHandle(hw); return (ARCHIVE_OK); settimes_failed: - free(ws); CloseHandle(hw); archive_set_error(&a->archive, EINVAL, "Can't restore time"); return (ARCHIVE_WARN); @@ -1861,11 +2030,11 @@ set_mode(struct archive_write_disk *a, int mode) */ if ((r = lazy_stat(a)) != ARCHIVE_OK) return (r); - if (a->pst->st_gid != a->gid) { + if (0 != a->gid) { mode &= ~ S_ISGID; } /* While we're here, double-check the UID. */ - if (a->pst->st_uid != a->uid + if (0 != a->uid && (a->todo & TODO_SUID)) { mode &= ~ S_ISUID; } @@ -1917,7 +2086,7 @@ set_mode(struct archive_write_disk *a, int mode) #endif /* If this platform lacks fchmod(), then * we'll just use chmod(). */ - if (chmod(a->name, mode) != 0) { + if (la_chmod(a->name, mode) != 0) { archive_set_error(&a->archive, errno, "Can't set permissions to 0%o", (int)mode); r = ARCHIVE_WARN; @@ -1935,7 +2104,7 @@ set_fflags(struct archive_write_disk *a) /* Default empty function body to satisfy mainline code. */ static int -set_acls(struct archive_write_disk *a, int fd, const char *name, +set_acls(struct archive_write_disk *a, int fd, const wchar_t *name, struct archive_acl *acl) { (void)a; /* UNUSED */ @@ -1987,20 +2156,43 @@ trivial_lookup_uid(void *private_data, const char *uname, int64_t uid) return (uid); } +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; + /* milli seconds base */ + *time = (time_t)(utc.QuadPart / 10000000); + /* nano seconds base */ + *ns = (long)(utc.QuadPart % 10000000) * 100; + } else { + *time = 0; + *ns = 0; + } +} /* * Test if file on disk is older than entry. */ static int -older(struct stat *st, struct archive_entry *entry) +older(BY_HANDLE_FILE_INFORMATION *st, struct archive_entry *entry) { + time_t sec; + long nsec; + + fileTimeToUtc(&st->ftLastWriteTime, &sec, &nsec); /* First, test the seconds and return if we have a definite answer. */ /* Definitely older. */ - if (st->st_mtime < archive_entry_mtime(entry)) + if (sec < archive_entry_mtime(entry)) return (1); /* Definitely younger. */ - if (st->st_mtime > archive_entry_mtime(entry)) + if (sec > archive_entry_mtime(entry)) return (0); - /* This system doesn't have high-res timestamps. */ + if (nsec < archive_entry_mtime_nsec(entry)) + return (1); /* Same age or newer, so not older. */ return (0); } |