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