Image capture configuration
authorEric Biggers <ebiggers3@gmail.com>
Mon, 27 Aug 2012 03:32:09 +0000 (22:32 -0500)
committerEric Biggers <ebiggers3@gmail.com>
Mon, 27 Aug 2012 03:32:09 +0000 (22:32 -0500)
Support a capture configuration file like Microsoft's imagex.exe does.  It lets
you specify patterns of files to exclude from capture, exclude from compression,
or align within the WIM file.  Currently, only the exclusion list is
implemented.  --config options have been added to `imagex capture' and `imagex
append'.

programs/imagex.c
src/modify.c
src/ntfs-capture.c
src/util.c
src/wimlib.h
src/wimlib_internal.h

index 134ff29..8afccd1 100644 (file)
@@ -82,7 +82,7 @@ static const char *usage_strings[] = {
 [APPEND] = 
 "    imagex append (DIRECTORY | NTFS_VOLUME) WIMFILE [\"IMAGE_NAME\"]\n"
 "                  [\"DESCRIPTION\"] [--boot] [--check] [--flags EDITIONID]\n"
-"                  [--dereference]\n",
+"                  [--dereference] [--config=FILE]\n",
 [APPLY] = 
 "    imagex apply WIMFILE [IMAGE_NUM | IMAGE_NAME | all]\n"
 "                 (DIRECTORY | NTFS_VOLUME) [--check] [--hardlink]\n"
@@ -90,7 +90,8 @@ static const char *usage_strings[] = {
 [CAPTURE] = 
 "    imagex capture (DIRECTORY | NTFS_VOLUME) WIMFILE [\"IMAGE_NAME\"]\n"
 "                   [\"DESCRIPTION\"] [--boot] [--check] [--compress[=TYPE]]\n"
-"                   [--flags \"EditionID\"] [--verbose] [--dereference]\n",
+"                   [--flags \"EditionID\"] [--verbose] [--dereference]\n"
+"                   [--config=FILE]\n",
 [DELETE] = 
 "    imagex delete WIMFILE (IMAGE_NUM | IMAGE_NAME | all) [--check]\n",
 [DIR] = 
@@ -128,6 +129,7 @@ static const struct option append_options[] = {
        {"check",  no_argument,       NULL, 'c'},
        {"flags",    required_argument, NULL, 'f'},
        {"dereference", no_argument, NULL, 'L'},
+       {"config", required_argument, NULL, 'C'},
        {NULL, 0, NULL, 0},
 };
 static const struct option apply_options[] = {
@@ -144,6 +146,7 @@ static const struct option capture_options[] = {
        {"flags",    required_argument, NULL, 'f'},
        {"verbose",  no_argument,       NULL,'v'},
        {"dereference", no_argument, NULL, 'L'},
+       {"config", required_argument, NULL, 'C'},
        {NULL, 0, NULL, 0},
 };
 static const struct option delete_options[] = {
@@ -294,6 +297,45 @@ static int get_compression_type(const char *optarg)
        }
 }
 
+static const char *file_get_contents(const char *filename, size_t *len_ret)
+{
+       struct stat stbuf;
+       char *buf;
+       size_t len;
+       FILE *fp;
+
+       if (stat(filename, &stbuf) != 0) {
+               imagex_error_with_errno("Failed to stat the file `%s'", filename);
+               return NULL;
+       }
+       len = stbuf.st_size;
+
+       fp = fopen(filename, "rb");
+       if (!fp) {
+               imagex_error_with_errno("Failed to open the file `%s'", filename);
+               return NULL;
+       }
+
+       buf = malloc(len);
+       if (!buf) {
+               imagex_error("Failed to allocate buffer of %zu bytes to hold "
+                            "contents of file `%s'", len, filename);
+               goto out_fclose;
+       }
+       if (fread(buf, 1, len, fp) != len) {
+               imagex_error_with_errno("Failed to read %lu bytes from the "
+                                       "file `%s'", len, filename);
+               goto out_free_buf;
+       }
+       *len_ret = len;
+       return buf;
+out_free_buf:
+       free(buf);
+out_fclose:
+       fclose(fp);
+       return NULL;
+}
+
 static int imagex_append(int argc, const char **argv)
 {
        int c;
@@ -305,6 +347,9 @@ static int imagex_append(int argc, const char **argv)
        const char *wimfile;
        const char *name;
        const char *desc;
+       const char *config_file = NULL;
+       const char *config_str = NULL;
+       size_t config_len = 0;
        WIMStruct *w;
        int ret;
 
@@ -317,6 +362,9 @@ static int imagex_append(int argc, const char **argv)
                        open_flags |= WIMLIB_OPEN_FLAG_CHECK_INTEGRITY;
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        break;
+               case 'C':
+                       config_file = optarg;
+                       break;
                case 'f':
                        flags_element = optarg;
                        break;
@@ -339,6 +387,12 @@ static int imagex_append(int argc, const char **argv)
        name    = (argc >= 3) ? argv[2] : path_basename(dir);
        desc    = (argc >= 4) ? argv[3] : NULL;
 
+       if (config_file) {
+               config_str = file_get_contents(config_file, &config_len);
+               if (!config_str)
+                       return -1;
+       }
+
        ret = wimlib_open_wim(wimfile, open_flags, &w);
        if (ret != 0)
                return ret;
@@ -355,6 +409,8 @@ static int imagex_append(int argc, const char **argv)
                        ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
                                                                name, desc,
                                                                flags_element,
+                                                               config_str,
+                                                               config_len,
                                                                add_image_flags);
                        goto out_write;
                }
@@ -364,7 +420,8 @@ static int imagex_append(int argc, const char **argv)
        }
 #endif
        ret = wimlib_add_image(w, dir, name, desc, 
-                              flags_element, add_image_flags);
+                              flags_element, config_str, config_len,
+                              add_image_flags);
 
 out_write:
        if (ret != 0)
