+static void free_dos_name_tree(struct rb_node *node) {
+ if (node) {
+ free_dos_name_tree(node->rb_left);
+ free_dos_name_tree(node->rb_right);
+ FREE(container_of(node, struct dos_name_node, rb_node));
+ }
+}
+
+static void destroy_dos_name_map(struct dos_name_map *map)
+{
+ free_dos_name_tree(map->rb_root.rb_node);
+}
+
+struct readdir_ctx {
+ struct wim_dentry *parent;
+ ntfs_inode *dir_ni;
+ char *path;
+ size_t path_len;
+ struct wim_lookup_table *lookup_table;
+ struct sd_set *sd_set;
+ struct dos_name_map *dos_name_map;
+ const struct capture_config *config;
+ ntfs_volume **ntfs_vol_p;
+ int add_image_flags;
+ wimlib_progress_func_t progress_func;
+};
+
+static int
+build_dentry_tree_ntfs_recursive(struct wim_dentry **root_p, ntfs_inode *dir_ni,
+ ntfs_inode *ni, char path[], size_t path_len,
+ int name_type,
+ struct wim_lookup_table *lookup_table,
+ struct sd_set *sd_set,
+ const struct capture_config *config,
+ ntfs_volume **ntfs_vol_p,
+ int add_image_flags,
+ wimlib_progress_func_t progress_func);
+
+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 wim_dentry *child;
+ int ret;
+ size_t path_len;
+
+ ctx = dirent;
+ if (name_type & FILE_NAME_DOS) {
+ /* If this is the entry for a DOS name, store it for later. */
+ ret = insert_dos_name(ctx->dos_name_map, name,
+ name_len, mref & MFT_REF_MASK_CPU);
+
+ /* Return now if an error occurred or if this is just a DOS name
+ * and not a Win32+DOS name. */
+ if (ret != 0 || name_type == FILE_NAME_DOS)
+ return ret;
+ }
+ ret = utf16_to_utf8((const char*)name, name_len * 2,
+ &utf8_name, &utf8_name_len);
+ if (ret != 0)
+ return -1;
+
+ if (utf8_name[0] == '.' &&
+ (utf8_name[1] == '\0' ||
+ (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
+ /* . or .. entries
+ *
+ * note: name_type is POSIX for these, so DOS names will not
+ * have been inserted for them. */
+ ret = 0;
+ goto out_free_utf8_name;
+ }
+
+ /* Open the inode for this directory entry and recursively capture the
+ * directory tree rooted at it */
+ ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
+ if (!ni) {
+ ERROR_WITH_ERRNO("Failed to open NTFS inode");
+ goto out_free_utf8_name;
+ }
+ 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;
+ child = NULL;
+ ret = build_dentry_tree_ntfs_recursive(&child, ctx->dir_ni,
+ ni, ctx->path, path_len, name_type,
+ ctx->lookup_table, ctx->sd_set,
+ ctx->config, ctx->ntfs_vol_p,
+ ctx->add_image_flags,
+ ctx->progress_func);
+ if (child)
+ dentry_add_child(ctx->parent, child);
+ ntfs_inode_close(ni);
+out_free_utf8_name:
+ FREE(utf8_name);
+ 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_recursive(struct wim_dentry **root_p,
+ ntfs_inode *dir_ni,
+ ntfs_inode *ni,
+ char path[],
+ size_t path_len,
+ int name_type,
+ struct wim_lookup_table *lookup_table,
+ struct sd_set *sd_set,
+ const struct capture_config *config,
+ ntfs_volume **ntfs_vol_p,
+ int add_image_flags,
+ wimlib_progress_func_t progress_func)
+{
+ u32 attributes;
+ int ret;
+ struct wim_dentry *root;
+
+ if (exclude_path(path, config, false)) {
+ /* Exclude a file or directory tree based on the capture
+ * configuration file */
+ if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
+ && progress_func)
+ {
+ union wimlib_progress_info info;
+ info.scan.cur_path = path;
+ info.scan.excluded = true;
+ progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
+ }
+ *root_p = NULL;
+ return 0;
+ }
+
+ /* Get file attributes */
+ struct SECURITY_CONTEXT ctx;
+ memset(&ctx, 0, sizeof(ctx));
+ ctx.vol = ni->vol;
+ ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ATTRIB,
+ ni, dir_ni, (char *)&attributes,
+ sizeof(u32));
+ if (ret != 4) {
+ ERROR_WITH_ERRNO("Failed to get NTFS attributes from `%s'",
+ path);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+
+ if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
+ && progress_func)
+ {
+ union wimlib_progress_info info;
+ info.scan.cur_path = path;
+ info.scan.excluded = false;
+ progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info);
+ }
+
+ /* Create the new WIM dentry */
+ root = new_dentry_with_timeless_inode(path_basename(path));
+ if (!root) {
+ if (errno == EILSEQ)
+ return WIMLIB_ERR_INVALID_UTF8_STRING;
+ else if (errno == ENOMEM)
+ return WIMLIB_ERR_NOMEM;
+ else
+ return WIMLIB_ERR_ICONV_NOT_AVAILABLE;
+ }
+ *root_p = root;
+
+ if (name_type & FILE_NAME_WIN32) /* Win32 or Win32+DOS name */
+ root->is_win32_name = 1;
+ root->d_inode->i_creation_time = le64_to_cpu(ni->creation_time);
+ root->d_inode->i_last_write_time = le64_to_cpu(ni->last_data_change_time);
+ root->d_inode->i_last_access_time = le64_to_cpu(ni->last_access_time);
+ root->d_inode->i_attributes = le32_to_cpu(attributes);
+ root->d_inode->i_ino = ni->mft_no;
+ root->d_inode->i_resolved = 1;