X-Git-Url: https://wimlib.net/git/?a=blobdiff_plain;f=src%2Fwin32.c;h=0836acaa8ad3ae128399c014d2657f0fef1bf5a6;hb=2b4002220a86b32fbae34c1bc59c5804289103c5;hp=f2e7eeb08988464c43db05f4eb3f9850a381d150;hpb=7ce0d372fae285051cbc9740c9fa316d22465d9d;p=wimlib diff --git a/src/win32.c b/src/win32.c index f2e7eeb0..0836acaa 100644 --- a/src/win32.c +++ b/src/win32.c @@ -170,13 +170,13 @@ win32_open_file_data_only(const wchar_t *path) } int -read_win32_file_prefix(const struct lookup_table_entry *lte, +read_win32_file_prefix(const struct wim_lookup_table_entry *lte, u64 size, consume_data_callback_t cb, void *ctx_or_buf, int _ignored_flags) { - int ret; + int ret = 0; void *out_buf; DWORD err; u64 bytes_remaining; @@ -186,8 +186,7 @@ read_win32_file_prefix(const struct lookup_table_entry *lte, err = GetLastError(); ERROR("Failed to open \"%ls\"", lte->file_on_disk); win32_error(err); - ret = WIMLIB_ERR_OPEN; - goto out; + return WIMLIB_ERR_OPEN; } if (cb) @@ -200,30 +199,189 @@ read_win32_file_prefix(const struct lookup_table_entry *lte, DWORD bytesToRead, bytesRead; bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining); - if (!ReadFile(h, out_buf, bytesToRead, &bytesRead, NULL) || + if (!ReadFile(hFile, out_buf, bytesToRead, &bytesRead, NULL) || bytesRead != bytesToRead) { err = GetLastError(); ERROR("Failed to read data from \"%ls\"", lte->file_on_disk); win32_error(err); ret = WIMLIB_ERR_READ; - goto out_close_handle; + break; } bytes_remaining -= bytesRead; if (cb) { - ret = cb(out_buf, bytesRead, ctx_or_buf); + ret = (*cb)(out_buf, bytesRead, ctx_or_buf); if (ret) - goto out_close_handle; + break; } else { out_buf += bytesRead; } } -out_close_handle: CloseHandle(hFile); -out: return ret; } +struct win32_encrypted_read_ctx { + consume_data_callback_t read_prefix_cb; + void *read_prefix_ctx_or_buf; + int wimlib_err_code; + void *buf; + size_t buf_filled; + u64 bytes_remaining; +}; + +static DWORD WINAPI +win32_encrypted_export_cb(unsigned char *_data, void *_ctx, unsigned long len) +{ + const void *data = _data; + struct win32_encrypted_read_ctx *ctx = _ctx; + int ret; + + DEBUG("len = %lu", len); + if (ctx->read_prefix_cb) { + /* The length of the buffer passed to the ReadEncryptedFileRaw() + * export callback is undocumented, so we assume it may be of + * arbitrary size. */ + size_t bytes_to_buffer = min(ctx->bytes_remaining - ctx->buf_filled, + len); + while (bytes_to_buffer) { + size_t bytes_to_copy_to_buf = + min(bytes_to_buffer, WIM_CHUNK_SIZE - ctx->buf_filled); + + memcpy(ctx->buf + ctx->buf_filled, data, + bytes_to_copy_to_buf); + ctx->buf_filled += bytes_to_copy_to_buf; + data += bytes_to_copy_to_buf; + bytes_to_buffer -= bytes_to_copy_to_buf; + + if (ctx->buf_filled == WIM_CHUNK_SIZE || + ctx->buf_filled == ctx->bytes_remaining) + { + ret = (*ctx->read_prefix_cb)(ctx->buf, + ctx->buf_filled, + ctx->read_prefix_ctx_or_buf); + if (ret) { + ctx->wimlib_err_code = ret; + /* Shouldn't matter what error code is returned + * here, as long as it isn't ERROR_SUCCESS. */ + return ERROR_READ_FAULT; + } + ctx->bytes_remaining -= ctx->buf_filled; + ctx->buf_filled = 0; + } + } + } else { + size_t len_to_copy = min(len, ctx->bytes_remaining); + memcpy(ctx->read_prefix_ctx_or_buf, data, len_to_copy); + ctx->bytes_remaining -= len_to_copy; + ctx->read_prefix_ctx_or_buf += len_to_copy; + } + return ERROR_SUCCESS; +} + +int +read_win32_encrypted_file_prefix(const struct wim_lookup_table_entry *lte, + u64 size, + consume_data_callback_t cb, + void *ctx_or_buf, + int _ignored_flags) +{ + struct win32_encrypted_read_ctx export_ctx; + DWORD err; + void *file_ctx; + int ret; + + DEBUG("Reading %"PRIu64" bytes from encryted file \"%ls\"", + size, lte->file_on_disk); + + export_ctx.read_prefix_cb = cb; + export_ctx.read_prefix_ctx_or_buf = ctx_or_buf; + export_ctx.wimlib_err_code = 0; + if (cb) { + export_ctx.buf = MALLOC(WIM_CHUNK_SIZE); + if (!export_ctx.buf) + return WIMLIB_ERR_NOMEM; + } else { + export_ctx.buf = NULL; + } + export_ctx.bytes_remaining = size; + + err = OpenEncryptedFileRawW(lte->file_on_disk, 0, &file_ctx); + if (err != ERROR_SUCCESS) { + ERROR("Failed to open encrypted file \"%ls\" for raw read", + lte->file_on_disk); + win32_error(err); + ret = WIMLIB_ERR_OPEN; + goto out_free_buf; + } + err = ReadEncryptedFileRaw(win32_encrypted_export_cb, + &export_ctx, file_ctx); + if (err != ERROR_SUCCESS) { + ERROR("Failed to read encrypted file \"%ls\"", + lte->file_on_disk); + win32_error(err); + ret = export_ctx.wimlib_err_code; + if (ret == 0) + ret = WIMLIB_ERR_READ; + } else if (export_ctx.bytes_remaining != 0) { + ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from " + "encryted file \"%ls\"", + size - export_ctx.bytes_remaining, size, + lte->file_on_disk); + ret = WIMLIB_ERR_READ; + } else { + ret = 0; + } + CloseEncryptedFileRaw(file_ctx); +out_free_buf: + FREE(export_ctx.buf); + return ret; +} + +/* Given a path, which may not yet exist, get a set of flags that describe the + * features of the volume the path is on. */ +static int +win32_get_vol_flags(const wchar_t *path, unsigned *vol_flags_ret) +{ + wchar_t *volume; + BOOL bret; + DWORD vol_flags; + + if (path[0] != L'\0' && path[0] != L'\\' && + path[0] != L'/' && path[1] == L':') + { + /* Path starts with a drive letter; use it. */ + volume = alloca(4 * sizeof(wchar_t)); + volume[0] = path[0]; + volume[1] = path[1]; + volume[2] = L'\\'; + volume[3] = L'\0'; + } else { + /* Path does not start with a drive letter; use the volume of + * the current working directory. */ + volume = NULL; + } + bret = GetVolumeInformationW(volume, /* lpRootPathName */ + NULL, /* lpVolumeNameBuffer */ + 0, /* nVolumeNameSize */ + NULL, /* lpVolumeSerialNumber */ + NULL, /* lpMaximumComponentLength */ + &vol_flags, /* lpFileSystemFlags */ + NULL, /* lpFileSystemNameBuffer */ + 0); /* nFileSystemNameSize */ + if (!bret) { + DWORD err = GetLastError(); + WARNING("Failed to get volume information for path \"%ls\"", path); + win32_error(err); + vol_flags = 0xffffffff; + } + + DEBUG("using vol_flags = %x", vol_flags); + *vol_flags_ret = vol_flags; + return 0; +} + + static u64 FILETIME_to_u64(const FILETIME *ft) { @@ -235,6 +393,7 @@ win32_get_short_name(struct wim_dentry *dentry, const wchar_t *path) { WIN32_FIND_DATAW dat; if (FindFirstFileW(path, &dat) && dat.cAlternateFileName[0] != L'\0') { + DEBUG("\"%ls\": short name \"%ls\"", path, dat.cAlternateFileName); size_t short_name_nbytes = wcslen(dat.cAlternateFileName) * sizeof(wchar_t); size_t n = short_name_nbytes + sizeof(wchar_t); @@ -340,7 +499,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, - struct win32_capture_state *state); + struct win32_capture_state *state, + unsigned vol_flags); /* Reads the directory entries of directory using a Win32 API and recursively * calls win32_build_dentry_tree() on them. */ @@ -354,13 +514,16 @@ win32_recurse_directory(struct wim_dentry *root, const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, - struct win32_capture_state *state) + struct win32_capture_state *state, + unsigned vol_flags) { WIN32_FIND_DATAW dat; HANDLE hFind; DWORD err; int ret; + DEBUG("Recurse to directory \"%ls\"", dir_path); + /* Begin reading the directory by calling FindFirstFileW. Unlike UNIX * opendir(), FindFirstFileW has file globbing built into it. But this * isn't what we actually want, so just add a dummy glob to get all @@ -407,7 +570,8 @@ win32_recurse_directory(struct wim_dentry *root, config, add_image_flags, progress_func, - state); + state, + vol_flags); dir_path[dir_path_num_chars] = L'\0'; if (ret) goto out_find_close; @@ -446,6 +610,8 @@ win32_capture_reparse_point(HANDLE hFile, struct wim_lookup_table *lookup_table, const wchar_t *path) { + DEBUG("Capturing reparse point \"%ls\"", path); + /* "Reparse point data, including the tag and optional GUID, * cannot exceed 16 kilobytes." - MSDN */ char reparse_point_buf[16 * 1024]; @@ -502,7 +668,6 @@ win32_capture_stream(const wchar_t *path, WIN32_FIND_STREAM_DATA *dat) { struct wim_ads_entry *ads_entry; - u8 hash[SHA1_HASH_SIZE]; struct wim_lookup_table_entry *lte; int ret; wchar_t *stream_name, *colon; @@ -510,11 +675,12 @@ win32_capture_stream(const wchar_t *path, 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; + DEBUG("Capture \"%ls\" stream \"%ls\"", path, dat->cStreamName); + /* The stream name should be returned as :NAME:TYPE */ stream_name = dat->cStreamName; if (*stream_name != L':') @@ -583,7 +749,10 @@ win32_capture_stream(const wchar_t *path, } lte->file_on_disk = spath; spath = NULL; - lte->resource_location = RESOURCE_WIN32; + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED && !is_named_stream) + lte->resource_location = RESOURCE_WIN32_ENCRYPTED; + else + lte->resource_location = RESOURCE_WIN32; lte->resource_entry.original_size = (u64)dat->StreamSize.QuadPart; u32 stream_id; @@ -594,8 +763,8 @@ win32_capture_stream(const wchar_t *path, stream_id = 0; inode->i_lte = lte; } - lookup_table_insert_unhashed(lookup_table, lte, inode, stream_id); + ret = 0; out_free_spath: FREE(spath); out: @@ -620,6 +789,9 @@ out_invalid_stream_name: * @file_size: Size of unnamed data stream. (Used only if alternate * data streams API appears to be unavailable.) * + * @vol_flags: Flags that specify features of the volume being + * captured. + * * Returns 0 on success; nonzero on failure. */ static int @@ -627,20 +799,23 @@ win32_capture_streams(const wchar_t *path, size_t path_num_chars, struct wim_inode *inode, struct wim_lookup_table *lookup_table, - u64 file_size) + u64 file_size, + unsigned vol_flags) { WIN32_FIND_STREAM_DATA dat; int ret; HANDLE hFind; DWORD err; - if (win32func_FindFirstStreamW == NULL) + DEBUG("Capturing streams from \"%ls\"", path); + + if (win32func_FindFirstStreamW == NULL || + !(vol_flags & FILE_NAMED_STREAMS)) goto unnamed_only; hFind = win32func_FindFirstStreamW(path, FindStreamInfoStandard, &dat, 0); if (hFind == INVALID_HANDLE_VALUE) { err = GetLastError(); - if (err == ERROR_CALL_NOT_IMPLEMENTED) goto unnamed_only; @@ -650,14 +825,14 @@ win32_capture_streams(const wchar_t *path, (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) && err == ERROR_HANDLE_EOF) { + DEBUG("ERROR_HANDLE_EOF (ok)"); return 0; } else { if (err == ERROR_ACCESS_DENIED) { - /* XXX This maybe should be an error. */ - WARNING("Failed to look up data streams " - "of \"%ls\": Access denied!\n%ls", - path, capture_access_denied_msg); - return 0; + ERROR("Failed to look up data streams " + "of \"%ls\": Access denied!\n%ls", + path, capture_access_denied_msg); + return WIMLIB_ERR_READ; } else { ERROR("Failed to look up data streams " "of \"%ls\"", path); @@ -684,8 +859,9 @@ out_find_close: FindClose(hFind); return ret; unnamed_only: - /* FindFirstStreamW() API is not available. Only capture the unnamed - * data stream. */ + /* FindFirstStreamW() API is not available, or the volume does not + * support named streams. Only capture the unnamed data stream. */ + DEBUG("Only capturing unnamed data stream"); if (inode->i_attributes & (FILE_ATTRIBUTE_REPARSE_POINT | FILE_ATTRIBUTE_DIRECTORY)) { @@ -714,7 +890,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, const struct wimlib_capture_config *config, int add_image_flags, wimlib_progress_func_t progress_func, - struct win32_capture_state *state) + struct win32_capture_state *state, + unsigned vol_flags) { struct wim_dentry *root = NULL; struct wim_inode *inode; @@ -795,7 +972,9 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); - if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS)) { + if (!(add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NO_ACLS) + && (vol_flags & FILE_PERSISTENT_ACLS)) + { ret = win32_get_security_descriptor(root, sd_set, path, state, add_image_flags); if (ret) @@ -814,7 +993,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, path_num_chars, inode, lookup_table, - file_size); + file_size, + vol_flags); if (ret) goto out_close_handle; ret = win32_recurse_directory(root, @@ -826,7 +1006,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, config, add_image_flags, progress_func, - state); + state, + vol_flags); } else if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { /* Reparse point: save the reparse tag and data. Alternate data * streams are not captured, if it's even possible for a reparse @@ -842,7 +1023,8 @@ win32_build_dentry_tree_recursive(struct wim_dentry **root_ret, path_num_chars, inode, lookup_table, - file_size); + file_size, + vol_flags); } out_close_handle: CloseHandle(hFile); @@ -902,11 +1084,14 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, wchar_t *path; int ret; struct win32_capture_state state; + unsigned vol_flags; path_nchars = wcslen(root_disk_path); if (path_nchars > 32767) return WIMLIB_ERR_INVALID_PARAM; + win32_get_vol_flags(root_disk_path, &vol_flags); + /* There is no check for overflow later when this buffer is being used! * But the max path length on NTFS is 32767 characters, and paths need * to be written specially to even go past 260 characters, so we should @@ -927,7 +1112,8 @@ win32_build_dentry_tree(struct wim_dentry **root_ret, config, add_image_flags, progress_func, - &state); + &state, + vol_flags); FREE(path); if (ret == 0) win32_do_capture_warnings(&state, add_image_flags); @@ -959,7 +1145,7 @@ win32_set_reparse_data(HANDLE h, * leave 8 bytes of space for them at the beginning of the buffer, then * set them manually. */ buf = alloca(len + 8); - ret = read_full_wim_resource(lte, buf + 8, 0); + ret = read_full_resource_into_buf(lte, buf + 8, false); if (ret) return ret; *(u32*)(buf + 0) = cpu_to_le32(reparse_tag); @@ -1011,10 +1197,12 @@ win32_set_compressed(HANDLE hFile, const wchar_t *path) NULL, 0, &bytesReturned, NULL)) { - /* Warning only */ + /* Could be a warning only, but we only call this if the volume + * supports compression. So I'm calling this an error. */ DWORD err = GetLastError(); - WARNING("Failed to set compression flag on \"%ls\"", path); + ERROR("Failed to set compression flag on \"%ls\"", path); win32_error(err); + return WIMLIB_ERR_WRITE; } return 0; } @@ -1028,10 +1216,12 @@ win32_set_sparse(HANDLE hFile, const wchar_t *path) NULL, 0, &bytesReturned, NULL)) { - /* Warning only */ + /* Could be a warning only, but we only call this if the volume + * supports sparse files. So I'm calling this an error. */ DWORD err = GetLastError(); WARNING("Failed to set sparse flag on \"%ls\"", path); win32_error(err); + return WIMLIB_ERR_WRITE; } return 0; } @@ -1110,7 +1300,7 @@ fail: static int -win32_extract_chunk(const void *buf, size_t len, u64 offset, void *arg) +win32_extract_chunk(const void *buf, size_t len, void *arg) { HANDLE hStream = arg; @@ -1135,6 +1325,14 @@ do_win32_extract_stream(HANDLE hStream, struct wim_lookup_table_entry *lte) win32_extract_chunk, hStream); } +static int +do_win32_extract_encrypted_stream(const wchar_t *path, + const struct wim_lookup_table_entry *lte) +{ + ERROR("Extrat encryted streams not implemented"); + return WIMLIB_ERR_INVALID_PARAM; +} + static bool path_is_root_of_drive(const wchar_t *path) { @@ -1206,34 +1404,54 @@ inode_has_special_attributes(const struct wim_inode *inode) FILE_ATTRIBUTE_SPARSE_FILE)) != 0; } +/* Set compression or sparse attributes, and reparse data, if supported by the + * volume. */ static int win32_set_special_attributes(HANDLE hFile, const struct wim_inode *inode, struct wim_lookup_table_entry *unnamed_stream_lte, - const wchar_t *path) + const wchar_t *path, unsigned vol_flags) { int ret; - if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { - DEBUG("Setting reparse data on \"%ls\"", path); - ret = win32_set_reparse_data(hFile, inode->i_reparse_tag, - unnamed_stream_lte, path); - if (ret) - return ret; - } - if (inode->i_attributes & FILE_ATTRIBUTE_COMPRESSED) { - DEBUG("Setting compression flag on \"%ls\"", path); - ret = win32_set_compressed(hFile, path); - if (ret) - return ret; + if (vol_flags & FILE_FILE_COMPRESSION) { + DEBUG("Setting compression flag on \"%ls\"", path); + ret = win32_set_compressed(hFile, path); + if (ret) + return ret; + } else { + DEBUG("Cannot set compression attribute on \"%ls\": " + "volume does not support transparent compression", + path); + } } if (inode->i_attributes & FILE_ATTRIBUTE_SPARSE_FILE) { - DEBUG("Setting sparse flag on \"%ls\"", path); - ret = win32_set_sparse(hFile, path); - if (ret) - return ret; + if (vol_flags & FILE_SUPPORTS_SPARSE_FILES) { + DEBUG("Setting sparse flag on \"%ls\"", path); + ret = win32_set_sparse(hFile, path); + if (ret) + return ret; + } else { + DEBUG("Cannot set sparse attribute on \"%ls\": " + "volume does not support sparse files", + path); + } } + + if (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT) { + if (vol_flags & FILE_SUPPORTS_REPARSE_POINTS) { + DEBUG("Setting reparse data on \"%ls\"", path); + ret = win32_set_reparse_data(hFile, inode->i_reparse_tag, + unnamed_stream_lte, path); + if (ret) + return ret; + } else { + DEBUG("Cannot set reparse data on \"%ls\": volume " + "does not support reparse points", path); + } + } + return 0; } @@ -1241,7 +1459,8 @@ static int win32_extract_stream(const struct wim_inode *inode, const wchar_t *path, const wchar_t *stream_name_utf16, - struct wim_lookup_table_entry *lte) + struct wim_lookup_table_entry *lte, + unsigned vol_flags) { wchar_t *stream_path; HANDLE h; @@ -1309,6 +1528,8 @@ win32_extract_stream(const struct wim_inode *inode, ret = 0; goto out; } + DEBUG("Directory \"%ls\" has special attributes!", + stream_path); creationDisposition = OPEN_EXISTING; } } @@ -1330,7 +1551,8 @@ win32_extract_stream(const struct wim_inode *inode, } if (stream_name_utf16 == NULL && inode_has_special_attributes(inode)) { - ret = win32_set_special_attributes(h, inode, lte, path); + ret = win32_set_special_attributes(h, inode, lte, path, + vol_flags); if (ret) goto fail_close_handle; } @@ -1339,7 +1561,15 @@ win32_extract_stream(const struct wim_inode *inode, if (lte) { DEBUG("Extracting \"%ls\" (len = %"PRIu64")", stream_path, wim_resource_size(lte)); - ret = do_win32_extract_stream(h, lte); + if (inode->i_attributes & FILE_ATTRIBUTE_ENCRYPTED + && stream_name_utf16 == NULL + && (vol_flags & FILE_SUPPORTS_ENCRYPTION)) + { + ret = do_win32_extract_encrypted_stream(stream_path, + lte); + } else { + ret = do_win32_extract_stream(h, lte); + } if (ret) goto fail_close_handle; } @@ -1375,17 +1605,22 @@ out: */ static int win32_extract_streams(const struct wim_inode *inode, - const wchar_t *path, u64 *completed_bytes_p) + const wchar_t *path, u64 *completed_bytes_p, + unsigned vol_flags) { struct wim_lookup_table_entry *unnamed_lte; int ret; unnamed_lte = inode_unnamed_lte_resolved(inode); - ret = win32_extract_stream(inode, path, NULL, unnamed_lte); + ret = win32_extract_stream(inode, path, NULL, unnamed_lte, + vol_flags); if (ret) goto out; if (unnamed_lte) *completed_bytes_p += wim_resource_size(unnamed_lte); + + if (!(vol_flags & FILE_NAMED_STREAMS)) + goto out; for (u16 i = 0; i < inode->i_num_ads; i++) { const struct wim_ads_entry *ads_entry = &inode->i_ads_entries[i]; if (ads_entry->stream_name_nbytes != 0) { @@ -1399,7 +1634,8 @@ win32_extract_streams(const struct wim_inode *inode, ret = win32_extract_stream(inode, path, ads_entry->stream_name, - ads_entry->lte); + ads_entry->lte, + vol_flags); if (ret) break; if (ads_entry->lte) @@ -1422,41 +1658,89 @@ win32_do_apply_dentry(const wchar_t *output_path, struct wim_inode *inode = dentry->d_inode; DWORD err; + if (!args->have_vol_flags) { + win32_get_vol_flags(output_path, &args->vol_flags); + args->have_vol_flags = true; + /* Warn the user about data that may not be extracted. */ + if (!(args->vol_flags & FILE_SUPPORTS_SPARSE_FILES)) + WARNING("Volume does not support sparse files!\n" + " Sparse files will be extracted as non-sparse."); + if (!(args->vol_flags & FILE_SUPPORTS_REPARSE_POINTS)) + WARNING("Volume does not support reparse points!\n" + " Reparse point data will not be extracted."); + if (!(args->vol_flags & FILE_NAMED_STREAMS)) { + WARNING("Volume does not support named data streams!\n" + " Named data streams will not be extracted."); + } + if (!(args->vol_flags & FILE_SUPPORTS_ENCRYPTION)) { + WARNING("Volume does not support encryption!\n" + " Encrypted files will be extracted as raw data."); + } + if (!(args->vol_flags & FILE_FILE_COMPRESSION)) { + WARNING("Volume does not support transparent compression!\n" + " Compressed files will be extracted as non-compressed."); + } + if (!(args->vol_flags & FILE_PERSISTENT_ACLS)) { + if (args->extract_flags & WIMLIB_EXTRACT_FLAG_STRICT_ACLS) { + ERROR("Strict ACLs requested, but the volume does not " + "support ACLs!"); + return WIMLIB_ERR_VOLUME_LACKS_FEATURES; + } else { + WARNING("Volume does not support persistent ACLS!\n" + " File permissions will not be extracted."); + } + } + } + if (inode->i_nlink > 1 && inode->i_extracted_file != NULL) { /* Linked file, with another name already extracted. Create a * hard link. */ + + /* There is a volume flag for this (FILE_SUPPORTS_HARD_LINKS), + * but it's only available on Windows 7 and later. So no use + * even checking it, really. Instead, CreateHardLinkW() will + * apparently return ERROR_INVALID_FUNCTION if the volume does + * not support hard links. */ DEBUG("Creating hard link \"%ls => %ls\"", output_path, inode->i_extracted_file); - if (!CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) { - err = GetLastError(); + if (CreateHardLinkW(output_path, inode->i_extracted_file, NULL)) + return 0; + + err = GetLastError(); + if (err != ERROR_INVALID_FUNCTION) { ERROR("Can't create hard link \"%ls => %ls\"", output_path, inode->i_extracted_file); win32_error(err); return WIMLIB_ERR_LINK; + } else { + WARNING("Can't create hard link \"%ls => %ls\":\n" + " Volume does not support hard links!\n" + " Falling back to extracting a copy of the file."); } - } else { - /* Create the file, directory, or reparse point, and extract the - * data streams. */ - ret = win32_extract_streams(inode, output_path, - &args->progress.extract.completed_bytes); + } + /* Create the file, directory, or reparse point, and extract the + * data streams. */ + ret = win32_extract_streams(inode, output_path, + &args->progress.extract.completed_bytes, + args->vol_flags); + if (ret) + return ret; + + if (inode->i_security_id >= 0 && + !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS) + && (args->vol_flags & FILE_PERSISTENT_ACLS)) + { + ret = win32_set_security_data(inode, output_path, args); if (ret) return ret; - - if (inode->i_security_id >= 0 && - !(args->extract_flags & WIMLIB_EXTRACT_FLAG_NO_ACLS)) - { - ret = win32_set_security_data(inode, output_path, args); - if (ret) - return ret; - } - if (inode->i_nlink > 1) { - /* Save extracted path for a later call to - * CreateHardLinkW() if this inode has multiple links. - * */ - inode->i_extracted_file = WSTRDUP(output_path); - if (!inode->i_extracted_file) - ret = WIMLIB_ERR_NOMEM; - } + } + if (inode->i_nlink > 1) { + /* Save extracted path for a later call to + * CreateHardLinkW() if this inode has multiple links. + * */ + inode->i_extracted_file = WSTRDUP(output_path); + if (!inode->i_extracted_file) + ret = WIMLIB_ERR_NOMEM; } return 0; }