@@ -481,6 +538,9 @@ static int imagex_capture(int argc, const char **argv)
        const char *wimfile;
        const char *name;
        const char *desc;
+       const char *config_file = NULL;
+       const char *config_str = NULL;
+       size_t config_len = 0;
        WIMStruct *w;
        int ret;
 
@@ -492,6 +552,9 @@ static int imagex_capture(int argc, const char **argv)
                case 'c':
                        write_flags |= WIMLIB_WRITE_FLAG_CHECK_INTEGRITY;
                        break;
+               case 'C':
+                       config_file = optarg;
+                       break;
                case 'x':
                        compression_type = get_compression_type(optarg);
                        if (compression_type == WIM_COMPRESSION_TYPE_INVALID)
@@ -524,6 +587,12 @@ static int imagex_capture(int argc, const char **argv)
        name    = (argc >= 3) ? argv[2] : dir;
        desc    = (argc >= 4) ? argv[3] : NULL;
 
+       if (config_file) {
+               config_str = file_get_contents(config_file, &config_len);
+               if (!config_str)
+                       return -1;
+       }
+
        ret = wimlib_create_new_wim(compression_type, &w);
        if (ret != 0)
                return ret;
@@ -540,6 +609,8 @@ static int imagex_capture(int argc, const char **argv)
                        ret = wimlib_add_image_from_ntfs_volume(w, ntfs_device,
                                                                name, desc,
                                                                flags_element,
+                                                               config_str,
+                                                               config_len,
                                                                add_image_flags);
                        goto out_write;
                }
@@ -548,8 +619,8 @@ static int imagex_capture(int argc, const char **argv)
                        imagex_error_with_errno("Failed to stat `%s'", dir);
        }
 #endif
