99d6af668058310869887553c8349b978dbf8564
[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 int
73 mangle_pat(tchar *pat, const tchar *path, unsigned long line_no)
74 {
75         if (!is_any_path_separator(pat[0]) &&
76             pat[0] != T('\0') && pat[1] == T(':'))
77         {
78                 /* Pattern begins with drive letter */
79                 if (!is_any_path_separator(pat[2])) {
80                         /* Something like c:file, which is actually a path
81                          * relative to the current working directory on the c:
82                          * drive.  We require paths with drive letters to be
83                          * absolute. */
84                         ERROR("%"TS":%lu: Invalid pattern \"%"TS"\":\n"
85                               "        Patterns including drive letters must be absolute!\n"
86                               "        Maybe try \"%"TC":%"TC"%"TS"\"?\n",
87                               path, line_no, pat,
88                               pat[0], OS_PREFERRED_PATH_SEPARATOR, &pat[2]);
89                         return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
90                 }
91
92                 WARNING("%"TS":%lu: Pattern \"%"TS"\" starts with a drive "
93                         "letter, which is being removed.",
94                         path, line_no, pat);
95
96                 /* Strip the drive letter.  */
97                 tmemmove(pat, pat + 2, tstrlen(pat + 2) + 1);
98         }
99
100         /* Collapse and translate path separators.
101          *
102          * Note: we require that this works for filesystem paths and WIM paths,
103          * so the desired path separators must be the same.  */
104         BUILD_BUG_ON(OS_PREFERRED_PATH_SEPARATOR != WIM_PATH_SEPARATOR);
105         do_canonicalize_path(pat, pat);
106
107         /* Relative patterns can only match file names.  */
108         if (pat[0] != OS_PREFERRED_PATH_SEPARATOR &&
109             tstrchr(pat, OS_PREFERRED_PATH_SEPARATOR))
110         {
111                 ERROR("%"TS":%lu: Invalid path \"%"TS"\":\n"
112                       "        Relative patterns can only include one path component!\n"
113                       "        Maybe try \"%"TC"%"TS"\"?",
114                       path, line_no, pat, OS_PREFERRED_PATH_SEPARATOR, pat);
115                 return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
116         }
117
118         return 0;
119 }
120
121 int
122 do_read_capture_config_file(const tchar *config_file, const void *buf,
123                             size_t bufsize, struct capture_config *config)
124 {
125         int ret;
126
127         /* [PrepopulateList] is used for apply, not capture.  But since we do
128          * understand it, recognize it (avoiding unrecognized section warning)
129          * and discard the strings.  */
130         STRING_SET(prepopulate_pats);
131
132         struct text_file_section sections[] = {
133                 {T("ExclusionList"),
134                         &config->exclusion_pats},
135                 {T("ExclusionException"),
136                         &config->exclusion_exception_pats},
137                 {T("PrepopulateList"),
138                         &prepopulate_pats},
139         };
140         void *mem;
141
142         ret = do_load_text_file(config_file, buf, bufsize, &mem,
143                                 sections, ARRAY_LEN(sections),
144                                 LOAD_TEXT_FILE_REMOVE_QUOTES, mangle_pat);
145         if (ret)
146                 return ret;
147
148         FREE(prepopulate_pats.strings);
149
150         config->buf = mem;
151         return 0;
152 }
153
154 void
155 destroy_capture_config(struct capture_config *config)
156 {
157         FREE(config->exclusion_pats.strings);
158         FREE(config->exclusion_exception_pats.strings);
159         FREE(config->buf);
160 }
161
162 bool
163 match_pattern_list(const tchar *path, size_t path_len,
164                    const struct string_set *list)
165 {
166         for (size_t i = 0; i < list->num_strings; i++)
167                 if (match_path(path, path_len, list->strings[i],
168                                OS_PREFERRED_PATH_SEPARATOR, true))
169                         return true;
170         return false;
171 }
172
173 /*
174  * Return true if the image capture configuration file indicates we should
175  * exclude the filename @path from capture.
176  *
177  * The passed in @path must be given relative to the root of the capture, but
178  * with a leading path separator.  For example, if the file "in/file" is being
179  * tested and the library user ran wimlib_add_image(wim, "in", ...), then the
180  * directory "in" is the root of the capture and the path should be specified as
181  * "/file".
182  *
183  * Also, all path separators in @path must be OS_PREFERRED_PATH_SEPARATOR, and
184  * there cannot be trailing slashes.
185  *
186  * As a special case, the empty string will be interpreted as a single path
187  * separator.
188  */
189 bool
190 exclude_path(const tchar *path, size_t path_nchars,
191              const struct capture_config *config)
192 {
193         tchar dummy[2];
194
195         if (!config)
196                 return false;
197
198         if (!*path) {
199                 dummy[0] = OS_PREFERRED_PATH_SEPARATOR;
200                 dummy[1] = T('\0');
201                 path = dummy;
202         }
203
204         return match_pattern_list(path, path_nchars, &config->exclusion_pats) &&
205               !match_pattern_list(path, path_nchars, &config->exclusion_exception_pats);
206
207 }