X-Git-Url: https://wimlib.net/git/?p=wimlib;a=blobdiff_plain;f=src%2Fadd_image.c;h=ff97bc1c603a7ee1ec7a4b5bad82a13eccaf6062;hp=e3f022adde848a860859418385c4a3aad18444e6;hb=ff556d243953bd6df1399466c452f327feae2ed9;hpb=7ce0d372fae285051cbc9740c9fa316d22465d9d diff --git a/src/add_image.c b/src/add_image.c index e3f022ad..ff97bc1c 100644 --- a/src/add_image.c +++ b/src/add_image.c @@ -59,8 +59,8 @@ add_new_dentry_tree(WIMStruct *w, struct wim_dentry *root_dentry, struct wim_security_data *sd) { struct wim_image_metadata *new_imd; - int ret; struct wim_lookup_table_entry *metadata_lte; + int ret; metadata_lte = new_lookup_table_entry(); if (!metadata_lte) @@ -123,25 +123,17 @@ static int unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, char *path, size_t path_len, - struct wim_lookup_table *lookup_table, - struct wim_inode_table *inode_table, - const struct wimlib_capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func); + struct add_image_params *params); static int unix_capture_directory(struct wim_dentry *dir_dentry, char *path, size_t path_len, - struct wim_lookup_table *lookup_table, - struct wim_inode_table *inode_table, - const struct wimlib_capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func) + struct add_image_params *params) { DIR *dir; - struct dirent entry, *result; + struct dirent *entry; struct wim_dentry *child; int ret; @@ -154,34 +146,31 @@ unix_capture_directory(struct wim_dentry *dir_dentry, } /* Recurse on directory contents */ - while (1) { + ret = 0; + for (;;) { errno = 0; - ret = readdir_r(dir, &entry, &result); - if (ret != 0) { - ret = WIMLIB_ERR_READ; - ERROR_WITH_ERRNO("Error reading the " - "directory `%s'", - path); + entry = readdir(dir); + if (!entry) { + if (errno) { + ret = WIMLIB_ERR_READ; + ERROR_WITH_ERRNO("Error reading the " + "directory `%s'", path); + } break; } - if (result == NULL) - break; - if (result->d_name[0] == '.' && (result->d_name[1] == '\0' - || (result->d_name[1] == '.' && result->d_name[2] == '\0'))) + + if (entry->d_name[0] == '.' && (entry->d_name[1] == '\0' + || (entry->d_name[1] == '.' && entry->d_name[2] == '\0'))) continue; - size_t name_len = strlen(result->d_name); + size_t name_len = strlen(entry->d_name); path[path_len] = '/'; - memcpy(&path[path_len + 1], result->d_name, name_len + 1); + memcpy(&path[path_len + 1], entry->d_name, name_len + 1); ret = unix_build_dentry_tree_recursive(&child, path, path_len + 1 + name_len, - lookup_table, - inode_table, - config, - add_image_flags, - progress_func); + params); if (ret) break; if (child) @@ -192,9 +181,10 @@ unix_capture_directory(struct wim_dentry *dir_dentry, } static int -unix_capture_symlink(const char *path, +unix_capture_symlink(struct wim_dentry **root_p, + const char *path, struct wim_inode *inode, - struct wim_lookup_table *lookup_table) + struct add_image_params *params) { char deref_name_buf[4096]; ssize_t deref_name_len; @@ -212,10 +202,31 @@ unix_capture_symlink(const char *path, deref_name_len = readlink(path, deref_name_buf, sizeof(deref_name_buf) - 1); if (deref_name_len >= 0) { - deref_name_buf[deref_name_len] = '\0'; - DEBUG("Read symlink `%s'", deref_name_buf); - ret = inode_set_symlink(inode, deref_name_buf, - lookup_table, NULL); + char *dest = deref_name_buf; + + dest[deref_name_len] = '\0'; + DEBUG("Read symlink `%s'", dest); + + if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_RPFIX) && + dest[0] == '/') + { + dest = fixup_symlink(dest, + params->capture_root_ino, + params->capture_root_dev); + if (!dest) { + WARNING("Ignoring out of tree absolute symlink " + "\"%s\" -> \"%s\"\n" + " (Use --norpfix to capture " + "absolute symlinks as-is)", + path, deref_name_buf); + free_dentry(*root_p); + *root_p = NULL; + return 0; + } + inode->i_not_rpfixed = 0; + } + ret = inode_set_symlink(inode, dest, + params->lookup_table, NULL); if (ret == 0) { /* Unfortunately, Windows seems to have the concept of * "file" symbolic links as being different from @@ -239,46 +250,37 @@ static int unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, char *path, size_t path_len, - struct wim_lookup_table *lookup_table, - struct wim_inode_table *inode_table, - const struct wimlib_capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func) + struct add_image_params *params) { struct wim_dentry *root = NULL; int ret = 0; struct wim_inode *inode; - if (exclude_path(path, path_len, config, true)) { - if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) { - ERROR("Cannot exclude the root directory from capture"); - ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG; - goto out; - } - if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE) - && progress_func) + if (exclude_path(path, path_len, params->config, true)) { + if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE) + && params->progress_func) { union wimlib_progress_info info; info.scan.cur_path = path; info.scan.excluded = true; - progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); + params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); } goto out; } - if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) - && progress_func) + if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) + && params->progress_func) { union wimlib_progress_info info; info.scan.cur_path = path; info.scan.excluded = false; - progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); + params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY, &info); } - /* UNIX version of capturing a directory tree */ struct stat stbuf; int (*stat_fn)(const char *restrict, struct stat *restrict); - if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) + if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE) || + (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT)) stat_fn = stat; else stat_fn = lstat; @@ -288,25 +290,6 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, ERROR_WITH_ERRNO("Failed to stat `%s'", path); goto out; } - - if ((add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) && - !S_ISDIR(stbuf.st_mode)) - { - /* Do a dereference-stat in case the root is a symbolic link. - * This case is allowed, provided that the symbolic link points - * to a directory. */ - ret = stat(path, &stbuf); - if (ret != 0) { - ERROR_WITH_ERRNO("Failed to stat `%s'", path); - ret = WIMLIB_ERR_STAT; - goto out; - } - if (!S_ISDIR(stbuf.st_mode)) { - ERROR("`%s' is not a directory", path); - ret = WIMLIB_ERR_NOTDIR; - goto out; - } - } if (!S_ISREG(stbuf.st_mode) && !S_ISDIR(stbuf.st_mode) && !S_ISLNK(stbuf.st_mode)) { ERROR("`%s' is not a regular file, directory, or symbolic link.", @@ -315,11 +298,9 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, goto out; } - ret = inode_table_new_dentry(inode_table, + ret = inode_table_new_dentry(params->inode_table, path_basename_with_len(path, path_len), - stbuf.st_ino, - stbuf.st_dev, - &root); + stbuf.st_ino, stbuf.st_dev, false, &root); if (ret) goto out; @@ -338,30 +319,29 @@ unix_build_dentry_tree_recursive(struct wim_dentry **root_ret, inode->i_last_access_time = unix_timestamp_to_wim(stbuf.st_atime); #endif inode->i_resolved = 1; - if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) { + if (params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_UNIX_DATA) { ret = inode_set_unix_data(inode, stbuf.st_uid, stbuf.st_gid, stbuf.st_mode, - lookup_table, + params->lookup_table, UNIX_DATA_ALL | UNIX_DATA_CREATE); if (ret) goto out; } - add_image_flags &= ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); + params->add_image_flags &= + ~(WIMLIB_ADD_IMAGE_FLAG_ROOT | WIMLIB_ADD_IMAGE_FLAG_SOURCE); if (S_ISREG(stbuf.st_mode)) ret = unix_capture_regular_file(path, stbuf.st_size, - inode, lookup_table); + inode, params->lookup_table); else if (S_ISDIR(stbuf.st_mode)) - ret = unix_capture_directory(root, path, path_len, - lookup_table, inode_table, config, - add_image_flags, progress_func); + ret = unix_capture_directory(root, path, path_len, params); else - ret = unix_capture_symlink(path, inode, lookup_table); + ret = unix_capture_symlink(&root, path, inode, params); out: if (ret == 0) *root_ret = root; else - free_dentry_tree(root, lookup_table); + free_dentry_tree(root, params->lookup_table); return ret; } @@ -376,20 +356,7 @@ out: * * @root_disk_path: The path to the root of the directory tree on disk. * - * @lookup_table: The lookup table for the WIM file. For each file added to the - * dentry tree being built, an entry is added to the lookup table, - * unless an identical stream is already in the lookup table. - * These lookup table entries that are added point to the path of - * the file on disk. - * - * @sd_set: Ignored. (Security data only captured in NTFS mode.) - * - * @config: - * Configuration for files to be excluded from capture. - * - * @add_flags: Bitwise or of WIMLIB_ADD_IMAGE_FLAG_* - * - * @extra_arg: Ignored + * @params: See doc for `struct add_image_params'. * * @return: 0 on success, nonzero on failure. It is a failure if any of * the files cannot be `stat'ed, or if any of the needed @@ -401,19 +368,31 @@ out: static int unix_build_dentry_tree(struct wim_dentry **root_ret, const char *root_disk_path, - struct wim_lookup_table *lookup_table, - struct wim_inode_table *inode_table, - struct sd_set *sd_set, - const struct wimlib_capture_config *config, - int add_image_flags, - wimlib_progress_func_t progress_func, - void *extra_arg) + struct add_image_params *params) { char *path_buf; int ret; size_t path_len; size_t path_bufsz; + { + struct stat root_stbuf; + if (stat(root_disk_path, &root_stbuf)) { + ERROR_WITH_ERRNO("Failed to stat \"%s\"", root_disk_path); + return WIMLIB_ERR_STAT; + } + + if ((params->add_image_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) && + !S_ISDIR(root_stbuf.st_mode)) + { + ERROR("Root of capture \"%s\" is not a directory", + root_disk_path); + return WIMLIB_ERR_NOTDIR; + } + params->capture_root_ino = root_stbuf.st_ino; + params->capture_root_dev = root_stbuf.st_dev; + } + path_bufsz = min(32790, PATH_MAX + 1); path_len = strlen(root_disk_path); @@ -424,14 +403,9 @@ unix_build_dentry_tree(struct wim_dentry **root_ret, if (!path_buf) return WIMLIB_ERR_NOMEM; memcpy(path_buf, root_disk_path, path_len + 1); - ret = unix_build_dentry_tree_recursive(root_ret, - path_buf, - path_len, - lookup_table, - inode_table, - config, - add_image_flags, - progress_func); + + ret = unix_build_dentry_tree_recursive(root_ret, path_buf, + path_len, params); FREE(path_buf); return ret; } @@ -645,9 +619,7 @@ new_filler_directory(const tchar *name, struct wim_dentry **dentry_ret) return ret; } -/* Transfers the children of @branch to @target. It is an error if @target is - * not a directory or if both @branch and @target contain a child dentry with - * the same name. */ +/* Overlays @branch onto @target, both of which must be directories. */ static int do_overlay(struct wim_dentry *target, struct wim_dentry *branch) { @@ -656,30 +628,36 @@ do_overlay(struct wim_dentry *target, struct wim_dentry *branch) DEBUG("Doing overlay \"%"WS"\" => \"%"WS"\"", branch->file_name, target->file_name); - if (!dentry_is_directory(target)) { - ERROR("Cannot overlay directory \"%"WS"\" " - "over non-directory", branch->file_name); + if (!dentry_is_directory(branch) || !dentry_is_directory(target)) { + ERROR("Cannot overlay \"%"WS"\" onto existing dentry: " + "is not directory-on-directory!", branch->file_name); return WIMLIB_ERR_INVALID_OVERLAY; } rb_root = &branch->d_inode->i_children; while (rb_root->rb_node) { /* While @branch has children... */ struct wim_dentry *child = rbnode_dentry(rb_root->rb_node); + struct wim_dentry *existing; + /* Move @child to the directory @target */ unlink_dentry(child); - if (!dentry_add_child(target, child)) { - /* Revert the change to avoid leaking the directory tree - * rooted at @child */ - dentry_add_child(branch, child); - ERROR("Overlay error: file \"%"WS"\" already exists " - "as a child of \"%"WS"\"", - child->file_name, target->file_name); - return WIMLIB_ERR_INVALID_OVERLAY; + existing = dentry_add_child(target, child); + + /* File or directory with same name already exists */ + if (existing) { + int ret; + ret = do_overlay(existing, child); + if (ret) { + /* Overlay failed. Revert the change to avoid + * leaking the directory tree rooted at @child. + * */ + dentry_add_child(branch, child); + return ret; + } } } free_dentry(branch); return 0; - } /* Attach or overlay a branch onto the WIM image. @@ -821,13 +799,7 @@ wimlib_add_image_multisource(WIMStruct *w, { int (*capture_tree)(struct wim_dentry **, const tchar *, - struct wim_lookup_table *, - struct wim_inode_table *, - struct sd_set *, - const struct wimlib_capture_config *, - int, - wimlib_progress_func_t, - void *); + struct add_image_params *); void *extra_arg; struct wim_dentry *root_dentry; struct wim_dentry *branch; @@ -835,8 +807,12 @@ wimlib_add_image_multisource(WIMStruct *w, struct wim_image_metadata *imd; struct wim_inode_table inode_table; struct list_head unhashed_streams; + struct add_image_params params; int ret; struct sd_set sd_set; +#ifdef WITH_NTFS_3G + struct _ntfs_volume *ntfs_vol = NULL; +#endif if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_NTFS) { #ifdef WITH_NTFS_3G @@ -850,7 +826,7 @@ wimlib_add_image_multisource(WIMStruct *w, return WIMLIB_ERR_INVALID_PARAM; } capture_tree = build_dentry_tree_ntfs; - extra_arg = &w->ntfs_vol; + extra_arg = &ntfs_vol; #else ERROR("wimlib was compiled without support for NTFS-3g, so\n" " cannot capture a WIM image directly from a NTFS volume!"); @@ -879,6 +855,24 @@ wimlib_add_image_multisource(WIMStruct *w, if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE) add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_EXCLUDE_VERBOSE; + if ((add_image_flags & (WIMLIB_ADD_IMAGE_FLAG_RPFIX | + WIMLIB_ADD_IMAGE_FLAG_RPFIX)) == + (WIMLIB_ADD_IMAGE_FLAG_RPFIX | WIMLIB_ADD_IMAGE_FLAG_NORPFIX)) + { + ERROR("Cannot specify RPFIX and NORPFIX flags at the same time!"); + return WIMLIB_ERR_INVALID_PARAM; + } + + if ((add_image_flags & (WIMLIB_ADD_IMAGE_FLAG_RPFIX | + WIMLIB_ADD_IMAGE_FLAG_NORPFIX)) == 0) + { + /* Do reparse-point fixups by default if the header flag is set + * from previous images, or if this is the first image being + * added. */ + if ((w->hdr.flags & WIM_HDR_FLAG_RP_FIX) || w->hdr.image_count == 0) + add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_RPFIX; + } + if (!name || !*name) { ERROR("Must specify a non-empty string for the image name"); return WIMLIB_ERR_INVALID_PARAM; @@ -933,6 +927,14 @@ wimlib_add_image_multisource(WIMStruct *w, INIT_LIST_HEAD(&unhashed_streams); w->lookup_table->unhashed_streams = &unhashed_streams; root_dentry = NULL; + + params.lookup_table = w->lookup_table; + params.inode_table = &inode_table; + params.sd_set = &sd_set; + params.config = config; + params.add_image_flags = add_image_flags; + params.progress_func = progress_func; + params.extra_arg = extra_arg; for (size_t i = 0; i < num_sources; i++) { int flags; union wimlib_progress_info progress; @@ -952,14 +954,8 @@ wimlib_add_image_multisource(WIMStruct *w, flags = add_image_flags | WIMLIB_ADD_IMAGE_FLAG_SOURCE; if (!*sources[i].wim_target_path) flags |= WIMLIB_ADD_IMAGE_FLAG_ROOT; - ret = (*capture_tree)(&branch, - sources[i].fs_source_path, - w->lookup_table, - &inode_table, - &sd_set, - config, - flags, - progress_func, extra_arg); + ret = (*capture_tree)(&branch, sources[i].fs_source_path, + ¶ms); if (ret) { ERROR("Failed to build dentry tree for `%"TS"'", sources[i].fs_source_path); @@ -991,12 +987,21 @@ wimlib_add_image_multisource(WIMStruct *w, } ret = add_new_dentry_tree(w, root_dentry, sd); - if (ret) + + if (ret) { +#ifdef WITH_NTFS_3G + if (ntfs_vol) + do_ntfs_umount(ntfs_vol); +#endif goto out_free_dentry_tree; + } imd = w->image_metadata[w->hdr.image_count - 1]; - INIT_LIST_HEAD(&imd->unhashed_streams); - list_splice(&unhashed_streams, &imd->unhashed_streams); + list_transfer(&unhashed_streams, &imd->unhashed_streams); + +#ifdef WITH_NTFS_3G + imd->ntfs_vol = ntfs_vol; +#endif DEBUG("Assigning hard link group IDs"); inode_table_prepare_inode_list(&inode_table, &imd->inode_list); @@ -1008,6 +1013,9 @@ wimlib_add_image_multisource(WIMStruct *w, if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_BOOT) wimlib_set_boot_idx(w, w->hdr.image_count); + if (add_image_flags & WIMLIB_ADD_IMAGE_FLAG_RPFIX) + w->hdr.flags |= WIM_HDR_FLAG_RP_FIX; + ret = 0; goto out_destroy_inode_table; out_put_imd: