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