+ 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)
+ DEBUG2("Successfully captured NTFS streams from `%s'", path);
+ else
+ ERROR("Failed to capture NTFS streams from `%s", path);
+ return ret;
+}
+
+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;
+ 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 = NULL;
+ int ret;
+ size_t path_len;
+
+ if (name_type == FILE_NAME_DOS)
+ return 0;
+
+ 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'))) {
+ ret = 0;
+ goto out_free_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");
+ 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;
+ 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;
+}
+
+static int change_dentry_short_name(struct wim_dentry *dentry,
+ const char short_name_utf8[],
+ int short_name_utf8_len)
+{
+ size_t short_name_utf16_len;
+ char *short_name_utf16;
+ int ret;
+
+ ret = utf8_to_utf16(short_name_utf8, short_name_utf8_len,
+ &short_name_utf16, &short_name_utf16_len);
+ if (ret == 0) {
+ dentry->short_name = short_name_utf16;
+ dentry->short_name_len = short_name_utf16_len;
+ }
+ 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 mrec_flags;
+ int ret;
+ struct wim_dentry *root;
+
+ if (exclude_path(path, config, false)) {
+ 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;
+ }
+
+ mrec_flags = ni->mrec->flags;
+ 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);
+ }
+
+ 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 (dir_ni && (name_type == FILE_NAME_WIN32_AND_DOS
+ || name_type == FILE_NAME_WIN32))
+ {
+ char dos_name_utf8[12 * 4 + 1] = {0};
+ ret = ntfs_get_ntfs_dos_name(ni, dir_ni, dos_name_utf8,
+ sizeof(dos_name_utf8) - 1);
+ if (ret > 0) {
+ DEBUG("Changing short name of `%s'", path);
+ ret = change_dentry_short_name(root, dos_name_utf8,
+ ret);
+ if (ret != 0)
+ return ret;
+ } else {
+ #ifdef ENODATA
+ if (errno != ENODATA) {
+ ERROR_WITH_ERRNO("Error getting DOS name "
+ "of `%s'", path);
+ return WIMLIB_ERR_NTFS_3G;
+ }
+ #endif
+ }
+ }
+
+ 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;
+
+ if (attributes & FILE_ATTR_REPARSE_POINT) {
+ /* 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) {
+
+ /* 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,
+ .add_image_flags = add_image_flags,
+ .progress_func = progress_func,
+ };
+ ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
+ if (ret != 0) {
+ ERROR_WITH_ERRNO("ntfs_readdir()");
+ ret = WIMLIB_ERR_NTFS_3G;
+ }
+ } else {
+ /* Normal file */
+ ret = capture_ntfs_streams(root, ni, path, path_len,
+ lookup_table, ntfs_vol_p,
+ AT_DATA);
+ }
+ if (ret != 0)
+ return ret;
+
+ char _sd[1];
+ char *sd = _sd;
+ errno = 0;
+ ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
+ ni, dir_ni, sd,
+ sizeof(sd));
+ if (ret > sizeof(sd)) {
+ sd = alloca(ret);
+ ret = ntfs_xattr_system_getxattr(&ctx, XATTR_NTFS_ACL,
+ ni, dir_ni, sd, ret);
+ }
+ if (ret > 0) {
+ root->d_inode->i_security_id = sd_set_add_sd(sd_set, sd, ret);
+ if (root->d_inode->i_security_id == -1) {
+ ERROR("Out of memory");
+ return WIMLIB_ERR_NOMEM;
+ }
+ DEBUG("Added security ID = %u for `%s'",
+ root->d_inode->i_security_id, path);
+ ret = 0;
+ } else if (ret < 0) {
+ ERROR_WITH_ERRNO("Failed to get security information from "
+ "`%s'", path);
+ ret = WIMLIB_ERR_NTFS_3G;
+ } else {
+ root->d_inode->i_security_id = -1;
+ DEBUG("No security ID for `%s'", path);
+ }
+ return ret;