Fixes; write --{no,}rpfix docs; enable --rpfix capture by default
authorEric Biggers <ebiggers3@gmail.com>
Thu, 25 Apr 2013 00:42:39 +0000 (19:42 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Thu, 25 Apr 2013 00:42:39 +0000 (19:42 -0500)
doc/imagex-apply.1.in
doc/imagex-capture.1.in
src/add_image.c
src/dentry.h
src/export_image.c
src/extract_image.c
src/mount_image.c
src/symlink.c
src/wimlib.h
tests/test-imagex-capture_and_apply
tests/test-imagex-mount

index b59a593..5db59f9 100644 (file)
@@ -263,6 +263,18 @@ be set exactly as specified in the WIM file.  The default behavior without this
 option is to fall back to setting a security descriptor with the SACL omitted,
 then only the default inherited security descriptor, if we do not have
 permission to set the desired one.
+.TP
+\fB--rpfix\fR, \fB--norpfix\fR
+Set whether to fix targets of absolute symbolic links (reparse points in Windows
+terminology) or not.  When enabled (\fB--rpfix\fR), extracted absolute symbolic
+links that are marked in the WIM image as being fixed are assumed to have
+absolute targets relative to the image root, and therefore have the actual root
+of extraction prepended to their targets.  The intention is that you can apply
+an image containing absolute symbolic links and still have them be valid after
+it's been applied to any location.
+
+The default behavior is \fB--rpfix\fR if any images in \fIWIMFILE\fR have been
+captured with reparse-point fixups done.  Otherwise, it is \fB--norpfix\fR.
 
 .SH NOTES
 
@@ -286,14 +298,14 @@ Extract the first image from the Windows PE image from the Windows Vista/7/8
 installation media to the directory "boot":
 .RS
 .PP
-image apply /media/windows/sources/boot.wim 1 boot
+@IMAGEX_PROGNAME@ apply /media/windows/sources/boot.wim 1 boot
 .RE
 .PP
 Extract all images from the Windows PE image from the Windows Vista/7/8
 installation media to the directory "boot", and hard link all identical files:
 .RS
 .PP
-image apply /media/windows8/sources/boot.wim all boot --hardlink
+@IMAGEX_PROGNAME@ apply /media/windows8/sources/boot.wim all boot --hardlink
 .RE
 .PP
 .SS NTFS extraction mode
index e20e097..05c43fd 100644 (file)
@@ -237,6 +237,24 @@ information.
 In the NTFS capture mode, do not capture security descriptors.  This flag is
 also available in the native Win32 build of wimlib.
 .TP
+\fB--rpfix\fR, \fB--norpfix\fR
+Set whether to fix targets of absolute symbolic links (reparse points in Windows
+terminology) or not.  When enabled (\fB--rpfix\fR), absolute symbolic links that
+point inside the directory tree being captured will be adjusted to be absolute
+relative to the root of the directory tree being captured.  In addition,
+absolute symbolic links that point outside the directory tree being captured
+will be ignored and not be captured at all.  When disabled (\fB--norpfix\fR),
+absolute symbolic links will be captured exactly as is.
+
+The default behavior for \fBimagex capture\fR is equivalent to \fB--rpfix\fR.
+The default behavior for \fBimagex append\fR will be \fB--rpfix\fR if reparse
+point fixups have previously been done on \fIWIMFILE\fR, otherwise
+\fB--norpfix\fR.
+
+Links are fixed up on a per-source basis in the case of a multi-source capture
+(\fB--source-list\fR specified), so you may wish to set \fB--norpfix\fR in that
+case.
+.TP
 \fB--strict-acls\fR
 In the Win32 native build of wimlib, fail immediately if the full security
 descriptor of any file or directory cannot be read.  The default behavior
index 8e97a8e..b50b05b 100644 (file)
@@ -865,8 +865,13 @@ wimlib_add_image_multisource(WIMStruct *w,
 
        if ((add_image_flags & (WIMLIB_ADD_IMAGE_FLAG_RPFIX |
                                WIMLIB_ADD_IMAGE_FLAG_NORPFIX)) == 0)
-               if (w->hdr.flags & WIM_HDR_FLAG_RP_FIX)
+       {
+               /* 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");
index 5c57e7e..5e42e91 100644 (file)
@@ -508,8 +508,8 @@ static inline bool
 inode_is_symlink(const struct wim_inode *inode)
 {
        return (inode->i_attributes & FILE_ATTRIBUTE_REPARSE_POINT)
-               && ((inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) ||
-                    inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+               && (inode->i_reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK ||
+                   inode->i_reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
 }
 
 static inline bool
index 34564e2..c5c85d5 100644 (file)
@@ -252,6 +252,12 @@ wimlib_export_image(WIMStruct *src_wim,
 
        if (export_flags & WIMLIB_EXPORT_FLAG_BOOT)
                dest_wim->hdr.boot_idx = dest_wim->hdr.image_count;
+       if (src_wim->hdr.flags & WIM_HDR_FLAG_RP_FIX)
+       {
+               /* Set the reparse point fixup flag on the destination WIM if
+                * the flag is set on the source WIM. */
+               dest_wim->hdr.flags |= WIM_HDR_FLAG_RP_FIX;
+       }
        ret = 0;
        goto out;
 out_xml_delete_image:
index 48a916a..aae779e 100644 (file)
@@ -281,7 +281,7 @@ out_extract_unix_data:
                        ret = 0;
                else
                        ret = fd_apply_unix_data(out_fd, &unix_data);
-               if (ret != 0)
+               if (ret)
                        goto out;
        }
        if (lte)
@@ -342,10 +342,13 @@ extract_symlink(struct wim_dentry *dentry,
        if (target[args->target_realpath_len] == '/' &&
            args->extract_flags & WIMLIB_EXTRACT_FLAG_RPFIX)
        {
+               /* Fix absolute symbolic link target to point into the actual
+                * extraction destination */
                memcpy(target, args->target_realpath,
                       args->target_realpath_len);
                fixed_target = target;
        } else {
+               /* Keep same link target */
                fixed_target = target + args->target_realpath_len;
        }
        ret = symlink(fixed_target, output_path);
@@ -421,10 +424,9 @@ dir_exists:
 }
 
 #ifndef __WIN32__
-static int unix_do_apply_dentry(const char *output_path,
-                               size_t output_path_len,
-                               struct wim_dentry *dentry,
-                               struct apply_args *args)
+static int
+unix_do_apply_dentry(const char *output_path, size_t output_path_len,
+                    struct wim_dentry *dentry, struct apply_args *args)
 {
        const struct wim_inode *inode = dentry->d_inode;
 
@@ -504,8 +506,8 @@ static int
 apply_dentry_normal(struct wim_dentry *dentry, void *arg)
 {
        struct apply_args *args = arg;
-       tchar *output_path;
        size_t len;
+       tchar *output_path;
 
        len = tstrlen(args->target);
        if (dentry_is_root(dentry)) {
@@ -546,7 +548,6 @@ apply_dentry_timestamps_normal(struct wim_dentry *dentry, void *arg)
                output_path[len] = T('\0');
        }
 
-
 #ifdef __WIN32__
        return win32_do_apply_dentry_timestamps(output_path, len, dentry, args);
 #else
@@ -637,8 +638,21 @@ inode_find_streams_for_extraction(struct wim_inode *inode,
                list_add_tail(&inode->i_lte_inode_list, &lte->inode_list);
                inode_added = true;
        }
-#ifdef WITH_NTFS_3G
-       if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS) {
+
+       /* Determine whether to include alternate data stream entries or not.
+        *
+        * UNIX:  Include them if extracting using NTFS-3g.
+        *
+        * Windows: Include them undconditionally, although if the filesystem is
+        * not NTFS we won't actually be able to extract them. */
+#if defined(WITH_NTFS_3G)
+       if (extract_flags & WIMLIB_EXTRACT_FLAG_NTFS)
+#elif defined(__WIN32__)
+       if (1)
+#else
+       if (0)
+#endif
+       {
                for (unsigned i = 0; i < inode->i_num_ads; i++) {
                        if (inode->i_ads_entries[i].stream_name_nbytes != 0) {
                                lte = inode->i_ads_entries[i].lte;
@@ -654,7 +668,6 @@ inode_find_streams_for_extraction(struct wim_inode *inode,
                        }
                }
        }
-#endif
 }
 
 static void
@@ -725,7 +738,7 @@ apply_stream_list(struct list_head *stream_list,
                                /* Extract the dentry if it was not already
                                 * extracted */
                                ret = maybe_apply_dentry(dentry, args);
-                               if (ret != 0)
+                               if (ret)
                                        return ret;
                                if (progress_func &&
                                    args->progress.extract.completed_bytes >= next_progress)
index 7856201..b00a2a6 100644 (file)
@@ -2024,10 +2024,16 @@ wimfs_readlink(const char *path, char *buf, size_t buf_len)
                return -errno;
        if (!inode_is_symlink(inode))
                return -EINVAL;
-
-       ret = inode_readlink(inode, buf, buf_len, ctx->wim, true);
-       if (ret > 0)
+       if (buf_len == 0)
+               return -ENAMETOOLONG;
+       ret = inode_readlink(inode, buf, buf_len - 1, ctx->wim, true);
+       if (ret >= 0) {
+               wimlib_assert(ret <= buf_len - 1);
+               buf[ret] = '\0';
                ret = 0;
+       } else if (ret == -ENAMETOOLONG) {
+               buf[buf_len - 1] = '\0';
+       }
        return ret;
 }
 
index 587b71c..ea92a2d 100644 (file)
 
 #include <sys/stat.h>
 
+#ifdef HAVE_ALLOCA_H
+#  include <alloca.h>
+#endif
+
 /*
  * Find the symlink target of a symbolic link or junction point in the WIM.
  *
@@ -77,7 +81,8 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf,
                header_size = 12;
                p += 4;
        }
-       if (header_size + substitute_name_offset + substitute_name_len > resource_len)
+       if (header_size +
+           substitute_name_offset + substitute_name_len > resource_len)
                return -EIO;
 
        ret = utf16le_to_tstr((const utf16lechar*)(p + substitute_name_offset),
@@ -86,11 +91,6 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf,
        if (ret)
                return -errno;
 
-       if (link_target_len + 1 > buf_len) {
-               ret = -ENAMETOOLONG;
-               goto out;
-       }
-
        DEBUG("Interpeting substitute name \"%s\" (ReparseTag=0x%x)",
              link_target, reparse_tag);
        translate_slashes = true;
@@ -133,7 +133,7 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf,
                        /* "Relative" symlink, without drive letter */
                        ;
        } else {
-               ERROR("Invalid reparse point: \"%s\"", translated_target);
+               ERROR("Invalid reparse point substitute name: \"%s\"", translated_target);
                ret = -EIO;
                goto out;
        }
@@ -142,8 +142,14 @@ get_symlink_name(const void *resource, size_t resource_len, char *buf,
                for (size_t i = 0; i < link_target_len; i++)
                        if (translated_target[i] == '\\')
                                translated_target[i] = '/';
-       memcpy(buf, translated_target, link_target_len + 1);
-       ret = link_target_len;
+
+       if (link_target_len > buf_len) {
+               link_target_len = buf_len;
+               ret = -ENAMETOOLONG;
+       } else {
+               ret = link_target_len;
+       }
+       memcpy(buf, translated_target, link_target_len);
 out:
        FREE(link_target);
        return ret;
@@ -291,6 +297,7 @@ inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len,
 {
        const struct wim_lookup_table_entry *lte;
        int ret;
+       u8 *res_buf;
 
        wimlib_assert(inode_is_symlink(inode));
 
@@ -301,12 +308,12 @@ inode_readlink(const struct wim_inode *inode, char *buf, size_t buf_len,
        if (wim_resource_size(lte) > REPARSE_POINT_MAX_SIZE)
                return -EIO;
 
-       u8 res_buf[wim_resource_size(lte)];
+       res_buf = alloca(wim_resource_size(lte));
        ret = read_full_resource_into_buf(lte, res_buf, threadsafe);
        if (ret)
                return -EIO;
-       return get_symlink_name(res_buf, wim_resource_size(lte), buf,
-                               buf_len, inode->i_reparse_tag);
+       return get_symlink_name(res_buf, wim_resource_size(lte),
+                               buf, buf_len, inode->i_reparse_tag);
 }
 
 /*
@@ -406,7 +413,8 @@ unix_get_ino_and_dev(const char *path, u64 *ino_ret, u64 *dev_ret)
 #  define os_get_ino_and_dev unix_get_ino_and_dev
 #endif
 
-/* Fix up reparse points--- mostly shared between UNIX and Windows */
+/* Fix up absolute symbolic link targets--- mostly shared between UNIX and
+ * Windows */
 tchar *
 fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev)
 {
@@ -433,6 +441,10 @@ fixup_symlink(tchar *dest, u64 capture_root_ino, u64 capture_root_dev)
                ret = os_get_ino_and_dev(dest, &ino, &dev);
                *p = save;
 
+               if (ret) /* stat() failed before we got to the capture root---
+                           assume the link points outside it. */
+                       return NULL;
+
                if (ino == capture_root_ino && dev == capture_root_dev) {
                        /* Link points inside capture root.  Return abbreviated
                         * path. */
index e7f89fa..ed036e7 100644 (file)
@@ -700,14 +700,14 @@ struct wimlib_capture_config {
  * current root; also exclude absolute symbolic links that point outside the
  * directory tree being captured.
  *
- * Without this flag, the default is to do this only if WIM_HDR_FLAG_RP_FIX is
- * set in the WIM header.  WIM_HDR_FLAG_RP_FIX is set if the first image in a
- * WIM is captured with WIMLIB_ADD_IMAGE_FLAG_RPFIX enabled and currently cannot
- * be changed. */
+ * Without this flag, the default is to do this if WIM_HDR_FLAG_RP_FIX is set in
+ * the WIM header or if this is the first image being added.
+ * WIM_HDR_FLAG_RP_FIX is set if the first image in a WIM is captured with
+ * reparse point fixups enabled and currently cannot be unset. */
 #define WIMLIB_ADD_IMAGE_FLAG_RPFIX                    0x00000100
 
-/* Don't do reparse point fixups.  Without this flag, the default is to do
- * reparse point fixes if WIM_HDR_FLAG_RP_FIX is set in the WIM header. */
+/* Don't do reparse point fixups.  The default behavior is described in the
+ * documentation for ::WIMLIB_ADD_IMAGE_FLAG_RPFIX. */
 #define WIMLIB_ADD_IMAGE_FLAG_NORPFIX                  0x00000200
 
 /******************************
@@ -758,7 +758,7 @@ struct wimlib_capture_config {
 #define WIMLIB_EXTRACT_FLAG_STRICT_ACLS                        0x00000080
 
 /* Extract equivalent to ::WIMLIB_ADD_IMAGE_FLAG_RPFIX; force reparse-point
- * fixups on, so absolute symbolic links are junction points will be fixed to be
+ * fixups on, so absolute symbolic links or junction points will be fixed to be
  * absolute relative to the actual extraction root.  Done by default if
  * WIM_HDR_FLAG_RP_FIX is set in the WIM header. */
 #define WIMLIB_EXTRACT_FLAG_RPFIX                      0x00000100
index dfa240f..ce54c74 100755 (executable)
@@ -39,7 +39,7 @@ do_test() {
                if [ -x /usr/bin/tree -a "$ctype" = "None" ]; then
                        tree in.dir --inodes -F -s --noreport
                fi
-               if ! imagex capture in.dir test.wim --compress=$ctype; then
+               if ! imagex capture in.dir test.wim --compress=$ctype --norpfix; then
                        error "Failed to capture directory tree into a WIM"
                fi
                if ! imagex apply test.wim 1 out.dir; then
index 906a3d6..d7c75e1 100755 (executable)
@@ -36,7 +36,7 @@ init() {
        echo 'testing' > dir2/file
        dd if=/dev/zero of=dir2/zeroes bs=4096 count=5
        mkdir tmp.empty tmp.mnt tmp.apply tmp.orig
-       imagex capture tmp.empty empty.wim
+       imagex capture tmp.empty empty.wim --norpfix
 }
 
 cleanup