-       ret = wimlib_add_image(w, dir, name, desc, flags_element, 
-                              add_image_flags);
+       ret = wimlib_add_image(w, dir, name, desc, flags_element, config_str,
+                              config_len, add_image_flags);
 
 out_write:
        if (ret != 0) {
index ed6b07f..e6e9f2c 100644 (file)
@@ -36,6 +36,7 @@
 #include <dirent.h>
 #include <string.h>
 #include <errno.h>
+#include <fnmatch.h>
 #include <unistd.h>
 
 /** Private flag: Used to mark that we currently adding the root directory of
@@ -72,16 +73,27 @@ void destroy_image_metadata(struct image_metadata *imd,struct lookup_table *lt)
  *             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 *root_disk_path,
+static int build_dentry_tree(struct dentry **root_ret, const char *root_disk_path,
                             struct lookup_table *lookup_table,
                             struct wim_security_data *sd,
+                            const struct capture_config *config,
                             int add_flags,
                             void *extra_arg)
 {
-       DEBUG("%s", root_disk_path);
        struct stat root_stbuf;
        int ret = 0;
        int (*stat_fn)(const char *restrict, struct stat *restrict);
+       struct dentry *root;
+
+       DEBUG("%s", root_disk_path);
+
+       if (exclude_path(root_disk_path, config)) {
+               if (add_flags & WIMLIB_ADD_IMAGE_FLAG_VERBOSE)
+                       printf("Excluding file `%s' from capture\n",
+                              root_disk_path);
+               return 0;
+       }
+
 
        if (add_flags & WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)
                stat_fn = stat;
@@ -108,6 +120,11 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
                ERROR("`%s' is not a regular file, directory, or symbolic link.");
                return WIMLIB_ERR_SPECIAL_FILE;
        }
+
+       root = new_dentry(path_basename(root_disk_path));
+       if (!root)
+               return WIMLIB_ERR_NOMEM;
+
        stbuf_to_dentry(&root_stbuf, root);
        add_flags &= ~WIMLIB_ADD_IMAGE_FLAG_ROOT;
        root->resolved = true;
@@ -138,11 +155,9 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
                              || (p->d_name[1] == '.' && p->d_name[2] == '\0')))
                                        continue;
                        strcpy(name + len + 1, p->d_name);
-                       child = new_dentry(p->d_name);
-                       if (!child)
-                               return WIMLIB_ERR_NOMEM;
-                       ret = build_dentry_tree(child, name, lookup_table,
-                                               sd, add_flags, extra_arg);
+                       ret = build_dentry_tree(&child, name, lookup_table,
+                                               sd, config,
+                                               add_flags, extra_arg);
                        link_dentry(child, root);
                        if (ret != 0)
                                break;
@@ -203,6 +218,7 @@ static int build_dentry_tree(struct dentry *root, const char *root_disk_path,
                }
                root->lte = lte;
        }
+       *root_ret = root;
        return ret;
 }
 
@@ -488,17 +504,209 @@ WIMLIBAPI int wimlib_delete_image(WIMStruct *w, int image)
        return 0;
 }
 
+enum pattern_type {
+       NONE = 0,
+       EXCLUSION_LIST,
+       EXCLUSION_EXCEPTION,
+       COMPRESSION_EXCLUSION_LIST,
+       ALIGNMENT_LIST,
+};
+
+static const char *default_config =
+"[ExclusionList]\n"
+"\\$ntfs.log\n"
+"\\hiberfil.sys\n"
+"\\pagefile.sys\n"
+"\"\\System Volume Information\"\n"
+"\\RECYCLER\n"
+"\\Windows\\CSC\n"
+"\n"
+"[CompressionExclusionList]\n"
+"*.mp3\n"
+"*.zip\n"
+"*.cab\n"
+"\\WINDOWS\\inf\\*.pnf\n";
+
+static void destroy_pattern_list(struct pattern_list *list)
+{
+       FREE(list->pats);
+}
+
+static void destroy_capture_config(struct capture_config *config)
+{
+       destroy_pattern_list(&config->exclusion_list);
+       destroy_pattern_list(&config->exclusion_exception);
+       destroy_pattern_list(&config->compression_exclusion_list);
+       destroy_pattern_list(&config->alignment_list);
+       FREE(config->config_str);
+       memset(config, 0, sizeof(*config));
+}
+
+static int pattern_list_add_pattern(struct pattern_list *list,
+                                   const char *pattern)
+{
+       const char **pats;
+       if (list->num_pats >= list->num_allocated_pats) {
+               pats = REALLOC(list->pats,
+                              sizeof(list->pats[0]) * (list->num_allocated_pats + 8));
+               if (!pats)
+                       return WIMLIB_ERR_NOMEM;
+               list->num_allocated_pats += 8;
+               list->pats = pats;
+       }
+       list->pats[list->num_pats++] = pattern;
+       return 0;
+}
+
+static int init_capture_config(const char *_config_str, size_t config_len,
+                              struct capture_config *config)
+{
+       char *config_str;
+       char *p;
+       char *eol;
+       char *next_p;
+       size_t next_bytes_remaining;
+       size_t bytes_remaining;
+       enum pattern_type type = NONE;
+       int ret;
+       unsigned long line_no = 0;
+
+       DEBUG("config_len = %zu", config_len);
+       bytes_remaining = config_len;
+       memset(config, 0, sizeof(*config));
+       config_str = MALLOC(config_len);
+       if (!config_str) {
+               ERROR("Could not duplicate capture config string");
+               return WIMLIB_ERR_NOMEM;
+       }
+       memcpy(config_str, _config_str, config_len);
+       next_p = config_str;
+       config->config_str = config_str;
+       while (bytes_remaining) {
+               line_no++;
+               p = next_p;
+               eol = memchr(p, '\n', bytes_remaining);
+               if (!eol) {
+                       ERROR("Expected end-of-line in capture config file on "
+                             "line %lu", line_no);
+                       ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
+                       goto out_destroy;
+               }
+               
+               next_p = eol + 1;
+               bytes_remaining -= (eol - p) + 1;
+               if (eol == p)
+                       continue;
+
+               if (*(eol - 1) == '\r')
+                       eol--;
+               *eol = '\0';
+
+               /* Translate backslash to forward slash */
+               for (char *pp = p; pp != eol; pp++)
+                       if (*pp == '\\')
+                               *pp = '/';
+
+               if (strcmp(p, "[ExclusionList]") == 0)
+                       type = EXCLUSION_LIST;
+               else if (strcmp(p, "[ExclusionException]") == 0)
+                       type = EXCLUSION_EXCEPTION;
+               else if (strcmp(p, "[CompressionExclusionList]") == 0)
+                       type = COMPRESSION_EXCLUSION_LIST;
+               else if (strcmp(p, "[AlignmentList]") == 0)
+                       type = ALIGNMENT_LIST;
+               else switch (type) {
+               case EXCLUSION_LIST:
+                       DEBUG("Adding pattern \"%s\" to exclusion list", p);
+                       ret = pattern_list_add_pattern(&config->exclusion_list, p);
+                       break;
+               case EXCLUSION_EXCEPTION:
+                       DEBUG("Adding pattern \"%s\" to exclusion exception list", p);
+                       ret = pattern_list_add_pattern(&config->exclusion_exception, p);
+                       break;
+               case COMPRESSION_EXCLUSION_LIST:
+                       DEBUG("Adding pattern \"%s\" to compression exclusion list", p);
+                       ret = pattern_list_add_pattern(&config->compression_exclusion_list, p);
+                       break;
+               case ALIGNMENT_LIST:
+                       DEBUG("Adding pattern \"%s\" to alignment list", p);
+                       ret = pattern_list_add_pattern(&config->alignment_list, p);
+                       break;
+               default:
+                       ERROR("Line %lu of capture configuration is not "
+                             "in a block (such as [ExclusionList])",
+                             line_no);
+                       ret = WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
+                       goto out_destroy;
+               }
+               if (ret != 0)
+                       goto out_destroy;
+       }
+       return 0;
+out_destroy:
+       destroy_capture_config(config);
+       return ret;
+}
+
+static bool match_pattern(const char *path, const char *path_basename,
+                         const struct pattern_list *list)
+{
+       for (size_t i = 0; i < list->num_pats; i++) {
+               const char *pat = list->pats[i];
+               const char *string;
+               if (pat[0] == '/')
+                       string = path;
+               else
+                       string = path_basename;
+               if (fnmatch(pat, string, FNM_PATHNAME) == 0) {
+                       DEBUG("`%s' matches the pattern \"%s\"",
+                             string, pat);
+                       return true;
+               }
+       }
+       return false;
+}
+
+static void print_pattern_list(const struct pattern_list *list)
+{
+       for (size_t i = 0; i < list->num_pats; i++)
+               printf("    %s\n", list->pats[i]);
+}
+
+static void print_capture_config(const struct capture_config *config)
+{
+       if (config->exclusion_list.num_pats) {
+               puts("Files or folders excluded from image capture:");
+               print_pattern_list(&config->exclusion_list);
+               putchar('\n');
+       }
+}
+
+bool exclude_path(const char *path, const struct capture_config *config)
+{
+       const char *basename = path_basename(path);
+       return match_pattern(path, basename, &config->exclusion_list) && 
+               !match_pattern(path, basename, &config->exclusion_exception);
+
+}
+
+
+
 int do_add_image(WIMStruct *w, const char *dir, const char *name,
                 const char *description, const char *flags_element,
+                const char *config_str, size_t config_len,
                 int flags,
-                int (*capture_tree)(struct dentry *, const char *,
+                int (*capture_tree)(struct dentry **, const char *,
                                     struct lookup_table *, 
-                                    struct wim_security_data *, int, void *),
+                                    struct wim_security_data *,
+                                    const struct capture_config *,
+                                    int, void *),
                 void *extra_arg)
 {
-       struct dentry *root_dentry;
+       struct dentry *root_dentry = NULL;
        struct image_metadata *imd;
        struct wim_security_data *sd;
+       struct capture_config config;
        int ret;
 
        DEBUG("Adding dentry tree from dir `%s'.", dir);
@@ -518,37 +726,44 @@ int do_add_image(WIMStruct *w, const char *dir, const char *name,
                return WIMLIB_ERR_IMAGE_NAME_COLLISION;
        }
 
-       DEBUG("Creating root dentry.");
-
-       root_dentry = new_dentry("");
-       if (!root_dentry)
-               return WIMLIB_ERR_NOMEM;
+       DEBUG("Initializing capture configuration");
+       if (!config_str) {
+               DEBUG("Using default capture configuration");
+               config_str = default_config;
+               config_len = strlen(default_config);
+       }
+       ret = init_capture_config(config_str, config_len, &config);
+       if (ret != 0)
+               return ret;
+       print_capture_config(&config);
 
+       DEBUG("Allocating security data");
 
        sd = CALLOC(1, sizeof(struct wim_security_data));
        if (!sd)
-               goto out_free_dentry_tree;
+               goto out_destroy_config;
        sd->total_length = 8;
        sd->refcnt = 1;
 
        DEBUG("Building dentry tree.");
-       ret = (*capture_tree)(root_dentry, dir, w->lookup_table, sd,
-                             flags | WIMLIB_ADD_IMAGE_FLAG_ROOT,
+       ret = (*capture_tree)(&root_dentry, dir, w->lookup_table, sd,
+                             &config, flags | WIMLIB_ADD_IMAGE_FLAG_ROOT,
                              extra_arg);
+       destroy_capture_config(&config);
 
        if (ret != 0) {
                ERROR("Failed to build dentry tree for `%s'", dir);
-               goto out_free_sd;
+               goto out_free_dentry_tree;
        }
 
        DEBUG("Calculating full paths of dentries.");
        ret = for_dentry_in_tree(root_dentry, calculate_dentry_full_path, NULL);
        if (ret != 0)
-               goto out_free_sd;
+               goto out_free_dentry_tree;
 
        ret = add_new_dentry_tree(w, root_dentry, sd);
        if (ret != 0)
-               goto out_free_sd;
+               goto out_free_dentry_tree;
 
        DEBUG("Inserting dentries into hard link group table");
        ret = for_dentry_in_tree(root_dentry, link_group_table_insert, 
@@ -571,10 +786,12 @@ out_destroy_imd:
                               w->lookup_table);
        w->hdr.image_count--;
        return ret;
-out_free_sd:
-       free_security_data(sd);
 out_free_dentry_tree:
        free_dentry_tree(root_dentry, w->lookup_table);
+out_free_sd:
+       free_security_data(sd);
+out_destroy_config:
+       destroy_capture_config(&config);
        return ret;
 }
 
@@ -583,8 +800,11 @@ out_free_dentry_tree:
  */
 WIMLIBAPI int wimlib_add_image(WIMStruct *w, const char *dir, 
                               const char *name, const char *description, 
-                              const char *flags_element, int flags)
+                              const char *flags_element,
+                              const char *config_str,
+                              size_t config_len, int flags)
 {
-       return do_add_image(w, dir, name, description, flags_element, flags,
+       return do_add_image(w, dir, name, description, flags_element,
+                           config_str, config_len, flags,
                            build_dentry_tree, NULL);
 }
index 56e1c53..8d1fe0e 100644 (file)
@@ -209,6 +209,9 @@ static int ntfs_attr_sha1sum(ntfs_inode *ni, ATTR_RECORD *ar,
        bytes_remaining = na->data_size;
        sha1_init(&ctx);
 
+       DEBUG("Calculating SHA1 message digest (%"PRIu64" bytes)",
+                       bytes_remaining);
+
        while (bytes_remaining) {
                s64 to_read = min(bytes_remaining, sizeof(buf));
                if (ntfs_attr_pread(na, pos, to_read, buf) != to_read) {
@@ -239,6 +242,8 @@ static int capture_ntfs_streams(struct dentry *dentry, ntfs_inode *ni,
        struct lookup_table_entry *lte;
        int ret = 0;
 
+       DEBUG("Capturing NTFS data streams from `%s'", path);
+
        /* Get context to search the streams of the NTFS file. */
        actx = ntfs_attr_get_search_ctx(ni, NULL);
        if (!actx) {
@@ -330,29 +335,36 @@ out_free_ntfs_loc:
        }
 out_put_actx:
        ntfs_attr_put_search_ctx(actx);
+       if (ret == 0)
+               DEBUG("Successfully captured NTFS streams from `%s'", path);
+       else
+               DEBUG("Failed to capture NTFS streams from `%s", path);
        return ret;
 }
 
 struct readdir_ctx {
-       struct dentry       *dentry;
+       struct dentry       *parent;
        ntfs_inode          *dir_ni;
        char                *path;
        size_t               path_len;
        struct lookup_table *lookup_table;
        struct sd_set       *sd_set;
+       const struct capture_config *config;
        ntfs_volume        **ntfs_vol_p;
 };
 
-static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
+static int __build_dentry_tree_ntfs(struct dentry **root_p, ntfs_inode *ni,
                                    char path[], size_t path_len,
                                    struct lookup_table *lookup_table,
                                    struct sd_set *sd_set,
+                                   const struct capture_config *config,
                                    ntfs_volume **ntfs_vol_p);
 
 
-static int 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)
+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;
@@ -368,6 +380,16 @@ static int filldir(void *dirent, const ntfschar *name,
        if (!utf8_name)
                goto out;
 
+       if (utf8_name[0] == '.' &&
+            (utf8_name[1] == '\0' ||
+             (utf8_name[1] == '.' && utf8_name[2] == '\0'))) {
+               DEBUG("Skipping dentry `%s'", utf8_name);
+               ret = 0;
+               goto out_free_utf8_name;
+       }
+
+       DEBUG("Opening inode for `%s'", utf8_name);
+
        ctx = dirent;
 
        ntfs_inode *ni = ntfs_inode_open(ctx->dir_ni->vol, mref);
@@ -375,16 +397,19 @@ static int filldir(void *dirent, const ntfschar *name,
                ERROR_WITH_ERRNO("Failed to open NTFS inode");
                ret = 1;
        }
-       child = new_dentry(utf8_name);
-       if (!child)
-               goto out_close_ni;
-
-       memcpy(ctx->path + ctx->path_len, utf8_name, utf8_name_len + 1);
-       path_len = ctx->path_len + utf8_name_len;
-       ret = __build_dentry_tree_ntfs(child, ni, ctx->path, path_len,
+       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(&child, ni, ctx->path, path_len,
                                       ctx->lookup_table, ctx->sd_set,
-                                      ctx->ntfs_vol_p);
-       link_dentry(child, ctx->dentry);
+                                      ctx->config, ctx->ntfs_vol_p);
+       DEBUG("Linking dentry `%s' with parent `%s'",
+             child->file_name_utf8, ctx->parent->file_name_utf8);
+
+       link_dentry(child, ctx->parent);
+       DEBUG("Return %d", ret);
 out_close_ni:
        ntfs_inode_close(ni);
 out_free_utf8_name:
@@ -397,53 +422,75 @@ out:
  * 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(struct dentry *dentry, ntfs_inode *ni,
+static int __build_dentry_tree_ntfs(struct dentry **root_p, ntfs_inode *ni,
                                    char path[], size_t path_len,
                                    struct lookup_table *lookup_table,
                                    struct sd_set *sd_set,
+                                   const struct capture_config *config,
                                    ntfs_volume **ntfs_vol_p)
 {
-       u32 attributes = ntfs_inode_get_attributes(ni);
-       int mrec_flags = ni->mrec->flags;
+       u32 attributes;
+       int mrec_flags;
        u32 sd_size;
        int ret = 0;
+       struct dentry *root;
+
+       if (exclude_path(path, config)) {
+               DEBUG("Excluding `%s' from capture", path);
+               return 0;
+       }
+
+       DEBUG("Starting recursive capture at path = `%s'", path);
+       mrec_flags = ni->mrec->flags;
+       attributes = ntfs_inode_get_attributes(ni);
+
+       root = new_dentry(path_basename(path));
+       if (!root)
+               return WIMLIB_ERR_NOMEM;
 
-       dentry->creation_time    = le64_to_cpu(ni->creation_time);
-       dentry->last_write_time  = le64_to_cpu(ni->last_data_change_time);
-       dentry->last_access_time = le64_to_cpu(ni->last_access_time);
-       dentry->security_id      = le32_to_cpu(ni->security_id);
-       dentry->attributes       = le32_to_cpu(attributes);
-       dentry->hard_link        = ni->mft_no;
-       dentry->resolved = true;
+       root->creation_time    = le64_to_cpu(ni->creation_time);
+       root->last_write_time  = le64_to_cpu(ni->last_data_change_time);
+       root->last_access_time = le64_to_cpu(ni->last_access_time);
+       root->security_id      = le32_to_cpu(ni->security_id);
+       root->attributes       = le32_to_cpu(attributes);
+       root->hard_link  = ni->mft_no;
+       root->resolved = true;
 
        if (attributes & FILE_ATTR_REPARSE_POINT) {
+               DEBUG("Reparse point `%s'", path);
                /* Junction point, symbolic link, or other reparse point */
-               ret = capture_ntfs_streams(dentry, ni, path, path_len,
+               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) {
+               DEBUG("Directory `%s'", path);
+
                /* Normal directory */
                s64 pos = 0;
                struct readdir_ctx ctx = {
-                       .dentry       = dentry,
+                       .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,
                };
-               ret = ntfs_readdir(ni, &pos, &ctx, filldir);
+               ret = ntfs_readdir(ni, &pos, &ctx, wim_ntfs_capture_filldir);
                if (ret != 0)
                        ret = WIMLIB_ERR_NTFS_3G;
        } else {
+               DEBUG("Normal file `%s'", path);
                /* Normal file */
-               ret = capture_ntfs_streams(dentry, ni, path, path_len,
+               ret = capture_ntfs_streams(root, ni, path, path_len,
                                           lookup_table, ntfs_vol_p,
                                           AT_DATA);
        }
        if (ret != 0)
                return ret;
+
+       DEBUG("Getting security information from `%s'", path);
        ret = ntfs_inode_get_security(ni,
                                      OWNER_SECURITY_INFORMATION |
                                      GROUP_SECURITY_INFORMATION |
@@ -457,18 +504,31 @@ static int __build_dentry_tree_ntfs(struct dentry *dentry, ntfs_inode *ni,
                                      DACL_SECURITY_INFORMATION  |
                                      SACL_SECURITY_INFORMATION,
                                      sd, sd_size, &sd_size);
-       dentry->security_id = sd_set_add_sd(sd_set, sd, sd_size);
-       if (dentry->security_id == -1) {
-               ERROR("Could not allocate security ID");
-               ret = WIMLIB_ERR_NOMEM;
+       if (ret == 0) {
+               ERROR_WITH_ERRNO("Failed to get security information from "
+                                "`%s'", path);
+               ret = WIMLIB_ERR_NTFS_3G;
+       } else {
+               if (ret > 0) {
+                       /*print_security_descriptor(sd, sd_size);*/
+                       root->security_id = sd_set_add_sd(sd_set, sd, sd_size);
+                       DEBUG("Added security ID = %u for `%s'",
+                             root->security_id, path);
+               } else { 
+                       root->security_id = -1;
+                       DEBUG("No security ID for `%s'", path);
+               }
+               ret = 0;
        }
+       *root_p = root;
        return ret;
 }
 
-static int build_dentry_tree_ntfs(struct dentry *root_dentry,
+static int build_dentry_tree_ntfs(struct dentry **root_p,
                                  const char *device,
                                  struct lookup_table *lookup_table,
                                  struct wim_security_data *sd,
+                                 const struct capture_config *config,
                                  int flags,
                                  void *extra_arg)
 {
@@ -479,6 +539,8 @@ static int build_dentry_tree_ntfs(struct dentry *root_dentry,
        tree.sd = sd;
        tree.root = NULL;
        ntfs_volume **ntfs_vol_p = extra_arg;
+
+       DEBUG("Mounting NTFS volume `%s' read-only", device);
        
        vol = ntfs_mount(device, MS_RDONLY);
        if (!vol) {
@@ -486,6 +548,10 @@ static int build_dentry_tree_ntfs(struct dentry *root_dentry,
                                 device);
                return WIMLIB_ERR_NTFS_3G;
        }
+
+       NVolClearShowSysFiles(vol);
+
+       DEBUG("Opening root NTFS dentry");
        root_ni = ntfs_inode_open(vol, FILE_root);
        if (!root_ni) {
                ERROR_WITH_ERRNO("Failed to open root inode of NTFS volume "
@@ -496,8 +562,9 @@ static int build_dentry_tree_ntfs(struct dentry *root_dentry,
        char path[4096];
        path[0] = '/';
        path[1] = '\0';
-       ret = __build_dentry_tree_ntfs(root_dentry, root_ni, path, 1,
-                                      lookup_table, &tree, ntfs_vol_p);
+       ret = __build_dentry_tree_ntfs(root_p, root_ni, path, 1,
+                                      lookup_table, &tree, config,
+                                      ntfs_vol_p);
        ntfs_inode_close(root_ni);
 
 out:
@@ -509,19 +576,23 @@ out:
        return ret;
 }
 
+
+
 WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
                                                const char *device,
                                                const char *name,
                                                const char *description,
                                                const char *flags_element,
+                                               const char *config_str,
+                                               size_t config_len,
                                                int flags)
 {
        if (flags & (WIMLIB_ADD_IMAGE_FLAG_DEREFERENCE)) {
                ERROR("Cannot dereference files when capturing directly from NTFS");
                return WIMLIB_ERR_INVALID_PARAM;
        }
-       return do_add_image(w, device, name, description, flags_element, flags,
-                           build_dentry_tree_ntfs,
+       return do_add_image(w, device, name, description, flags_element,
+                           config_str, config_len, flags, build_dentry_tree_ntfs,
                            &w->ntfs_vol);
 }
 
@@ -531,7 +602,9 @@ WIMLIBAPI int wimlib_add_image_from_ntfs_volume(WIMStruct *w,
                                                const char *name,
                                                const char *description,
                                                const char *flags_element,
-                                               int flags)
+                                               int flags,
+                                               const char *config_str,
+                                               size_t config_len)
 {
        ERROR("wimlib was compiled without support for NTFS-3g, so");
        ERROR("we cannot capture a WIM image directly from a NTFS volume");
index b2d2625..fdb0c85 100644 (file)
@@ -127,6 +127,8 @@ static const char *error_strings[] = {
                = "Tried to add an image with a name that is already in use",
        [WIMLIB_ERR_INTEGRITY] 
                = "The WIM failed an integrity check",
+       [WIMLIB_ERR_INVALID_CAPTURE_CONFIG]
+               = "The capture configuration string was invalid",
        [WIMLIB_ERR_INVALID_CHUNK_SIZE] 
                = "The WIM is compressed but does not have a chunk "
                        "size of 32768",
index 6d9eef4..f8471f7 100644 (file)
@@ -305,6 +305,7 @@ enum wimlib_error_code {
        WIMLIB_ERR_IMAGE_COUNT,
        WIMLIB_ERR_IMAGE_NAME_COLLISION,
        WIMLIB_ERR_INTEGRITY,
+       WIMLIB_ERR_INVALID_CAPTURE_CONFIG,
        WIMLIB_ERR_INVALID_CHUNK_SIZE,
        WIMLIB_ERR_INVALID_COMPRESSION_TYPE,
        WIMLIB_ERR_INVALID_DENTRY,
@@ -405,12 +406,16 @@ enum wimlib_error_code {
  */
 extern int wimlib_add_image(WIMStruct *wim, const char *dir, 
                            const char *name, const char *description, 
-                           const char *flags_element, int flags);
+                           const char *flags_element,
+                           const char *config, size_t config_len,
+                           int flags);
 
 extern int wimlib_add_image_from_ntfs_volume(WIMStruct *w, const char *device,
                                             const char *name,
                                             const char *description,
                                             const char *flags_element,
+                                            const char *config,
+                                            size_t config_len,
                                             int flags);
 
 extern int wimlib_apply_image_to_ntfs_volume(WIMStruct *w, int image,
index 1a2654c..b6a90ba 100644 (file)
@@ -312,6 +312,20 @@ wim_get_current_image_metadata(WIMStruct *w)
        return &w->image_metadata[w->current_image - 1];
 }
 
+struct pattern_list {
+       const char **pats;
+       size_t num_pats;
+       size_t num_allocated_pats;
+};
+
+struct capture_config {
+       struct pattern_list exclusion_list;
+       struct pattern_list exclusion_exception;
+       struct pattern_list compression_exclusion_list;
+       struct pattern_list alignment_list;
+       char *config_str;
+};
+
 /* hardlink.c */
 
 struct link_group_table *new_link_group_table(u64 capacity);
@@ -336,12 +350,17 @@ extern int check_wim_integrity(WIMStruct *w, int show_progress, int *status);
 /* modify.c */
 extern void destroy_image_metadata(struct image_metadata *imd,
                                   struct lookup_table *lt);
+extern bool exclude_path(const char *path,
+                        const struct capture_config *config);
 extern int do_add_image(WIMStruct *w, const char *dir, const char *name,
                        const char *description, const char *flags_element,
+                       const char *config_str, size_t config_len,
                        int flags,
-                       int (*capture_tree)(struct dentry *, const char *,
+                       int (*capture_tree)(struct dentry **, const char *,
                                     struct lookup_table *, 
-                                    struct wim_security_data *, int, void *),
+                                    struct wim_security_data *, 
+                                    const struct capture_config *,
+                                    int, void *),
                        void *extra_arg);
 
 /* resource.c */