endif
if WINDOWS_NATIVE_BUILD
-libwim_la_SOURCES += src/win32_common.c \
+libwim_la_SOURCES += src/wimboot.c \
+ src/win32_common.c \
src/win32_apply.c \
src/win32_capture.c \
src/win32_replacements.c \
- src/wimboot.c \
- include/wimlib/win32_common.h \
- include/wimlib/win32.h \
+ src/win32_vss.c \
include/wimlib/wimboot.h \
+ include/wimlib/win32.h \
+ include/wimlib/win32_common.h \
+ include/wimlib/win32_vss.h \
include/wimlib/wof.h
else
libwim_la_SOURCES += src/unix_apply.c \
descriptor of the target directory of an extraction even when the
'--no-acls' option was specified.
+ Added experimental support for Windows VSS (Volume Shadow Copy Service).
+ The new '--snapshot' argument to 'wimcapture' makes wimlib automatically
+ create and use a temporary VSS snapshot when capturing a WIM image.
+
Added a new '--image-property' option to 'wimcapture', 'wimappend', and
'wiminfo'. This option lets you assign values to elements in a WIM
file's XML document by name.
.TP
\fB--unsafe-compact\fR
See the documentation for this option in \fBwimlib-imagex-optimize\fR (1).
+.TP
+\fB--snapshot\fR
+EXPERIMENTAL: create a temporary filesystem snapshot of the source directory and
+capture the files from it. Currently, this option is only supported on Windows,
+where it uses the Volume Shadow Copy Service (VSS). Using this option, you can
+create a consistent backup of the system volume of a running Windows system
+without running into problems with locked files. For the VSS snapshot to be
+successfully created, \fBwimlib-imagex\fR must be run as an Administrator, and
+it cannot be run in WoW64 mode (i.e. if Windows is 64-bit, then
+\fBwimlib-imagex\fR must be 64-bit as well).
.SH NOTES
\fBwimlib-imagex append\fR does not support appending an image to a split WIM.
.PP
*/
#define WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION 0x00004000
+/**
+ * EXPERIMENTAL, since wimlib v1.8.4: create a temporary filesystem snapshot of
+ * the source directory and add the files from it. Currently, this option is
+ * only supported on Windows, where it uses the Volume Shadow Copy Service
+ * (VSS). Using this option, you can create a consistent backup of the system
+ * volume of a running Windows system without running into problems with locked
+ * files. For the VSS snapshot to be successfully created, your application
+ * must be run as an Administrator, and it cannot be run in WoW64 mode (i.e. if
+ * Windows is 64-bit, then your application must be 64-bit as well).
+ */
+#define WIMLIB_ADD_FLAG_SNAPSHOT 0x00008000
+
/* Note: the WIMLIB_ADD_IMAGE_FLAG names are retained for source compatibility.
* Use the WIMLIB_ADD_FLAG names in new code. */
#define WIMLIB_ADD_IMAGE_FLAG_NTFS WIMLIB_ADD_FLAG_NTFS
WIMLIB_ERR_IMAGE_HAS_MULTIPLE_REFERENCES = 86,
WIMLIB_ERR_DUPLICATE_EXPORTED_IMAGE = 87,
WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED = 88,
+ WIMLIB_ERR_SNAPSHOT_FAILURE = 89,
};
#endif
#ifdef __WIN32__
- /* Windows only: the blob's data is available as the contents of the
- * data stream named by @file_on_disk. @file_on_disk is an NT namespace
- * path that may be longer than the Win32-level MAX_PATH. Furthermore,
- * the stream may require "backup semantics" to access. */
- BLOB_IN_WINNT_FILE_ON_DISK,
-
- /* Windows only: the blob's data is available as the raw encrypted data
- * of the external file named by @file_on_disk. @file_on_disk is a
- * Win32 namespace path. */
- BLOB_WIN32_ENCRYPTED,
+ /* Windows only: the blob's data is available in the file (or named data
+ * stream) specified by @windows_file. The data might be only properly
+ * accessible through the Windows API. */
+ BLOB_IN_WINDOWS_FILE,
#endif
};
union {
/* BLOB_IN_FILE_ON_DISK
- * BLOB_IN_WINNT_FILE_ON_DISK
- * BLOB_WIN32_ENCRYPTED */
+ * BLOB_IN_WINDOWS_FILE */
struct {
- tchar *file_on_disk;
+ union {
+ tchar *file_on_disk;
+ struct windows_file *windows_file;
+ };
struct wim_inode *file_inode;
- #ifdef __WIN32__
- u64 sort_key;
- #endif
};
/* BLOB_IN_ATTACHED_BUFFER */
{
return blob->blob_location == BLOB_IN_FILE_ON_DISK
#ifdef __WIN32__
- || blob->blob_location == BLOB_IN_WINNT_FILE_ON_DISK
- || blob->blob_location == BLOB_WIN32_ENCRYPTED
+ || blob->blob_location == BLOB_IN_WINDOWS_FILE
#endif
;
}
+#ifdef __WIN32__
+extern const wchar_t *
+get_windows_file_path(const struct windows_file *file);
+#endif
+
+static inline const tchar *
+blob_file_path(const struct blob_descriptor *blob)
+{
+#ifdef __WIN32__
+ if (blob->blob_location == BLOB_IN_WINDOWS_FILE)
+ return get_windows_file_path(blob->windows_file);
+#endif
+ return blob->file_on_disk;
+}
+
extern struct blob_descriptor *
new_blob_from_data_buffer(const void *buffer, size_t size,
struct blob_table *blob_table);
+/*
+ * win32.h - Windows-specific declarations needed by non-Windows-specific files.
+ */
+
#ifndef _WIMLIB_WIN32_H
#define _WIMLIB_WIN32_H
struct blob_descriptor;
struct read_blob_callbacks;
+struct windows_file;
+
+extern struct windows_file *
+clone_windows_file(const struct windows_file *file);
+
+extern void
+free_windows_file(struct windows_file *file);
extern int
-read_winnt_stream_prefix(const struct blob_descriptor *blob, u64 size,
- const struct read_blob_callbacks *cbs);
+cmp_windows_files(const struct windows_file *file1,
+ const struct windows_file *file2);
extern int
-read_win32_encrypted_file_prefix(const struct blob_descriptor *blob,
- u64 size,
- const struct read_blob_callbacks *cbs);
+read_windows_file_prefix(const struct blob_descriptor *blob, u64 size,
+ const struct read_blob_callbacks *cbs);
extern int
win32_global_init(int init_flags);
+/*
+ * win32_common.h - common header for Windows-specific files. This always
+ * should be included first.
+ */
+
#ifndef _WIMLIB_WIN32_COMMON_H
#define _WIMLIB_WIN32_COMMON_H
--- /dev/null
+/*
+ * win32_vss.h - Declarations for managing VSS snapshots. This header should
+ * only be included by Windows-specific files.
+ */
+
+#ifndef _WIMLIB_WIN32_VSS_H
+#define _WIMLIB_WIN32_VSS_H
+
+#include "wimlib/win32_common.h"
+
+/* A reference counter for a VSS snapshot. This is embedded in another data
+ * structure only visible to win32_vss.c. */
+struct vss_snapshot {
+ size_t refcnt;
+};
+
+extern void
+vss_delete_snapshot(struct vss_snapshot *snapshot);
+
+/* Acquire a reference to the specified VSS snapshot. */
+static inline struct vss_snapshot *
+vss_get_snapshot(struct vss_snapshot *snapshot)
+{
+ if (snapshot)
+ snapshot->refcnt++;
+ return snapshot;
+}
+
+/* Release a reference to the specified VSS snapshot. When the last reference
+ * is released, the snapshot is deleted. */
+static inline void
+vss_put_snapshot(struct vss_snapshot *snapshot)
+{
+ if (snapshot && --snapshot->refcnt == 0)
+ vss_delete_snapshot(snapshot);
+}
+
+extern int
+vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
+ struct vss_snapshot **snapshot_ret);
+
+extern void
+vss_global_cleanup(void);
+
+#endif /* _WIMLIB_WIN32_VSS_H */
IMAGEX_REF_OPTION,
IMAGEX_RESUME_OPTION,
IMAGEX_RPFIX_OPTION,
+ IMAGEX_SNAPSHOT_OPTION,
IMAGEX_SOFT_OPTION,
IMAGEX_SOLID_CHUNK_SIZE_OPTION,
IMAGEX_SOLID_COMPRESS_OPTION,
{T("delta-from"), required_argument, NULL, IMAGEX_DELTA_FROM_OPTION},
{T("wimboot"), no_argument, NULL, IMAGEX_WIMBOOT_OPTION},
{T("unsafe-compact"), no_argument, NULL, IMAGEX_UNSAFE_COMPACT_OPTION},
+ {T("snapshot"), no_argument, NULL, IMAGEX_SNAPSHOT_OPTION},
{NULL, 0, NULL, 0},
};
}
write_flags |= WIMLIB_WRITE_FLAG_UNSAFE_COMPACT;
break;
+ case IMAGEX_SNAPSHOT_OPTION:
+ add_flags |= WIMLIB_ADD_FLAG_SNAPSHOT;
+ break;
default:
goto out_usage;
}
#include "wimlib/resource.h"
#include "wimlib/unaligned.h"
#include "wimlib/util.h"
+#include "wimlib/win32.h"
#include "wimlib/write.h"
/* A hash table mapping SHA-1 message digests to blob descriptors */
break;
case BLOB_IN_FILE_ON_DISK:
-#ifdef __WIN32__
- case BLOB_IN_WINNT_FILE_ON_DISK:
- case BLOB_WIN32_ENCRYPTED:
-#endif
#ifdef WITH_FUSE
case BLOB_IN_STAGING_FILE:
STATIC_ASSERT((void*)&old->file_on_disk ==
if (new->file_on_disk == NULL)
goto out_free;
break;
+#ifdef __WIN32__
+ case BLOB_IN_WINDOWS_FILE:
+ new->windows_file = clone_windows_file(old->windows_file);
+ break;
+#endif
case BLOB_IN_ATTACHED_BUFFER:
new->attached_buffer = memdup(old->attached_buffer, old->size);
if (new->attached_buffer == NULL)
break;
}
case BLOB_IN_FILE_ON_DISK:
-#ifdef __WIN32__
- case BLOB_IN_WINNT_FILE_ON_DISK:
- case BLOB_WIN32_ENCRYPTED:
-#endif
#ifdef WITH_FUSE
case BLOB_IN_STAGING_FILE:
STATIC_ASSERT((void*)&blob->file_on_disk ==
(void*)&blob->attached_buffer);
FREE(blob->file_on_disk);
break;
+#ifdef __WIN32__
+ case BLOB_IN_WINDOWS_FILE:
+ free_windows_file(blob->windows_file);
+ break;
+#endif
#ifdef WITH_NTFS_3G
case BLOB_IN_NTFS_VOLUME:
- if (blob->ntfs_loc)
- free_ntfs_location(blob->ntfs_loc);
+ free_ntfs_location(blob->ntfs_loc);
break;
#endif
}
case BLOB_IN_FILE_ON_DISK:
#ifdef WITH_FUSE
case BLOB_IN_STAGING_FILE:
-#endif
-#ifdef __WIN32__
- case BLOB_IN_WINNT_FILE_ON_DISK:
- case BLOB_WIN32_ENCRYPTED:
- /* Windows: compare by starting LCN (logical cluster number) */
- v = cmp_u64(blob1->sort_key, blob2->sort_key);
- if (v)
- return v;
#endif
/* Compare files by path: just a heuristic that will place files
* in the same directory next to each other. */
return tstrcmp(blob1->file_on_disk, blob2->file_on_disk);
+#ifdef __WIN32__
+ case BLOB_IN_WINDOWS_FILE:
+ return cmp_windows_files(blob1->windows_file, blob2->windows_file);
+#endif
#ifdef WITH_NTFS_3G
case BLOB_IN_NTFS_VOLUME:
return cmp_ntfs_locations(blob1->ntfs_loc, blob2->ntfs_loc);
= T("The destination WIM already contains one of the source images"),
[WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED]
= T("A file being added to a WIM image was concurrently modified"),
+ [WIMLIB_ERR_SNAPSHOT_FAILURE]
+ = T("Unable to create a filesystem snapshot"),
};
WIMLIBAPI const tchar *
[BLOB_IN_NTFS_VOLUME] = read_ntfs_attribute_prefix,
#endif
#ifdef __WIN32__
- [BLOB_IN_WINNT_FILE_ON_DISK] = read_winnt_stream_prefix,
- [BLOB_WIN32_ENCRYPTED] = read_win32_encrypted_file_prefix,
+ [BLOB_IN_WINDOWS_FILE] = read_windows_file_prefix,
#endif
};
wimlib_assert(blob->blob_location < ARRAY_LEN(handlers)
" Path: \"%"TS"\"\n"
" Expected SHA-1: %"TS"\n"
" Actual SHA-1: %"TS"\n",
- blob->file_on_disk, expected_hashstr, actual_hashstr);
+ blob_file_path(blob), expected_hashstr, actual_hashstr);
return WIMLIB_ERR_CONCURRENT_MODIFICATION_DETECTED;
} else if (blob->blob_location == BLOB_IN_WIM) {
const struct wim_resource_descriptor *rdesc = blob->rdesc;
break;
case BLOB_IN_FILE_ON_DISK:
#ifdef __WIN32__
- case BLOB_IN_WINNT_FILE_ON_DISK:
+ case BLOB_IN_WINDOWS_FILE:
#endif
blob_set_solid_sort_name_from_inode(blob, blob->file_inode);
break;
WIMLIB_ADD_FLAG_WINCONFIG |
WIMLIB_ADD_FLAG_WIMBOOT |
WIMLIB_ADD_FLAG_NO_REPLACE |
- WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION))
+ WIMLIB_ADD_FLAG_TEST_FILE_EXCLUSION |
+ WIMLIB_ADD_FLAG_SNAPSHOT))
return WIMLIB_ERR_INVALID_PARAM;
bool is_entire_image = WIMLIB_IS_WIM_ROOT_PATH(cmd->add.wim_target_path);
#endif
#ifdef __WIN32__
- /* Check for flags not supported on Windows */
+ /* Check for flags not supported on Windows. */
if (add_flags & WIMLIB_ADD_FLAG_UNIX_DATA) {
ERROR("Capturing UNIX-specific data is not supported on Windows");
return WIMLIB_ERR_UNSUPPORTED;
ERROR("Dereferencing symbolic links is not supported on Windows");
return WIMLIB_ERR_UNSUPPORTED;
}
+#else
+ /* Check for flags only supported on Windows. */
+
+ /* Currently, SNAPSHOT means Windows VSS. In the future, it perhaps
+ * could be implemented for other types of snapshots, such as btrfs. */
+ if (add_flags & WIMLIB_ADD_FLAG_SNAPSHOT) {
+ ERROR("Snapshot mode is only supported on Windows (VSS)");
+ return WIMLIB_ERR_UNSUPPORTED;
+ }
#endif
/* VERBOSE implies EXCLUDE_VERBOSE */
#include "wimlib/error.h"
#include "wimlib/paths.h"
#include "wimlib/reparse.h"
+#include "wimlib/win32_vss.h"
#include "wimlib/wof.h"
struct winnt_scan_ctx {
/* True if WOF is definitely not attached to the volume being scanned;
* false if it may be */
bool wof_not_attached;
+
+ /* A reference to the VSS snapshot being used, or NULL if none */
+ struct vss_snapshot *snapshot;
};
static inline const wchar_t *
return full_path + 4;
}
+/* Description of where data is located on a Windows filesystem */
+struct windows_file {
+
+ /* Is the data the raw encrypted data of an EFS-encrypted file? */
+ u64 is_encrypted : 1;
+
+ /* The file's LCN (logical cluster number) for sorting, or 0 if unknown.
+ */
+ u64 sort_key : 63;
+
+ /* A reference to the VSS snapshot containing the file, or NULL if none.
+ */
+ struct vss_snapshot *snapshot;
+
+ /* The path to the file. If 'is_encrypted=0' this is an NT namespace
+ * path; if 'is_encrypted=1' this is a Win32 namespace path. */
+ wchar_t path[];
+};
+
+/* Allocate a 'struct windows_file' to describe the location of a data stream.
+ */
+static struct windows_file *
+alloc_windows_file(bool is_encrypted, struct vss_snapshot *snapshot,
+ const wchar_t *path, size_t path_nchars,
+ const wchar_t *stream_name, size_t stream_name_nchars)
+{
+ struct windows_file *file;
+ wchar_t *p;
+
+ file = MALLOC(sizeof(struct windows_file) +
+ (path_nchars + (stream_name_nchars ? 1 : 0) +
+ stream_name_nchars + 1) * sizeof(wchar_t));
+ if (!file)
+ return NULL;
+
+ file->is_encrypted = is_encrypted;
+ file->sort_key = 0;
+ file->snapshot = vss_get_snapshot(snapshot);
+ p = wmempcpy(file->path, path, path_nchars);
+ if (stream_name_nchars) {
+ /* Named data stream */
+ *p++ = L':';
+ p = wmempcpy(p, stream_name, stream_name_nchars);
+ }
+ *p = L'\0';
+ return file;
+}
+
+/* Add a stream, located on a Windows filesystem, to the specified WIM inode.
+ */
+static int
+add_stream(struct wim_inode *inode, bool is_encrypted,
+ struct vss_snapshot *snapshot, u64 size,
+ const wchar_t *path, size_t path_nchars,
+ int stream_type, const utf16lechar *stream_name, size_t stream_name_nchars,
+ struct list_head *unhashed_blobs)
+{
+ struct blob_descriptor *blob = NULL;
+ struct wim_inode_stream *strm;
+
+ /* If the stream is nonempty, create a blob descriptor for it. */
+ if (size) {
+ blob = new_blob_descriptor();
+ if (!blob)
+ goto err_nomem;
+
+ blob->windows_file = alloc_windows_file(is_encrypted, snapshot,
+ path, path_nchars,
+ stream_name,
+ stream_name_nchars);
+ if (!blob->windows_file)
+ goto err_nomem;
+
+ blob->blob_location = BLOB_IN_WINDOWS_FILE;
+ blob->file_inode = inode;
+ blob->size = size;
+ }
+
+ strm = inode_add_stream(inode, stream_type, stream_name, blob);
+ if (!strm)
+ goto err_nomem;
+
+ prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
+ return 0;
+
+err_nomem:
+ free_blob_descriptor(blob);
+ return WIMLIB_ERR_NOMEM;
+}
+
+struct windows_file *
+clone_windows_file(const struct windows_file *file)
+{
+ struct windows_file *new;
+
+ new = memdup(file, sizeof(struct windows_file) +
+ (wcslen(file->path) + 1) * sizeof(wchar_t));
+ if (new)
+ vss_get_snapshot(new->snapshot);
+ return new;
+}
+
+void
+free_windows_file(struct windows_file *file)
+{
+ vss_put_snapshot(file->snapshot);
+ FREE(file);
+}
+
+int
+cmp_windows_files(const struct windows_file *file1,
+ const struct windows_file *file2)
+{
+ /* Compare by starting LCN (logical cluster number) */
+ int v = cmp_u64(file1->sort_key, file2->sort_key);
+ if (v)
+ return v;
+
+ /* Compare files by path: just a heuristic that will place files
+ * in the same directory next to each other. */
+ return wcscmp(file1->path, file2->path);
+}
+
+const wchar_t *
+get_windows_file_path(const struct windows_file *file)
+{
+ return file->path;
+}
+
/*
* If cur_dir is not NULL, open an existing file relative to the already-open
* directory cur_dir.
return status;
}
-/* Read the first @size bytes from the file, or named data stream of a file,
- * described by @blob. */
-int
-read_winnt_stream_prefix(const struct blob_descriptor *blob, u64 size,
+static int
+read_winnt_stream_prefix(const wchar_t *path, u64 size,
const struct read_blob_callbacks *cbs)
{
- const wchar_t *path;
HANDLE h;
NTSTATUS status;
u8 buf[BUFFER_SIZE];
u64 bytes_remaining;
int ret;
- /* This is an NT namespace path. */
- path = blob->file_on_disk;
-
status = winnt_openat(NULL, path, wcslen(path),
FILE_READ_DATA | SYNCHRONIZE, &h);
if (!NT_SUCCESS(status)) {
return ERROR_SUCCESS;
}
-int
-read_win32_encrypted_file_prefix(const struct blob_descriptor *blob,
- u64 size,
+static int
+read_win32_encrypted_file_prefix(const wchar_t *path, bool is_dir, u64 size,
const struct read_blob_callbacks *cbs)
{
struct win32_encrypted_read_ctx export_ctx;
int ret;
DWORD flags = 0;
- if (blob->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
+ if (is_dir)
flags |= CREATE_FOR_DIR;
export_ctx.cbs = cbs;
export_ctx.wimlib_err_code = 0;
export_ctx.bytes_remaining = size;
- err = OpenEncryptedFileRaw(blob->file_on_disk, flags, &file_ctx);
+ err = OpenEncryptedFileRaw(path, flags, &file_ctx);
if (err != ERROR_SUCCESS) {
win32_error(err,
L"Failed to open encrypted file \"%ls\" for raw read",
- printable_path(blob->file_on_disk));
+ printable_path(path));
return WIMLIB_ERR_OPEN;
}
err = ReadEncryptedFileRaw(win32_encrypted_export_cb,
if (ret == 0) {
win32_error(err,
L"Failed to read encrypted file \"%ls\"",
- printable_path(blob->file_on_disk));
+ printable_path(path));
ret = WIMLIB_ERR_READ;
}
} else if (export_ctx.bytes_remaining != 0) {
ERROR("Only could read %"PRIu64" of %"PRIu64" bytes from "
"encrypted file \"%ls\"",
size - export_ctx.bytes_remaining, size,
- printable_path(blob->file_on_disk));
+ printable_path(path));
ret = WIMLIB_ERR_READ;
} else {
ret = 0;
return ret;
}
+/* Read the first @size bytes from the file, or named data stream of a file,
+ * described by @blob. */
+int
+read_windows_file_prefix(const struct blob_descriptor *blob, u64 size,
+ const struct read_blob_callbacks *cbs)
+{
+ const struct windows_file *file = blob->windows_file;
+
+ if (unlikely(file->is_encrypted)) {
+ bool is_dir = (blob->file_inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY);
+ return read_win32_encrypted_file_prefix(file->path, is_dir, size, cbs);
+ }
+
+ return read_winnt_stream_prefix(file->path, size, cbs);
+}
+
/*
* Load the short name of a file into a WIM dentry.
*/
}
static int
-winnt_scan_efsrpc_raw_data(struct wim_inode *inode, const wchar_t *nt_path,
- struct list_head *unhashed_blobs)
+winnt_scan_efsrpc_raw_data(struct wim_inode *inode,
+ wchar_t *path, size_t path_nchars,
+ struct list_head *unhashed_blobs,
+ struct vss_snapshot *snapshot)
{
- struct blob_descriptor *blob;
- struct wim_inode_stream *strm;
+ const bool is_dir = (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY);
+ u64 size;
int ret;
- blob = new_blob_descriptor();
- if (!blob)
- goto err_nomem;
-
- blob->file_on_disk = WCSDUP(nt_path);
- if (!blob->file_on_disk)
- goto err_nomem;
- blob->blob_location = BLOB_WIN32_ENCRYPTED;
-
/* OpenEncryptedFileRaw() expects a Win32 name. */
- wimlib_assert(!wmemcmp(blob->file_on_disk, L"\\??\\", 4));
- blob->file_on_disk[1] = L'\\';
+ wimlib_assert(!wmemcmp(path, L"\\??\\", 4));
+ path[1] = L'\\';
- blob->file_inode = inode;
-
- ret = win32_get_encrypted_file_size(blob->file_on_disk,
- (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY),
- &blob->size);
+ ret = win32_get_encrypted_file_size(path, is_dir, &size);
if (ret)
- goto err;
+ goto out;
/* Empty EFSRPC data does not make sense */
- wimlib_assert(blob->size != 0);
-
- strm = inode_add_stream(inode, STREAM_TYPE_EFSRPC_RAW_DATA,
- NO_STREAM_NAME, blob);
- if (!strm)
- goto err_nomem;
-
- prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
- return 0;
+ wimlib_assert(size != 0);
-err_nomem:
- ret = WIMLIB_ERR_NOMEM;
-err:
- free_blob_descriptor(blob);
+ ret = add_stream(inode, true, snapshot, size,
+ path, path_nchars,
+ STREAM_TYPE_EFSRPC_RAW_DATA, NO_STREAM_NAME, 0,
+ unhashed_blobs);
+out:
+ path[1] = L'?';
return ret;
}
return true;
}
-/* Build the path to the data stream. For unnamed streams, this is simply the
- * path to the file. For named streams, this is the path to the file, followed
- * by a colon, followed by the stream name. */
-static wchar_t *
-build_data_stream_path(const wchar_t *path, size_t path_nchars,
- const wchar_t *stream_name, size_t stream_name_nchars)
-{
- size_t stream_path_nchars;
- wchar_t *stream_path;
- wchar_t *p;
-
- stream_path_nchars = path_nchars;
- if (stream_name_nchars)
- stream_path_nchars += 1 + stream_name_nchars;
-
- stream_path = MALLOC((stream_path_nchars + 1) * sizeof(wchar_t));
- if (stream_path) {
- p = wmempcpy(stream_path, path, path_nchars);
- if (stream_name_nchars) {
- *p++ = L':';
- p = wmempcpy(p, stream_name, stream_name_nchars);
- }
- *p++ = L'\0';
- }
- return stream_path;
-}
-
static int
winnt_scan_data_stream(const wchar_t *path, size_t path_nchars,
wchar_t *raw_stream_name, size_t raw_stream_name_nchars,
u64 stream_size,
- struct wim_inode *inode, struct list_head *unhashed_blobs)
+ struct wim_inode *inode, struct list_head *unhashed_blobs,
+ struct vss_snapshot *snapshot)
{
wchar_t *stream_name;
size_t stream_name_nchars;
- struct blob_descriptor *blob;
- struct wim_inode_stream *strm;
/* Given the raw stream name (which is something like
* :streamname:$DATA), extract just the stream name part (streamname).
stream_name[stream_name_nchars] = L'\0';
- /* If the stream is non-empty, set up a blob descriptor for it. */
- if (stream_size != 0) {
- blob = new_blob_descriptor();
- if (!blob)
- goto err_nomem;
- blob->file_on_disk = build_data_stream_path(path,
- path_nchars,
- stream_name,
- stream_name_nchars);
- if (!blob->file_on_disk)
- goto err_nomem;
- blob->blob_location = BLOB_IN_WINNT_FILE_ON_DISK;
- blob->size = stream_size;
- blob->file_inode = inode;
- } else {
- blob = NULL;
- }
-
- strm = inode_add_stream(inode, STREAM_TYPE_DATA, stream_name, blob);
- if (!strm)
- goto err_nomem;
-
- prepare_unhashed_blob(blob, inode, strm->stream_id, unhashed_blobs);
- return 0;
-
-err_nomem:
- free_blob_descriptor(blob);
- return WIMLIB_ERR_NOMEM;
+ return add_stream(inode, false, snapshot, stream_size,
+ path, path_nchars,
+ STREAM_TYPE_DATA, stream_name, stream_name_nchars,
+ unhashed_blobs);
}
/*
static noinline_for_stack int
winnt_scan_data_streams(HANDLE h, const wchar_t *path, size_t path_nchars,
struct wim_inode *inode, struct list_head *unhashed_blobs,
- u64 file_size, u32 vol_flags)
+ u64 file_size, u32 vol_flags,
+ struct vss_snapshot *snapshot)
{
int ret;
u8 _buf[4096] _aligned_attribute(8);
info->StreamName,
info->StreamNameLength / 2,
info->StreamSize.QuadPart,
- inode, unhashed_blobs);
+ inode, unhashed_blobs,
+ snapshot);
if (ret)
goto out_free_buf;
{
wchar_t stream_name[] = L"::$DATA";
ret = winnt_scan_data_stream(path, path_nchars, stream_name, 7,
- file_size, inode, unhashed_blobs);
+ file_size, inode, unhashed_blobs,
+ snapshot);
}
out_free_buf:
/* Free buffer if allocated on heap. */
for (unsigned i = 0; i < inode->i_num_streams; i++) {
struct wim_inode_stream *strm = &inode->i_streams[i];
struct blob_descriptor *blob = stream_blob_resolved(strm);
- if (blob && (blob->blob_location == BLOB_IN_WINNT_FILE_ON_DISK ||
- blob->blob_location == BLOB_WIN32_ENCRYPTED))
- blob->sort_key = sort_key;
+ if (blob && blob->blob_location == BLOB_IN_WINDOWS_FILE)
+ blob->windows_file->sort_key = sort_key;
}
}
* needed. */
(*func_NtClose)(h);
h = NULL;
- ret = winnt_scan_efsrpc_raw_data(inode, full_path,
- params->unhashed_blobs);
+ ret = winnt_scan_efsrpc_raw_data(inode,
+ full_path,
+ full_path_nchars,
+ params->unhashed_blobs,
+ ctx->snapshot);
if (ret)
goto out;
} else {
inode,
params->unhashed_blobs,
file_info.end_of_file,
- ctx->vol_flags);
+ ctx->vol_flags,
+ ctx->snapshot);
if (ret)
goto out;
}
const wchar_t *root_disk_path,
struct capture_params *params)
{
- wchar_t *path;
- int ret;
+ wchar_t *path = NULL;
+ struct winnt_scan_ctx ctx = {};
UNICODE_STRING ntpath;
- struct winnt_scan_ctx ctx;
size_t ntpath_nchars;
+ int ret;
/* WARNING: There is no check for overflow later when this buffer is
* being used! But it's as long as the maximum path length understood
if (!path)
return WIMLIB_ERR_NOMEM;
- ret = win32_path_to_nt_path(root_disk_path, &ntpath);
+ if (params->add_flags & WIMLIB_ADD_FLAG_SNAPSHOT)
+ ret = vss_create_snapshot(root_disk_path, &ntpath, &ctx.snapshot);
+ else
+ ret = win32_path_to_nt_path(root_disk_path, &ntpath);
+
if (ret)
- goto out_free_path;
+ goto out;
if (ntpath.Length < 4 * sizeof(wchar_t) ||
ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) ||
}
HeapFree(GetProcessHeap(), 0, ntpath.Buffer);
if (ret)
- goto out_free_path;
-
- memset(&ctx, 0, sizeof(ctx));
+ goto out;
ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
path, ntpath_nchars,
L"", 0, params, &ctx);
-out_free_path:
+out:
+ vss_put_snapshot(ctx.snapshot);
FREE(path);
if (ret == 0)
winnt_do_scan_warnings(root_disk_path, &ctx);
#include "wimlib/error.h"
#include "wimlib/util.h"
+#include "wimlib/win32_vss.h"
static bool
win32_modify_privilege(const wchar_t *privilege, bool enable)
void
win32_global_cleanup(void)
{
+ vss_global_cleanup();
+
if (acquired_privileges)
win32_release_capture_and_apply_privileges();
--- /dev/null
+/*
+ * win32_vss.c - Experimental Windows-specific code for creating VSS (Volume
+ * Shadow Copy Service) snapshots.
+ */
+
+/*
+ * Copyright (C) 2015 Eric Biggers
+ *
+ * This file is free software; you can redistribute it and/or modify it under
+ * the terms of the GNU Lesser General Public License as published by the Free
+ * Software Foundation; either version 3 of the License, or (at your option) any
+ * later version.
+ *
+ * This file is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+ * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+ * details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this file; if not, see http://www.gnu.org/licenses/.
+ */
+
+#ifdef __WIN32__
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include "wimlib/win32_common.h"
+
+#include <cguid.h>
+#include <pthread.h>
+
+#include "wimlib/error.h"
+#include "wimlib/util.h"
+#include "wimlib/win32_vss.h"
+
+/*----------------------------------------------------------------------------*
+ * VSS API declarations *
+ *----------------------------------------------------------------------------*/
+
+typedef GUID VSS_ID;
+typedef LONGLONG VSS_TIMESTAMP;
+typedef WCHAR *VSS_PWSZ;
+
+typedef enum {
+ VSS_BT_UNDEFINED = 0,
+ VSS_BT_FULL = 1,
+ VSS_BT_INCREMENTAL = 2,
+ VSS_BT_DIFFERENTIAL = 3,
+ VSS_BT_LOG = 4,
+ VSS_BT_COPY = 5,
+ VSS_BT_OTHER = 6,
+} VSS_BACKUP_TYPE;
+
+typedef enum {
+ VSS_SS_UNKNOWN = 0x00,
+ VSS_SS_PREPARING = 0x01,
+ VSS_SS_PROCESSING_PREPARE = 0x02,
+ VSS_SS_PREPARED = 0x03,
+ VSS_SS_PROCESSING_PRECOMMIT = 0x04,
+ VSS_SS_PRECOMMITTED = 0x05,
+ VSS_SS_PROCESSING_COMMIT = 0x06,
+ VSS_SS_COMMITTED = 0x07,
+ VSS_SS_PROCESSING_POSTCOMMIT = 0x08,
+ VSS_SS_PROCESSING_PREFINALCOMMIT = 0x09,
+ VSS_SS_PREFINALCOMMITTED = 0x0a,
+ VSS_SS_PROCESSING_POSTFINALCOMMIT = 0x0b,
+ VSS_SS_CREATED = 0x0c,
+ VSS_SS_ABORTED = 0x0d,
+ VSS_SS_DELETED = 0x0e,
+ VSS_SS_POSTCOMMITTED = 0x0f,
+ VSS_SS_COUNT = 0x10,
+} VSS_SNAPSHOT_STATE;
+
+typedef enum {
+ VSS_VOLSNAP_ATTR_PERSISTENT = 0x00000001,
+ VSS_VOLSNAP_ATTR_NO_AUTORECOVERY = 0x00000002,
+ VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE = 0x00000004,
+ VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE = 0x00000008,
+ VSS_VOLSNAP_ATTR_NO_WRITERS = 0x00000010,
+ VSS_VOLSNAP_ATTR_TRANSPORTABLE = 0x00000020,
+ VSS_VOLSNAP_ATTR_NOT_SURFACED = 0x00000040,
+ VSS_VOLSNAP_ATTR_NOT_TRANSACTED = 0x00000080,
+ VSS_VOLSNAP_ATTR_HARDWARE_ASSISTED = 0x00010000,
+ VSS_VOLSNAP_ATTR_DIFFERENTIAL = 0x00020000,
+ VSS_VOLSNAP_ATTR_PLEX = 0x00040000,
+ VSS_VOLSNAP_ATTR_IMPORTED = 0x00080000,
+ VSS_VOLSNAP_ATTR_EXPOSED_LOCALLY = 0x00100000,
+ VSS_VOLSNAP_ATTR_EXPOSED_REMOTELY = 0x00200000,
+ VSS_VOLSNAP_ATTR_AUTORECOVER = 0x00400000,
+ VSS_VOLSNAP_ATTR_ROLLBACK_RECOVERY = 0x00800000,
+ VSS_VOLSNAP_ATTR_DELAYED_POSTSNAPSHOT = 0x01000000,
+ VSS_VOLSNAP_ATTR_TXF_RECOVERY = 0x02000000,
+} VSS_VOLUME_SNAPSHOT_ATTRIBUTES;
+
+typedef enum {
+ VSS_CTX_BACKUP = 0,
+ VSS_CTX_FILE_SHARE_BACKUP = VSS_VOLSNAP_ATTR_NO_WRITERS,
+ VSS_CTX_NAS_ROLLBACK = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ),
+ VSS_CTX_APP_ROLLBACK = ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ),
+ VSS_CTX_CLIENT_ACCESSIBLE = ( ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ) | VSS_VOLSNAP_ATTR_NO_WRITERS ),
+ VSS_CTX_CLIENT_ACCESSIBLE_WRITERS = ( ( VSS_VOLSNAP_ATTR_PERSISTENT | VSS_VOLSNAP_ATTR_CLIENT_ACCESSIBLE ) | VSS_VOLSNAP_ATTR_NO_AUTO_RELEASE ),
+ VSS_CTX_ALL = 0xffffffff,
+} VSS_SNAPSHOT_CONTEXT;
+
+typedef struct {
+ VSS_ID m_SnapshotId;
+ VSS_ID m_SnapshotSetId;
+ LONG m_lSnapshotsCount;
+ VSS_PWSZ m_pwszSnapshotDeviceObject;
+ VSS_PWSZ m_pwszOriginalVolumeName;
+ VSS_PWSZ m_pwszOriginatingMachine;
+ VSS_PWSZ m_pwszServiceMachine;
+ VSS_PWSZ m_pwszExposedName;
+ VSS_PWSZ m_pwszExposedPath;
+ VSS_ID m_ProviderId;
+ LONG m_lSnapshotAttributes;
+ VSS_TIMESTAMP m_tsCreationTimestamp;
+ VSS_SNAPSHOT_STATE m_eStatus;
+} VSS_SNAPSHOT_PROP;
+
+typedef struct IVssAsyncVTable IVssAsyncVTable;
+
+typedef struct {
+ IVssAsyncVTable *vtable;
+} IVssAsync;
+
+struct IVssAsyncVTable {
+ void *QueryInterface;
+ void *AddRef;
+ ULONG (WINAPI *Release)(IVssAsync *this);
+ void *Cancel;
+ HRESULT (WINAPI *Wait)(IVssAsync *this, DWORD dwMilliseconds);
+ void *QueryStatus;
+};
+
+typedef struct IVssBackupComponentsVTable IVssBackupComponentsVTable;
+
+typedef struct {
+ IVssBackupComponentsVTable *vtable;
+} IVssBackupComponents;
+
+struct IVssBackupComponentsVTable {
+ void *QueryInterface;
+ void *AddRef;
+ ULONG (WINAPI *Release)(IVssBackupComponents *this);
+ void *GetWriterComponentsCount;
+ void *GetWriterComponents;
+ HRESULT (WINAPI *InitializeForBackup)(IVssBackupComponents *this,
+ BSTR bstrXML);
+ HRESULT (WINAPI *SetBackupState)(IVssBackupComponents *this,
+ BOOLEAN bSelectComponents,
+ BOOLEAN bBackupBootableSystemState,
+ VSS_BACKUP_TYPE backupType,
+ BOOLEAN bPartialFileSupport);
+ void *InitializeForRestore;
+ void *SetRestoreState;
+ HRESULT (WINAPI *GatherWriterMetadata)(IVssBackupComponents *this,
+ IVssAsync **ppAsync);
+ void *GetWriterMetadataCount;
+ void *GetWriterMetadata;
+ void *FreeWriterMetadata;
+ void *AddComponent;
+ HRESULT (WINAPI *PrepareForBackup)(IVssBackupComponents *this,
+ IVssAsync **ppAsync);
+ void *AbortBackup;
+ void *GatherWriterStatus;
+ void *GetWriterStatusCount;
+ void *FreeWriterStatus;
+ void *GetWriterStatus;
+ void *SetBackupSucceeded;
+ void *SetBackupOptions;
+ void *SetSelectedForRestore;
+ void *SetRestoreOptions;
+ void *SetAdditionalRestores;
+ void *SetPreviousBackupStamp;
+ void *SaveAsXML;
+ void *BackupComplete;
+ void *AddAlternativeLocationMapping;
+ void *AddRestoreSubcomponent;
+ void *SetFileRestoreStatus;
+ void *AddNewTarget;
+ void *SetRangesFilePath;
+ void *PreRestore;
+ void *PostRestore;
+ HRESULT (WINAPI *SetContext)(IVssBackupComponents *this,
+ LONG lContext);
+ HRESULT (WINAPI *StartSnapshotSet)(IVssBackupComponents *this,
+ VSS_ID *pSnapshotSetId);
+ HRESULT (WINAPI *AddToSnapshotSet)(IVssBackupComponents *this,
+ VSS_PWSZ pwszVolumeName,
+ VSS_ID ProviderId,
+ VSS_ID *pidSnapshot);
+ HRESULT (WINAPI *DoSnapshotSet)(IVssBackupComponents *this,
+ IVssAsync **ppAsync);
+ void *DeleteSnapshots;
+ void *ImportSnapshots;
+ void *BreakSnapshotSet;
+ HRESULT (WINAPI *GetSnapshotProperties)(IVssBackupComponents *this,
+ VSS_ID SnapshotId,
+ VSS_SNAPSHOT_PROP *pprop);
+ void *Query;
+ void *IsVolumeSupported;
+ void *DisableWriterClasses;
+ void *EnableWriterClasses;
+ void *DisableWriterInstances;
+ void *ExposeSnapshot;
+ void *RevertToSnapshot;
+ void *QueryRevertStatus;
+};
+
+/*----------------------------------------------------------------------------*
+ * VSS API initialization *
+ *----------------------------------------------------------------------------*/
+
+static bool vss_initialized;
+static pthread_mutex_t vss_initialization_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* vssapi.dll */
+static HANDLE hVssapi;
+static HRESULT (WINAPI *func_CreateVssBackupComponentsInternal)(IVssBackupComponents **ppBackup);
+static void (WINAPI *func_VssFreeSnapshotPropertiesInternal)(VSS_SNAPSHOT_PROP *pProp);
+
+/* ole32.dll */
+static HANDLE hOle32;
+static void (WINAPI *func_CoInitialize)(LPVOID *pvReserved);
+static void (WINAPI *func_CoUninitialize)(void);
+
+static bool
+vss_global_init_impl(void)
+{
+ hVssapi = LoadLibrary(L"vssapi.dll");
+ if (!hVssapi) {
+ ERROR("vssapi.dll not found");
+ goto err;
+ }
+
+ func_CreateVssBackupComponentsInternal =
+ (void *)GetProcAddress(hVssapi, "CreateVssBackupComponentsInternal");
+ if (!func_CreateVssBackupComponentsInternal) {
+ ERROR("CreateVssBackupComponentsInternal() not found in vssapi.dll");
+ goto err_vssapi;
+ }
+
+ func_VssFreeSnapshotPropertiesInternal =
+ (void *)GetProcAddress(hVssapi, "VssFreeSnapshotPropertiesInternal");
+ if (!func_VssFreeSnapshotPropertiesInternal) {
+ ERROR("VssFreeSnapshotPropertiesInternal() not found in vssapi.dll");
+ goto err_vssapi;
+ }
+
+ hOle32 = LoadLibrary(L"ole32.dll");
+ if (!hOle32) {
+ ERROR("ole32.dll not found");
+ goto err_vssapi;
+ }
+
+ func_CoInitialize = (void *)GetProcAddress(hOle32, "CoInitialize");
+ if (!func_CoInitialize) {
+ ERROR("CoInitialize() not found in ole32.dll");
+ goto err_ole32;
+ }
+
+ func_CoUninitialize = (void *)GetProcAddress(hOle32, "CoUninitialize");
+ if (!func_CoUninitialize) {
+ ERROR("CoUninitialize() not found in ole32.dll");
+ goto err_ole32;
+ }
+
+ (*func_CoInitialize)(NULL);
+ return true;
+
+err_ole32:
+ FreeLibrary(hOle32);
+err_vssapi:
+ FreeLibrary(hVssapi);
+err:
+ return false;
+}
+
+static bool
+vss_global_init(void)
+{
+ if (vss_initialized)
+ return true;
+
+ pthread_mutex_lock(&vss_initialization_mutex);
+ if (!vss_initialized)
+ vss_initialized = vss_global_init_impl();
+ pthread_mutex_unlock(&vss_initialization_mutex);
+
+ if (vss_initialized)
+ return true;
+ ERROR("The Volume Shadow Copy Service (VSS) API could not be "
+ "initialized. Probably it isn't supported on this computer.");
+ return false;
+}
+
+void
+vss_global_cleanup(void)
+{
+ if (!vss_initialized)
+ return;
+
+ pthread_mutex_lock(&vss_initialization_mutex);
+ if (vss_initialized) {
+ (*func_CoUninitialize)();
+ FreeLibrary(hOle32);
+ FreeLibrary(hVssapi);
+ vss_initialized = false;
+ }
+ pthread_mutex_unlock(&vss_initialization_mutex);
+}
+
+/*----------------------------------------------------------------------------*
+ * VSS implementation *
+ *----------------------------------------------------------------------------*/
+
+struct vss_snapshot_internal {
+ struct vss_snapshot base;
+ IVssBackupComponents *vss;
+ VSS_SNAPSHOT_PROP props;
+};
+
+/* Delete the specified VSS snapshot. */
+void
+vss_delete_snapshot(struct vss_snapshot *snapshot)
+{
+ struct vss_snapshot_internal *internal;
+
+ internal = container_of(snapshot, struct vss_snapshot_internal, base);
+
+ if (internal->props.m_pwszSnapshotDeviceObject)
+ (*func_VssFreeSnapshotPropertiesInternal)(&internal->props);
+ if (internal->vss)
+ internal->vss->vtable->Release(internal->vss);
+ FREE(internal);
+}
+
+static HRESULT
+wait_and_release(IVssAsync *async)
+{
+ HRESULT res = async->vtable->Wait(async, INFINITE);
+ async->vtable->Release(async);
+ return res;
+}
+
+static bool
+request_vss_snapshot(IVssBackupComponents *vss, wchar_t *volume,
+ VSS_ID *snapshot_id)
+{
+ HRESULT res;
+ IVssAsync *async;
+
+ res = vss->vtable->InitializeForBackup(vss, NULL);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.InitializeForBackup() error: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->SetContext(vss, VSS_CTX_BACKUP);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.SetContext() error: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->SetBackupState(vss, FALSE, TRUE, VSS_BT_COPY, FALSE);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.SetBackupState() error: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->StartSnapshotSet(vss, snapshot_id);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.StartSnapshotSet() error: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->AddToSnapshotSet(vss, volume, (GUID){}, snapshot_id);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.AddToSnapshotSet() error: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->PrepareForBackup(vss, &async);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.PrepareForBackup() error: %x", res);
+ return false;
+ }
+ res = wait_and_release(async);
+ if (FAILED(res)) {
+ ERROR("IVssAsync.Wait() error while preparing for backup: %x", res);
+ return false;
+ }
+
+ res = vss->vtable->DoSnapshotSet(vss, &async);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.DoSnapshotSet() error: %x", res);
+ return false;
+ }
+ res = wait_and_release(async);
+ if (FAILED(res)) {
+ ERROR("IVssAsync.Wait() error while doing snapshot set: %x", res);
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * Create a VSS snapshot of the specified @volume. Return the NT namespace path
+ * to the snapshot root directory in @vss_path_ret and a handle to the snapshot
+ * in @snapshot_ret.
+ */
+int
+vss_create_snapshot(const wchar_t *source, UNICODE_STRING *vss_path_ret,
+ struct vss_snapshot **snapshot_ret)
+{
+ wchar_t *source_abspath;
+ wchar_t volume[4];
+ VSS_ID snapshot_id;
+ struct vss_snapshot_internal *snapshot = NULL;
+ IVssBackupComponents *vss;
+ HRESULT res;
+ int ret;
+
+ source_abspath = realpath(source, NULL);
+ if (!source_abspath) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto err;
+ }
+
+ if (source_abspath[0] == L'\0' || source_abspath[1] != L':' ||
+ source_abspath[2] != L'\\') {
+ ERROR("\"%ls\" (full path \"%ls\"): Path format not recognized",
+ source, source_abspath);
+ ret = WIMLIB_ERR_UNSUPPORTED;
+ goto err;
+ }
+
+ wsprintf(volume, L"%lc:\\", source_abspath[0]);
+
+ snapshot = CALLOC(1, sizeof(*snapshot));
+ if (!snapshot) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto err;
+ }
+
+ if (!vss_global_init())
+ goto vss_err;
+
+ res = (*func_CreateVssBackupComponentsInternal)(&vss);
+ if (FAILED(res)) {
+ ERROR("CreateVssBackupComponents error: %x", res);
+ goto vss_err;
+ }
+
+ snapshot->vss = vss;
+
+ if (!request_vss_snapshot(vss, volume, &snapshot_id))
+ goto vss_err;
+
+ res = vss->vtable->GetSnapshotProperties(vss, snapshot_id, &snapshot->props);
+ if (FAILED(res)) {
+ ERROR("IVssBackupComponents.GetSnapshotProperties() error: %x", res);
+ goto vss_err;
+ }
+
+ if (wcsncmp(snapshot->props.m_pwszSnapshotDeviceObject, L"\\\\?\\", 4)) {
+ ERROR("Unexpected volume shadow device path: %ls",
+ snapshot->props.m_pwszSnapshotDeviceObject);
+ goto vss_err;
+ }
+
+ vss_path_ret->MaximumLength = sizeof(wchar_t) *
+ (wcslen(snapshot->props.m_pwszSnapshotDeviceObject) +
+ 1 + wcslen(&source_abspath[3]) + 1);
+ vss_path_ret->Length = vss_path_ret->MaximumLength - sizeof(wchar_t);
+ vss_path_ret->Buffer = HeapAlloc(GetProcessHeap(), 0,
+ vss_path_ret->MaximumLength);
+ if (!vss_path_ret->Buffer) {
+ ret = WIMLIB_ERR_NOMEM;
+ goto err;
+ }
+
+ wsprintf(vss_path_ret->Buffer, L"\\??\\%ls\\%ls",
+ &snapshot->props.m_pwszSnapshotDeviceObject[4],
+ &source_abspath[3]);
+ *snapshot_ret = &snapshot->base;
+ snapshot->base.refcnt = 1;
+ ret = 0;
+ goto out;
+
+vss_err:
+ ret = WIMLIB_ERR_SNAPSHOT_FAILURE;
+ ERROR("A problem occurred while creating a VSS snapshot of \"%ls\".\n"
+ " Aborting the operation.", volume);
+err:
+ if (snapshot)
+ vss_delete_snapshot(&snapshot->base);
+out:
+ FREE(source_abspath);
+ return ret;
+}
+
+#endif /* __WIN32__ */
{
int ret;
struct wim_inode *inode;
+ const tchar *path;
tchar *cookie1;
tchar *cookie2;
if (--inode->i_num_remaining_streams > 0)
return 0;
- cookie1 = progress_get_streamless_path(blob->file_on_disk);
- cookie2 = progress_get_win32_path(blob->file_on_disk);
+ path = blob_file_path(blob);
- ret = done_with_file(blob->file_on_disk, progfunc, progctx);
+ cookie1 = progress_get_streamless_path(path);
+ cookie2 = progress_get_win32_path(path);
+
+ ret = done_with_file(path, progfunc, progctx);
progress_put_win32_path(cookie2);
progress_put_streamless_path(cookie1);