1*d685da8dShaad /* $NetBSD: dev-cache.c,v 1.4 2009/12/02 00:58:03 haad Exp $ */
256a34939Shaad
356a34939Shaad /*
456a34939Shaad * Copyright (C) 2001-2004 Sistina Software, Inc. All rights reserved.
556a34939Shaad * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
656a34939Shaad *
756a34939Shaad * This file is part of LVM2.
856a34939Shaad *
956a34939Shaad * This copyrighted material is made available to anyone wishing to use,
1056a34939Shaad * modify, copy, or redistribute it subject to the terms and conditions
1156a34939Shaad * of the GNU Lesser General Public License v.2.1.
1256a34939Shaad *
1356a34939Shaad * You should have received a copy of the GNU Lesser General Public License
1456a34939Shaad * along with this program; if not, write to the Free Software Foundation,
1556a34939Shaad * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
1656a34939Shaad */
1756a34939Shaad
1856a34939Shaad #include "lib.h"
1956a34939Shaad #include "dev-cache.h"
2056a34939Shaad #include "lvm-types.h"
2156a34939Shaad #include "btree.h"
2256a34939Shaad #include "filter.h"
2356a34939Shaad #include "filter-persistent.h"
2456a34939Shaad #include "toolcontext.h"
2556a34939Shaad
2656a34939Shaad #include <unistd.h>
2756a34939Shaad #include <sys/param.h>
2856a34939Shaad #include <dirent.h>
2956a34939Shaad
30c6f73bb8Shaad #ifdef __NetBSD__
31c6f73bb8Shaad #include "netbsd.h"
32c6f73bb8Shaad #endif
33c6f73bb8Shaad
3456a34939Shaad struct dev_iter {
3556a34939Shaad struct btree_iter *current;
3656a34939Shaad struct dev_filter *filter;
3756a34939Shaad };
3856a34939Shaad
3956a34939Shaad struct dir_list {
4056a34939Shaad struct dm_list list;
4156a34939Shaad char dir[0];
4256a34939Shaad };
4356a34939Shaad
4456a34939Shaad static struct {
4556a34939Shaad struct dm_pool *mem;
4656a34939Shaad struct dm_hash_table *names;
4756a34939Shaad struct btree *devices;
4856a34939Shaad struct dm_regex *preferred_names_matcher;
4956a34939Shaad
5056a34939Shaad int has_scanned;
5156a34939Shaad struct dm_list dirs;
5256a34939Shaad struct dm_list files;
5356a34939Shaad
5456a34939Shaad } _cache;
5556a34939Shaad
5656a34939Shaad #define _alloc(x) dm_pool_zalloc(_cache.mem, (x))
5756a34939Shaad #define _free(x) dm_pool_free(_cache.mem, (x))
5856a34939Shaad #define _strdup(x) dm_pool_strdup(_cache.mem, (x))
5956a34939Shaad
6056a34939Shaad static int _insert(const char *path, int rec);
6156a34939Shaad
dev_create_file(const char * filename,struct device * dev,struct str_list * alias,int use_malloc)6256a34939Shaad struct device *dev_create_file(const char *filename, struct device *dev,
6356a34939Shaad struct str_list *alias, int use_malloc)
6456a34939Shaad {
6556a34939Shaad int allocate = !dev;
6656a34939Shaad
6756a34939Shaad if (allocate) {
6856a34939Shaad if (use_malloc) {
6956a34939Shaad if (!(dev = dm_malloc(sizeof(*dev)))) {
7056a34939Shaad log_error("struct device allocation failed");
7156a34939Shaad return NULL;
7256a34939Shaad }
7356a34939Shaad if (!(alias = dm_malloc(sizeof(*alias)))) {
7456a34939Shaad log_error("struct str_list allocation failed");
7556a34939Shaad dm_free(dev);
7656a34939Shaad return NULL;
7756a34939Shaad }
7856a34939Shaad if (!(alias->str = dm_strdup(filename))) {
7956a34939Shaad log_error("filename strdup failed");
8056a34939Shaad dm_free(dev);
8156a34939Shaad dm_free(alias);
8256a34939Shaad return NULL;
8356a34939Shaad }
8456a34939Shaad dev->flags = DEV_ALLOCED;
8556a34939Shaad } else {
8656a34939Shaad if (!(dev = _alloc(sizeof(*dev)))) {
8756a34939Shaad log_error("struct device allocation failed");
8856a34939Shaad return NULL;
8956a34939Shaad }
9056a34939Shaad if (!(alias = _alloc(sizeof(*alias)))) {
9156a34939Shaad log_error("struct str_list allocation failed");
9256a34939Shaad _free(dev);
9356a34939Shaad return NULL;
9456a34939Shaad }
9556a34939Shaad if (!(alias->str = _strdup(filename))) {
9656a34939Shaad log_error("filename strdup failed");
9756a34939Shaad return NULL;
9856a34939Shaad }
9956a34939Shaad }
10056a34939Shaad } else if (!(alias->str = dm_strdup(filename))) {
10156a34939Shaad log_error("filename strdup failed");
10256a34939Shaad return NULL;
10356a34939Shaad }
10456a34939Shaad
10556a34939Shaad dev->flags |= DEV_REGULAR;
10656a34939Shaad dm_list_init(&dev->aliases);
10756a34939Shaad dm_list_add(&dev->aliases, &alias->list);
10856a34939Shaad dev->end = UINT64_C(0);
10956a34939Shaad dev->dev = 0;
11056a34939Shaad dev->fd = -1;
11156a34939Shaad dev->open_count = 0;
11256a34939Shaad dev->block_size = -1;
113*d685da8dShaad dev->read_ahead = -1;
11456a34939Shaad memset(dev->pvid, 0, sizeof(dev->pvid));
11556a34939Shaad dm_list_init(&dev->open_list);
11656a34939Shaad
11756a34939Shaad return dev;
11856a34939Shaad }
11956a34939Shaad
_dev_create(dev_t d)12056a34939Shaad static struct device *_dev_create(dev_t d)
12156a34939Shaad {
12256a34939Shaad struct device *dev;
12356a34939Shaad
12456a34939Shaad if (!(dev = _alloc(sizeof(*dev)))) {
12556a34939Shaad log_error("struct device allocation failed");
12656a34939Shaad return NULL;
12756a34939Shaad }
12856a34939Shaad dev->flags = 0;
12956a34939Shaad dm_list_init(&dev->aliases);
13056a34939Shaad dev->dev = d;
13156a34939Shaad dev->fd = -1;
13256a34939Shaad dev->open_count = 0;
13356a34939Shaad dev->block_size = -1;
134*d685da8dShaad dev->read_ahead = -1;
13556a34939Shaad dev->end = UINT64_C(0);
13656a34939Shaad memset(dev->pvid, 0, sizeof(dev->pvid));
13756a34939Shaad dm_list_init(&dev->open_list);
13856a34939Shaad
13956a34939Shaad return dev;
14056a34939Shaad }
14156a34939Shaad
dev_set_preferred_name(struct str_list * sl,struct device * dev)14256a34939Shaad void dev_set_preferred_name(struct str_list *sl, struct device *dev)
14356a34939Shaad {
14456a34939Shaad /*
14556a34939Shaad * Don't interfere with ordering specified in config file.
14656a34939Shaad */
14756a34939Shaad if (_cache.preferred_names_matcher)
14856a34939Shaad return;
14956a34939Shaad
15056a34939Shaad log_debug("%s: New preferred name", sl->str);
15156a34939Shaad dm_list_del(&sl->list);
15256a34939Shaad dm_list_add_h(&dev->aliases, &sl->list);
15356a34939Shaad }
15456a34939Shaad
15556a34939Shaad /* Return 1 if we prefer path1 else return 0 */
_compare_paths(const char * path0,const char * path1)15656a34939Shaad static int _compare_paths(const char *path0, const char *path1)
15756a34939Shaad {
15856a34939Shaad int slash0 = 0, slash1 = 0;
15956a34939Shaad int m0, m1;
16056a34939Shaad const char *p;
16156a34939Shaad char p0[PATH_MAX], p1[PATH_MAX];
16256a34939Shaad char *s0, *s1;
16356a34939Shaad struct stat stat0, stat1;
16456a34939Shaad
16556a34939Shaad /*
16656a34939Shaad * FIXME Better to compare patterns one-at-a-time against all names.
16756a34939Shaad */
16856a34939Shaad if (_cache.preferred_names_matcher) {
16956a34939Shaad m0 = dm_regex_match(_cache.preferred_names_matcher, path0);
17056a34939Shaad m1 = dm_regex_match(_cache.preferred_names_matcher, path1);
17156a34939Shaad
17256a34939Shaad if (m0 != m1) {
17356a34939Shaad if (m0 < 0)
17456a34939Shaad return 1;
17556a34939Shaad if (m1 < 0)
17656a34939Shaad return 0;
17756a34939Shaad if (m0 < m1)
17856a34939Shaad return 1;
17956a34939Shaad if (m1 < m0)
18056a34939Shaad return 0;
18156a34939Shaad }
18256a34939Shaad }
18356a34939Shaad
18456a34939Shaad /*
18556a34939Shaad * Built-in rules.
18656a34939Shaad */
18756a34939Shaad
18856a34939Shaad /* Return the path with fewer slashes */
18956a34939Shaad for (p = path0; p++; p = (const char *) strchr(p, '/'))
19056a34939Shaad slash0++;
19156a34939Shaad
19256a34939Shaad for (p = path1; p++; p = (const char *) strchr(p, '/'))
19356a34939Shaad slash1++;
19456a34939Shaad
19556a34939Shaad if (slash0 < slash1)
19656a34939Shaad return 0;
19756a34939Shaad if (slash1 < slash0)
19856a34939Shaad return 1;
19956a34939Shaad
20056a34939Shaad strncpy(p0, path0, PATH_MAX);
20156a34939Shaad strncpy(p1, path1, PATH_MAX);
20256a34939Shaad s0 = &p0[0] + 1;
20356a34939Shaad s1 = &p1[0] + 1;
20456a34939Shaad
20556a34939Shaad /* We prefer symlinks - they exist for a reason!
20656a34939Shaad * So we prefer a shorter path before the first symlink in the name.
20756a34939Shaad * FIXME Configuration option to invert this? */
20856a34939Shaad while (s0) {
20956a34939Shaad s0 = strchr(s0, '/');
21056a34939Shaad s1 = strchr(s1, '/');
21156a34939Shaad if (s0) {
21256a34939Shaad *s0 = '\0';
21356a34939Shaad *s1 = '\0';
21456a34939Shaad }
21556a34939Shaad if (lstat(p0, &stat0)) {
21656a34939Shaad log_sys_very_verbose("lstat", p0);
21756a34939Shaad return 1;
21856a34939Shaad }
21956a34939Shaad if (lstat(p1, &stat1)) {
22056a34939Shaad log_sys_very_verbose("lstat", p1);
22156a34939Shaad return 0;
22256a34939Shaad }
22356a34939Shaad if (S_ISLNK(stat0.st_mode) && !S_ISLNK(stat1.st_mode))
22456a34939Shaad return 0;
22556a34939Shaad if (!S_ISLNK(stat0.st_mode) && S_ISLNK(stat1.st_mode))
22656a34939Shaad return 1;
22756a34939Shaad if (s0) {
22856a34939Shaad *s0++ = '/';
22956a34939Shaad *s1++ = '/';
23056a34939Shaad }
23156a34939Shaad }
23256a34939Shaad
23356a34939Shaad /* ASCII comparison */
23456a34939Shaad if (strcmp(path0, path1) < 0)
23556a34939Shaad return 0;
23656a34939Shaad else
23756a34939Shaad return 1;
23856a34939Shaad }
23956a34939Shaad
_add_alias(struct device * dev,const char * path)24056a34939Shaad static int _add_alias(struct device *dev, const char *path)
24156a34939Shaad {
24256a34939Shaad struct str_list *sl = _alloc(sizeof(*sl));
24356a34939Shaad struct str_list *strl;
24456a34939Shaad const char *oldpath;
24556a34939Shaad int prefer_old = 1;
24656a34939Shaad
24756a34939Shaad if (!sl)
24856a34939Shaad return_0;
24956a34939Shaad
25056a34939Shaad /* Is name already there? */
25156a34939Shaad dm_list_iterate_items(strl, &dev->aliases) {
25256a34939Shaad if (!strcmp(strl->str, path)) {
25356a34939Shaad log_debug("%s: Already in device cache", path);
25456a34939Shaad return 1;
25556a34939Shaad }
25656a34939Shaad }
25756a34939Shaad
25856a34939Shaad if (!(sl->str = dm_pool_strdup(_cache.mem, path)))
25956a34939Shaad return_0;
26056a34939Shaad
26156a34939Shaad if (!dm_list_empty(&dev->aliases)) {
26256a34939Shaad oldpath = dm_list_item(dev->aliases.n, struct str_list)->str;
26356a34939Shaad prefer_old = _compare_paths(path, oldpath);
26456a34939Shaad log_debug("%s: Aliased to %s in device cache%s",
26556a34939Shaad path, oldpath, prefer_old ? "" : " (preferred name)");
26656a34939Shaad
26756a34939Shaad } else
26856a34939Shaad log_debug("%s: Added to device cache", path);
26956a34939Shaad
27056a34939Shaad if (prefer_old)
27156a34939Shaad dm_list_add(&dev->aliases, &sl->list);
27256a34939Shaad else
27356a34939Shaad dm_list_add_h(&dev->aliases, &sl->list);
27456a34939Shaad
27556a34939Shaad return 1;
27656a34939Shaad }
27756a34939Shaad
27856a34939Shaad /*
27956a34939Shaad * Either creates a new dev, or adds an alias to
28056a34939Shaad * an existing dev.
28156a34939Shaad */
_insert_dev(const char * path,dev_t d)28256a34939Shaad static int _insert_dev(const char *path, dev_t d)
28356a34939Shaad {
28456a34939Shaad struct device *dev;
28556a34939Shaad static dev_t loopfile_count = 0;
28656a34939Shaad int loopfile = 0;
28756a34939Shaad
28856a34939Shaad /* Generate pretend device numbers for loopfiles */
28956a34939Shaad if (!d) {
29056a34939Shaad if (dm_hash_lookup(_cache.names, path))
29156a34939Shaad return 1;
29256a34939Shaad d = ++loopfile_count;
29356a34939Shaad loopfile = 1;
29456a34939Shaad }
29556a34939Shaad
29656a34939Shaad /* is this device already registered ? */
29756a34939Shaad if (!(dev = (struct device *) btree_lookup(_cache.devices,
29856a34939Shaad (uint32_t) d))) {
29956a34939Shaad /* create new device */
30056a34939Shaad if (loopfile) {
30156a34939Shaad if (!(dev = dev_create_file(path, NULL, NULL, 0)))
30256a34939Shaad return_0;
30356a34939Shaad } else if (!(dev = _dev_create(d)))
30456a34939Shaad return_0;
30556a34939Shaad
30656a34939Shaad if (!(btree_insert(_cache.devices, (uint32_t) d, dev))) {
307*d685da8dShaad log_error("Couldn't insert device into binary tree.");
30856a34939Shaad _free(dev);
30956a34939Shaad return 0;
31056a34939Shaad }
31156a34939Shaad }
31256a34939Shaad
31356a34939Shaad if (!loopfile && !_add_alias(dev, path)) {
314*d685da8dShaad log_error("Couldn't add alias to dev cache.");
31556a34939Shaad return 0;
31656a34939Shaad }
31756a34939Shaad
31856a34939Shaad if (!dm_hash_insert(_cache.names, path, dev)) {
319*d685da8dShaad log_error("Couldn't add name to hash in dev cache.");
32056a34939Shaad return 0;
32156a34939Shaad }
32256a34939Shaad
32356a34939Shaad return 1;
32456a34939Shaad }
32556a34939Shaad
_join(const char * dir,const char * name)32656a34939Shaad static char *_join(const char *dir, const char *name)
32756a34939Shaad {
32856a34939Shaad size_t len = strlen(dir) + strlen(name) + 2;
32956a34939Shaad char *r = dm_malloc(len);
33056a34939Shaad if (r)
33156a34939Shaad snprintf(r, len, "%s/%s", dir, name);
33256a34939Shaad
33356a34939Shaad return r;
33456a34939Shaad }
33556a34939Shaad
33656a34939Shaad /*
33756a34939Shaad * Get rid of extra slashes in the path string.
33856a34939Shaad */
_collapse_slashes(char * str)33956a34939Shaad static void _collapse_slashes(char *str)
34056a34939Shaad {
34156a34939Shaad char *ptr;
34256a34939Shaad int was_slash = 0;
34356a34939Shaad
34456a34939Shaad for (ptr = str; *ptr; ptr++) {
34556a34939Shaad if (*ptr == '/') {
34656a34939Shaad if (was_slash)
34756a34939Shaad continue;
34856a34939Shaad
34956a34939Shaad was_slash = 1;
35056a34939Shaad } else
35156a34939Shaad was_slash = 0;
35256a34939Shaad *str++ = *ptr;
35356a34939Shaad }
35456a34939Shaad
35556a34939Shaad *str = *ptr;
35656a34939Shaad }
35756a34939Shaad
_insert_dir(const char * dir)35856a34939Shaad static int _insert_dir(const char *dir)
35956a34939Shaad {
36056a34939Shaad int n, dirent_count, r = 1;
36156a34939Shaad struct dirent **dirent;
36256a34939Shaad char *path;
36356a34939Shaad
36456a34939Shaad dirent_count = scandir(dir, &dirent, NULL, alphasort);
36556a34939Shaad if (dirent_count > 0) {
36656a34939Shaad for (n = 0; n < dirent_count; n++) {
36756a34939Shaad if (dirent[n]->d_name[0] == '.') {
36856a34939Shaad free(dirent[n]);
36956a34939Shaad continue;
37056a34939Shaad }
37156a34939Shaad
37256a34939Shaad if (!(path = _join(dir, dirent[n]->d_name)))
37356a34939Shaad return_0;
37456a34939Shaad
37556a34939Shaad _collapse_slashes(path);
37656a34939Shaad r &= _insert(path, 1);
37756a34939Shaad dm_free(path);
37856a34939Shaad
37956a34939Shaad free(dirent[n]);
38056a34939Shaad }
38156a34939Shaad free(dirent);
38256a34939Shaad }
38356a34939Shaad
38456a34939Shaad return r;
38556a34939Shaad }
38656a34939Shaad
_insert_file(const char * path)38756a34939Shaad static int _insert_file(const char *path)
38856a34939Shaad {
38956a34939Shaad struct stat info;
39056a34939Shaad
39156a34939Shaad if (stat(path, &info) < 0) {
39256a34939Shaad log_sys_very_verbose("stat", path);
39356a34939Shaad return 0;
39456a34939Shaad }
39556a34939Shaad
39656a34939Shaad if (!S_ISREG(info.st_mode)) {
39756a34939Shaad log_debug("%s: Not a regular file", path);
39856a34939Shaad return 0;
39956a34939Shaad }
40056a34939Shaad
40156a34939Shaad if (!_insert_dev(path, 0))
40256a34939Shaad return_0;
40356a34939Shaad
40456a34939Shaad return 1;
40556a34939Shaad }
40656a34939Shaad
_insert(const char * path,int rec)40756a34939Shaad static int _insert(const char *path, int rec)
40856a34939Shaad {
40956a34939Shaad struct stat info;
41056a34939Shaad int r = 0;
41156a34939Shaad
41256a34939Shaad if (stat(path, &info) < 0) {
41356a34939Shaad log_sys_very_verbose("stat", path);
41456a34939Shaad return 0;
41556a34939Shaad }
41656a34939Shaad
41756a34939Shaad if (S_ISDIR(info.st_mode)) { /* add a directory */
41856a34939Shaad /* check it's not a symbolic link */
41956a34939Shaad if (lstat(path, &info) < 0) {
42056a34939Shaad log_sys_very_verbose("lstat", path);
42156a34939Shaad return 0;
42256a34939Shaad }
42356a34939Shaad
42456a34939Shaad if (S_ISLNK(info.st_mode)) {
42556a34939Shaad log_debug("%s: Symbolic link to directory", path);
42656a34939Shaad return 0;
42756a34939Shaad }
42856a34939Shaad
42956a34939Shaad if (rec)
43056a34939Shaad r = _insert_dir(path);
43156a34939Shaad
432c6f73bb8Shaad } else {
433c6f73bb8Shaad /* add a device */
434c6f73bb8Shaad #ifdef __NetBSD__
435c6f73bb8Shaad /*
436c6f73bb8Shaad * In NetBSD we have two different types of devices
437c6f73bb8Shaad * raw and block. I can insert only existing
438c6f73bb8Shaad * raw and block device.
439c6f73bb8Shaad */
440c954cd0eSjoerg if (S_ISBLK(info.st_mode)) {
441c954cd0eSjoerg log_debug("%s: Not a raw device", path);
442c6f73bb8Shaad return_0;
44356a34939Shaad }
444c954cd0eSjoerg if (nbsd_check_dev(MAJOR(info.st_rdev),path) < 0) {
445c954cd0eSjoerg log_debug("%s: Not a known raw device", path);
446c954cd0eSjoerg return_0;
447c954cd0eSjoerg }
448c6f73bb8Shaad #else
449c6f73bb8Shaad if (!S_ISBLK(info.st_mode))
450c6f73bb8Shaad log_debug("%s: Not a block device", path);
451c6f73bb8Shaad #endif
452c6f73bb8Shaad if (!_insert_dev(path, info.st_rdev)) {
45356a34939Shaad return_0;
454c6f73bb8Shaad }
45556a34939Shaad
45656a34939Shaad r = 1;
45756a34939Shaad }
45856a34939Shaad
45956a34939Shaad return r;
46056a34939Shaad }
46156a34939Shaad
_full_scan(int dev_scan)46256a34939Shaad static void _full_scan(int dev_scan)
46356a34939Shaad {
46456a34939Shaad struct dir_list *dl;
46556a34939Shaad
46656a34939Shaad if (_cache.has_scanned && !dev_scan)
46756a34939Shaad return;
46856a34939Shaad
46956a34939Shaad dm_list_iterate_items(dl, &_cache.dirs)
47056a34939Shaad _insert_dir(dl->dir);
47156a34939Shaad
47256a34939Shaad dm_list_iterate_items(dl, &_cache.files)
47356a34939Shaad _insert_file(dl->dir);
47456a34939Shaad
47556a34939Shaad _cache.has_scanned = 1;
47656a34939Shaad init_full_scan_done(1);
47756a34939Shaad }
47856a34939Shaad
dev_cache_has_scanned(void)47956a34939Shaad int dev_cache_has_scanned(void)
48056a34939Shaad {
48156a34939Shaad return _cache.has_scanned;
48256a34939Shaad }
48356a34939Shaad
dev_cache_scan(int do_scan)48456a34939Shaad void dev_cache_scan(int do_scan)
48556a34939Shaad {
48656a34939Shaad if (!do_scan)
48756a34939Shaad _cache.has_scanned = 1;
48856a34939Shaad else
48956a34939Shaad _full_scan(1);
49056a34939Shaad }
49156a34939Shaad
_init_preferred_names(struct cmd_context * cmd)49256a34939Shaad static int _init_preferred_names(struct cmd_context *cmd)
49356a34939Shaad {
49456a34939Shaad const struct config_node *cn;
49556a34939Shaad struct config_value *v;
49656a34939Shaad struct dm_pool *scratch = NULL;
49756a34939Shaad char **regex;
49856a34939Shaad unsigned count = 0;
49956a34939Shaad int i, r = 0;
50056a34939Shaad
50156a34939Shaad _cache.preferred_names_matcher = NULL;
50256a34939Shaad
50356a34939Shaad if (!(cn = find_config_tree_node(cmd, "devices/preferred_names")) ||
50456a34939Shaad cn->v->type == CFG_EMPTY_ARRAY) {
50556a34939Shaad log_very_verbose("devices/preferred_names not found in config file: "
50656a34939Shaad "using built-in preferences");
50756a34939Shaad return 1;
50856a34939Shaad }
50956a34939Shaad
51056a34939Shaad for (v = cn->v; v; v = v->next) {
51156a34939Shaad if (v->type != CFG_STRING) {
51256a34939Shaad log_error("preferred_names patterns must be enclosed in quotes");
51356a34939Shaad return 0;
51456a34939Shaad }
51556a34939Shaad
51656a34939Shaad count++;
51756a34939Shaad }
51856a34939Shaad
51956a34939Shaad if (!(scratch = dm_pool_create("preferred device name matcher", 1024)))
52056a34939Shaad return_0;
52156a34939Shaad
52256a34939Shaad if (!(regex = dm_pool_alloc(scratch, sizeof(*regex) * count))) {
52356a34939Shaad log_error("Failed to allocate preferred device name "
52456a34939Shaad "pattern list.");
52556a34939Shaad goto out;
52656a34939Shaad }
52756a34939Shaad
52856a34939Shaad for (v = cn->v, i = count - 1; v; v = v->next, i--) {
52956a34939Shaad if (!(regex[i] = dm_pool_strdup(scratch, v->v.str))) {
53056a34939Shaad log_error("Failed to allocate a preferred device name "
53156a34939Shaad "pattern.");
53256a34939Shaad goto out;
53356a34939Shaad }
53456a34939Shaad }
53556a34939Shaad
53656a34939Shaad if (!(_cache.preferred_names_matcher =
53756a34939Shaad dm_regex_create(_cache.mem,(const char **) regex, count))) {
53856a34939Shaad log_error("Preferred device name pattern matcher creation failed.");
53956a34939Shaad goto out;
54056a34939Shaad }
54156a34939Shaad
54256a34939Shaad r = 1;
54356a34939Shaad
54456a34939Shaad out:
54556a34939Shaad dm_pool_destroy(scratch);
54656a34939Shaad
54756a34939Shaad return r;
54856a34939Shaad }
54956a34939Shaad
dev_cache_init(struct cmd_context * cmd)55056a34939Shaad int dev_cache_init(struct cmd_context *cmd)
55156a34939Shaad {
55256a34939Shaad _cache.names = NULL;
55356a34939Shaad _cache.has_scanned = 0;
55456a34939Shaad
55556a34939Shaad if (!(_cache.mem = dm_pool_create("dev_cache", 10 * 1024)))
55656a34939Shaad return_0;
55756a34939Shaad
55856a34939Shaad if (!(_cache.names = dm_hash_create(128))) {
55956a34939Shaad dm_pool_destroy(_cache.mem);
56056a34939Shaad _cache.mem = 0;
56156a34939Shaad return_0;
56256a34939Shaad }
56356a34939Shaad
56456a34939Shaad if (!(_cache.devices = btree_create(_cache.mem))) {
565*d685da8dShaad log_error("Couldn't create binary tree for dev-cache.");
56656a34939Shaad goto bad;
56756a34939Shaad }
56856a34939Shaad
56956a34939Shaad dm_list_init(&_cache.dirs);
57056a34939Shaad dm_list_init(&_cache.files);
57156a34939Shaad
57256a34939Shaad if (!_init_preferred_names(cmd))
57356a34939Shaad goto_bad;
57456a34939Shaad
57556a34939Shaad return 1;
57656a34939Shaad
57756a34939Shaad bad:
57856a34939Shaad dev_cache_exit();
57956a34939Shaad return 0;
58056a34939Shaad }
58156a34939Shaad
_check_closed(struct device * dev)58256a34939Shaad static void _check_closed(struct device *dev)
58356a34939Shaad {
58456a34939Shaad if (dev->fd >= 0)
585*d685da8dShaad log_error("Device '%s' has been left open.", dev_name(dev));
58656a34939Shaad }
58756a34939Shaad
_check_for_open_devices(void)58856a34939Shaad static void _check_for_open_devices(void)
58956a34939Shaad {
59056a34939Shaad dm_hash_iter(_cache.names, (dm_hash_iterate_fn) _check_closed);
59156a34939Shaad }
59256a34939Shaad
dev_cache_exit(void)59356a34939Shaad void dev_cache_exit(void)
59456a34939Shaad {
59556a34939Shaad if (_cache.names)
59656a34939Shaad _check_for_open_devices();
59756a34939Shaad
59856a34939Shaad if (_cache.preferred_names_matcher)
59956a34939Shaad _cache.preferred_names_matcher = NULL;
60056a34939Shaad
60156a34939Shaad if (_cache.mem) {
60256a34939Shaad dm_pool_destroy(_cache.mem);
60356a34939Shaad _cache.mem = NULL;
60456a34939Shaad }
60556a34939Shaad
60656a34939Shaad if (_cache.names) {
60756a34939Shaad dm_hash_destroy(_cache.names);
60856a34939Shaad _cache.names = NULL;
60956a34939Shaad }
61056a34939Shaad
61156a34939Shaad _cache.devices = NULL;
61256a34939Shaad _cache.has_scanned = 0;
61356a34939Shaad dm_list_init(&_cache.dirs);
61456a34939Shaad dm_list_init(&_cache.files);
61556a34939Shaad }
61656a34939Shaad
dev_cache_add_dir(const char * path)61756a34939Shaad int dev_cache_add_dir(const char *path)
61856a34939Shaad {
61956a34939Shaad struct dir_list *dl;
62056a34939Shaad struct stat st;
62156a34939Shaad
62256a34939Shaad if (stat(path, &st)) {
62356a34939Shaad log_error("Ignoring %s: %s", path, strerror(errno));
62456a34939Shaad /* But don't fail */
62556a34939Shaad return 1;
62656a34939Shaad }
62756a34939Shaad
62856a34939Shaad if (!S_ISDIR(st.st_mode)) {
62956a34939Shaad log_error("Ignoring %s: Not a directory", path);
63056a34939Shaad return 1;
63156a34939Shaad }
63256a34939Shaad
63356a34939Shaad if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
63456a34939Shaad log_error("dir_list allocation failed");
63556a34939Shaad return 0;
63656a34939Shaad }
63756a34939Shaad
63856a34939Shaad strcpy(dl->dir, path);
63956a34939Shaad dm_list_add(&_cache.dirs, &dl->list);
64056a34939Shaad return 1;
64156a34939Shaad }
64256a34939Shaad
dev_cache_add_loopfile(const char * path)64356a34939Shaad int dev_cache_add_loopfile(const char *path)
64456a34939Shaad {
64556a34939Shaad struct dir_list *dl;
64656a34939Shaad struct stat st;
64756a34939Shaad
64856a34939Shaad if (stat(path, &st)) {
64956a34939Shaad log_error("Ignoring %s: %s", path, strerror(errno));
65056a34939Shaad /* But don't fail */
65156a34939Shaad return 1;
65256a34939Shaad }
65356a34939Shaad
65456a34939Shaad if (!S_ISREG(st.st_mode)) {
65556a34939Shaad log_error("Ignoring %s: Not a regular file", path);
65656a34939Shaad return 1;
65756a34939Shaad }
65856a34939Shaad
65956a34939Shaad if (!(dl = _alloc(sizeof(*dl) + strlen(path) + 1))) {
66056a34939Shaad log_error("dir_list allocation failed for file");
66156a34939Shaad return 0;
66256a34939Shaad }
66356a34939Shaad
66456a34939Shaad strcpy(dl->dir, path);
66556a34939Shaad dm_list_add(&_cache.files, &dl->list);
66656a34939Shaad return 1;
66756a34939Shaad }
66856a34939Shaad
66956a34939Shaad /* Check cached device name is still valid before returning it */
67056a34939Shaad /* This should be a rare occurrence */
67156a34939Shaad /* set quiet if the cache is expected to be out-of-date */
67256a34939Shaad /* FIXME Make rest of code pass/cache struct device instead of dev_name */
dev_name_confirmed(struct device * dev,int quiet)67356a34939Shaad const char *dev_name_confirmed(struct device *dev, int quiet)
67456a34939Shaad {
67556a34939Shaad struct stat buf;
67656a34939Shaad const char *name;
67756a34939Shaad int r;
67856a34939Shaad
67956a34939Shaad if ((dev->flags & DEV_REGULAR))
68056a34939Shaad return dev_name(dev);
68156a34939Shaad
68256a34939Shaad while ((r = stat(name = dm_list_item(dev->aliases.n,
68356a34939Shaad struct str_list)->str, &buf)) ||
68456a34939Shaad (buf.st_rdev != dev->dev)) {
68556a34939Shaad if (r < 0) {
68656a34939Shaad if (quiet)
68756a34939Shaad log_sys_debug("stat", name);
68856a34939Shaad else
68956a34939Shaad log_sys_error("stat", name);
69056a34939Shaad }
69156a34939Shaad if (quiet)
69256a34939Shaad log_debug("Path %s no longer valid for device(%d,%d)",
69356a34939Shaad name, (int) MAJOR(dev->dev),
69456a34939Shaad (int) MINOR(dev->dev));
69556a34939Shaad else
69656a34939Shaad log_error("Path %s no longer valid for device(%d,%d)",
69756a34939Shaad name, (int) MAJOR(dev->dev),
69856a34939Shaad (int) MINOR(dev->dev));
69956a34939Shaad
70056a34939Shaad /* Remove the incorrect hash entry */
70156a34939Shaad dm_hash_remove(_cache.names, name);
70256a34939Shaad
70356a34939Shaad /* Leave list alone if there isn't an alternative name */
70456a34939Shaad /* so dev_name will always find something to return. */
70556a34939Shaad /* Otherwise add the name to the correct device. */
70656a34939Shaad if (dm_list_size(&dev->aliases) > 1) {
70756a34939Shaad dm_list_del(dev->aliases.n);
70856a34939Shaad if (!r)
70956a34939Shaad _insert(name, 0);
71056a34939Shaad continue;
71156a34939Shaad }
71256a34939Shaad
71356a34939Shaad /* Scanning issues this inappropriately sometimes. */
71456a34939Shaad log_debug("Aborting - please provide new pathname for what "
71556a34939Shaad "used to be %s", name);
71656a34939Shaad return NULL;
71756a34939Shaad }
71856a34939Shaad
71956a34939Shaad return dev_name(dev);
72056a34939Shaad }
72156a34939Shaad
dev_cache_get(const char * name,struct dev_filter * f)72256a34939Shaad struct device *dev_cache_get(const char *name, struct dev_filter *f)
72356a34939Shaad {
72456a34939Shaad struct stat buf;
72556a34939Shaad struct device *d = (struct device *) dm_hash_lookup(_cache.names, name);
72656a34939Shaad
72756a34939Shaad if (d && (d->flags & DEV_REGULAR))
72856a34939Shaad return d;
72956a34939Shaad
73056a34939Shaad /* If the entry's wrong, remove it */
73156a34939Shaad if (d && (stat(name, &buf) || (buf.st_rdev != d->dev))) {
73256a34939Shaad dm_hash_remove(_cache.names, name);
73356a34939Shaad d = NULL;
73456a34939Shaad }
73556a34939Shaad
73656a34939Shaad if (!d) {
73756a34939Shaad _insert(name, 0);
73856a34939Shaad d = (struct device *) dm_hash_lookup(_cache.names, name);
73956a34939Shaad if (!d) {
74056a34939Shaad _full_scan(0);
74156a34939Shaad d = (struct device *) dm_hash_lookup(_cache.names, name);
74256a34939Shaad }
74356a34939Shaad }
74456a34939Shaad
74556a34939Shaad return (d && (!f || (d->flags & DEV_REGULAR) ||
74656a34939Shaad f->passes_filter(f, d))) ? d : NULL;
74756a34939Shaad }
74856a34939Shaad
dev_iter_create(struct dev_filter * f,int dev_scan)74956a34939Shaad struct dev_iter *dev_iter_create(struct dev_filter *f, int dev_scan)
75056a34939Shaad {
75156a34939Shaad struct dev_iter *di = dm_malloc(sizeof(*di));
75256a34939Shaad
75356a34939Shaad if (!di) {
75456a34939Shaad log_error("dev_iter allocation failed");
75556a34939Shaad return NULL;
75656a34939Shaad }
75756a34939Shaad
75856a34939Shaad if (dev_scan && !trust_cache()) {
75956a34939Shaad /* Flag gets reset between each command */
76056a34939Shaad if (!full_scan_done())
76156a34939Shaad persistent_filter_wipe(f); /* Calls _full_scan(1) */
76256a34939Shaad } else
76356a34939Shaad _full_scan(0);
76456a34939Shaad
76556a34939Shaad di->current = btree_first(_cache.devices);
76656a34939Shaad di->filter = f;
76756a34939Shaad
76856a34939Shaad return di;
76956a34939Shaad }
77056a34939Shaad
dev_iter_destroy(struct dev_iter * iter)77156a34939Shaad void dev_iter_destroy(struct dev_iter *iter)
77256a34939Shaad {
77356a34939Shaad dm_free(iter);
77456a34939Shaad }
77556a34939Shaad
_iter_next(struct dev_iter * iter)77656a34939Shaad static struct device *_iter_next(struct dev_iter *iter)
77756a34939Shaad {
77856a34939Shaad struct device *d = btree_get_data(iter->current);
77956a34939Shaad iter->current = btree_next(iter->current);
78056a34939Shaad return d;
78156a34939Shaad }
78256a34939Shaad
dev_iter_get(struct dev_iter * iter)78356a34939Shaad struct device *dev_iter_get(struct dev_iter *iter)
78456a34939Shaad {
78556a34939Shaad while (iter->current) {
78656a34939Shaad struct device *d = _iter_next(iter);
78756a34939Shaad if (!iter->filter || (d->flags & DEV_REGULAR) ||
78856a34939Shaad iter->filter->passes_filter(iter->filter, d))
78956a34939Shaad return d;
79056a34939Shaad }
79156a34939Shaad
79256a34939Shaad return NULL;
79356a34939Shaad }
79456a34939Shaad
dev_fd(struct device * dev)79556a34939Shaad int dev_fd(struct device *dev)
79656a34939Shaad {
79756a34939Shaad return dev->fd;
79856a34939Shaad }
79956a34939Shaad
dev_name(const struct device * dev)80056a34939Shaad const char *dev_name(const struct device *dev)
80156a34939Shaad {
80256a34939Shaad return (dev) ? dm_list_item(dev->aliases.n, struct str_list)->str :
80356a34939Shaad "unknown device";
80456a34939Shaad }
805