+ /* The file attributes and timestamps were cached from the MFT. */
+ inode->i_attributes = ni->attributes;
+ inode->i_creation_time = ni->creation_time;
+ inode->i_last_write_time = ni->last_write_time;
+ inode->i_last_access_time = ni->last_access_time;
+
+ /* Set the security descriptor if needed. */
+ if (!(ctx->params->add_flags & WIMLIB_ADD_FLAG_NO_ACLS)) {
+ /* Look up the WIM security ID that corresponds to the on-disk
+ * security ID. */
+ s32 wim_security_id =
+ security_map_lookup(security_map, ni->security_id);
+ if (likely(wim_security_id >= 0)) {
+ /* The mapping for this security ID is already cached.*/
+ inode->i_security_id = wim_security_id;
+ } else {
+ HANDLE h;
+ NTSTATUS status;
+
+ /* Create a mapping for this security ID and insert it
+ * into the security map. */
+
+ status = winnt_open(path, path_nchars,
+ READ_CONTROL |
+ ACCESS_SYSTEM_SECURITY, &h);
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"Can't open \"%ls\" to "
+ "read security descriptor",
+ printable_path(path));
+ ret = WIMLIB_ERR_OPEN;
+ goto out;
+ }
+ ret = winnt_load_security_descriptor(h, inode, path, ctx);
+ NtClose(h);
+ if (ret)
+ goto out;
+
+ ret = security_map_insert(security_map, ni->security_id,
+ inode->i_security_id);
+ if (ret)
+ goto out;
+ }
+ }
+
+ /* Add data streams based on the cached information from the MFT. */
+ ns = FIRST_STREAM(ni);
+ for (u32 i = 0; i < ni->num_streams; i++) {
+ struct windows_file *windows_file;
+
+ /* Reference the stream by path if it's a named data stream, or
+ * if the volume doesn't support "open by file ID", or if the
+ * application hasn't explicitly opted in to "open by file ID".
+ * Otherwise, only save the inode number (file ID). */
+ if (*ns->name ||
+ !(ctx->vol_flags & FILE_SUPPORTS_OPEN_BY_FILE_ID) ||
+ !(ctx->params->add_flags & WIMLIB_ADD_FLAG_FILE_PATHS_UNNEEDED))
+ {
+ windows_file = alloc_windows_file(path,
+ path_nchars,
+ ns->name,
+ wcslen(ns->name),
+ ctx->snapshot,
+ false);
+ } else {
+ windows_file = alloc_windows_file_for_file_id(ni->ino,
+ path,
+ ctx->params->capture_root_nchars + 1,
+ ctx->snapshot);
+ }
+
+ ret = add_stream(inode, windows_file, ns->size,
+ STREAM_TYPE_DATA, ns->name,
+ ctx->params->unhashed_blobs);
+ if (ret)
+ goto out;
+ ns = NEXT_STREAM(ns);
+ }
+
+ set_sort_key(inode, ni->starting_lcn);
+
+ /* If processing a directory, then recurse to its children. In this
+ * version there is no need to go to disk, as we already have the list
+ * of children cached from the MFT. */
+ if (inode_is_directory(inode)) {
+ const struct ntfs_dentry *nd = ni->first_child;
+
+ while (nd != NULL) {
+ const size_t name_len = wcslen(nd->name);
+ wchar_t *p = path + path_nchars;
+ struct wim_dentry *child;
+ const struct ntfs_dentry *next = nd->next_child;
+
+ if (*(p - 1) != L'\\')
+ *p++ = L'\\';
+ p = wmempcpy(p, nd->name, name_len);
+ *p = '\0';
+
+ ret = generate_wim_structures_recursive(
+ &child,
+ path,
+ p - path,
+ nd->name,
+ nd->is_primary,
+ (void *)nd - nd->offset_from_inode,
+ ctx,
+ inode_map,
+ security_map);
+
+ path[path_nchars] = L'\0';
+
+ if (ret)
+ goto out;
+
+ attach_scanned_tree(root, child, ctx->params->blob_table);
+ nd = next;
+ }
+ }
+
+out_progress:
+ ctx->params->progress.scan.cur_path = path;
+ if (likely(root))
+ ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_OK, inode);
+ else
+ ret = do_capture_progress(ctx->params, WIMLIB_SCAN_DENTRY_EXCLUDED, NULL);
+out:
+ if (--ni->num_aliases == 0) {
+ /* Memory usage optimization: when we don't need the ntfs_inode
+ * (and its names and streams) anymore, free it. */
+ ntfs_inode_map_remove(inode_map, ni);
+ }
+ if (unlikely(ret)) {
+ free_dentry_tree(root, ctx->params->blob_table);
+ root = NULL;
+ }
+ *root_ret = root;
+ return ret;
+}
+
+static int
+winnt_build_dentry_tree_fast(struct wim_dentry **root_ret, wchar_t *path,
+ size_t path_nchars, struct winnt_scan_ctx *ctx)
+{
+ struct ntfs_inode_map inode_map = { .root = NULL };
+ struct security_map security_map = { .root = NULL };
+ struct ntfs_inode *root = NULL;
+ bool adjust_path;
+ int ret;
+
+ adjust_path = (path[path_nchars - 1] == L'\\');
+ if (adjust_path)
+ path[path_nchars - 1] = L'\0';
+
+ ret = load_files_from_mft(path, &inode_map);
+
+ if (adjust_path)
+ path[path_nchars - 1] = L'\\';
+
+ if (ret)
+ goto out;
+
+ ret = build_children_lists(&inode_map, &root);
+ if (ret)
+ goto out;
+
+ if (!root) {
+ ERROR("The MFT listing for volume \"%ls\" did not include a "
+ "root directory!", path);
+ ret = WIMLIB_ERR_UNSUPPORTED;
+ goto out;
+ }
+
+ root->num_aliases = 1;
+
+ ret = generate_wim_structures_recursive(root_ret, path, path_nchars,
+ L"", false, root, ctx,
+ &inode_map, &security_map);
+out:
+ ntfs_inode_map_destroy(&inode_map);
+ security_map_destroy(&security_map);
+ return ret;
+}
+
+#endif /* ENABLE_FAST_MFT_SCAN */
+
+/*----------------------------------------------------------------------------*
+ * Entry point for directory tree scans on Windows *
+ *----------------------------------------------------------------------------*/
+
+#define WINDOWS_NT_MAX_PATH 32768
+
+int
+win32_build_dentry_tree(struct wim_dentry **root_ret,
+ const wchar_t *root_disk_path,
+ struct capture_params *params)
+{
+ wchar_t *path = NULL;
+ struct winnt_scan_ctx ctx = { .params = params };
+ UNICODE_STRING ntpath;
+ size_t ntpath_nchars;
+ HANDLE h = NULL;
+ NTSTATUS status;
+ 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
+ * by Windows NT (which is NOT the same as MAX_PATH). */
+ path = MALLOC((WINDOWS_NT_MAX_PATH + 1) * sizeof(wchar_t));
+ if (!path)
+ return WIMLIB_ERR_NOMEM;
+
+ 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;
+
+ if (ntpath.Length < 4 * sizeof(wchar_t) ||
+ ntpath.Length > WINDOWS_NT_MAX_PATH * sizeof(wchar_t) ||
+ wmemcmp(ntpath.Buffer, L"\\??\\", 4))
+ {
+ ERROR("\"%ls\": unrecognized path format", root_disk_path);
+ ret = WIMLIB_ERR_INVALID_PARAM;
+ } else {
+ ntpath_nchars = ntpath.Length / sizeof(wchar_t);
+ wmemcpy(path, ntpath.Buffer, ntpath_nchars);
+ path[ntpath_nchars] = L'\0';
+
+ params->capture_root_nchars = ntpath_nchars;
+ if (path[ntpath_nchars - 1] == L'\\')
+ params->capture_root_nchars--;
+ ret = 0;
+ }
+ HeapFree(GetProcessHeap(), 0, ntpath.Buffer);
+ if (ret)
+ goto out;
+
+ status = winnt_open(path, ntpath_nchars, FILE_READ_ATTRIBUTES, &h);
+ if (!NT_SUCCESS(status)) {
+ winnt_error(status, L"Can't open \"%ls\"", printable_path(path));
+ if (status == STATUS_FVE_LOCKED_VOLUME)
+ ret = WIMLIB_ERR_FVE_LOCKED_VOLUME;
+ else
+ ret = WIMLIB_ERR_OPEN;
+ goto out;
+ }
+
+ get_volume_information(h, path, &ctx);
+
+ NtClose(h);
+
+#ifdef ENABLE_FAST_MFT_SCAN
+ if (ctx.is_ntfs && !_wgetenv(L"WIMLIB_DISABLE_QUERY_FILE_LAYOUT")) {
+ ret = winnt_build_dentry_tree_fast(root_ret, path,
+ ntpath_nchars, &ctx);
+ if (ret >= 0 && ret != WIMLIB_ERR_UNSUPPORTED)
+ goto out;
+ if (ret >= 0) {
+ WARNING("A problem occurred during the fast MFT scan.\n"
+ " Falling back to the standard "
+ "recursive directory tree scan.");
+ }
+ }
+#endif
+ ret = winnt_build_dentry_tree_recursive(root_ret, NULL,
+ path, ntpath_nchars,
+ path, ntpath_nchars,
+ L"", &ctx);
+out:
+ vss_put_snapshot(ctx.snapshot);