Symbolic links (IN PROGRESS)
authorEric Biggers <ebiggers3@gmail.com>
Sat, 18 Aug 2012 19:52:05 +0000 (14:52 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Sat, 18 Aug 2012 19:52:05 +0000 (14:52 -0500)
18 files changed:
Makefile.am
doc/imagex-append.1.in
doc/imagex-capture.1.in
programs/imagex.c
src/dentry.c
src/dentry.h
src/extract.c
src/io.h
src/lookup_table.c
src/lookup_table.h
src/modify.c
src/mount.c
src/resource.c
src/symlink.c [new file with mode: 0644]
src/util.c
src/wimlib.h
src/wimlib_internal.h
src/write.c

index a34bc9a2fac22482555e34523b8e9097caaf6824..b694fa1d373a078c5add15ad59e15cb428ddb153 100644 (file)
@@ -35,6 +35,7 @@ libwim_la_SOURCES =           \
        src/sha1.c              \
        src/sha1.h              \
        src/split.c             \
+       src/symlink.c           \
        src/timestamp.h         \
        src/util.c              \
        src/util.h              \
index 027959f74e9b43a650a788e752f013874a0a9127..b48bfdb975a89b6684cbe1e1b22a6cbffb35028d 100644 (file)
@@ -30,6 +30,10 @@ is included in the new WIM file, even if there was one before.
 .TP
 \fB--flags\fR \fIEDITIONID\fR
 Specify a string to use in the <FLAGS> element of the XML data for the image.
+.TP
+\fB--dereference\fR
+Follow symlinks; archive and dump the files they point to.  (The default is to
+archive the symlinks themselves)
 
 .SH EXAMPLES
 .IP 
index 6e3edd42048c378c4f543a50656a03234c09647b..5e76499d3296eda60aeb59e8d00b68f83dc1fe57 100644 (file)
@@ -37,6 +37,10 @@ Specify a string to use in the <FLAGS> element of the XML data for the image.
 .TP
 \fB--verbose\fR
 Print the names of files and directories as they are captured.
+.TP
+\fB--dereference\fR
+Follow symlinks; archive and dump the files they point to.  (The default is to
+archive the symlinks themselves)
 
 .SH EXAMPLES
 .IP 
index 11becdd2593b354a28dd3cdd8542beaf813eed75..ffd0d9451e0ebaf64493e3eb9ca98db54c9cb971 100644 (file)
@@ -75,14 +75,14 @@ static const char *path_basename(const char *path)
 static const char *usage_strings[] = {
 [APPEND] = 
 "    imagex append DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"] [--boot]\n"
-"                  [--check] [--flags EDITIONID]\n",
+"                  [--check] [--flags EDITIONID] [--dereference]\n",
 [APPLY] = 
 "    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all] DIRECTORY [--check]\n"
 "                 [--hardlink] [--symlink] [--verbose]\n",
 [CAPTURE] = 
 "    imagex capture DIRECTORY WIMFILE [\"IMAGE_NAME\"] [\"DESCRIPTION\"]\n"
 "       l           [--boot] [--check] [--compress[=TYPE]]\n"
-"                   [--flags \"EditionID\"] [--verbose]\n",
+"                   [--flags \"EditionID\"] [--verbose] [--dereference]\n",
 [DELETE] = 
 "    imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
 [DIR] = 
@@ -119,6 +119,7 @@ static const struct option append_options[] = {
        {"boot",   no_argument,       NULL, 'b'},
        {"check",  no_argument,       NULL, 'c'},
        {"flags",    required_argument, NULL, 'f'},
+       {"dereference", no_argument, NULL, 'L'},
        {NULL, 0, NULL, 0},
 };
 static const struct option apply_options[] = {
@@ -136,6 +137,7 @@ static const struct option capture_options[] = {
        {"flags",    required_argument, NULL, 'f'},
        {"verbose",  no_argument,       NULL,'v'},
        {"ntfs",     no_argument,       NULL, 'N'},
+       {"dereference", no_argument, NULL, 'L'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
@@ -312,6 +314,9 @@ static int imagex_append(int argc, const char **argv)
                case 'f':
                        flags_element = optarg;
                        break;
+               case 'L':
+                       add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
+                       break;
                default:
                        usage(APPEND);
                        return -1;
@@ -455,6 +460,9 @@ static int imagex_capture(int argc, const char **argv)
                case 'N':
                        add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_NTFS;
                        break;
+               case 'L':
+                       add_image_flags |= WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE;
+                       break;
                default:
                        usage(CAPTURE);
                        return -1;
index a25680f9f7d5f4ec4d514784f8040da7f3f46d6f..684468227da7ec09019cdc745ec2a1c517e9fe86 100644 (file)
@@ -52,10 +52,14 @@ u64 dentry_total_length(const struct dentry *dentry)
 /* Transfers file attributes from a `stat' buffer to a struct dentry. */
 void stbuf_to_dentry(const struct stat *stbuf, struct dentry *dentry)
 {
-       if (S_ISDIR(stbuf->st_mode))
+       if (S_ISLNK(stbuf->st_mode)) {
+               dentry->attributes = FILE_ATTRIBUTE_REPARSE_POINT;
+               dentry->reparse_tag = WIM_IO_REPARSE_TAG_SYMLINK;
+       } else if (S_ISDIR(stbuf->st_mode)) {
                dentry->attributes = FILE_ATTRIBUTE_DIRECTORY;
-       else
+       } else {
                dentry->attributes = FILE_ATTRIBUTE_NORMAL;
+       }
 }
 
 /* Transfers file attributes from a struct dentry to a `stat' buffer. */
@@ -360,11 +364,10 @@ int print_dentry(struct dentry *dentry, void *lookup_table)
        printf("Subdir offset     = %"PRIu64"\n", dentry->subdir_offset);
        /*printf("Unused1           = 0x%"PRIu64"\n", dentry->unused1);*/
        /*printf("Unused2           = %"PRIu64"\n", dentry->unused2);*/
-       printf("Creation Time     = %"PRIu64"\n", dentry->creation_time);
-       printf("Last Access Time  = %"PRIu64"\n", dentry->last_access_time);
-       printf("Last Write Time   = %"PRIu64"\n", dentry->last_write_time);
        printf("Creation Time     = 0x%"PRIx64"\n", dentry->creation_time);
-       printf("Hash              = "); 
+       printf("Last Access Time  = 0x%"PRIx64"\n", dentry->last_access_time);
+       printf("Last Write Time   = 0x%"PRIx64"\n", dentry->last_write_time);
+       printf("Hash              = 0x"); 
        print_hash(dentry->hash); 
        putchar('\n');
        printf("Reparse Tag       = 0x%"PRIx32"\n", dentry->reparse_tag);
@@ -395,6 +398,9 @@ int print_dentry(struct dentry *dentry, void *lookup_table)
                printf("Name = \"%s\"\n", dentry->ads_entries[i].stream_name_utf8);
                printf("Name Length (UTF-16) = %u\n",
                                dentry->ads_entries[i].stream_name_len);
+               printf("Hash              = 0x"); 
+               print_hash(dentry->ads_entries[i].hash); 
+               putchar('\n');
                lte = lookup_resource(lookup_table, dentry->ads_entries[i].hash);
                if (lte)
                        print_lookup_table_entry(lte, NULL);
@@ -438,6 +444,17 @@ struct dentry *new_dentry(const char *name)
        return dentry;
 }
 
+static void dentry_free_ads_entries(struct dentry *dentry)
+{
+       for (u16 i = 0; i < dentry->num_ads; i++) {
+               FREE(dentry->ads_entries[i].stream_name);
+               FREE(dentry->ads_entries[i].stream_name_utf8);
+       }
+       FREE(dentry->ads_entries);
+       dentry->ads_entries = NULL;
+       dentry->num_ads = 0;
+}
+
 
 void free_dentry(struct dentry *dentry)
 {
@@ -445,6 +462,7 @@ void free_dentry(struct dentry *dentry)
        FREE(dentry->file_name_utf8);
        FREE(dentry->short_name);
        FREE(dentry->full_path_utf8);
+       dentry_free_ads_entries(dentry);
        FREE(dentry);
 }
 
@@ -932,6 +950,7 @@ out_free_file_name:
 static u8 *write_dentry(const struct dentry *dentry, u8 *p)
 {
        u8 *orig_p = p;
+       unsigned padding;
        memset(p, 0, dentry->length);
        p = put_u64(p, dentry->length);
        p = put_u32(p, dentry->attributes);
@@ -952,6 +971,13 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
        p = put_bytes(p, dentry->file_name_len, (u8*)dentry->file_name);
        p = put_u16(p, 0); /* filename padding, 2 bytes. */
        p = put_bytes(p, dentry->short_name_len, (u8*)dentry->short_name);
+
+       wimlib_assert(p - orig_p <= dentry->length);
+       if (p - orig_p < dentry->length)
+               p = put_zeroes(p, dentry->length - (p - orig_p));
+
+       p = put_zeroes(p, (8 - (p - orig_p) % 8) % 8);
+
        for (u16 i = 0; i < dentry->num_ads; i++) {
                p = put_u64(p, ads_entry_length(&dentry->ads_entries[i]));
                p = put_u64(p, 0); /* Unused */
@@ -959,8 +985,9 @@ static u8 *write_dentry(const struct dentry *dentry, u8 *p)
                p = put_u16(p, dentry->ads_entries[i].stream_name_len);
                p = put_bytes(p, dentry->ads_entries[i].stream_name_len,
                                 (u8*)dentry->ads_entries[i].stream_name);
+               p = put_zeroes(p, (8 - (p - orig_p) % 8) % 8);
        }
-       return orig_p + dentry->length;
+       return p;
 }
 
 /* Recursive function that writes a dentry tree rooted at @tree, not including
@@ -1089,3 +1116,17 @@ int read_dentry_tree(const u8 metadata_resource[], u64 metadata_resource_len,
        dentry->children = first_child;
        return ret;
 }
+
+int dentry_set_symlink_buf(struct dentry *dentry, const u8 symlink_buf_hash[])
+{
+       struct ads_entry *ads_entries;
+
+       ads_entries = CALLOC(2, sizeof(struct ads_entry));
+       if (!ads_entries)
+               return WIMLIB_ERR_NOMEM;
+       memcpy(ads_entries[1].hash, symlink_buf_hash, WIM_HASH_SIZE);
+       dentry_free_ads_entries(dentry);
+       dentry->num_ads = 2;
+       dentry->ads_entries = ads_entries;
+       return 0;
+}
index 6c260fc2538f37f305b84c49f2d022156ada4938..2aabf02e9899a674dccfea67cf674645af2254d0 100644 (file)
@@ -5,16 +5,22 @@
 #include "config.h"
 #include <string.h>
 
+struct stat;
+struct lookup_table;
+typedef struct WIMStruct WIMStruct;
+
 /* Size of the struct dentry up to and including the file_name_len. */
 #define WIM_DENTRY_DISK_SIZE    102
 
 #define WIM_ADS_ENTRY_DISK_SIZE 38
 
+#ifndef WIM_HASH_SIZE
+#define WIM_HASH_SIZE 20
+#endif
+
 /* 
  * Reparse tags documented at 
  * http://msdn.microsoft.com/en-us/library/dd541667(v=prot.10).aspx
- *
- * IO_REPARSE_TAG_SYMLINK is the only one we really care about.
  */
 #define WIM_IO_REPARSE_TAG_RESERVED_ZERO       0x00000000
 #define WIM_IO_REPARSE_TAG_RESERVED_ONE                0x00000001
@@ -241,6 +247,9 @@ extern int read_dentry_tree(const u8 metadata_resource[],
 
 extern u8 *write_dentry_tree(const struct dentry *tree, u8 *p);
 
+extern int dentry_set_symlink_buf(struct dentry *dentry,
+                                 const u8 symlink_buf_hash[]);
+
 /* Inline utility functions for WIMDentries */
 
 /*
@@ -275,15 +284,18 @@ static inline bool dentry_is_directory(const struct dentry *dentry)
        return (dentry->attributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
 }
 
+/* For our purposes, we consider "real" symlinks and "junction points" to both
+ * be symlinks. */
 static inline bool dentry_is_symlink(const struct dentry *dentry)
 {
        return (dentry->attributes & FILE_ATTRIBUTE_REPARSE_POINT)
-               && (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK);
+               && ((dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) ||
+                    dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
 }
 
 static inline bool dentry_is_regular_file(const struct dentry *dentry)
 {
-       return !dentry_is_directory(dentry);
+       return !dentry_is_directory(dentry) && !dentry_is_symlink(dentry);
 }
 
 static inline bool dentry_is_empty_directory(const struct dentry *dentry)
index a04e3f2ea81dc96c8c0704677255ed9c73b25e07..49433f0813401d1d2a00a3e8a28925eb26971798 100644 (file)
@@ -197,6 +197,25 @@ done:
        return ret;
 }
 
+static int extract_symlink(const struct dentry *dentry, const char *output_path,
+                          const WIMStruct *w)
+{
+       char target[4096];
+       ssize_t ret = dentry_readlink(dentry, target, sizeof(target), w);
+       if (ret <= 0) {
+               ERROR("Could not read the symbolic link from dentry `%s'",
+                     dentry->full_path_utf8);
+               return WIMLIB_ERR_INVALID_DENTRY;
+       }
+       ret = symlink(target, output_path);
+       if (ret != 0) {
+               ERROR_WITH_ERRNO("Failed to symlink `%s' to `%s'",
+                                output_path, target);
+               return WIMLIB_ERR_LINK;
+       }
+       return 0;
+}
+
 /* 
  * Extracts a directory from the WIM archive. 
  *
@@ -239,13 +258,14 @@ struct extract_args {
  * @dentry:    The dentry to extract.
  * @arg:       A pointer to the WIMStruct for the WIM file.
  */
-static int extract_regular_file_or_directory(struct dentry *dentry, void *arg)
+static int extract_dentry(struct dentry *dentry, void *arg)
 {
        struct extract_args *args = arg;
        WIMStruct *w = args->w;
        int extract_flags = args->extract_flags;
        size_t len = strlen(w->output_dir);
        char output_path[len + dentry->full_path_utf8_len + 1];
+       int ret = 0;
 
        if (extract_flags & WIMLIB_EXTRACT_FLAG_VERBOSE)
                puts(dentry->full_path_utf8);
@@ -254,13 +274,13 @@ static int extract_regular_file_or_directory(struct dentry *dentry, void *arg)
        memcpy(output_path + len, dentry->full_path_utf8, dentry->full_path_utf8_len);
        output_path[len + dentry->full_path_utf8_len] = '\0';
 
-       if (dentry_is_regular_file(dentry)) {
-               return extract_regular_file(w, dentry, output_path, extract_flags);
+       if (dentry_is_symlink(dentry)) {
+               ret = extract_symlink(dentry, output_path, w);
+       } else if (dentry_is_directory(dentry)) {
+               if (!dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
+                       ret = extract_directory(dentry, output_path);
        } else {
-               if (dentry_is_root(dentry)) /* Root doesn't need to be extracted. */
-                       return 0;
-               else
-                       return extract_directory(dentry, output_path);
+               ret = extract_regular_file(w, dentry, output_path, extract_flags);
        }
 }
 
@@ -282,8 +302,7 @@ static int extract_single_image(WIMStruct *w, int image, int extract_flags)
        #endif
        };
 
-       return for_dentry_in_tree(wim_root_dentry(w),
-                                 extract_regular_file_or_directory, &args);
+       return for_dentry_in_tree(wim_root_dentry(w), extract_dentry, &args);
 }
 
 
index ac5ea506a1f43d7232bd64d52d4780a8f7ed51fd..5ab46d22c5f8f42262976bb6b68658967568d2f9 100644 (file)
--- a/src/io.h
+++ b/src/io.h
@@ -108,6 +108,12 @@ static inline const u8 *get_bytes(const u8 *p, size_t num_bytes, void *res)
        return p + num_bytes;
 }
 
+static inline u8 *put_zeroes(u8 *p, size_t num_bytes)
+{
+       memset(p, 0, num_bytes);
+       return p + num_bytes;
+}
+
 static inline u8 *put_bytes(u8 *p, size_t num_bytes, const u8 *input)
 {
        memcpy(p, input, num_bytes);
index 9358093429216993661c2bd7cc40a6e20eb70ea9..821954ebe0122243014046b774cf85d9a051f708 100644 (file)
@@ -348,3 +348,9 @@ WIMLIBAPI void wimlib_print_lookup_table(WIMStruct *w)
        for_lookup_table_entry(w->lookup_table, 
                               print_lookup_table_entry, NULL);
 }
+
+/*struct lookup_table_entry *lookup_resource(const struct lookup_table *table,*/
+                                       /*const char *path, int lookup_flags)*/
+/*{*/
+       /*return lookup_resource(w->lookup_table, dentry->hash);*/
+/*}*/
index 6deb86fc531f7a74842b6adf6c1ca4e3a03c6798..a269e512ebef8465dc8d151eb02432278e046739 100644 (file)
@@ -7,6 +7,8 @@
 /* Size of each lookup table entry in the WIM file. */
 #define WIM_LOOKUP_TABLE_ENTRY_DISK_SIZE 50
 
+#define LOOKUP_FLAG_ADS_OK
+
 
 /* A lookup table that is used to translate the hash codes of dentries into the
  * offsets and sizes of uncompressed or compressed file resources.  It is
@@ -35,6 +37,11 @@ struct lookup_table_entry {
        /* Number of times this lookup table entry is referenced by dentries. */
        u32 refcnt;
 
+       /* If %true, this lookup table entry corresponds to a symbolic link
+        * reparse buffer.  @symlink_reparse_data_buf will give the target of
+        * the symbolic link. */
+       bool is_symlink;
+
        union {
                /* SHA1 hash of the file resource pointed to by this lookup
                 * table entry */
@@ -56,6 +63,7 @@ struct lookup_table_entry {
        union {
                char *file_on_disk;
                char *staging_file_name;
+               void *symlink_buf;
                struct lookup_table_entry *next_lte_in_swm;
        };
 
@@ -133,6 +141,7 @@ wim_lookup_resource(const WIMStruct *w, const struct dentry *dentry)
        return lookup_resource(w->lookup_table, dentry->hash);
 }
 
+
 extern int zero_out_refcnts(struct lookup_table_entry *entry, void *ignore);
 
 extern int print_lookup_table_entry(struct lookup_table_entry *entry, void *ignore);
index 5694ec202a53342ac7d8b57a10e9234f7dc0431d..6b1239da3c2b738f17e62b0456bca69db7ba0b97 100644 (file)
 #include <dirent.h>
 #include <string.h>
 #include <errno.h>
+#include <unistd.h>
+
+/** Private flag: Used to mark that we currently adding the root directory of
+ * the WIM. */
+#define WIMLIB_ADD_IMAGE_FLAG_ROOT 0x80000000
 
 static void destroy_image_metadata(struct image_metadata *imd,
                                   struct lookup_table *lt)
@@ -68,31 +73,52 @@ static void destroy_image_metadata(struct image_metadata *imd,
  *             to the WIM may still occur later when trying to actually read 
  *             the regular files in the tree into the WIM as file resources.
  */
-static int build_dentry_tree(struct dentry *root, const char *source_path, 
-                            struct stat *root_stat,
-                            struct lookup_table* lookup_table)
+static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
+                            struct lookup_table* lookup_table,
+                            int add_flags)
 {
-       int ret = 0;
+       DEBUG("`%s'", root_disk_path);
+       struct stat root_stbuf;
+       int ret;
+       int (*stat_fn)(const char *restrict, struct stat *restrict);
+
+       if (add_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)
+               stat_fn = stat;
+       else
+               stat_fn = lstat;
+
+
+       ret = (*stat_fn)(root_disk_path, &root_stbuf);
+       if (ret != 0) {
+               ERROR_WITH_ERRNO("Failed to stat `%s'", root_disk_path);
+               return WIMLIB_ERR_STAT;
+       }
+
+       if ((add_flags & WIMLIB_ADD_IMAGE_FLAG_ROOT) && 
+             !S_ISDIR(root_stbuf.st_mode)) {
+               ERROR("`%s' is not a directory", root_disk_path);
+               return WIMLIB_ERR_NOTDIR;
+       }
+       stbuf_to_dentry(&root_stbuf, root);
+       add_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
 
-       stbuf_to_dentry(root_stat, root);
        if (dentry_is_directory(root)) {
                /* Open the directory on disk */
                DIR *dir;
                struct dirent *p;
-               struct stat child_stat;
                struct dentry *child;
 
-               dir = opendir(source_path);
+               dir = opendir(root_disk_path);
                if (!dir) {
                        ERROR_WITH_ERRNO("Failed to open the directory `%s'",
-                                        source_path);
+                                        root_disk_path);
                        return WIMLIB_ERR_OPEN;
                }
 
                /* Buffer for names of files in directory. */
-               size_t len = strlen(source_path);
+               size_t len = strlen(root_disk_path);
                char name[len + 1 + FILENAME_MAX + 1];
-               memcpy(name, source_path, len);
+               memcpy(name, root_disk_path, len);
                name[len] = '/';
 
                /* Create a dentry for each entry in the directory on disk, and recurse
@@ -102,23 +128,69 @@ static int build_dentry_tree(struct dentry *root, const char *source_path,
                              || (p->d_name[1] == '.' && p->d_name[2] == '\0')))
                                        continue;
                        strcpy(name + len + 1, p->d_name);
-                       if (stat(name, &child_stat) != 0) {
-                               ERROR_WITH_ERRNO("Cannot stat `%s'", name);
-                               ret = WIMLIB_ERR_STAT;
-                               break;
-                       }
                        child = new_dentry(p->d_name);
                        if (!child) {
                                ERROR("No memory to allocate new dentry");
                                return WIMLIB_ERR_NOMEM;
                        }
-                       ret = build_dentry_tree(child, name, &child_stat, 
-                                               lookup_table);
+                       ret = build_dentry_tree(child, name, lookup_table,
+                                               add_flags);
                        link_dentry(child, root);
                        if (ret != 0)
                                break;
                }
                closedir(dir);
+       } else if (dentry_is_symlink(root)) {
+               /* Archiving a symbolic link */
+               size_t symlink_buf_len;
+               char deref_name_buf[4096];
+               ssize_t ret = readlink(root_disk_path, deref_name_buf,
+                                      sizeof(deref_name_buf) - 1);
+               if (ret == -1) {
+                       ERROR_WITH_ERRNO("Failed to read target of "
+                                        "symbolic link `%s'", root_disk_path);
+                       return WIMLIB_ERR_STAT;
+               }
+               deref_name_buf[ret] = '\0';
+               DEBUG("Read symlink `%s'", deref_name_buf);
+               void *symlink_buf = make_symlink_reparse_data_buf(deref_name_buf,
+                                                                 &symlink_buf_len);
+               if (!symlink_buf)
+                       return WIMLIB_ERR_NOMEM;
+               DEBUG("Made symlink reparse data buf (len = %zu, name len = %zu)",
+                               symlink_buf_len, ret);
+               
+               u8 symlink_buf_hash[WIM_HASH_SIZE];
+               sha1_buffer(symlink_buf, symlink_buf_len, symlink_buf_hash);
+
+               ret = dentry_set_symlink_buf(root, symlink_buf_hash);
+
+               if (ret != 0) {
+                       FREE(symlink_buf);
+                       return ret;
+               }
+               DEBUG("Created symlink buf");
+
+               struct lookup_table_entry *lte;
+               struct lookup_table_entry *existing_lte;
+
+               existing_lte = lookup_resource(lookup_table, symlink_buf_hash);
+               if (existing_lte) {
+                       existing_lte->refcnt++;
+               } else {
+                       DEBUG("Creating new lookup table entry");
+                       lte = new_lookup_table_entry();
+                       if (!lte) {
+                               FREE(symlink_buf);
+                               return WIMLIB_ERR_NOMEM;
+                       }
+                       lte->symlink_buf = symlink_buf;
+                       lte->resource_entry.original_size = symlink_buf_len;
+                       lte->resource_entry.size = symlink_buf_len;
+                       lte->is_symlink = true;
+                       memcpy(lte->hash, symlink_buf_hash, WIM_HASH_SIZE);
+                       lookup_table_insert(lookup_table, lte);
+               }
        } else {
                struct lookup_table_entry *lte;
 
@@ -126,7 +198,7 @@ static int build_dentry_tree(struct dentry *root, const char *source_path,
                 * in the lookup table already; if it is, we increment its
                 * refcnt; otherwise, we create a new lookup table entry and
                 * insert it. */
-               ret = sha1sum(source_path, root->hash);
+               ret = sha1sum(root_disk_path, root->hash);
                if (ret != 0)
                        return ret;
 
@@ -134,7 +206,7 @@ static int build_dentry_tree(struct dentry *root, const char *source_path,
                if (lte) {
                        lte->refcnt++;
                } else {
-                       char *file_on_disk = STRDUP(source_path);
+                       char *file_on_disk = STRDUP(root_disk_path);
                        if (!file_on_disk) {
                                ERROR("Failed to allocate memory for file path");
                                return WIMLIB_ERR_NOMEM;
@@ -145,11 +217,8 @@ static int build_dentry_tree(struct dentry *root, const char *source_path,
                                return WIMLIB_ERR_NOMEM;
                        }
                        lte->file_on_disk = file_on_disk;
-                       lte->resource_entry.flags = 0;
-                       lte->refcnt       = 1;
-                       lte->part_number  = 1;
-                       lte->resource_entry.original_size = root_stat->st_size;
-                       lte->resource_entry.size = root_stat->st_size;
+                       lte->resource_entry.original_size = root_stbuf.st_size;
+                       lte->resource_entry.size = root_stbuf.st_size;
                        memcpy(lte->hash, root->hash, WIM_HASH_SIZE);
                        lookup_table_insert(lookup_table, lte);
                }
@@ -480,19 +549,9 @@ WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir,
 
        root_dentry->attributes |= FILE_ATTRIBUTE_DIRECTORY;
 
-       /* Construct the dentry tree from the outside filesystem. */
-       if (stat(dir, &root_stat) != 0) {
-               ERROR_WITH_ERRNO("Failed to stat `%s'", dir);
-               ret = WIMLIB_ERR_STAT;
-               goto out_free_dentry_tree;
-       }
-       if (!S_ISDIR(root_stat.st_mode)) {
-               ERROR("`%s' is not a directory", dir);
-               ret = WIMLIB_ERR_NOTDIR;
-               goto out_free_dentry_tree;
-       }
        DEBUG("Building dentry tree.");
-       ret = build_dentry_tree(root_dentry, dir, &root_stat, w->lookup_table);
+       ret = build_dentry_tree(root_dentry, dir, w->lookup_table,
+                               flags | WIMLIB_ADD_IMAGE_FLAG_ROOT);
 
        if (ret != 0) {
                ERROR("Failed to build dentry tree for `%s'", dir);
index a4359947b83974aa1b5cea116d9c7f1d297fe23b..640b27c686176559d262a9235acb804637c6f4b7 100644 (file)
@@ -618,7 +618,7 @@ static int wimfs_open(const char *path, struct fuse_file_info *fi)
        dentry = get_dentry(w, path);
 
        if (!dentry)
-               return -EEXIST;
+               return -ENOENT;
        if (dentry_is_directory(dentry))
                return -EISDIR;
        lte = wim_lookup_resource(w, dentry);
@@ -780,87 +780,20 @@ static int wimfs_readdir(const char *path, void *buf, fuse_fill_dir_t filler,
        return 0;
 }
 
-/*
- * Find the symlink target of a symbolic link dentry in the WIM.
- *
- * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx
- * Except the first 8 bytes aren't included in the resource (presumably because
- * we already know the reparse tag from the dentry, and we already know the
- * reparse tag len from the lookup table entry resource length).
- */
-static int get_symlink_name(const u8 *resource, size_t resource_len,
-                           char *buf, size_t buf_len)
-{
-       const u8 *p = resource;
-       u16 substitute_name_offset;
-       u16 substitute_name_len;
-       u16 print_name_offset;
-       u16 print_name_len;
-       u32 flags;
-       char *link_target;
-       size_t link_target_len;
-       int ret;
-       if (resource_len < 12)
-               return -EIO;
-       p = get_u16(p, &substitute_name_offset);
-       p = get_u16(p, &substitute_name_len);
-       p = get_u16(p, &print_name_offset);
-       p = get_u16(p, &print_name_len);
-       p = get_u32(p, &flags);
-       if (12 + substitute_name_offset + substitute_name_len > resource_len)
-               return -EIO;
-       link_target = utf16_to_utf8(p + substitute_name_offset,
-                                   substitute_name_len,
-                                   &link_target_len);
-       if (!link_target)
-               return -EIO;
-       if (link_target_len + 1 > buf_len) {
-               ret = -ENAMETOOLONG;
-               goto out;
-       }
-       memcpy(buf, link_target, link_target_len + 1);
-       ret = 0;
-out:
-       free(link_target);
-       return ret;
-}
 
 static int wimfs_readlink(const char *path, char *buf, size_t buf_len)
 {
        struct dentry *dentry = get_dentry(w, path);
-       struct ads_entry *ads;
-       struct lookup_table_entry *entry;
-       struct resource_entry *res_entry;
+       int ret;
        if (!dentry)
                return -ENOENT;
        if (!dentry_is_symlink(dentry))
                return -EINVAL;
 
-       /* 
-        * This is of course not actually documented, but what I think is going
-        * on here is that the symlink dentries have 2 alternate data streams;
-        * one is the default data stream, which is not used and is empty, and
-        * one is the symlink buffer data stream, which is confusingly also
-        * unnamed, but isn't empty as it contains the symlink target within the
-        * resource.
-        */
-       if (dentry->num_ads != 2)
-               return -EIO;
-       if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[0].hash)))
-               goto do_readlink;
-       if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[1].hash)))
-               goto do_readlink;
-       return -EIO;
-do_readlink:
-       res_entry = &entry->resource_entry;
-       char res_buf[res_entry->original_size];
-       if (read_full_resource(w->fp, res_entry->size, 
-                              res_entry->original_size,
-                              res_entry->offset,
-                              wim_resource_compression_type(w, res_entry),
-                              res_buf) != 0)
-               return -EIO;
-       return get_symlink_name(res_buf, res_entry->original_size, buf, buf_len);
+       ret = dentry_readlink(dentry, buf, buf_len, w);
+       if (ret > 0)
+               ret = 0;
+       return ret;
 }
 
 /* Close a file. */
index 5e6707d97dee941d1514bf7a3accad3bac4ece7d..03754e08d41877e06f82c8213ebdb45800678456 100644 (file)
@@ -1090,37 +1090,12 @@ int write_metadata_resource(WIMStruct *w)
        return 0;
 }
 
-/* 
- * Writes a file resource to the output file. 
- *
- * @dentry:  The dentry for the file resource.
- * @wim_p:   A pointer to the WIMStruct.  The fields of interest to this
- *          function are the input and output file streams and the lookup
- *          table. 
- *
- * @return zero on success, nonzero on failure. 
- */
-int write_file_resource(struct dentry *dentry, void *wim_p)
+static int write_file_resource(WIMStruct *w, const u8 hash[])
 {
-       WIMStruct *w;
-       FILE *out_fp;
-       FILE *in_fp;
-       struct lookup_table_entry *lte;
-       int in_wim_ctype;
-       int out_wim_ctype;
-       struct resource_entry *output_res_entry;
-       u64 len;
-       int ret;
-
-       w = wim_p;
-       out_fp = w->out_fp;
-
-       /* Directories don't need file resources. */
-       if (dentry_is_directory(dentry))
-               return 0;
-
        /* Get the lookup entry for the file resource. */
-       lte = wim_lookup_resource(w, dentry);
+       struct lookup_table_entry *lte;
+       
+       lte = lookup_resource(w->lookup_table, hash);
        if (!lte)
                return 0;
 
@@ -1129,16 +1104,43 @@ int write_file_resource(struct dentry *dentry, void *wim_p)
        if (++lte->out_refcnt != 1)
                return 0;
 
-       out_wim_ctype = wimlib_get_compression_type(w);
-       output_res_entry = &lte->output_resource_entry;
-
        /* do not write empty resources */
        if (lte->resource_entry.original_size == 0)
                return 0;
 
+       int out_wim_ctype = wimlib_get_compression_type(w);
+       struct resource_entry *output_res_entry = &lte->output_resource_entry;
+       u64 len;
+       FILE *in_fp;
+       FILE *out_fp = w->out_fp;
+       int ret = 0;
+
        /* Figure out if we can read the resource from the WIM file, or
-        * if we have to read it from the filesystem outside. */
-       if (lte->file_on_disk) {
+        * if we have to read it from the filesystem outside, or if it's a
+        * symbolic link with the data already in memory pointed to by a field
+        * of the lookup table entry. */
+       if (lte->is_symlink) {
+               off_t offset = ftello(w->out_fp);
+               u64 new_size;
+
+               if (offset == -1) {
+                       ERROR_WITH_ERRNO("Could not get position in output "
+                                        "file");
+                       return WIMLIB_ERR_WRITE;
+               }
+
+               wimlib_assert(lte->symlink_buf);
+
+               len = lte->resource_entry.original_size;
+
+               recompress_resource(in_fp, lte->symlink_buf, len, len, 0,
+                                   0, out_fp, out_wim_ctype, &new_size);
+               output_res_entry->size = new_size;
+               output_res_entry->original_size = len;
+               output_res_entry->offset = offset;
+               output_res_entry->flags = (out_wim_ctype == WIM_COMPRESSION_TYPE_NONE)
+                                               ? 0 : WIM_RESHDR_FLAG_COMPRESSED;
+       } else if (lte->file_on_disk) {
 
                /* Read from disk (uncompressed) */
 
@@ -1156,6 +1158,7 @@ int write_file_resource(struct dentry *dentry, void *wim_p)
                                             out_wim_ctype, output_res_entry);
                fclose(in_fp);
        } else {
+               int in_wim_ctype;
 
                /* Read from input WIM (possibly compressed) */
 
@@ -1185,3 +1188,34 @@ int write_file_resource(struct dentry *dentry, void *wim_p)
        }
        return ret;
 }
+
+/* 
+ * Writes a dentry's resources to the output file. 
+ *
+ * @dentry:  The dentry for the file resource.
+ * @wim_p:   A pointer to the WIMStruct.  The fields of interest to this
+ *          function are the input and output file streams and the lookup
+ *          table, and the alternate data streams.
+ *
+ * @return zero on success, nonzero on failure. 
+ */
+int write_dentry_resources(struct dentry *dentry, void *wim_p)
+{
+       WIMStruct *w = wim_p;
+       int ret;
+
+       /* Directories don't need file resources. */
+       if (dentry_is_directory(dentry))
+               return 0;
+
+       ret = write_file_resource(w, dentry->hash);
+       if (ret != 0)
+               return ret;
+       for (u16 i = 0; i < dentry->num_ads; i++) {
+               ret = write_file_resource(w, dentry->ads_entries[i].hash);
+               if (ret != 0)
+                       return ret;
+       }
+       return 0;
+}
+
diff --git a/src/symlink.c b/src/symlink.c
new file mode 100644 (file)
index 0000000..42a771e
--- /dev/null
@@ -0,0 +1,162 @@
+#include "dentry.h"
+#include "io.h"
+#include "lookup_table.h"
+
+/*
+ * Find the symlink target of a symbolic link or junction point in the WIM.
+ *
+ * See http://msdn.microsoft.com/en-us/library/cc232006(v=prot.10).aspx
+ * Except the first 8 bytes aren't included in the resource (presumably because
+ * we already know the reparse tag from the dentry, and we already know the
+ * reparse tag len from the lookup table entry resource length).
+ */
+static ssize_t get_symlink_name(const u8 *resource, size_t resource_len,
+                               char *buf, size_t buf_len,
+                               bool is_junction_point)
+{
+       const u8 *p = resource;
+       u16 substitute_name_offset;
+       u16 substitute_name_len;
+       u16 print_name_offset;
+       u16 print_name_len;
+       char *link_target;
+       size_t link_target_len;
+       ssize_t ret;
+       unsigned header_size;
+       char *translated_target;
+       bool is_absolute;
+
+       if (resource_len < 12)
+               return -EIO;
+       p = get_u16(p, &substitute_name_offset);
+       p = get_u16(p, &substitute_name_len);
+       p = get_u16(p, &print_name_offset);
+       p = get_u16(p, &print_name_len);
+       if (is_junction_point) {
+               header_size = 8;
+       } else {
+               u32 flags;
+               p = get_u32(p, &flags);
+               is_absolute = (flags & 1) ? false : true;
+               header_size = 12;
+       }
+       if (header_size + substitute_name_offset + substitute_name_len > resource_len)
+               return -EIO;
+       link_target = utf16_to_utf8(p + substitute_name_offset,
+                                   substitute_name_len,
+                                   &link_target_len);
+
+       if (!link_target)
+               return -EIO;
+
+       if (link_target_len + 1 > buf_len) {
+               ret = -ENAMETOOLONG;
+               goto out;
+       }
+
+       translated_target = link_target;
+       if (is_junction_point || is_absolute) {
+               if (link_target_len < 7
+                     || memcmp(translated_target, "\\??\\", 4) != 0
+                     || translated_target[4] == '\0'
+                     || translated_target[5] != ':'
+                     || translated_target[6] != '\\') {
+                       ret = -EIO;
+                       goto out;
+               }
+               translated_target += 4;
+               link_target_len -= 4;
+       }
+       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;
+out:
+       FREE(link_target);
+       return ret;
+}
+
+void *make_symlink_reparse_data_buf(const char *symlink_target, size_t *len_ret)
+{
+       size_t utf8_len = strlen(symlink_target);
+       size_t utf16_len;
+       char *name_utf16 = utf8_to_utf16(symlink_target, utf8_len, &utf16_len);
+       if (!name_utf16)
+               return NULL;
+       /*DEBUG("utf16_len = %zu", utf16_len);*/
+       for (size_t i = 0; i < utf16_len / 2; i++)
+               if (((u16*)name_utf16)[i] == to_le16('/'))
+                       ((u16*)name_utf16)[i] = to_le16('\\');
+       size_t len = 12 + utf16_len * 2;
+       void *buf = MALLOC(len);
+       if (!buf)
+               goto out;
+
+       u8 *p = buf;
+       p = put_u16(p, 0); /* Substitute name offset */
+       p = put_u16(p, utf16_len); /* Substitute name length */
+       p = put_u16(p, utf16_len); /* Print name offset */
+       p = put_u16(p, utf16_len); /* Print name length */
+       p = put_u32(p, (symlink_target[0] == '/') ?  0 : 1);
+       p = put_bytes(p, utf16_len, name_utf16);
+       p = put_bytes(p, utf16_len, name_utf16);
+       /*DEBUG("utf16_len = %zu, len = %zu", utf16_len, len);*/
+       *len_ret = len;
+out:
+       FREE(name_utf16);
+       return buf;
+}
+
+/* Get the symlink target from a dentry that's already checked to be either a
+ * "real" symlink or a junction point. */
+ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
+                       const WIMStruct *w)
+{
+       struct ads_entry *ads;
+       struct lookup_table_entry *entry;
+       struct resource_entry *res_entry;
+       bool is_junction_point;
+
+       wimlib_assert(dentry_is_symlink(dentry));
+
+       if (dentry->reparse_tag == WIM_IO_REPARSE_TAG_SYMLINK) {
+               is_junction_point = false;
+               /* 
+                * This is of course not actually documented, but what I think is going
+                * on here is that the symlink dentries have 2 alternate data streams;
+                * one is the default data stream, which is not used and is empty, and
+                * one is the symlink buffer data stream, which is confusingly also
+                * unnamed, but isn't empty as it contains the symlink target within the
+                * resource.
+                */
+               if (dentry->num_ads != 2)
+                       return -EIO;
+               if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[0].hash)))
+                       goto do_readlink;
+               if ((entry = lookup_resource(w->lookup_table, dentry->ads_entries[1].hash)))
+                       goto do_readlink;
+       } else {
+               wimlib_assert(dentry->reparse_tag == WIM_IO_REPARSE_TAG_MOUNT_POINT);
+
+               is_junction_point = true;
+
+               if ((entry = lookup_resource(w->lookup_table, dentry->hash)))
+                       goto do_readlink;
+       }
+       return -EIO;
+do_readlink:
+       res_entry = &entry->resource_entry;
+       if (res_entry->original_size > 10000)
+               return -EIO;
+       char res_buf[res_entry->original_size];
+       if (read_full_resource(w->fp, res_entry->size, 
+                              res_entry->original_size,
+                              res_entry->offset,
+                              wim_resource_compression_type(w, res_entry),
+                              res_buf) != 0)
+               return -EIO;
+       return get_symlink_name(res_buf, res_entry->original_size, buf,
+                               buf_len, is_junction_point);
+}
index 47b76a828bc534ac8cb91c8d3d825bdfe07bebe3..725b12725c4fb91c2279244397be38fe75b070bc 100644 (file)
@@ -295,8 +295,8 @@ static iconv_t cd_utf8_to_utf16 = (iconv_t)(-1);
 
 /* Converts a string in the UTF-8 encoding to a newly allocated string in the
  * UTF-16 encoding.  */
