/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define DEV_EXPAND 32 #define DO_DISABLE 0 #define DO_ENABLE 1 /* * Utility functions for iiadm and rdcadm/sndradm. */ typedef struct hash_data_s { union { char *users; char *mode; } u; char *path; char *node; int setno; } hash_data_t; typedef struct { dev_t rdev; mode_t mode; char *path; } device_t; static hash_data_t *make_svol_data(char *, char *, char *, int); static hash_data_t *make_dsvol_data(char *, char *, char *, int); static void delete_svol_data(void *); static void delete_dsvol_data(void *); static int sv_action(char *, CFGFILE *, char *, int); static int add_dev_entry(const char *); static int compare(const void *, const void *); static char *find_devid(const char *); static void free_dev_entries(); static void rebuild_devhash(); static hash_node_t **dsvol; static int dsvol_loaded = 0; static hash_node_t **svol; static int svol_loaded = 0; static hash_node_t **shadowvol; static hash_node_t **devhash; static device_t *devlist; static int devcount = 0; static int devalloc = 0; /* * cfg_add_user * * Description: * Adds the calling tool as a user of the volume. * * Inputs: * char *path: The pathname of the volume to be enabled. * char *cnode: The device group name, or NULL if -C local or not cluster * CFGFILE *cfg: A pointer to the current config file, or NULL if this * function is to open/write/commit/close the change itself. * * Return values: * CFG_USER_FIRST: Indicates that this is the first user of this * particular volume. * CFG_USER_OK: Indicates that the volume has already been entered into * the config file. * CFG_USER_ERR: Indicates that some failure has occurred and no changes * to the config file have been made. * CFG_USER_REPEAT: Indicates that this user has already registered for * the volume. */ int cfg_add_user(CFGFILE* cfg, char *path, char *cnode, char *user) { int self_open, self_loaded, change_made; char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; int retval, rc; hash_data_t *data; self_open = (cfg == NULL); self_loaded = 0; change_made = 0; if (self_open) { cfg = cfg_open(NULL); if (cfg == NULL) { return (CFG_USER_ERR); } if (!cfg_lock(cfg, CFG_WRLOCK)) { /* oops */ cfg_close(cfg); return (CFG_USER_ERR); } } /* Check cnode */ ctag = cfg_get_resource(cfg); if (cnode) { if (ctag) { if (strcmp(cnode, ctag)) return (CFG_USER_ERR); } else cfg_resource(cfg, cnode); } else cnode = ctag; if (!dsvol_loaded) { if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } self_loaded = 1; } /* find the volume */ (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); data = nsc_lookup(dsvol, search_key); if (!data) { /* whoops, not found. Add as new user */ cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s", path, cnode, user); rc = cfg_put_cstring(cfg, "dsvol", buf, strlen(buf)); if (rc < 0) { if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } /* reload hash, if we need to */ if (!self_loaded) { cfg_unload_dsvols(); if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } } retval = CFG_USER_FIRST; change_made = 1; } else { /* Check to ensure we're not already listed */ char *p = strdup(data->u.users); char *q = strtok(p, ","); while (q && (strcmp(q, user) != 0)) { q = strtok(0, ","); } free(p); /* not using data; only testing 'q' ptr */ if (!q) { /* not listed as a user */ cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(buf, CFG_MAX_BUF, "%s %s %s,%s", data->path, data->node, data->u.users, user); (void) snprintf(search_key, CFG_MAX_KEY, "dsvol.set%d", data->setno); if (cfg_put_cstring(cfg, search_key, buf, strlen(buf)) < 0) { if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } /* * Since we deleted an entry from the config * file, we don't know what all the new * set numbers are. We need to reload * everything */ if (!self_loaded) { cfg_unload_dsvols(); if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } } change_made = 1; retval = CFG_USER_OK; } else { retval = CFG_USER_REPEAT; } } if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { if (change_made) (void) cfg_commit(cfg); cfg_close(cfg); } return (retval); } /* * cfg_rem_user * * Description: * Removes a user from the config file. * * Inputs: * char *path: The pathname of the volume to be enabled. * char *cnode: The device group name, or NULL if -C local or not cluster * char *user: The subsystem that is adding this tag (sv, ii, sndr) * CFGFILE *cfg: A pointer to the current config file, or NULL if this * function is to open/write/commit/close the change itself. * Return values: * CFG_USER_ERR: An error occurred during the processing of this * directive. * CFG_USER_OK: User successfully removed; volume in use by other(s). * CFG_USER_LAST: User successfuly removed; no other users registered * CFG_USER_GONE: The volume is no longer listed in the dsvol section, * indicating some sort of application-level error. * */ int cfg_rem_user(CFGFILE *cfg, char *path, char *cnode, char *user) { int self_open, self_loaded, change_made; char *ctag, search_key[ CFG_MAX_KEY ], buf[ CFG_MAX_BUF ]; char cfg_key[ CFG_MAX_KEY ]; hash_data_t *data; int retval; int force_remove; self_open = (cfg == NULL); self_loaded = 0; change_made = 0; force_remove = (strcmp(user, "sv") == 0); if ('-' == *user) { ++user; } /* Check cnode */ ctag = cfg_get_resource(cfg); if (cnode) { if (ctag) { if (strcmp(cnode, ctag)) return (CFG_USER_ERR); } else cfg_resource(cfg, cnode); } else cnode = ctag; if (self_open) { cfg = cfg_open(NULL); if (cfg == NULL) { return (CFG_USER_ERR); } if (!cfg_lock(cfg, CFG_WRLOCK)) { /* oops */ cfg_close(cfg); return (CFG_USER_ERR); } } change_made = 0; if (!dsvol_loaded) { if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } self_loaded = 1; } /* find the volume */ (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); data = nsc_lookup(dsvol, search_key); if (!data) { /* yipes */ retval = CFG_USER_GONE; } else if (force_remove) { retval = CFG_USER_LAST; cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d", data->setno); if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) { if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } if (!self_loaded) { cfg_unload_dsvols(); if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } } } else { char *p = strdup(data->u.users); char *q = strtok(p, ","); int appended = 0; (void) snprintf(buf, CFG_MAX_BUF, "%s %s ", data->path, data->node); while (q && (strcmp(q, user) != 0)) { if (appended) { strcat(buf, ","); strcat(buf, q); } else { strcat(buf, q); appended = 1; } q = strtok(0, ","); } if (!q) { /* uh-oh */ retval = CFG_USER_GONE; } else { /* old user skipped; add in remaining users */ while (q = strtok(0, ", ")) { if (appended) { strcat(buf, ","); strcat(buf, q); } else { strcat(buf, q); appended = 1; } } if (appended) { retval = CFG_USER_OK; cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d", data->setno); if (cfg_put_cstring(cfg, cfg_key, buf, strlen(buf)) < 0) { if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } if (!self_loaded) { cfg_unload_dsvols(); if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } } } else { retval = CFG_USER_LAST; cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(cfg_key, CFG_MAX_KEY, "dsvol.set%d", data->setno); if (cfg_put_cstring(cfg, cfg_key, NULL, 0) < 0) { if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } /* * Since we deleted an entry from the config * file, we don't know what all the new * set numbers are. We need to reload * everything */ if (!self_loaded) { cfg_unload_dsvols(); if (cfg_load_dsvols(cfg) < 0) { if (self_open) { cfg_close(cfg); } return (CFG_USER_ERR); } } } change_made = 1; } } if (self_loaded) { cfg_unload_dsvols(); } if (self_open) { if (change_made) (void) cfg_commit(cfg); cfg_close(cfg); } return (retval); } /* * Enable a volume under SV control (or add this char *user to the list * of users of that volume). * * Parameters: * cfg - The config file to use. * path - The pathname of the volume * ctag - The cluster tag for this volume (if any) * user - The user (sv, ii, sndr) of the volume. */ int cfg_vol_enable(CFGFILE *cfg, char *path, char *ctag, char *user) { int rc; int retval; if (!ctag || *ctag == '\0') { ctag = "-"; } retval = -1; rc = cfg_add_user(cfg, path, ctag, user); switch (rc) { case CFG_USER_ERR: spcs_log("dsvol", NULL, gettext("unable to set up dsvol section of config for %s"), path); break; case CFG_USER_OK: retval = 0; break; case CFG_USER_FIRST: /* enable sv! */ retval = sv_action(path, cfg, ctag, DO_ENABLE); if (retval < 0) { (void) cfg_rem_user(cfg, path, ctag, user); } break; default: spcs_log("dsvol", NULL, gettext("unexpected return from cfg_add_user(%d)"), rc); break; } return (retval); } /* * Disable a volume from SV control (or remove this char *user from the list * of users of that volume). * * Parameters: * cfg - The config file to use. * path - The pathname of the volume * ctag - The cluster tag for this volume (if any) * user - The user (sv, ii, sndr) of the volume. */ int cfg_vol_disable(CFGFILE *cfg, char *path, char *ctag, char *user) { int rc; int retval; if (!ctag || *ctag == '\0') { ctag = "-"; } retval = -1; rc = cfg_rem_user(cfg, path, ctag, user); switch (rc) { case CFG_USER_ERR: spcs_log("dsvol", NULL, gettext("unable to set up dsvol section of config for %s"), path); break; case CFG_USER_OK: retval = 0; break; case CFG_USER_GONE: spcs_log("dsvol", NULL, gettext("%s tried to remove non-existent tag for %s"), user, path); break; case CFG_USER_LAST: /* diable sv! */ retval = sv_action(path, cfg, ctag, DO_DISABLE); break; default: spcs_log("dsvol", NULL, gettext("unexpected return from cfg_rem_user(%d)"), rc); break; } return (retval); } /* * cfg_load_dsvols * * Description: * Loads the dsvol section of the config file into a giant hash, to * make searching faster. The important bit to remember is to not * release the write lock between calling cfg_load_dsvols() and the * cfg_*_user() functions. * * Assumptions: * 1/ cfg file is open * 2/ cfg file has been write-locked * 3/ user of this routine may already be using hcreate/hsearch * * Return value: * -1 if error, or total number of sets found */ int cfg_load_dsvols(CFGFILE *cfg) { int set, rc, entries; char search_key[ CFG_MAX_KEY ]; char *buf; char **entry, *path, *cnode, *users; hash_data_t *data; int devs_added = 0; int offset = 0; char *ctag = cfg_get_resource(cfg); if (!ctag || *ctag == '\0') { ctag = "-"; } dsvol = nsc_create_hash(); if (!dsvol) { return (-1); } rc = 0; cfg_rewind(cfg, CFG_SEC_CONF); entries = cfg_get_section(cfg, &entry, "dsvol"); for (set = 1; set <= entries; set++) { buf = entry[set - 1]; /* split up the line */ if (!(path = strtok(buf, " "))) { /* oops, now what? */ free(buf); break; } if (!(cnode = strtok(0, " "))) { free(buf); break; } if (ctag && (strcmp(cnode, ctag) != 0)) { ++offset; free(buf); continue; } if (!(users = strtok(0, " "))) { free(buf); break; } data = make_dsvol_data(path, cnode, users, set - offset); if (!data) { free(buf); break; } (void) snprintf(search_key, CFG_MAX_KEY, "%s:%s", path, cnode); rc = nsc_insert_node(dsvol, data, search_key); if (rc < 0) { free(buf); break; } /* we also need to keep track of node information */ rc = add_dev_entry(path); if (rc < 0) { free(buf); break; } else if (rc) ++devs_added; free(buf); rc = 0; } while (set < entries) free(entry[set++]); if (entries) free(entry); if (devs_added) { qsort(devlist, devcount, sizeof (device_t), compare); rebuild_devhash(); } dsvol_loaded = 1; return (rc < 0? rc : entries); } /* * cfg_unload_dsvols * * Description: * Free all memory allocated with cfg_load_dsvols. */ void cfg_unload_dsvols() { if (dsvol) { nsc_remove_all(dsvol, delete_dsvol_data); dsvol = 0; dsvol_loaded = 0; } } /* * cfg_load_svols * * Description: * Loads the sv section of the config file into a giant hash, to make * searching faster. The important bit to remember is to not release * the write lock between calling cfg_load_svols() and the cfg_*_user() * functions. * * Assumptions: * 1/ cfg file is open * 2/ cfg file has been write-locked * 3/ user of this routine may already be using builtin hcreate/hsearch */ int cfg_load_svols(CFGFILE *cfg) { int set, entries, offset = 0; char *buf, **entry; char *path, *mode, *cnode; hash_data_t *data; char *ctag = cfg_get_resource(cfg); if (!ctag || *ctag == '\0') { ctag = "-"; } svol = nsc_create_hash(); if (!svol) { return (-1); } cfg_rewind(cfg, CFG_SEC_CONF); entries = cfg_get_section(cfg, &entry, "sv"); for (set = 1; set <= entries; set++) { buf = entry[set - 1]; /* split up the line */ if (!(path = strtok(buf, " "))) { free(buf); break; } if (!(mode = strtok(0, " "))) { free(buf); break; } if (!(cnode = strtok(0, " "))) { cnode = ""; } if (ctag && (strcmp(cnode, ctag) != 0)) { ++offset; free(buf); continue; } data = make_svol_data(path, mode, cnode, set - offset); if (!data) { free(buf); break; } if (nsc_insert_node(svol, data, path) < 0) { free(buf); break; } free(buf); } while (set < entries) free(entry[set++]); if (entries) free(entry); svol_loaded = 1; return (0); } /* * cfg_unload_svols * * Description: * Frees all memory allocated with cfg_load_dsvols */ void cfg_unload_svols() { if (svol) { nsc_remove_all(svol, delete_svol_data); svol = 0; svol_loaded = 0; } } /* * cfg_get_canonical_name * * Description: * Find out whether a device is already known by another name in * the config file. * * Parameters: * cfg - The config file to use * path - The pathname of the device * result - (output) The name it is otherwise known as. This parameter * must be freed by the caller. * * Return values: * -1: error * 0: name is as expected, or is not known * 1: Name is known by different name (stored in 'result') */ int cfg_get_canonical_name(CFGFILE *cfg, const char *path, char **result) { int self_loaded; char *alt_path; int retval; if (devlist) { self_loaded = 0; } else { if (cfg_load_shadows(cfg) < 0) { return (-1); } self_loaded = 1; } /* see if it exists under a different name */ alt_path = find_devid(path); if (!alt_path || strcmp(path, alt_path) == 0) { *result = NULL; retval = 0; } else { /* a-ha */ *result = strdup(alt_path); retval = 1; } if (self_loaded) { free_dev_entries(); } return (retval); } /* * cfg_load_shadows * * Description: * Load in shadow and bitmap volumes from the II section of the * config file. SNDR's volumes are handled already by cfg_load_dsvols. * Not all shadow volumes are listed under dsvol: they can be exported. * * Parameters: * cfg - The config file to use * * Return values: * -1: error * 0: success */ int cfg_load_shadows(CFGFILE *cfg) { int set, self_loaded, rc, entries; char *buf, **entry, *ptr; int devs_added = 0; if (dsvol_loaded) { self_loaded = 0; } else { if (cfg_load_dsvols(cfg) < 0) { return (-1); } self_loaded = 1; } shadowvol = nsc_create_hash(); if (!shadowvol) { return (-1); } rc = 0; cfg_rewind(cfg, CFG_SEC_CONF); entries = cfg_get_section(cfg, &entry, "ii"); for (set = 1; set <= entries; set++) { buf = entry[set - 1]; /* skip the master vol */ ptr = strtok(buf, " "); /* shadow is next */ ptr = strtok(NULL, " "); rc = add_dev_entry(ptr); if (rc < 0) { free(buf); break; } else if (rc) ++devs_added; /* and next is bitmap */ ptr = strtok(NULL, " "); rc = add_dev_entry(ptr); if (rc < 0) { free(buf); break; } else if (rc) ++devs_added; rc = 0; free(buf); } while (set < entries) free(entry[set++]); if (entries) free(entry); if (self_loaded) { cfg_unload_dsvols(); } if (devs_added) { /* sort it, in preparation for lookups */ qsort(devlist, devcount, sizeof (device_t), compare); rebuild_devhash(); } return (rc); } void cfg_unload_shadows() { /* do nothing */ } /* ---------------------------------------------------------------------- */ static hash_data_t * make_dsvol_data(char *path, char *cnode, char *users, int set) { hash_data_t *data; data = (hash_data_t *)malloc(sizeof (hash_data_t)); if (!data) { return (0); } data->u.users = strdup(users); data->path = strdup(path); data->node = strdup(cnode); data->setno = set; return (data); } static void delete_dsvol_data(void *data) { hash_data_t *p = (hash_data_t *)data; free(p->u.users); free(p->path); free(p->node); free(p); } static hash_data_t * make_svol_data(char *path, char *mode, char *cnode, int set) { hash_data_t *data; data = (hash_data_t *)malloc(sizeof (hash_data_t)); if (!data) { return (0); } data->u.mode = strdup(mode); data->path = strdup(path); data->node = strdup(cnode); data->setno = set; return (data); } static void delete_svol_data(void *data) { hash_data_t *p = (hash_data_t *)data; free(p->u.mode); free(p->path); free(p->node); free(p); } static int sv_action(char *path, CFGFILE *caller_cfg, char *ctag, int enable) { struct stat stb; sv_conf_t svc; int fd = -1; int cfg_changed = 0; CFGFILE *cfg; int print_log = 0; int err = 0, rc; int sv_ioctl, spcs_err, self_loaded; char *log_str1, *log_str2; char key[ CFG_MAX_KEY ]; char buf[ CFG_MAX_BUF ]; hash_data_t *node; device_t *statinfo = 0; if (caller_cfg == NULL) { cfg = cfg_open(NULL); if (cfg == NULL) return (-1); if (ctag) cfg_resource(cfg, ctag); } else cfg = caller_cfg; self_loaded = 0; sv_ioctl = (enable? SVIOC_ENABLE : SVIOC_DISABLE); log_str1 = (enable? gettext("enabled %s") : gettext("disabled %s")); log_str2 = (enable? gettext("unable to enable %s") : gettext("unable to disable %s")); spcs_err = (enable? SV_EENABLED : SV_EDISABLED); bzero(&svc, sizeof (svc)); if (devhash) statinfo = nsc_lookup(devhash, path); if (statinfo) { if (!S_ISCHR(statinfo->mode)) goto error; svc.svc_major = major(statinfo->rdev); svc.svc_minor = minor(statinfo->rdev); } else { if (stat(path, &stb) != 0) goto error; if (!S_ISCHR(stb.st_mode)) goto error; svc.svc_major = major(stb.st_rdev); svc.svc_minor = minor(stb.st_rdev); } strncpy(svc.svc_path, path, sizeof (svc.svc_path)); fd = open(SV_DEVICE, O_RDONLY); if (fd < 0) goto error; svc.svc_flag = (NSC_DEVICE | NSC_CACHE); svc.svc_error = spcs_s_ucreate(); do { rc = ioctl(fd, sv_ioctl, &svc); } while (rc < 0 && errno == EINTR); if (rc < 0) { if (errno != spcs_err) { spcs_log("sv", &svc.svc_error, log_str2, svc.svc_path); if (enable) goto error; else err = errno; } else err = spcs_err; } spcs_log("sv", NULL, log_str1, svc.svc_path); /* SV enable succeeded */ if (caller_cfg == NULL) /* was not previously locked */ if (!cfg_lock(cfg, CFG_WRLOCK)) goto error; if (err != spcs_err) { /* already enabled, already in config */ if (enable) { cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(buf, CFG_MAX_BUF, "%s - %s", path, ctag? ctag : "-"); if (cfg_put_cstring(cfg, "sv", buf, CFG_MAX_BUF) < 0) { /* SV config not updated, so SV disable again */ (void) ioctl(fd, SVIOC_DISABLE, &svc); print_log++; } else cfg_changed = 1; } else { /* pull it out of the config */ if (!svol_loaded) { if (cfg_load_svols(cfg) < 0) { if (NULL == caller_cfg) { cfg_close(cfg); } return (-1); } self_loaded = 1; } node = nsc_lookup(svol, svc.svc_path); if (node) { cfg_rewind(cfg, CFG_SEC_CONF); (void) snprintf(key, CFG_MAX_KEY, "sv.set%d", node->setno); if (cfg_put_cstring(cfg, key, NULL, NULL) < 0) { spcs_log("sv", NULL, gettext("failed to remove %s from " "sv config"), svc.svc_path); } /* * Since we deleted an entry from the config * file, we don't know what all the new * set numbers are. We need to reload * everything */ if (!self_loaded) { cfg_unload_svols(); if (cfg_load_svols(cfg) < 0) { if (NULL == caller_cfg) { cfg_close(cfg); } return (-1); } } cfg_changed = 1; } if (self_loaded) { cfg_unload_svols(); self_loaded = 0; } } } #ifdef lint (void) printf("extra line to shut lint up %s\n", module_names[0]); #endif error: if (fd >= 0) (void) close(fd); if (cfg == NULL) return (-1); if (cfg_changed) if (caller_cfg == NULL) /* we opened config */ (void) cfg_commit(cfg); if (caller_cfg == NULL) cfg_close(cfg); if ((cfg_changed) || (err == spcs_err)) return (1); if (print_log) spcs_log("sv", NULL, gettext("unable to add to configuration, disabled %s"), svc.svc_path); spcs_s_ufree(&svc.svc_error); return (-1); } /* * add_dev_entry * * Add an entry into the devlist and the devhash for future lookups. * * Return values: * -1 An error occurred. * 0 Entry added * 1 Entry already exists. */ static int add_dev_entry(const char *path) { struct stat buf; device_t *newmem; hash_data_t *data; if (!devhash) { devhash = nsc_create_hash(); if (!devhash) { return (-1); } } else { data = nsc_lookup(devhash, path); if (data) { return (1); } } if (stat(path, &buf) < 0) { /* ignore error, we are most likely deleting entry anyway */ buf.st_rdev = 0; } if (devcount >= devalloc) { /* make some room */ devalloc += DEV_EXPAND; newmem = (device_t *)realloc(devlist, devalloc * sizeof (device_t)); if (!newmem) { free_dev_entries(); return (-1); } else { devlist = newmem; } } devlist[ devcount ].path = strdup(path); devlist[ devcount ].rdev = buf.st_rdev; devlist[ devcount ].mode = buf.st_mode; if (nsc_insert_node(devhash, &devlist[devcount], path) < 0) { return (-1); } ++devcount; return (0); } static void rebuild_devhash() { int i; if (!devhash) nsc_remove_all(devhash, 0); devhash = nsc_create_hash(); if (!devhash) return; for (i = 0; i < devcount; i++) { nsc_insert_node(devhash, &devlist[i], devlist[i].path); } } static int compare(const void *va, const void *vb) { device_t *a = (device_t *)va; device_t *b = (device_t *)vb; return (b->rdev - a->rdev); } static char * find_devid(const char *path) { device_t key; device_t *result; struct stat buf; if (!devlist || !devhash) return (NULL); /* See if we already know the device id by this name */ result = (device_t *)nsc_lookup(devhash, path); if (result) { return (NULL); } /* try to find it by another name */ if (stat(path, &buf) < 0) return (NULL); key.rdev = buf.st_rdev; /* it's storted, so we use the binary-chop method to find it */ result = bsearch(&key, devlist, devcount, sizeof (device_t), compare); if (result) { return (result->path); } return (NULL); } static void free_dev_entries() { int i; device_t *p; if (!devlist) { return; } for (i = 0, p = devlist; i < devcount; i++, p++) { free(p->path); } free(devlist); devlist = NULL; devcount = 0; devalloc = 0; if (devhash) { nsc_remove_all(devhash, 0); devhash = NULL; } }