X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fwin32.c;h=ba3dc588dc0dcb6149260aa99ae56503e0d5a75d;hp=fb30b5f08d3f29e9577bb05ef29578e92e91e3c2;hb=eea32b7418fab652606ca47320b0bc1277908b5c;hpb=9fb3aaca115429b0af2a623bf20bfceef74f047f diff --git a/src/win32.c b/src/win32.c index fb30b5f0..ba3dc588 100644 --- a/src/win32.c +++ b/src/win32.c @@ -44,6 +44,12 @@ #include +static const char *access_denied_msg = +" If you are not running this program as the administrator, you may\n" +" need to do so, so that all data and metadata can be backed up.\n" +" Otherwise, there may be no way to access the desired data or\n" +" metadata without taking ownership of the file or directory.\n"; + #ifdef ENABLE_ERROR_MESSAGES void win32_error(u32 err_code) @@ -171,10 +177,17 @@ win32_get_security_descriptor(struct wim_dentry *dentry, err = GetLastError(); } } - ERROR("Win32 API: Failed to read security descriptor of \"%ls\"", - path_utf16); - win32_error(err); - return WIMLIB_ERR_READ; + + if (err == ERROR_ACCESS_DENIED) { + WARNING("Failed to read security descriptor of \"%ls\": " + "Access denied!\n%s", path_utf16, access_denied_msg); + return 0; + } else { + ERROR("Win32 API: Failed to read security descriptor of \"%ls\"", + path_utf16); + win32_error(err); + return WIMLIB_ERR_READ; + } } /* Reads the directory entries of directory using a Win32 API and recursively @@ -223,13 +236,14 @@ win32_recurse_directory(struct wim_dentry *root, ret = 0; do { /* Skip . and .. entries */ - if (!(dat.cFileName[0] == L'.' && - (dat.cFileName[1] == L'\0' || - (dat.cFileName[1] == L'.' && dat.cFileName[2] == L'\0')))) + if (!(dat.cFileName[0] == cpu_to_le16(L'.') && + (dat.cFileName[1] == cpu_to_le16(L'\0') || + (dat.cFileName[1] == cpu_to_le16(L'.') && + dat.cFileName[2] == cpu_to_le16(L'\0'))))) { struct wim_dentry *child; - char *mbs_name; + mbchar *mbs_name; size_t mbs_name_nbytes; ret = utf16le_to_mbs(dat.cFileName, wcslen(dat.cFileName) * sizeof(wchar_t), @@ -238,7 +252,7 @@ win32_recurse_directory(struct wim_dentry *root, if (ret) goto out_find_close; - char name[strlen(root_disk_path) + 1 + mbs_name_nbytes + 1]; + mbchar name[strlen(root_disk_path) + 1 + mbs_name_nbytes + 1]; sprintf(name, "%s/%s", root_disk_path, mbs_name); FREE(mbs_name); ret = win32_build_dentry_tree(&child, name, lookup_table, @@ -273,15 +287,14 @@ out_find_close: * for the reparse point unless an entry already exists for * the exact same data stream. * - * @path: External path to the parse point (UTF-8). Used for error messages - * only. + * @path: External path to the reparse point. Used for error messages only. * * Returns 0 on success; nonzero on failure. */ static int win32_capture_reparse_point(HANDLE hFile, struct wim_inode *inode, struct wim_lookup_table *lookup_table, - const char *path) + const mbchar *path) { /* "Reparse point data, including the tag and optional GUID, * cannot exceed 16 kilobytes." - MSDN */ @@ -289,8 +302,14 @@ win32_capture_reparse_point(HANDLE hFile, DWORD bytesReturned; if (!DeviceIoControl(hFile, FSCTL_GET_REPARSE_POINT, - NULL, 0, reparse_point_buf, - sizeof(reparse_point_buf), &bytesReturned, NULL)) + NULL, /* "Not used with this operation; set to NULL" */ + 0, /* "Not used with this operation; set to 0" */ + reparse_point_buf, /* "A pointer to a buffer that + receives the reparse point data */ + sizeof(reparse_point_buf), /* "The size of the output + buffer, in bytes */ + &bytesReturned, + NULL)) { DWORD err = GetLastError(); ERROR("Win32 API: Failed to get reparse data of \"%s\"", path); @@ -303,7 +322,7 @@ win32_capture_reparse_point(HANDLE hFile, } inode->i_reparse_tag = le32_to_cpu(*(u32*)reparse_point_buf); return inode_add_ads_with_data(inode, "", - (const u8*)reparse_point_buf + 8, + reparse_point_buf + 8, bytesReturned - 8, lookup_table); } @@ -377,18 +396,22 @@ win32_capture_stream(const wchar_t *path_utf16, u8 hash[SHA1_HASH_SIZE]; struct wim_lookup_table_entry *lte; int ret; - wchar_t *p, *colon; + wchar_t *stream_name, *colon; + size_t stream_name_nchars; bool is_named_stream; wchar_t *spath; size_t spath_nchars; DWORD err; + size_t spath_buf_nbytes; + const wchar_t *relpath_prefix; + const wchar_t *colonchar; /* The stream name should be returned as :NAME:TYPE */ - p = dat->cStreamName; - if (*p != L':') + stream_name = dat->cStreamName; + if (*stream_name != L':') goto out_invalid_stream_name; - p += 1; - colon = wcschr(p, L':'); + stream_name += 1; + colon = wcschr(stream_name, L':'); if (colon == NULL) goto out_invalid_stream_name; @@ -398,47 +421,56 @@ win32_capture_stream(const wchar_t *path_utf16, goto out; } - is_named_stream = (p != colon); + *colon = '\0'; + + stream_name_nchars = colon - stream_name; + is_named_stream = (stream_name_nchars != 0); + if (is_named_stream) { /* Allocate an ADS entry for the named stream. */ - char *mbs_stream_name; - size_t mbs_stream_name_nbytes; - ret = utf16le_to_mbs(p, - (colon - p) * sizeof(wchar_t), - &mbs_stream_name, - &mbs_stream_name_nbytes); - if (ret) - goto out; - ads_entry = inode_add_ads(inode, mbs_stream_name); - FREE(mbs_stream_name); + ads_entry = inode_add_ads_utf16le(inode, stream_name, + stream_name_nchars * 2); if (!ads_entry) { ret = WIMLIB_ERR_NOMEM; goto out; } } - /* Create a UTF-16 string @spath that gives the filename, then a colon, - * then the stream name. Or, if it's an unnamed stream, just the + /* Create a UTF-16LE string @spath that gives the filename, then a + * colon, then the stream name. Or, if it's an unnamed stream, just the * filename. It is MALLOC()'ed so that it can be saved in the - * wim_lookup_table_entry if needed. */ - *colon = '\0'; + * wim_lookup_table_entry if needed. + * + * As yet another special case, relative paths need to be changed to + * begin with an explicit "./" so that, for example, a file t:ads, where + * :ads is the part we added, is not interpreted as a file on the t: + * drive. */ spath_nchars = path_utf16_nchars; - if (is_named_stream) - spath_nchars += colon - p + 1; - - spath = MALLOC((spath_nchars + 1) * sizeof(wchar_t)); - memcpy(spath, path_utf16, path_utf16_nchars * sizeof(wchar_t)); + relpath_prefix = L""; + colonchar = L""; if (is_named_stream) { - spath[path_utf16_nchars] = L':'; - memcpy(&spath[path_utf16_nchars + 1], p, (colon - p) * sizeof(wchar_t)); + spath_nchars += 1 + stream_name_nchars; + colonchar = L":"; + if (path_utf16_nchars == 1 && + path_utf16[0] != cpu_to_le16('/') && + path_utf16[0] != cpu_to_le16('\\')) + { + spath_nchars += 2; + relpath_prefix = L"./"; + } } - spath[spath_nchars] = L'\0'; + + spath_buf_nbytes = (spath_nchars + 1) * sizeof(wchar_t); + spath = MALLOC(spath_buf_nbytes); + + swprintf(spath, spath_buf_nbytes, L"%ls%ls%ls%ls", + relpath_prefix, path_utf16, colonchar, stream_name); ret = win32_sha1sum(spath, hash); if (ret) { err = GetLastError(); ERROR("Win32 API: Failed to read \"%ls\" to calculate SHA1sum", - path_utf16); + spath); win32_error(err); goto out_free_spath; } @@ -514,10 +546,17 @@ win32_capture_streams(const wchar_t *path_utf16, { return 0; } else { - ERROR("Win32 API: Failed to look up data streams of \"%ls\"", - path_utf16); - win32_error(err); - return WIMLIB_ERR_READ; + if (err == ERROR_ACCESS_DENIED) { + WARNING("Failed to look up data streams of \"%ls\": " + "Access denied!\n%s", path_utf16, + access_denied_msg); + return 0; + } else { + ERROR("Win32 API: Failed to look up data streams of \"%ls\"", + path_utf16); + win32_error(err); + return WIMLIB_ERR_READ; + } } } do { @@ -542,7 +581,7 @@ out_find_close: /* Win32 version of capturing a directory tree */ int win32_build_dentry_tree(struct wim_dentry **root_ret, - const char *root_disk_path, + const mbchar *root_disk_path, struct wim_lookup_table *lookup_table, struct wim_security_data *sd, const struct capture_config *config, @@ -813,7 +852,7 @@ win32_extract_stream(const struct wim_inode *inode, if (stream_name_utf16) { /* Named stream. Create a buffer that contains the UTF-16LE - * string [./]@path:@stream_name_utf16. This is needed to + * string [.\]@path:@stream_name_utf16. This is needed to * create and open the stream using CreateFileW(). I'm not * aware of any other APIs to do this. Note: the '$DATA' suffix * seems to be unneeded. Additional note: a "./" prefix needs @@ -827,7 +866,11 @@ win32_extract_stream(const struct wim_inode *inode, path_nchars = wcslen(path); stream_name_nchars = wcslen(stream_name_utf16); stream_path_nchars = path_nchars + 1 + stream_name_nchars; - if (path[0] != L'/' && path[0] != L'\\') { + if (path[0] != cpu_to_le16(L'\0') && + path[0] != cpu_to_le16(L'/') && + path[0] != cpu_to_le16(L'\\') && + path[1] != cpu_to_le16(L':')) + { prefix = L"./"; stream_path_nchars += 2; } else { @@ -1160,7 +1203,7 @@ realpath(const mbchar *path, mbchar *resolved_path) if (!ret) goto fail_win32; - resolved_path = MALLOC(ret + 1); + resolved_path = MALLOC(ret); if (!resolved_path) goto fail; ret = GetFullPathNameA(path, ret, resolved_path, NULL); @@ -1183,3 +1226,20 @@ nl_langinfo(nl_item item) strcpy(buf, "Unknown"); return buf; } + +/* rename() on Windows fails if the destination file exists. Fix it. */ +int +rename_replacement(const char *oldpath, const char *newpath) +{ + if (MoveFileExA(oldpath, newpath, MOVEFILE_REPLACE_EXISTING)) { + return 0; + } else { + /* As usual, the possible error values are not documented */ + DWORD err = GetLastError(); + ERROR("MoveFileExA(): Can't rename \"%s\" to \"%s\"", + oldpath, newpath); + win32_error(err); + errno = 0; + return -1; + } +}