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