]> wimlib.net Git - wimlib/blob - src/capture_common.c
mangle_pat(): Remove quotes
[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 #include "wimlib/textfile.h"
35 #include "wimlib/wildcard.h"
36
37 #include <string.h>
38
39 void
40 do_capture_progress(struct add_image_params *params, int status,
41                     const struct wim_inode *inode)
42 {
43         switch (status) {
44         case WIMLIB_SCAN_DENTRY_OK:
45                 if (!(params->add_flags & WIMLIB_ADD_FLAG_VERBOSE))
46                         return;
47         case WIMLIB_SCAN_DENTRY_UNSUPPORTED:
48         case WIMLIB_SCAN_DENTRY_EXCLUDED:
49         case WIMLIB_SCAN_DENTRY_EXCLUDED_SYMLINK:
50                 if (!(params->add_flags & WIMLIB_ADD_FLAG_EXCLUDE_VERBOSE))
51                         return;
52         }
53         params->progress.scan.status = status;
54         if (status == WIMLIB_SCAN_DENTRY_OK && inode->i_nlink == 1) {
55                 const struct wim_lookup_table_entry *lte;
56                 for (unsigned i = 0; i <= inode->i_num_ads; i++) {
57                         lte = inode_stream_lte_resolved(inode, i);
58                         if (lte != NULL)
59                                 params->progress.scan.num_bytes_scanned += lte->size;
60                 }
61                 if (inode->i_attributes & FILE_ATTRIBUTE_DIRECTORY)
62                         params->progress.scan.num_dirs_scanned++;
63                 else
64                         params->progress.scan.num_nondirs_scanned++;
65         }
66         if (params->progress_func) {
67                 params->progress_func(WIMLIB_PROGRESS_MSG_SCAN_DENTRY,
68                                       &params->progress);
69         }
70 }
71
72 static int
73 mangle_pat(tchar *pat, const tchar *path, unsigned long line_no)
74 {
75         /* Remove quotes  */
76         if (pat[0] == T('"') || pat[0] == T('\'')) {
77                 tchar quote = pat[0];
78                 tchar *last = pat + tstrlen(pat) - 1;
79                 if (last > pat && *last == quote) {
80                         tmemmove(pat, pat + 1, last - (pat + 1));
81                         *(last - 1) = T('\0');
82                 }
83         }
84
85         if (!is_any_path_separator(pat[0]) &&
86             pat[0] != T('\0') && pat[1] == T(':'))
87         {
88                 /* Pattern begins with drive letter */
89                 if (!is_any_path_separator(pat[2])) {
90                         /* Something like c:file, which is actually a path
91                          * relative to the current working directory on the c:
92                          * drive.  We require paths with drive letters to be
93                          * absolute. */
94                         ERROR("%"TS":%lu: Invalid path \"%"TS"\"; paths including "
95                               "drive letters must be absolute!\n"
96                               "        Maybe try \"%"TC":\\%"TS"\"?",
97                               path, line_no, pat, pat[0], &pat[2]);
98                         return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
99                 }
100
101                 WARNING("%"TS":%lu: Pattern \"%"TS"\" starts with a drive "
102                         "letter, which is being removed.",
103                         path, line_no, pat);
104
105                 /* Strip the drive letter.  */
106                 tmemmove(pat, pat + 2, tstrlen(pat + 2) + 1);
107         }
108
109         /* Translate all possible path separators into the operating system's
110          * preferred path separator.  */
111         for (tchar *p = pat; *p; p++)
112                 if (is_any_path_separator(*p))
113                         *p = OS_PREFERRED_PATH_SEPARATOR;
114         return 0;
115 }
116
117 int
118 do_read_capture_config_file(const tchar *config_file, tchar *buf, size_t buflen,
119                             struct capture_config *config)
120 {
121         int ret;
122         struct text_file_section sections[] = {
123                 {T("ExclusionList"),
124                         &config->exclusion_pats},
125                 {T("ExclusionException"),
126                         &config->exclusion_exception_pats},
127         };
128
129         ret = do_load_text_file(config_file, buf, buflen, &buf,
130                                 sections, ARRAY_LEN(sections), mangle_pat);
131         if (ret)
132                 return ret;
133
134         config->buf = buf;
135         return 0;
136 }
137
138 void
139 destroy_capture_config(struct capture_config *config)
140 {
141         FREE(config->exclusion_pats.strings);
142         FREE(config->exclusion_exception_pats.strings);
143         FREE(config->buf);
144 }
145
146 static bool
147 match_pattern(const tchar *path,
148               const tchar *path_basename,
149               const struct string_set *list)
150 {
151         for (size_t i = 0; i < list->num_strings; i++) {
152
153                 const tchar *pat = list->strings[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                 }
180         }
181         return false;
182 }
183
184 /* Return true if the image capture configuration file indicates we should
185  * exclude the filename @path from capture.
186  *
187  * If @exclude_prefix is %true, the part of the path up and including the name
188  * of the directory being captured is not included in the path for matching
189  * purposes.  This allows, for example, a pattern like /hiberfil.sys to match a
190  * file /mnt/windows7/hiberfil.sys if we are capturing the /mnt/windows7
191  * directory.
192  */
193 bool
194 exclude_path(const tchar *path, size_t path_len,
195              const struct capture_config *config, bool exclude_prefix)
196 {
197         if (!config)
198                 return false;
199         const tchar *basename = path_basename_with_len(path, path_len);
200         if (exclude_prefix) {
201                 wimlib_assert(path_len >= config->prefix_num_tchars);
202                 if (!tmemcmp(config->prefix, path, config->prefix_num_tchars) &&
203                     path[config->prefix_num_tchars] == OS_PREFERRED_PATH_SEPARATOR)
204                 {
205                         path += config->prefix_num_tchars;
206                 }
207         }
208         return match_pattern(path, basename, &config->exclusion_pats) &&
209                 !match_pattern(path, basename, &config->exclusion_exception_pats);
210
211 }