Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
3 : : * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
4 : : *
5 : : * This file is part of LVM2.
6 : : *
7 : : * This copyrighted material is made available to anyone wishing to use,
8 : : * modify, copy, or redistribute it subject to the terms and conditions
9 : : * of the GNU Lesser General Public License v.2.1.
10 : : *
11 : : * You should have received a copy of the GNU Lesser General Public License
12 : : * along with this program; if not, write to the Free Software Foundation,
13 : : * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
14 : : */
15 : :
16 : : #include "lib.h"
17 : : #include "config.h"
18 : : #include "dev-cache.h"
19 : : #include "filter-persistent.h"
20 : : #include "lvm-file.h"
21 : : #include "lvm-string.h"
22 : :
23 : : #include <sys/stat.h>
24 : : #include <fcntl.h>
25 : : #include <unistd.h>
26 : :
27 : : struct pfilter {
28 : : char *file;
29 : : struct dm_hash_table *devices;
30 : : struct dev_filter *real;
31 : : time_t ctime;
32 : : };
33 : :
34 : : /*
35 : : * The hash table holds one of these two states
36 : : * against each entry.
37 : : */
38 : : #define PF_BAD_DEVICE ((void *) 1)
39 : : #define PF_GOOD_DEVICE ((void *) 2)
40 : :
41 : 3 : static int _init_hash(struct pfilter *pf)
42 : : {
43 [ - + ]: 3 : if (pf->devices)
44 : 0 : dm_hash_destroy(pf->devices);
45 : :
46 [ - + ]: 3 : if (!(pf->devices = dm_hash_create(128)))
47 : 0 : return_0;
48 : :
49 : 3 : return 1;
50 : : }
51 : :
52 : 0 : int persistent_filter_wipe(struct dev_filter *f)
53 : : {
54 : 0 : struct pfilter *pf = (struct pfilter *) f->private;
55 : :
56 : 0 : log_verbose("Wiping cache of LVM-capable devices");
57 : 0 : dm_hash_wipe(pf->devices);
58 : :
59 : : /* Trigger complete device scan */
60 : 0 : dev_cache_scan(1);
61 : :
62 : 0 : return 1;
63 : : }
64 : :
65 : 1 : static int _read_array(struct pfilter *pf, struct config_tree *cft,
66 : : const char *path, void *data)
67 : : {
68 : : const struct config_node *cn;
69 : : struct config_value *cv;
70 : :
71 [ - + ]: 1 : if (!(cn = find_config_node(cft->root, path))) {
72 : 0 : log_very_verbose("Couldn't find %s array in '%s'",
73 : : path, pf->file);
74 : 0 : return 0;
75 : : }
76 : :
77 : : /*
78 : : * iterate through the array, adding
79 : : * devices as we go.
80 : : */
81 [ + + ]: 17 : for (cv = cn->v; cv; cv = cv->next) {
82 [ - + ]: 16 : if (cv->type != CFG_STRING) {
83 : 0 : log_verbose("Devices array contains a value "
84 : : "which is not a string ... ignoring");
85 : 0 : continue;
86 : : }
87 : :
88 [ - + ]: 16 : if (!dm_hash_insert(pf->devices, cv->v.str, data))
89 : 0 : log_verbose("Couldn't add '%s' to filter ... ignoring",
90 : : cv->v.str);
91 : : /* Populate dev_cache ourselves */
92 : 16 : dev_cache_get(cv->v.str, NULL);
93 : : }
94 : 1 : return 1;
95 : : }
96 : :
97 : 1 : int persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out)
98 : : {
99 : 1 : struct pfilter *pf = (struct pfilter *) f->private;
100 : : struct config_tree *cft;
101 : : struct stat info;
102 : 1 : int r = 0;
103 : :
104 [ + - ]: 1 : if (!stat(pf->file, &info))
105 : 1 : pf->ctime = info.st_ctime;
106 : : else {
107 : 0 : log_very_verbose("%s: stat failed: %s", pf->file,
108 : : strerror(errno));
109 : 0 : return_0;
110 : : }
111 : :
112 [ - + ]: 1 : if (!(cft = create_config_tree(pf->file, 1)))
113 : 0 : return_0;
114 : :
115 [ - + ]: 1 : if (!read_config_file(cft))
116 : 0 : goto_out;
117 : :
118 : 1 : _read_array(pf, cft, "persistent_filter_cache/valid_devices",
119 : : PF_GOOD_DEVICE);
120 : : /* We don't gain anything by holding invalid devices */
121 : : /* _read_array(pf, cft, "persistent_filter_cache/invalid_devices",
122 : : PF_BAD_DEVICE); */
123 : :
124 : : /* Did we find anything? */
125 [ + - ]: 1 : if (dm_hash_get_num_entries(pf->devices)) {
126 : : /* We populated dev_cache ourselves */
127 : 1 : dev_cache_scan(0);
128 : 1 : r = 1;
129 : : }
130 : :
131 : 1 : log_very_verbose("Loaded persistent filter cache from %s", pf->file);
132 : :
133 : : out:
134 [ + - ][ - + ]: 1 : if (r && cft_out)
135 : 0 : *cft_out = cft;
136 : : else
137 : 1 : destroy_config_tree(cft);
138 : 1 : return r;
139 : : }
140 : :
141 : 0 : static void _write_array(struct pfilter *pf, FILE *fp, const char *path,
142 : : void *data)
143 : : {
144 : : void *d;
145 : 0 : int first = 1;
146 : : char buf[2 * PATH_MAX];
147 : : struct dm_hash_node *n;
148 : :
149 [ # # ]: 0 : for (n = dm_hash_get_first(pf->devices); n;
150 : 0 : n = dm_hash_get_next(pf->devices, n)) {
151 : 0 : d = dm_hash_get_data(pf->devices, n);
152 : :
153 [ # # ]: 0 : if (d != data)
154 : 0 : continue;
155 : :
156 [ # # ]: 0 : if (!first)
157 : 0 : fprintf(fp, ",\n");
158 : : else {
159 : 0 : fprintf(fp, "\t%s=[\n", path);
160 : 0 : first = 0;
161 : : }
162 : :
163 : 0 : escape_double_quotes(buf, dm_hash_get_key(pf->devices, n));
164 : 0 : fprintf(fp, "\t\t\"%s\"", buf);
165 : : }
166 : :
167 [ # # ]: 0 : if (!first)
168 : 0 : fprintf(fp, "\n\t]\n");
169 : 0 : }
170 : :
171 : 1 : int persistent_filter_dump(struct dev_filter *f)
172 : : {
173 : : struct pfilter *pf;
174 : : char *tmp_file;
175 : : struct stat info, info2;
176 : 1 : struct config_tree *cft = NULL;
177 : : FILE *fp;
178 : : int lockfd;
179 : 1 : int r = 0;
180 : :
181 [ - + ]: 1 : if (!f)
182 : 0 : return_0;
183 : 1 : pf = (struct pfilter *) f->private;
184 : :
185 [ + - ]: 1 : if (!dm_hash_get_num_entries(pf->devices)) {
186 : 1 : log_very_verbose("Internal persistent device cache empty "
187 : : "- not writing to %s", pf->file);
188 : 1 : return 0;
189 : : }
190 [ # # ]: 0 : if (!dev_cache_has_scanned()) {
191 : 0 : log_very_verbose("Device cache incomplete - not writing "
192 : : "to %s", pf->file);
193 : 0 : return 0;
194 : : }
195 : :
196 : 0 : log_very_verbose("Dumping persistent device cache to %s", pf->file);
197 : :
198 : : while (1) {
199 [ # # ]: 0 : if ((lockfd = fcntl_lock_file(pf->file, F_WRLCK, 0)) < 0)
200 : 0 : return_0;
201 : :
202 : : /*
203 : : * Ensure we locked the file we expected
204 : : */
205 [ # # ]: 0 : if (fstat(lockfd, &info)) {
206 : 0 : log_sys_error("fstat", pf->file);
207 : 0 : goto out;
208 : : }
209 [ # # ]: 0 : if (stat(pf->file, &info2)) {
210 : 0 : log_sys_error("stat", pf->file);
211 : 0 : goto out;
212 : : }
213 : :
214 [ # # ][ # # ]: 0 : if (is_same_inode(info, info2))
215 : : break;
216 : :
217 : 0 : fcntl_unlock_file(lockfd);
218 : 0 : }
219 : :
220 : : /*
221 : : * If file contents changed since we loaded it, merge new contents
222 : : */
223 [ # # ]: 0 : if (info.st_ctime != pf->ctime)
224 : : /* Keep cft open to avoid losing lock */
225 : 0 : persistent_filter_load(f, &cft);
226 : :
227 : 0 : tmp_file = alloca(strlen(pf->file) + 5);
228 : 0 : sprintf(tmp_file, "%s.tmp", pf->file);
229 : :
230 [ # # ]: 0 : if (!(fp = fopen(tmp_file, "w"))) {
231 : : /* EACCES has been reported over NFS */
232 [ # # ][ # # ]: 0 : if (errno != EROFS && errno != EACCES)
233 : 0 : log_sys_error("fopen", tmp_file);
234 : 0 : goto out;
235 : : }
236 : :
237 : 0 : fprintf(fp, "# This file is automatically maintained by lvm.\n\n");
238 : 0 : fprintf(fp, "persistent_filter_cache {\n");
239 : :
240 : 0 : _write_array(pf, fp, "valid_devices", PF_GOOD_DEVICE);
241 : : /* We don't gain anything by remembering invalid devices */
242 : : /* _write_array(pf, fp, "invalid_devices", PF_BAD_DEVICE); */
243 : :
244 : 0 : fprintf(fp, "}\n");
245 [ # # ]: 0 : if (lvm_fclose(fp, tmp_file))
246 : 0 : goto_out;
247 : :
248 [ # # ]: 0 : if (rename(tmp_file, pf->file))
249 : 0 : log_error("%s: rename to %s failed: %s", tmp_file, pf->file,
250 : : strerror(errno));
251 : :
252 : 0 : r = 1;
253 : :
254 : : out:
255 : 0 : fcntl_unlock_file(lockfd);
256 : :
257 [ # # ]: 0 : if (cft)
258 : 0 : destroy_config_tree(cft);
259 : :
260 : 1 : return r;
261 : : }
262 : :
263 : 39 : static int _lookup_p(struct dev_filter *f, struct device *dev)
264 : : {
265 : 39 : struct pfilter *pf = (struct pfilter *) f->private;
266 : 39 : void *l = dm_hash_lookup(pf->devices, dev_name(dev));
267 : : struct str_list *sl;
268 : :
269 [ + - ]: 39 : if (!l) {
270 [ - + ]: 39 : l = pf->real->passes_filter(pf->real, dev) ?
271 : : PF_GOOD_DEVICE : PF_BAD_DEVICE;
272 : :
273 [ + + ]: 160 : dm_list_iterate_items(sl, &dev->aliases)
274 : 121 : dm_hash_insert(pf->devices, sl->str, l);
275 : :
276 [ # # ]: 0 : } else if (l == PF_BAD_DEVICE)
277 : 0 : log_debug("%s: Skipping (cached)", dev_name(dev));
278 : :
279 : 39 : return (l == PF_BAD_DEVICE) ? 0 : 1;
280 : : }
281 : :
282 : 3 : static void _persistent_destroy(struct dev_filter *f)
283 : : {
284 : 3 : struct pfilter *pf = (struct pfilter *) f->private;
285 : :
286 : 3 : dm_hash_destroy(pf->devices);
287 : 3 : dm_free(pf->file);
288 : 3 : pf->real->destroy(pf->real);
289 : 3 : dm_free(pf);
290 : 3 : dm_free(f);
291 : 3 : }
292 : :
293 : 3 : struct dev_filter *persistent_filter_create(struct dev_filter *real,
294 : : const char *file)
295 : : {
296 : : struct pfilter *pf;
297 : 3 : struct dev_filter *f = NULL;
298 : :
299 [ - + ]: 3 : if (!(pf = dm_malloc(sizeof(*pf))))
300 : 0 : return_NULL;
301 : 3 : memset(pf, 0, sizeof(*pf));
302 : :
303 [ - + ]: 3 : if (!(pf->file = dm_malloc(strlen(file) + 1)))
304 : 0 : goto_bad;
305 : :
306 : 3 : strcpy(pf->file, file);
307 : 3 : pf->real = real;
308 : :
309 [ - + ]: 3 : if (!(_init_hash(pf))) {
310 : 0 : log_error("Couldn't create hash table for persistent filter.");
311 : 0 : goto bad;
312 : : }
313 : :
314 [ - + ]: 3 : if (!(f = dm_malloc(sizeof(*f))))
315 : 0 : goto_bad;
316 : :
317 : 3 : f->passes_filter = _lookup_p;
318 : 3 : f->destroy = _persistent_destroy;
319 : 3 : f->private = pf;
320 : :
321 : 3 : return f;
322 : :
323 : : bad:
324 : 0 : dm_free(pf->file);
325 [ # # ]: 0 : if (pf->devices)
326 : 0 : dm_hash_destroy(pf->devices);
327 : 0 : dm_free(pf);
328 : 0 : dm_free(f);
329 : 3 : return NULL;
330 : : }
|