+ ret = 0;
+ goto out_put_actx;
+out_free_lte:
+ free_lookup_table_entry(lte);
+out_free_ntfs_loc:
+ if (ntfs_loc) {
+ FREE(ntfs_loc->path_utf8);
+ FREE(ntfs_loc->stream_name_utf16);
+ FREE(ntfs_loc);
+ }
+out_put_actx:
+ ntfs_attr_put_search_ctx(actx);
+ if (ret == 0)
+ DEBUG("Successfully captured NTFS streams from `%s'", path);
+ else
+ DEBUG("Failed to capture NTFS streams from `%s", path);
+ return ret;
+}
+
+struct readdir_ctx {
+ struct dentry *parent;
+ ntfs_inode *dir_ni;
+ char *path;
+ size_t path_len;
+ struct lookup_table *lookup_table;
+ struct sd_set *sd_set;
+ const struct capture_config *config;
+ ntfs_volume **ntfs_vol_p;
+};
+
+static int __build_dentry_tree_ntfs(struct dentry **root_p, ntfs_inode *ni,
+ char path[], size_t path_len,
+ struct lookup_table *lookup_table,
+ struct sd_set *sd_set,
+ const struct capture_config *config,
+ ntfs_volume **ntfs_vol_p);
+
+
+static int wim_ntfs_capture_filldir(void *dirent, const ntfschar *name,
+ const int name_len, const int name_type,
+ const s64 pos, const MFT_REF mref,
+ const unsigned dt_type)
+{
+ struct readdir_ctx *ctx;
+ size_t utf8_name_len;
+ char *utf8_name;
+ struct dentry *child;
+ int ret;
+ size_t path_len;
+
+ ret = -1;
+
+ utf8_name = utf16_to_utf8((const u8*)name, name_len * 2,
+ &utf8_name_len);
+ if (!utf8_name)
+ goto out;
+
+ if (utf8_name[0] == '.' &&
+ (utf8_name[1] == '\0' ||
+ (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
+ DEBUG("Skipping dentry `%s'", utf8_name);
+ ret = 0;
+ goto out_free_utf8_name;
+ }
+
+ DEBUG("Opening inode for `%s'", utf8_name);
+
+ ctx = dirent;
+
+ ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
+ if (!ni) {
+ ERROR_WITH_ERRNO("Failed to open NTFS inode");
+ ret = 1;
+ }
+ path_len = ctx->path_len;
+ if (path_len != 1)
+ ctx->path[path_len++] = '/';
+ memcpy(ctx->path + path_len, utf8_name, utf8_name_len + 1);
+ path_len += utf8_name_len;
+ ret = __build_dentry_tree_ntfs(&child, ni, ctx->path, path_len,
+ ctx->lookup_table, ctx->sd_set,
+ ctx->config, ctx->ntfs_vol_p);
+ DEBUG("Linking dentry `%s' with parent `%s'",
+ child->file_name_utf8, ctx->parent->file_name_utf8);
+
+ link_dentry(child, ctx->parent);
+ DEBUG("Return %d", ret);
+out_close_ni:
+ ntfs_inode_close(ni);
+out_free_utf8_name:
+ FREE(utf8_name);
+out:
+ return ret;
+}
+
+/* Recursively build a WIM dentry tree corresponding to a NTFS volume.
+ * At the same time, update the WIM lookup table with lookup table entries for
+ * the NTFS streams, and build an array of security descriptors.
+ */
+static int __build_dentry_tree_ntfs(struct dentry **root_p, ntfs_inode *ni,
+ char path[], size_t path_len,
+ struct lookup_table *lookup_table,
+ struct sd_set *sd_set,
+ const struct capture_config *config,
+ ntfs_volume **ntfs_vol_p)
+{
+ u32 attributes;
+ int mrec_flags;
+ u32 sd_size;
+ int ret = 0;
+ struct dentry *root;
+
+ if (exclude_path(path, config, false)) {
+ DEBUG("Excluding `%s' from capture", path);
+ return 0;
+ }
+
+ DEBUG("Starting recursive capture at path = `%s'", path);
+ mrec_flags = ni->mrec->flags;
+ attributes = ntfs_inode_get_attributes(ni);
+
+ root = new_dentry(path_basename(path));
+ if (!root)
+ return WIMLIB_ERR_NOMEM;
+
+ root->creation_time = le64_to_cpu(ni->creation_time);
+ root->last_write_time = le64_to_cpu(ni->last_data_change_time);
+ root->last_access_time = le64_to_cpu(ni->last_access_time);
+ root->security_id = le32_to_cpu(ni->security_id);
+ root->attributes = le32_to_cpu(attributes);
+ root->hard_link = ni->mft_no;
+ root->resolved = true;
+
+ if (attributes & FILE_ATTR_REPARSE_POINT) {
+ DEBUG("Reparse point `%s'", path);
+ /* Junction point, symbolic link, or other reparse point */
+ ret = capture_ntfs_streams(root, ni, path, path_len,
+ lookup_table, ntfs_vol_p,
+ AT_REPARSE_POINT);
+ } else if (mrec_flags & MFT_RECORD_IS_DIRECTORY) {
+ DEBUG("Directory `%s'", path);
+
+ /* Normal directory */
+ s64 pos = 0;
+ struct readdir_ctx ctx = {
+ .parent = root,
+ .dir_ni = ni,
+ .path = path,
+ .path_len = path_len,
+ .lookup_table = lookup_table,
+ .sd_set = sd_set,
+ .config = config,
+ .ntfs_vol_p = ntfs_vol_p,
+ };
+ ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
+ if (ret != 0)
+ ret = WIMLIB_ERR_NTFS_3G;
+ } else {
+ DEBUG("Normal file `%s'", path);
+ /* Normal file */
+ ret = capture_ntfs_streams(root, ni, path, path_len,
+ lookup_table, ntfs_vol_p,
+ AT_DATA);
+ }
+ if (ret != 0)
+ return ret;
+
+ DEBUG("Getting security information from `%s'", path);