]> wimlib.net Git - wimlib/blob - src/capture_common.c
wimlib.h: C++ compatibility
[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/error.h"
31 #include "wimlib/paths.h"
32
33 #ifdef __WIN32__
34 #  include "wimlib/win32.h" /* for fnmatch() equivalent */
35 #else
36 #  include <fnmatch.h>
37 #endif
38 #include <string.h>
39
40
41 static int
42 canonicalize_pattern(const tchar *pat, tchar **canonical_pat_ret)
43 {
44         tchar *canonical_pat;
45
46         if (!is_any_path_separator(pat[0]) &&
47             pat[0] != T('\0') && pat[1] == T(':'))
48         {
49                 /* Pattern begins with drive letter */
50                 if (!is_any_path_separator(pat[2])) {
51                         /* Something like c:file, which is actually a path
52                          * relative to the current working directory on the c:
53                          * drive.  We require paths with drive letters to be
54                          * absolute. */
55                         ERROR("Invalid path \"%"TS"\"; paths including drive letters "
56                               "must be absolute!", pat);
57                         ERROR("Maybe try \"%"TC":\\%"TS"\"?",
58                               pat[0], pat + 2);
59                         return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
60                 }
61
62                 WARNING("Pattern \"%"TS"\" starts with a drive letter, which is "
63                         "being removed.", pat);
64                 /* Strip the drive letter */
65                 pat += 2;
66         }
67         canonical_pat = canonicalize_fs_path(pat);
68         if (!canonical_pat)
69                 return WIMLIB_ERR_NOMEM;
70
71         /* Translate all possible path separators into the operating system's
72          * preferred path separator. */
73         for (tchar *p = canonical_pat; *p; p++)
74                 if (is_any_path_separator(*p))
75                         *p = OS_PREFERRED_PATH_SEPARATOR;
76         *canonical_pat_ret = canonical_pat;
77         return 0;
78 }
79
80 static int
81 copy_and_canonicalize_pattern_list(const struct wimlib_pattern_list *list,
82                                    struct wimlib_pattern_list *copy)
83 {
84         int ret = 0;
85
86         copy->pats = CALLOC(list->num_pats, sizeof(list->pats[0]));
87         if (!copy->pats)
88                 return WIMLIB_ERR_NOMEM;
89         copy->num_pats = list->num_pats;
90         for (size_t i = 0; i < list->num_pats; i++) {
91                 ret = canonicalize_pattern(list->pats[i], &copy->pats[i]);
92                 if (ret)
93                         break;
94         }
95         return ret;
96 }
97
98 int
99 copy_and_canonicalize_capture_config(const struct wimlib_capture_config *config,
100                                      struct wimlib_capture_config **config_copy_ret)
101 {
102         struct wimlib_capture_config *config_copy;
103         int ret;
104
105         config_copy = CALLOC(1, sizeof(struct wimlib_capture_config));
106         if (!config_copy) {
107                 ret = WIMLIB_ERR_NOMEM;
108                 goto out_free_capture_config;
109         }
110         ret = copy_and_canonicalize_pattern_list(&config->exclusion_pats,
111                                                  &config_copy->exclusion_pats);
112         if (ret)
113                 goto out_free_capture_config;
114         ret = copy_and_canonicalize_pattern_list(&config->exclusion_exception_pats,
115                                                  &config_copy->exclusion_exception_pats);
116         if (ret)
117                 goto out_free_capture_config;
118         *config_copy_ret = config_copy;
119         goto out;
120 out_free_capture_config:
121         free_capture_config(config_copy);
122 out:
123         return ret;
124 }
125
126 static void
127 destroy_pattern_list(struct wimlib_pattern_list *list)
128 {
129         for (size_t i = 0; i < list->num_pats; i++)
130                 FREE(list->pats[i]);
131         FREE(list->pats);
132 }
133
134 void
135 free_capture_config(struct wimlib_capture_config *config)
136 {
137         if (config) {
138                 destroy_pattern_list(&config->exclusion_pats);
139                 destroy_pattern_list(&config->exclusion_exception_pats);
140                 FREE(config);
141         }
142 }
143
144 static bool
145 match_pattern(const tchar *path,
146               const tchar *path_basename,
147               const struct wimlib_pattern_list *list)
148 {
149         for (size_t i = 0; i < list->num_pats; i++) {
150
151                 const tchar *pat = list->pats[i];
152                 const tchar *string;
153
154                 if (*pat == OS_PREFERRED_PATH_SEPARATOR) {
155                         /* Absolute path from root of capture */
156                         string = path;
157                 } else {
158                         if (tstrchr(pat, OS_PREFERRED_PATH_SEPARATOR))
159                                 /* Relative path from root of capture */
160                                 string = path + 1;
161                         else
162                                 /* A file name pattern */
163                                 string = path_basename;
164                 }
165
166                 /* Warning: on Windows native builds, fnmatch() calls the
167                  * replacement function in win32.c. */
168                 if (fnmatch(pat, string, FNM_PATHNAME | FNM_NOESCAPE
169                                 #ifdef FNM_CASEFOLD
170                                         | FNM_CASEFOLD
171                                 #endif
172                             ) == 0)
173                 {
174                         DEBUG("\"%"TS"\" matches the pattern \"%"TS"\"",
175                               string, pat);
176                         return true;
177                 } else {
178                         DEBUG2("\"%"TS"\" does not match the pattern \"%"TS"\"",
179                                string, pat);
180                 }
181         }
182         return false;
183 }
184
185 void
186 do_capture_progress(struct add_image_params *params, int status)
187 {
188         switch (status) {
189         case WIMLIB_SCAN_DENTRY_OK:
190                 if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE))
191                         return;
192         case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
193         case WIMLIB_SCAN_DENTRY_EXCLUDED:
194                 if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE))
195                         return;
196         }
197         params->progress.scan.status = status;
198         if (params->progress_func) {
199                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY,
200                                       &params->progress);
201         }
202 }
203
204 /* Return true if the image capture configuration file indicates we should
205  * exclude the filename @path from capture.
206  *
207  * If @exclude_prefix is %true, the part of the path up and including the name
208  * of the directory being captured is not included in the path for matching
209  * purposes.  This allows, for example, a pattern like /hiberfil.sys to match a
210  * file /mnt/windows7/hiberfil.sys if we are capturing the /mnt/windows7
211  * directory.
212  */
213 bool
214 exclude_path(const tchar *path, size_t path_len,
215              const struct wimlib_capture_config *config, bool exclude_prefix)
216 {
217         if (!config)
218                 return false;
219         const tchar *basename = path_basename_with_len(path, path_len);
220         if (exclude_prefix) {
221                 wimlib_assert(path_len >= config->_prefix_num_tchars);
222                 if (!tmemcmp(config->_prefix, path, config->_prefix_num_tchars) &&
223                     path[config->_prefix_num_tchars] == OS_PREFERRED_PATH_SEPARATOR)
224                 {
225                         path += config->_prefix_num_tchars;
226                 }
227         }
228         return match_pattern(path, basename, &config->exclusion_pats) &&
229                 !match_pattern(path, basename, &config->exclusion_exception_pats);
230
231 }