Initial update functionality (library only)
[wimlib] / src / capture_common.c
1 /*
2  * Copyright (C) 2013 Eric Biggers
3  *
4  * This file is part of wimlib, a library for working with WIM files.
5  *
6  * wimlib is free software; you can redistribute it and/or modify it under the
7  * terms of the GNU General Public License as published by the Free
8  * Software Foundation; either version 3 of the License, or (at your option)
9  * any later version.
10  *
11  * wimlib is distributed in the hope that it will be useful, but WITHOUT ANY
12  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
13  * A PARTICULAR PURPOSE. See the GNU General Public License for more
14  * details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with wimlib; if not, see http://www.gnu.org/licenses/.
18  */
19
20 #include "wimlib_internal.h"
21
22 #include <string.h>
23
24 #ifdef __WIN32__
25 #  include "win32.h"
26 #else
27 #  include <fnmatch.h>
28 #endif
29
30 static int
31 canonicalize_pat(tchar **pat_p)
32 {
33         tchar *pat = *pat_p;
34
35         /* Turn all backslashes in the pattern into forward slashes. */
36         zap_backslashes(pat);
37
38         if (*pat != T('/') && *pat != T('\0') && *(pat + 1) == T(':')) {
39                 /* Pattern begins with drive letter */
40                 if (*(pat + 2) != T('/')) {
41                         /* Something like c:file, which is actually a path
42                          * relative to the current working directory on the c:
43                          * drive.  We require paths with drive letters to be
44                          * absolute. */
45                         ERROR("Invalid path \"%"TS"\"; paths including drive letters "
46                               "must be absolute!", pat);
47                         ERROR("Maybe try \"%"TC":/%"TS"\"?",
48                               *pat, pat + 2);
49                         return WIMLIB_ERR_INVALID_CAPTURE_CONFIG;
50                 }
51
52                 WARNING("Pattern \"%"TS"\" starts with a drive letter, which is "
53                         "being removed.", pat);
54                 /* Strip the drive letter */
55                 pat += 2;
56                 *pat_p = pat;
57         }
58         return 0;
59 }
60
61 static int
62 canonicalize_pat_list(struct wimlib_pattern_list *pat_list)
63 {
64         int ret = 0;
65         for (size_t i = 0; i < pat_list->num_pats; i++) {
66                 ret = canonicalize_pat(&pat_list->pats[i]);
67                 if (ret)
68                         break;
69         }
70         return ret;
71 }
72
73 int
74 canonicalize_capture_config(struct wimlib_capture_config *config)
75 {
76         int ret = canonicalize_pat_list(&config->exclusion_pats);
77         if (ret)
78                 return ret;
79         return canonicalize_pat_list(&config->exclusion_exception_pats);
80 }
81
82 static bool
83 copy_pattern_list(struct wimlib_pattern_list *copy,
84                   const struct wimlib_pattern_list *list)
85 {
86         copy->pats = CALLOC(list->num_pats, sizeof(list->pats[0]));
87         if (!copy->pats)
88                 return false;
89         copy->num_pats = list->num_pats;
90         for (size_t i = 0; i < list->num_pats; i++) {
91                 copy->pats[i] = TSTRDUP(list->pats[i]);
92                 if (!copy->pats[i])
93                         return false;
94         }
95         return true;
96 }
97
98 struct wimlib_capture_config *
99 copy_capture_config(const struct wimlib_capture_config *config)
100 {
101         struct wimlib_capture_config *copy;
102
103         copy = CALLOC(1, sizeof(struct wimlib_capture_config));
104         if (!copy)
105                 goto oom;
106         if (!copy_pattern_list(&copy->exclusion_pats, &config->exclusion_pats))
107                 goto oom;
108         if (!copy_pattern_list(&copy->exclusion_exception_pats,
109                                &config->exclusion_exception_pats))
110                 goto oom;
111         goto out;
112 oom:
113         free_capture_config(copy);
114         copy = NULL;
115 out:
116         return copy;
117 }
118
119 static void
120 destroy_pattern_list(struct wimlib_pattern_list *list)
121 {
122         for (size_t i = 0; i < list->num_pats; i++)
123                 FREE(list->pats[i]);
124         FREE(list->pats);
125 }
126
127 void
128 free_capture_config(struct wimlib_capture_config *config)
129 {
130         if (config) {
131                 destroy_pattern_list(&config->exclusion_pats);
132                 destroy_pattern_list(&config->exclusion_exception_pats);
133                 FREE(config);
134         }
135 }
136
137 static bool
138 match_pattern(const tchar *path,
139               const tchar *path_basename,
140               const struct wimlib_pattern_list *list)
141 {
142         for (size_t i = 0; i < list->num_pats; i++) {
143
144                 const tchar *pat = list->pats[i];
145                 const tchar *string;
146
147                 if (*pat == T('/')) {
148                         /* Absolute path from root of capture */
149                         string = path;
150                 } else {
151                         if (tstrchr(pat, T('/')))
152                                 /* Relative path from root of capture */
153                                 string = path + 1;
154                         else
155                                 /* A file name pattern */
156                                 string = path_basename;
157                 }
158
159                 /* Warning: on Windows native builds, fnmatch() calls the
160                  * replacement function in win32.c. */
161                 if (fnmatch(pat, string, FNM_PATHNAME | FNM_NOESCAPE
162                                 #ifdef FNM_CASEFOLD
163                                         | FNM_CASEFOLD
164                                 #endif
165                             ) == 0)
166                 {
167                         DEBUG("\"%"TS"\" matches the pattern \"%"TS"\"",
168                               string, pat);
169                         return true;
170                 } else {
171                         DEBUG2("\"%"TS"\" does not match the pattern \"%"TS"\"",
172                                string, pat);
173                 }
174         }
175         return false;
176 }
177
178 /* Return true if the image capture configuration file indicates we should
179  * exclude the filename @path from capture.
180  *
181  * If @exclude_prefix is %true, the part of the path up and including the name
182  * of the directory being captured is not included in the path for matching
183  * purposes.  This allows, for example, a pattern like /hiberfil.sys to match a
184  * file /mnt/windows7/hiberfil.sys if we are capturing the /mnt/windows7
185  * directory.
186  */
187 bool
188 exclude_path(const tchar *path, size_t path_len,
189              const struct wimlib_capture_config *config, bool exclude_prefix)
190 {
191         const tchar *basename = path_basename_with_len(path, path_len);
192         if (exclude_prefix) {
193                 wimlib_assert(path_len >= config->_prefix_num_tchars);
194                 if (!tmemcmp(config->_prefix, path, config->_prefix_num_tchars) &&
195                     path[config->_prefix_num_tchars] == T('/'))
196                 {
197                         path += config->_prefix_num_tchars;
198                 }
199         }
200         return match_pattern(path, basename, &config->exclusion_pats) &&
201                 !match_pattern(path, basename, &config->exclusion_exception_pats);
202
203 }