+HANDLE
+win32_open_file_data_only(const wchar_t *path)
+{
+ return win32_open_existing_file(path, FILE_READ_DATA);
+}
+
+int
+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 = 0;
+ void *out_buf;
+ DWORD err;
+ u64 bytes_remaining;
+
+ HANDLE hFile = win32_open_file_data_only(lte->file_on_disk);
+ if (hFile == INVALID_HANDLE_VALUE) {
+ err = GetLastError();
+ ERROR("Failed to open \"%ls\"", lte->file_on_disk);
+ win32_error(err);
+ return WIMLIB_ERR_OPEN;
+ }
+
+ if (cb)
+ out_buf = alloca(WIM_CHUNK_SIZE);
+ else
+ out_buf = ctx_or_buf;
+
+ bytes_remaining = size;
+ while (bytes_remaining) {
+ DWORD bytesToRead, bytesRead;
+
+ bytesToRead = min(WIM_CHUNK_SIZE, bytes_remaining);
+ 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;
+ break;
+ }
+ bytes_remaining -= bytesRead;
+ if (cb) {
+ ret = (*cb)(out_buf, bytesRead, ctx_or_buf);
+ if (ret)
+ break;
+ } else {
+ out_buf += bytesRead;
+ }
+ }
+ CloseHandle(hFile);
+ 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)
+{
+ return ((u64)ft->dwHighDateTime << 32) | (u64)ft->dwLowDateTime;
+}
+
+static int
+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);
+ dentry->short_name = MALLOC(n);
+ if (!dentry->short_name)
+ return WIMLIB_ERR_NOMEM;
+ memcpy(dentry->short_name, dat.cAlternateFileName, n);
+ dentry->short_name_nbytes = short_name_nbytes;
+ }
+ /* If we can't read the short filename for some reason, we just ignore
+ * the error and assume the file has no short name. I don't think this
+ * should be an issue, since the short names are essentially obsolete
+ * anyway. */
+ return 0;
+}
+
+static int
+win32_get_security_descriptor(struct wim_dentry *dentry,
+ struct sd_set *sd_set,
+ const wchar_t *path,
+ struct win32_capture_state *state,
+ int add_image_flags)
+{
+ SECURITY_INFORMATION requestedInformation;
+ DWORD lenNeeded = 0;
+ BOOL status;
+ DWORD err;
+ unsigned long n;
+
+ requestedInformation = DACL_SECURITY_INFORMATION |
+ SACL_SECURITY_INFORMATION |
+ OWNER_SECURITY_INFORMATION |
+ GROUP_SECURITY_INFORMATION;
+again:
+ /* Request length of security descriptor */
+ status = GetFileSecurityW(path, requestedInformation,
+ NULL, 0, &lenNeeded);
+ err = GetLastError();
+ if (!status && err == ERROR_INSUFFICIENT_BUFFER) {
+ DWORD len = lenNeeded;
+ char buf[len];
+ if (GetFileSecurityW(path, requestedInformation,
+ (PSECURITY_DESCRIPTOR)buf, len, &lenNeeded))
+ {
+ int security_id = sd_set_add_sd(sd_set, buf, len);
+ if (security_id < 0)
+ return WIMLIB_ERR_NOMEM;
+ else {
+ dentry->d_inode->i_security_id = security_id;
+ return 0;
+ }
+ } else {
+ err = GetLastError();
+ }
+ }
+
+ if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_STRICT_ACLS)
+ goto fail;
+
+ switch (err) {
+ case ERROR_PRIVILEGE_NOT_HELD:
+ if (requestedInformation & SACL_SECURITY_INFORMATION) {
+ n = state->num_get_sacl_priv_notheld++;
+ requestedInformation &= ~SACL_SECURITY_INFORMATION;
+ if (n < MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
+ WARNING(
+"We don't have enough privileges to read the full security\n"
+" descriptor of \"%ls\"!\n"
+" Re-trying with SACL omitted.\n", path);
+ } else if (n == MAX_GET_SACL_PRIV_NOTHELD_WARNINGS) {
+ WARNING(
+"Suppressing further privileges not held error messages when reading\n"
+" security descriptors.");
+ }
+ goto again;
+ }
+ /* Fall through */
+ case ERROR_ACCESS_DENIED:
+ n = state->num_get_sd_access_denied++;
+ if (n < MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
+ WARNING("Failed to read security descriptor of \"%ls\": "
+ "Access denied!\n%ls", path, capture_access_denied_msg);
+ } else if (n == MAX_GET_SD_ACCESS_DENIED_WARNINGS) {
+ WARNING("Suppressing further access denied errors messages i"
+ "when reading security descriptors");
+ }
+ return 0;
+ default:
+fail:
+ ERROR("Failed to read security descriptor of \"%ls\"", path);
+ win32_error(err);
+ return WIMLIB_ERR_READ;
+ }
+}
+
+static int
+win32_build_dentry_tree_recursive(struct wim_dentry **root_ret,
+ wchar_t *path,
+ size_t path_num_chars,
+ struct wim_lookup_table *lookup_table,
+ struct wim_inode_table *inode_table,
+ struct sd_set *sd_set,
+ const struct wimlib_capture_config *config,
+ int add_image_flags,
+ wimlib_progress_func_t progress_func,
+ 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. */
+static int
+win32_recurse_directory(struct wim_dentry *root,
+ wchar_t *dir_path,
+ size_t dir_path_num_chars,
+ struct wim_lookup_table *lookup_table,
+ struct wim_inode_table *inode_table,
+ struct sd_set *sd_set,
+ const struct wimlib_capture_config *config,
+ int add_image_flags,
+ wimlib_progress_func_t progress_func,
+ struct win32_capture_state *state,
+ unsigned vol_flags)