1186457Sgnn/* $NetBSD$ */ 2186457Sgnn 3186457Sgnn/* 4186457Sgnn * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved. 5186457Sgnn * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved. 6186457Sgnn * 7186457Sgnn * This file is part of LVM2. 8186457Sgnn * 9186457Sgnn * This copyrighted material is made available to anyone wishing to use, 10186457Sgnn * modify, copy, or redistribute it subject to the terms and conditions 11186457Sgnn * of the GNU Lesser General Public License v.2.1. 12186457Sgnn * 13186457Sgnn * You should have received a copy of the GNU Lesser General Public License 14186457Sgnn * along with this program; if not, write to the Free Software Foundation, 15186457Sgnn * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 16186457Sgnn */ 17186457Sgnn 18186457Sgnn#include "lib.h" 19186457Sgnn#include "config.h" 20186457Sgnn#include "dev-cache.h" 21186457Sgnn#include "filter-persistent.h" 22186457Sgnn#include "lvm-file.h" 23186457Sgnn#include "lvm-string.h" 24186457Sgnn 25186457Sgnn#include <sys/stat.h> 26186457Sgnn#include <fcntl.h> 27186457Sgnn#include <unistd.h> 28186457Sgnn 29186457Sgnnstruct pfilter { 30186457Sgnn char *file; 31186457Sgnn struct dm_hash_table *devices; 32186457Sgnn struct dev_filter *real; 33186457Sgnn time_t ctime; 34186457Sgnn}; 35186457Sgnn 36186457Sgnn/* 37186457Sgnn * The hash table holds one of these two states 38186457Sgnn * against each entry. 39186457Sgnn */ 40186457Sgnn#define PF_BAD_DEVICE ((void *) 1) 41186457Sgnn#define PF_GOOD_DEVICE ((void *) 2) 42186457Sgnn 43186457Sgnnstatic int _init_hash(struct pfilter *pf) 44186457Sgnn{ 45186457Sgnn if (pf->devices) 46186457Sgnn dm_hash_destroy(pf->devices); 47186457Sgnn 48186457Sgnn if (!(pf->devices = dm_hash_create(128))) 49186457Sgnn return_0; 50186457Sgnn 51186457Sgnn return 1; 52186457Sgnn} 53186457Sgnn 54186457Sgnnint persistent_filter_wipe(struct dev_filter *f) 55186457Sgnn{ 56186457Sgnn struct pfilter *pf = (struct pfilter *) f->private; 57186457Sgnn 58186457Sgnn log_verbose("Wiping cache of LVM-capable devices"); 59186457Sgnn dm_hash_wipe(pf->devices); 60186457Sgnn 61186457Sgnn /* Trigger complete device scan */ 62186457Sgnn dev_cache_scan(1); 63186457Sgnn 64186457Sgnn return 1; 65186457Sgnn} 66186457Sgnn 67186457Sgnnstatic int _read_array(struct pfilter *pf, struct config_tree *cft, 68186457Sgnn const char *path, void *data) 69186457Sgnn{ 70186457Sgnn const struct config_node *cn; 71186457Sgnn struct config_value *cv; 72186457Sgnn 73186457Sgnn if (!(cn = find_config_node(cft->root, path))) { 74186457Sgnn log_very_verbose("Couldn't find %s array in '%s'", 75186457Sgnn path, pf->file); 76186457Sgnn return 0; 77186457Sgnn } 78186457Sgnn 79186457Sgnn /* 80186457Sgnn * iterate through the array, adding 81186457Sgnn * devices as we go. 82186457Sgnn */ 83186457Sgnn for (cv = cn->v; cv; cv = cv->next) { 84186457Sgnn if (cv->type != CFG_STRING) { 85186457Sgnn log_verbose("Devices array contains a value " 86186457Sgnn "which is not a string ... ignoring"); 87186457Sgnn continue; 88186457Sgnn } 89186457Sgnn 90186457Sgnn if (!dm_hash_insert(pf->devices, cv->v.str, data)) 91186457Sgnn log_verbose("Couldn't add '%s' to filter ... ignoring", 92186457Sgnn cv->v.str); 93186457Sgnn /* Populate dev_cache ourselves */ 94186457Sgnn dev_cache_get(cv->v.str, NULL); 95186457Sgnn } 96186457Sgnn return 1; 97186457Sgnn} 98186457Sgnn 99186457Sgnnint persistent_filter_load(struct dev_filter *f, struct config_tree **cft_out) 100186457Sgnn{ 101186457Sgnn struct pfilter *pf = (struct pfilter *) f->private; 102186457Sgnn struct config_tree *cft; 103186457Sgnn struct stat info; 104186457Sgnn int r = 0; 105186457Sgnn 106186457Sgnn if (!stat(pf->file, &info)) 107186457Sgnn pf->ctime = info.st_ctime; 108186457Sgnn else { 109186457Sgnn log_very_verbose("%s: stat failed: %s", pf->file, 110186457Sgnn strerror(errno)); 111186457Sgnn return_0; 112186457Sgnn } 113186457Sgnn 114186457Sgnn if (!(cft = create_config_tree(pf->file, 1))) 115186457Sgnn return_0; 116186457Sgnn 117186457Sgnn if (!read_config_file(cft)) 118186457Sgnn goto_out; 119186457Sgnn 120186457Sgnn _read_array(pf, cft, "persistent_filter_cache/valid_devices", 121186457Sgnn PF_GOOD_DEVICE); 122186457Sgnn /* We don't gain anything by holding invalid devices */ 123186457Sgnn /* _read_array(pf, cft, "persistent_filter_cache/invalid_devices", 124186457Sgnn PF_BAD_DEVICE); */ 125186457Sgnn 126186457Sgnn /* Did we find anything? */ 127186457Sgnn if (dm_hash_get_num_entries(pf->devices)) { 128186457Sgnn /* We populated dev_cache ourselves */ 129186457Sgnn dev_cache_scan(0); 130186457Sgnn r = 1; 131186457Sgnn } 132186457Sgnn 133186457Sgnn log_very_verbose("Loaded persistent filter cache from %s", pf->file); 134186457Sgnn 135186457Sgnn out: 136186457Sgnn if (r && cft_out) 137186457Sgnn *cft_out = cft; 138186457Sgnn else 139186457Sgnn destroy_config_tree(cft); 140186457Sgnn return r; 141186457Sgnn} 142186457Sgnn 143186457Sgnnstatic void _write_array(struct pfilter *pf, FILE *fp, const char *path, 144186457Sgnn void *data) 145186457Sgnn{ 146186457Sgnn void *d; 147186457Sgnn int first = 1; 148186457Sgnn char *buf, *str; 149186457Sgnn struct dm_hash_node *n; 150186457Sgnn 151186457Sgnn for (n = dm_hash_get_first(pf->devices); n; 152186457Sgnn n = dm_hash_get_next(pf->devices, n)) { 153186457Sgnn d = dm_hash_get_data(pf->devices, n); 154186457Sgnn 155186457Sgnn if (d != data) 156186457Sgnn continue; 157186457Sgnn 158186457Sgnn if (!first) 159186457Sgnn fprintf(fp, ",\n"); 160186457Sgnn else { 161186457Sgnn fprintf(fp, "\t%s=[\n", path); 162186457Sgnn first = 0; 163186457Sgnn } 164186457Sgnn 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 180int 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 263out: 264 fcntl_unlock_file(lockfd); 265 266 if (cft) 267 destroy_config_tree(cft); 268 269 return r; 270} 271 272static 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 291static 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 302struct 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