/* * Copyright (c) 2004, 2005 Christophe Varoqui */ #include #include #include #include #include #include #include #include #include "devmapper.h" #include "kpartx.h" #define _UUID_PREFIX "part" #define UUID_PREFIX _UUID_PREFIX "%d-" #define _UUID_PREFIX_LEN (sizeof(_UUID_PREFIX) - 1) #define MAX_PREFIX_LEN (_UUID_PREFIX_LEN + 4) #define PARAMS_SIZE 1024 int dm_prereq(char * str, uint32_t x, uint32_t y, uint32_t z) { int r = 1; struct dm_task *dmt; struct dm_versions *target; struct dm_versions *last_target; if (!(dmt = dm_task_create(DM_DEVICE_LIST_VERSIONS))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; target = dm_task_get_versions(dmt); /* Fetch targets and print 'em */ do { last_target = target; if (!strncmp(str, target->name, strlen(str)) && /* dummy prereq on multipath version */ target->version[0] >= x && target->version[1] >= y && target->version[2] >= z ) r = 0; target = (void *) target + target->next; } while (last_target != target); out: dm_task_destroy(dmt); return r; } int dm_simplecmd(int task, const char *name, int no_flush, uint16_t udev_flags) { int r = 0; int udev_wait_flag = (task == DM_DEVICE_RESUME || task == DM_DEVICE_REMOVE); #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; #endif struct dm_task *dmt; if (!(dmt = dm_task_create(task))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); dm_task_skip_lockfs(dmt); if (no_flush) dm_task_no_flush(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags |= DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (udev_wait_flag && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; #endif r = dm_task_run(dmt); #ifdef LIBDM_API_COOKIE if (udev_wait_flag) dm_udev_wait(cookie); #endif out: dm_task_destroy(dmt); return r; } static void strip_slash (char * device) { char * p = device; while (*(p++) != 0x0) { if (*p == '/') *p = '!'; } } static int format_partname(char *buf, size_t bufsiz, const char *mapname, const char *delim, int part) { if (safe_snprintf(buf, bufsiz, "%s%s%d", mapname, delim, part)) return 0; strip_slash(buf); return 1; } static char *make_prefixed_uuid(int part, const char *uuid) { char *prefixed_uuid; int len = MAX_PREFIX_LEN + strlen(uuid) + 1; prefixed_uuid = malloc(len); if (!prefixed_uuid) { fprintf(stderr, "cannot create prefixed uuid : %s\n", strerror(errno)); return NULL; } snprintf(prefixed_uuid, len, UUID_PREFIX "%s", part, uuid); return prefixed_uuid; } int dm_addmap(int task, const char *name, const char *target, const char *params, uint64_t size, int ro, const char *uuid, int part, mode_t mode, uid_t uid, gid_t gid) { int r = 0; struct dm_task *dmt; char *prefixed_uuid = NULL; #ifdef LIBDM_API_COOKIE uint32_t cookie = 0; uint16_t udev_flags = 0; #endif if (!(dmt = dm_task_create (task))) return 0; if (!dm_task_set_name (dmt, name)) goto addout; if (!dm_task_add_target (dmt, 0, size, target, params)) goto addout; if (ro && !dm_task_set_ro (dmt)) goto addout; if (task == DM_DEVICE_CREATE && uuid) { prefixed_uuid = make_prefixed_uuid(part, uuid); if (prefixed_uuid == NULL) goto addout; if (!dm_task_set_uuid(dmt, prefixed_uuid)) goto addout; } if (!dm_task_set_mode(dmt, mode)) goto addout; if (!dm_task_set_uid(dmt, uid)) goto addout; if (!dm_task_set_gid(dmt, gid)) goto addout; dm_task_no_open_count(dmt); #ifdef LIBDM_API_COOKIE if (!udev_sync) udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; if (task == DM_DEVICE_CREATE && !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto addout; #endif r = dm_task_run (dmt); #ifdef LIBDM_API_COOKIE if (task == DM_DEVICE_CREATE) dm_udev_wait(cookie); #endif addout: dm_task_destroy (dmt); free(prefixed_uuid); return r; } static int dm_map_present(char *str, char **uuid) { int r = 0; struct dm_task *dmt; const char *uuidtmp; struct dm_info info; if (uuid) *uuid = NULL; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, str)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = 1; if (uuid) { uuidtmp = dm_task_get_uuid(dmt); if (uuidtmp && strlen(uuidtmp)) *uuid = strdup(uuidtmp); } out: dm_task_destroy(dmt); return r; } static int dm_rename (const char *old, const char *new) { int r = 0; struct dm_task *dmt; uint16_t udev_flags = DM_UDEV_DISABLE_LIBRARY_FALLBACK; uint32_t cookie = 0; dmt = dm_task_create(DM_DEVICE_RENAME); if (!dmt) return r; if (!dm_task_set_name(dmt, old) || !dm_task_set_newname(dmt, new) || !dm_task_no_open_count(dmt) || !dm_task_set_cookie(dmt, &cookie, udev_flags)) goto out; r = dm_task_run(dmt); dm_udev_wait(cookie); out: dm_task_destroy(dmt); return r; } static char *dm_find_uuid(const char *uuid) { struct dm_task *dmt; char *name = NULL; const char *tmp; if ((dmt = dm_task_create(DM_DEVICE_INFO)) == NULL) return NULL; if (!dm_task_set_uuid(dmt, uuid) || !dm_task_run(dmt)) goto out; tmp = dm_task_get_name(dmt); if (tmp != NULL && *tmp != '\0') name = strdup(tmp); out: dm_task_destroy(dmt); return name; } char * dm_mapname(int major, int minor) { struct dm_task *dmt; char *mapname = NULL; const char *map; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; dm_task_no_open_count(dmt); dm_task_set_major(dmt, major); dm_task_set_minor(dmt, minor); if (!dm_task_run(dmt)) goto out; map = dm_task_get_name(dmt); if (map && strlen(map)) mapname = strdup(map); out: dm_task_destroy(dmt); return mapname; } /* * dm_get_first_dep * * Return the device number of the first dependent device * for a given target. */ dev_t dm_get_first_dep(char *devname) { struct dm_task *dmt; struct dm_deps *dm_deps; dev_t ret = 0; if ((dmt = dm_task_create(DM_DEVICE_DEPS)) == NULL) { return ret; } if (!dm_task_set_name(dmt, devname)) { goto out; } if (!dm_task_run(dmt)) { goto out; } if ((dm_deps = dm_task_get_deps(dmt)) == NULL) { goto out; } if (dm_deps->count > 0) { ret = dm_deps->device[0]; } out: dm_task_destroy(dmt); return ret; } char * dm_mapuuid(const char *mapname) { struct dm_task *dmt; const char *tmp; char *uuid = NULL; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return NULL; if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; tmp = dm_task_get_uuid(dmt); if (tmp[0] != '\0') uuid = strdup(tmp); out: dm_task_destroy(dmt); return uuid; } int dm_devn (const char * mapname, unsigned int *major, unsigned int *minor) { int r = 1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 1; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info) || info.exists == 0) goto out; *major = info.major; *minor = info.minor; r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_map(const char *mapname, char * outparams) { int r = 1; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params = NULL; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 1; if (!dm_task_set_name(dmt, mapname)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms); if (snprintf(outparams, PARAMS_SIZE, "%s", params) <= PARAMS_SIZE) r = 0; out: dm_task_destroy(dmt); return r; } static int dm_get_opencount (const char * mapname) { int r = -1; struct dm_task *dmt; struct dm_info info; if (!(dmt = dm_task_create(DM_DEVICE_INFO))) return 0; if (!dm_task_set_name(dmt, mapname)) goto out; if (!dm_task_run(dmt)) goto out; if (!dm_task_get_info(dmt, &info)) goto out; if (!info.exists) goto out; r = info.open_count; out: dm_task_destroy(dmt); return r; } /* * returns: * 1 : match * 0 : no match * -1 : empty map */ static int dm_type(const char * name, char * type) { int r = 0; struct dm_task *dmt; uint64_t start, length; char *target_type = NULL; char *params; if (!(dmt = dm_task_create(DM_DEVICE_TABLE))) return 0; if (!dm_task_set_name(dmt, name)) goto out; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; /* Fetch 1st target */ if (dm_get_next_target(dmt, NULL, &start, &length, &target_type, ¶ms) != NULL) /* more than one target */ r = -1; else if (!target_type) r = -1; else if (!strcmp(target_type, type)) r = 1; out: dm_task_destroy(dmt); return r; } /* * returns: * 0 : if both uuids end with same suffix which starts with UUID_PREFIX * 1 : otherwise */ int dm_compare_uuid(const char *mapuuid, const char *partname) { char *partuuid; int r = 1; partuuid = dm_mapuuid(partname); if (!partuuid) return 1; if (!strncmp(partuuid, _UUID_PREFIX, _UUID_PREFIX_LEN)) { char *p = partuuid + _UUID_PREFIX_LEN; /* skip partition number */ while (isdigit(*p)) p++; if (p != partuuid + _UUID_PREFIX_LEN && *p == '-' && !strcmp(mapuuid, p + 1)) r = 0; } free(partuuid); return r; } struct remove_data { int verbose; }; static int do_foreach_partmaps (const char * mapname, const char *uuid, dev_t devt, int (*partmap_func)(const char *, void *), void *data) { struct dm_task *dmt; struct dm_names *names; struct remove_data *rd = data; unsigned next = 0; char params[PARAMS_SIZE]; unsigned int major, minor; char dev_t[32]; int r = 1; int is_dmdev = 1; if (!(dmt = dm_task_create(DM_DEVICE_LIST))) return 1; dm_task_no_open_count(dmt); if (!dm_task_run(dmt)) goto out; if (!(names = dm_task_get_names(dmt))) goto out; if (!names->dev) { r = 0; /* this is perfectly valid */ goto out; } if (dm_devn(mapname, &major, &minor) || (major != major(devt) || minor != minor(devt))) /* * The latter could happen if a dm device "/dev/mapper/loop0" * exits while kpartx is called on "/dev/loop0". */ is_dmdev = 0; sprintf(dev_t, "%d:%d", major(devt), minor(devt)); do { /* * skip our devmap */ if (is_dmdev && !strcmp(names->name, mapname)) goto next; /* * skip if we cannot fetch the map table from the kernel */ if (dm_get_map(names->name, ¶ms[0])) goto next; /* * skip if the table does not map over the multipath map */ if (!strstr(params, dev_t)) goto next; /* * skip if devmap target is not "linear" */ if (dm_type(names->name, "linear") != 1) { if (rd->verbose) printf("%s: is not a linear target. Not removing\n", names->name); goto next; } /* * skip if uuids don't match */ if (uuid && dm_compare_uuid(uuid, names->name)) { if (rd->verbose) printf("%s: is not a kpartx partition. Not removing\n", names->name); goto next; } if (partmap_func(names->name, data) != 0) goto out; next: next = names->next; names = (void *) names + next; } while (next); r = 0; out: dm_task_destroy (dmt); return r; } static int remove_partmap(const char *name, void *data) { struct remove_data *rd = (struct remove_data *)data; int r = 0; if (dm_get_opencount(name)) { if (rd->verbose) printf("%s is in use. Not removing", name); return 1; } if (!dm_simplecmd(DM_DEVICE_REMOVE, name, 0, 0)) { if (rd->verbose) printf("%s: failed to remove\n", name); r = 1; } else if (rd->verbose) printf("del devmap : %s\n", name); return r; } int dm_remove_partmaps (char * mapname, char *uuid, dev_t devt, int verbose) { struct remove_data rd = { verbose }; return do_foreach_partmaps(mapname, uuid, devt, remove_partmap, &rd); } int dm_find_part(const char *parent, const char *delim, int part, const char *parent_uuid, char *name, size_t namesiz, char **part_uuid, int verbose) { int r; char params[PARAMS_SIZE]; char *tmp; char *uuid; unsigned int major, minor; char dev_t[32]; if (!format_partname(name, namesiz, parent, delim, part)) { if (verbose) fprintf(stderr, "partname too small\n"); return 0; } r = dm_map_present(name, part_uuid); if (r == 1 || parent_uuid == NULL || *parent_uuid == '\0') return r; uuid = make_prefixed_uuid(part, parent_uuid); if (!uuid) return 0; tmp = dm_find_uuid(uuid); if (tmp == NULL) goto out; /* Sanity check on partition, see dm_foreach_partmaps */ if (dm_type(tmp, "linear") != 1) goto out; /* * Try nondm uuid first. That way we avoid confusing * a device name with a device mapper name. */ if (!nondm_parse_uuid(parent_uuid, &major, &minor) && dm_devn(parent, &major, &minor)) goto out; snprintf(dev_t, sizeof(dev_t), "%d:%d", major, minor); if (dm_get_map(tmp, params)) goto out; if (!strstr(params, dev_t)) goto out; if (verbose) fprintf(stderr, "found map %s for uuid %s, renaming to %s\n", tmp, uuid, name); r = dm_rename(tmp, name); if (r == 1) { free(tmp); *part_uuid = uuid; return 1; } if (verbose) fprintf(stderr, "renaming %s->%s failed\n", tmp, name); out: free(uuid); free(tmp); return r; } char *nondm_create_uuid(dev_t devt) { #define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \ sizeof(NONDM_UUID_SUFFIX)) static char uuid_buf[NONDM_UUID_BUFLEN]; snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s", NONDM_UUID_PREFIX, major(devt), minor(devt), NONDM_UUID_SUFFIX); uuid_buf[NONDM_UUID_BUFLEN-1] = '\0'; return uuid_buf; } int nondm_parse_uuid(const char *uuid, unsigned int *major, unsigned int *minor) { const char *p; char *e; int ma, mi; if (strncmp(uuid, NONDM_UUID_PREFIX "_", sizeof(NONDM_UUID_PREFIX))) return 0; p = uuid + sizeof(NONDM_UUID_PREFIX); ma = strtoul(p, &e, 10); if (e == p || *e != ':') return 0; p = e + 1; mi = strtoul(p, &e, 10); if (e == p || *e != '_') return 0; p = e + 1; if (strcmp(p, NONDM_UUID_SUFFIX)) return 0; *major = ma; *minor = mi; return 1; }