]> wimlib.net Git - wimlib/blob - src/capture_common.c
c45ae152345e7ecba77c983ded9d3d2a81ea5b39
[wimlib] / src / capture_common.c
1 /*
2  * capture_common.c - Mostly code to handle excluding paths from capture.
3  */
4
5 /*
6  * Copyright (C) 2013 Eric Biggers
7  *
8  * This file is part of wimlib, a library for working with WIM files.
9  *
10  * wimlib is free software; you can redistribute it and/or modify it under the
11  * terms of the GNU General Public License as published by the Free
12  * Software Foundation; either version 3 of the License, or (at your option)
13  * any later version.
14  *
15  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
16  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
17  * A PARTICULAR PURPOSE. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with wimlib; if not, see http://www.gnu.org/licenses/.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #  include "config.h"
26 #endif
27
28 #include "wimlib/assert.h"
29 #include "wimlib/capture.h"
30 #include "wimlib/dentry.h"
31 #include "wimlib/error.h"
32 #include "wimlib/lookup_table.h"
33 #include "wimlib/paths.h"
34
35 #ifdef __WIN32__
36 #  include "wimlib/win32.h" /* for fnmatch() equivalent */
37 #else
38 #  include <fnmatch.h>
39 #endif
40 #include <string.h>
41
42
43 static int
44 canonicalize_pattern(const tchar *pat, tchar **canonical_pat_ret)
45 {
46         tchar *canonical_pat;
47
48         if (!is_any_path_separator(pat[0]) &&
49             pat[0] != T('\0') && pat[1] == T(':'))
50         {
51                 /* Pattern begins with drive letter */
52                 if (!is_any_path_separator(pat[2])) {
53                         /* Something like c:file, which is actually a path
54                          * relative to the current working directory on the c:
55                          * drive.  We require paths with drive letters to be
56                          * absolute. */
57                         ERROR("Invalid path \"%"TS"\"; paths including drive letters "
58                               "must be absolute!", pat);
59                         ERROR("Maybe try \"%"TC":\\%"TS"\"?",
60                               pat[0], pat + 2);
61                         return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
62                 }
63
64                 WARNING("Pattern \"%"TS"\" starts with a drive letter, which is "
65                         "being removed.", pat);
66                 /* Strip the drive letter */
67                 pat += 2;
68         }
69         canonical_pat = canonicalize_fs_path(pat);
70         if (!canonical_pat)
71                 return WIMLIB_ERR_NOMEM;
72
73         /* Translate all possible path separators into the operating system's
74          * preferred path separator. */
75         for (tchar *p = canonical_pat; *p; p++)
76                 if (is_any_path_separator(*p))
77                         *p = OS_PREFERRED_PATH_SEPARATOR;
78         *canonical_pat_ret = canonical_pat;
79         return 0;
80 }
81
82 static int
83 copy_and_canonicalize_pattern_list(const struct wimlib_pattern_list *list,
84                                    struct wimlib_pattern_list *copy)
85 {
86         int ret = 0;
87
88         copy->pats = CALLOC(list->num_pats, sizeof(list->pats[0]));
89         if (!copy->pats)
90                 return WIMLIB_ERR_NOMEM;
91         copy->num_pats = list->num_pats;
92         for (size_t i = 0; i < list->num_pats; i++) {
93                 ret = canonicalize_pattern(list->pats[i], &copy->pats[i]);
94                 if (ret)
95                         break;
96         }
97         return ret;
98 }
99
100 int
101 copy_and_canonicalize_capture_config(const struct wimlib_capture_config *config,
102                                      struct wimlib_capture_config **config_copy_ret)
103 {
104         struct wimlib_capture_config *config_copy;
105         int ret;
106
107         config_copy = CALLOC(1, sizeof(struct wimlib_capture_config));
108         if (!config_copy) {
109                 ret = WIMLIB_ERR_NOMEM;
110                 goto out_free_capture_config;
111         }
112         ret = copy_and_canonicalize_pattern_list(&config->exclusion_pats,
113                                                  &config_copy->exclusion_pats);
114         if (ret)
115                 goto out_free_capture_config;
116         ret = copy_and_canonicalize_pattern_list(&config->exclusion_exception_pats,
117                                                  &config_copy->exclusion_exception_pats);
118         if (ret)
119                 goto out_free_capture_config;
120         *config_copy_ret = config_copy;
121         goto out;
122 out_free_capture_config:
123         free_capture_config(config_copy);
124 out:
125         return ret;
126 }
127
128 static void
129 destroy_pattern_list(struct wimlib_pattern_list *list)
130 {
131         for (size_t i = 0; i < list->num_pats; i++)
132                 FREE(list->pats[i]);
133         FREE(list->pats);
134 }
135
136 void
137 free_capture_config(struct wimlib_capture_config *config)
138 {
139         if (config) {
140                 destroy_pattern_list(&config->exclusion_pats);
141                 destroy_pattern_list(&config->exclusion_exception_pats);
142                 FREE(config);
143         }
144 }
145
146 static bool
147 match_pattern(const tchar *path,
148               const tchar *path_basename,
149               const struct wimlib_pattern_list *list)
150 {
151         for (size_t i = 0; i < list->num_pats; i++) {
152
153                 const tchar *pat = list->pats[i];
154                 const tchar *string;
155
156                 if (*pat == OS_PREFERRED_PATH_SEPARATOR) {
157                         /* Absolute path from root of capture */
158                         string = path;
159                 } else {
160                         if (tstrchr(pat, OS_PREFERRED_PATH_SEPARATOR))
161                                 /* Relative path from root of capture */
162                                 string = path + 1;
163                         else
164                                 /* A file name pattern */
165                                 string = path_basename;
166                 }
167
168                 /* Warning: on Windows native builds, fnmatch() calls the
169                  * replacement function in win32.c. */
170                 if (fnmatch(pat, string, FNM_PATHNAME | FNM_NOESCAPE
171                                 #ifdef FNM_CASEFOLD
172                                         | FNM_CASEFOLD
173                                 #endif
174                             ) == 0)
175                 {
176                         DEBUG("\"%"TS"\" matches the pattern \"%"TS"\"",
177                               string, pat);
178                         return true;
179                 } else {
180                         DEBUG2("\"%"TS"\" does not match the pattern \"%"TS"\"",
181                                string, pat);
182                 }
183         }
184         return false;
185 }
186
187 void
188 do_capture_progress(struct add_image_params *params, int status,
189                     const struct wim_inode *inode)
190 {
191         switch (status) {
192         case WIMLIB_SCAN_DENTRY_OK:
193                 if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE))
194                         return;
195         case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
196         case WIMLIB_SCAN_DENTRY_EXCLUDED:
197                 if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE))
198                         return;
199         }
200         params->progress.scan.status = status;
201         if (status == WIMLIB_SCAN_DENTRY_OK && inode->i_nlink == 1) {
202                 const struct wim_lookup_table_entry *lte;
203                 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
204                         lte = inode_stream_lte_resolved(inode, i);
205                         if (lte != NULL)
206                                 params->progress.scan.num_bytes_scanned += lte->size;
207                 }
208                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
209                         params->progress.scan.num_dirs_scanned++;
210                 else
211                         params->progress.scan.num_nondirs_scanned++;
212         }
213         if (params->progress_func) {
214                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY,
215                                       &params->progress);
216         }
217 }
218
219 /* Return true if the image capture configuration file indicates we should
220  * exclude the filename @path from capture.
221  *
222  * If @exclude_prefix is %true, the part of the path up and including the name
223  * of the directory being captured is not included in the path for matching
224  * purposes.  This allows, for example, a pattern like /hiberfil.sys to match a
225  * file /mnt/windows7/hiberfil.sys if we are capturing the /mnt/windows7
226  * directory.
227  */
228 bool
229 exclude_path(const tchar *path, size_t path_len,
230              const struct wimlib_capture_config *config, bool exclude_prefix)
231 {
232         if (!config)
233                 return false;
234         const tchar *basename = path_basename_with_len(path, path_len);
235         if (exclude_prefix) {
236                 wimlib_assert(path_len >= config->_prefix_num_tchars);
237                 if (!tmemcmp(config->_prefix, path, config->_prefix_num_tchars) &&
238                     path[config->_prefix_num_tchars] == OS_PREFERRED_PATH_SEPARATOR)
239                 {
240                         path += config->_prefix_num_tchars;
241                 }
242         }
243         return match_pattern(path, basename, &config->exclusion_pats) &&
244                 !match_pattern(path, basename, &config->exclusion_exception_pats);
245
246 }