-char *utf8_to_utf16(const char *utf8_str, size_t utf8_len, 
-                                               size_t *utf16_len_ret)
+char *utf8_to_utf16(const char *utf8_str, size_t utf8_len,
+                   size_t *utf16_len_ret)
 {
        if (cd_utf8_to_utf16 == (iconv_t)(-1)) {
                cd_utf8_to_utf16 = iconv_open("UTF-16LE", "UTF-8");
index dde93c61eba722a631c3556382e3deca8e15c30e..81218635ae1a57076ed9e262b06f6068b8fc7421 100644 (file)
@@ -276,6 +276,9 @@ enum wim_compression_type {
  * mounted with NTFS-3g, and wimlib was compiled with support for NTFS-3g  */
 #define WIMLIB_ADD_IMAGE_FLAG_NTFS             0x00000004
 
+/** Follow symlinks; archive and dump the files they point to. */
+#define WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE      0x00000008
+
 /** See documentation for wimlib_export_image(). */
 #define WIMLIB_EXPORT_FLAG_BOOT                        0x00000001
 
index 28edf93ff510f28ae52c0fd7f870b69174d1b0f2..b59b05493d33ab052828331f8ce08bcb5e82259c 100644 (file)
 
 struct stat;
 
+#ifndef WIM_HASH_SIZE
 #define WIM_HASH_SIZE  20
+#endif
+
 #define WIM_MAGIC_LEN  8
 #define WIM_GID_LEN    16
 #define WIM_UNUSED_LEN 60
@@ -44,8 +47,8 @@ struct stat;
  * of this size. */
 #define WIM_CHUNK_SIZE 32768
 
-/* Version of the WIM file.  I don't know if there has ever been a different
- * version or not. */
+/* Version of the WIM file.  There is an older version, but we don't support it
+ * yet.  The differences between the versions are undocumented. */
 #define WIM_VERSION 0x10d00
 
 enum wim_integrity_status {
@@ -367,7 +370,7 @@ static inline int read_full_resource(FILE *fp, u64 resource_size,
                                resource_original_size, 0, contents_ret);
 }
 
-extern int write_file_resource(struct dentry *dentry, void *wim_p);
+extern int write_dentry_resources(struct dentry *dentry, void *wim_p);
 extern int copy_resource(struct lookup_table_entry *lte, void *w);
 extern int copy_between_files(FILE *in, off_t in_offset, FILE *out, size_t len);
 extern int write_resource_from_memory(const u8 resource[], int out_ctype,
@@ -375,6 +378,7 @@ extern int write_resource_from_memory(const u8 resource[], int out_ctype,
                                      u64 *resource_size_ret);
 extern int write_metadata_resource(WIMStruct *w);
 
+
 /* security.c */
 int read_security_data(const u8 metadata_resource[], 
                u64 metadata_resource_len, struct wim_security_data **sd_p);
@@ -383,6 +387,12 @@ void print_security_data(const struct wim_security_data *sd);
 u8 *write_security_data(const struct wim_security_data *sd, u8 *p);
 void free_security_data(struct wim_security_data *sd);
 
+/* symlink.c */
+ssize_t dentry_readlink(const struct dentry *dentry, char *buf, size_t buf_len,
+                       const WIMStruct *w);
+extern void *make_symlink_reparse_data_buf(const char *symlink_target,
+                                          size_t *len_ret);
+
 /* wim.c */
 extern WIMStruct *new_wim_struct();
 extern int wimlib_select_image(WIMStruct *w, int image);
index 3866bed6934c80c78a902a7dffddbaa649b2170b..b4aa2655207d819d30799a879d4de1a93cb46c71 100644 (file)
@@ -247,7 +247,7 @@ static int write_file_resources(WIMStruct *w)
 {
 
        DEBUG("Writing file resources for image %u.", w->current_image);
-       return for_dentry_in_tree(wim_root_dentry(w), write_file_resource, w);
+       return for_dentry_in_tree(wim_root_dentry(w), write_dentry_resources, w);
 }
 
 /* Write the lookup table, xml data, and integrity table, then overwrite the WIM