From: DongHun Kwak Date: Fri, 14 Jan 2022 04:50:19 +0000 (+0900) Subject: Imported Upstream version 0.8.2 X-Git-Tag: upstream/0.8.2^0 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=11d7ecbf0b8d9e0b8d9aa593265353cfc150cb88;p=platform%2Fupstream%2Fmultipath-tools.git Imported Upstream version 0.8.2 --- diff --git a/kpartx/Makefile b/kpartx/Makefile index 7eb467e..2906a98 100644 --- a/kpartx/Makefile +++ b/kpartx/Makefile @@ -3,7 +3,7 @@ # include ../Makefile.inc -CFLAGS += $(BIN_CFLAGS) -I. -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 +CFLAGS += $(BIN_CFLAGS) -I. -I$(multipathdir) -D_LARGEFILE64_SOURCE -D_FILE_OFFSET_BITS=64 LDFLAGS += $(BIN_LDFLAGS) LIBDEPS += -ldevmapper diff --git a/kpartx/dasd.c b/kpartx/dasd.c index 61b609a..d95d8ca 100644 --- a/kpartx/dasd.c +++ b/kpartx/dasd.c @@ -138,6 +138,8 @@ read_dasd_pt(int fd, struct slice all, struct slice *sp, int ns) return -1; } else { fd_dasd = dup(fd); + if (fd_dasd < 0) + return -1; } if (ioctl(fd_dasd, BIODASDINFO, (unsigned long)&info) != 0) { diff --git a/kpartx/dasd.h b/kpartx/dasd.h index 749af49..8ad5d62 100644 --- a/kpartx/dasd.h +++ b/kpartx/dasd.h @@ -68,7 +68,7 @@ typedef struct volume_label char res3[28]; /* reserved */ uint8_t ldl_version; /* version number, valid for ldl format */ uint64_t formatted_blocks; /* valid when ldl_version >= f2 */ -} __attribute__ ((packed)) volume_label_t; +} __attribute__ ((packed, aligned(__alignof__(int)))) volume_label_t; typedef struct extent diff --git a/kpartx/kpartx.8 b/kpartx/kpartx.8 index ba58acb..08bb349 100644 --- a/kpartx/kpartx.8 +++ b/kpartx/kpartx.8 @@ -25,7 +25,7 @@ kpartx \- Create device maps from partition tables. .RB [\| \-p \|] .RB [\| \-f \|] .RB [\| \-g \|] -.RB [\| \-s \|] +.RB [\| \-s | \-n \|] .RB [\| \-v \|] .B wholedisk . @@ -77,7 +77,11 @@ Force GUID partition table (GPT). . .TP .B \-s -Sync mode. Don't return until the partitions are created. +Sync mode (Default). Don't return until the partitions are created. +. +.TP +.B \-n +Nosync mode. Return before the partitions are created. . .TP .B \-v diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index d4fb53b..d3620c5 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -37,6 +37,7 @@ #include "crc32.h" #include "lopart.h" #include "kpartx.h" +#include "version.h" #define SIZE(a) (sizeof(a)/sizeof((a)[0])) @@ -95,7 +96,9 @@ int force_devmap=0; static int usage(void) { - printf("usage : kpartx [-a|-d|-l] [-f] [-v] wholedisk\n"); + printf(VERSION_STRING); + printf("Usage:\n"); + printf(" kpartx [-a|-d|-u|-l] [-r] [-p] [-f] [-g] [-s|-n] [-v] wholedisk\n"); printf("\t-a add partition devmappings\n"); printf("\t-r devmappings will be readonly\n"); printf("\t-d del partition devmappings\n"); @@ -106,7 +109,7 @@ usage(void) { printf("\t-f force devmap create\n"); printf("\t-v verbose\n"); printf("\t-n nosync mode. Return before the partitions are created\n"); - printf("\t-s sync mode. Don't return until the partitions are created. Default.\n"); + printf("\t-s sync mode (Default). Don't return until the partitions are created\n"); return 1; } diff --git a/libmpathcmd/mpath_cmd.c b/libmpathcmd/mpath_cmd.c index df4ca54..f00bf7e 100644 --- a/libmpathcmd/mpath_cmd.c +++ b/libmpathcmd/mpath_cmd.c @@ -26,6 +26,7 @@ #include #include #include +#include #include "mpath_cmd.h" @@ -93,29 +94,52 @@ static size_t write_all(int fd, const void *buf, size_t len) /* * connect to a unix domain socket */ -int mpath_connect(void) +int __mpath_connect(int nonblocking) { int fd, len; struct sockaddr_un addr; + int flags = 0; memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; + strncpy(&addr.sun_path[1], DEFAULT_SOCKET, sizeof(addr.sun_path) - 1); len = strlen(DEFAULT_SOCKET) + 1 + sizeof(sa_family_t); - strncpy(&addr.sun_path[1], DEFAULT_SOCKET, len); + if (len > sizeof(struct sockaddr_un)) + len = sizeof(struct sockaddr_un); fd = socket(AF_LOCAL, SOCK_STREAM, 0); if (fd == -1) return -1; + if (nonblocking) { + flags = fcntl(fd, F_GETFL, 0); + if (flags != -1) + (void)fcntl(fd, F_SETFL, flags|O_NONBLOCK); + } + if (connect(fd, (struct sockaddr *)&addr, len) == -1) { + int err = errno; + close(fd); + errno = err; return -1; } + if (nonblocking && flags != -1) + (void)fcntl(fd, F_SETFL, flags); + return fd; } +/* + * connect to a unix domain socket + */ +int mpath_connect(void) +{ + return __mpath_connect(0); +} + int mpath_disconnect(int fd) { return close(fd); diff --git a/libmpathcmd/mpath_cmd.h b/libmpathcmd/mpath_cmd.h index 15aeb06..ccfd35f 100644 --- a/libmpathcmd/mpath_cmd.h +++ b/libmpathcmd/mpath_cmd.h @@ -34,6 +34,21 @@ extern "C" { #define DEFAULT_REPLY_TIMEOUT 4000 +/* + * DESCRIPTION: + * Same as mpath_connect() (see below) except for the "nonblocking" + * parameter. + * If "nonblocking" is set, connects in non-blocking mode. This is + * useful to avoid blocking if the listening socket's backlog is + * exceeded. In this case, errno will be set to EAGAIN. + * In case of success, the returned file descriptor is in in blocking + * mode, even if "nonblocking" was true. + * + * RETURNS: + * A file descriptor on success. -1 on failure (with errno set). + */ +int __mpath_connect(int nonblocking); + /* * DESCRIPTION: * Connect to the running multipathd daemon. On systems with the diff --git a/libmpathpersist/mpath_persist.c b/libmpathpersist/mpath_persist.c index 6505774..53022f5 100644 --- a/libmpathpersist/mpath_persist.c +++ b/libmpathpersist/mpath_persist.c @@ -16,6 +16,7 @@ #include "config.h" #include "switchgroup.h" #include "discovery.h" +#include "configure.h" #include "dmparser.h" #include #include "propsel.h" @@ -96,16 +97,21 @@ updatepaths (struct multipath * mpp) continue; } pp->mpp = mpp; - if (pp->state == PATH_UNCHECKED || - pp->state == PATH_WILD) { + if (pp->udev == NULL) { + pp->udev = get_udev_device(pp->dev_t, DEV_DEVT); + if (pp->udev == NULL) { + pp->state = PATH_DOWN; + continue; + } conf = get_multipath_config(); - pathinfo(pp, conf, DI_CHECKER); + pathinfo(pp, conf, DI_SYSFS|DI_CHECKER); put_multipath_config(conf); + continue; } - - if (pp->priority == PRIO_UNDEF) { + if (pp->state == PATH_UNCHECKED || + pp->state == PATH_WILD) { conf = get_multipath_config(); - pathinfo(pp, conf, DI_PRIO); + pathinfo(pp, conf, DI_CHECKER); put_multipath_config(conf); } } @@ -152,48 +158,47 @@ mpath_prin_activepath (struct multipath *mpp, int rq_servact, int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose) { - struct stat info; - vector curmp = NULL; - vector pathvec = NULL; - char * alias; - struct multipath * mpp; - int map_present; - int major, minor; - int ret; - struct config *conf; + int ret = mpath_persistent_reserve_init_vecs(verbose); - conf = get_multipath_config(); - conf->verbosity = verbose; - put_multipath_config(conf); + if (ret != MPATH_PR_SUCCESS) + return ret; + ret = __mpath_persistent_reserve_in(fd, rq_servact, resp, noisy); + mpath_persistent_reserve_free_vecs(); + return ret; +} - if (fstat( fd, &info) != 0){ - condlog(0, "stat error %d", fd); - return MPATH_PR_FILE_ERROR; - } - if(!S_ISBLK(info.st_mode)){ - condlog(0, "Failed to get major:minor. fd = %d", fd); - return MPATH_PR_FILE_ERROR; - } +int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) +{ + int ret = mpath_persistent_reserve_init_vecs(verbose); - major = major(info.st_rdev); - minor = minor(info.st_rdev); - condlog(4, "Device %d:%d: ", major, minor); + if (ret != MPATH_PR_SUCCESS) + return ret; + ret = __mpath_persistent_reserve_out(fd, rq_servact, rq_scope, rq_type, + paramp, noisy); + mpath_persistent_reserve_free_vecs(); + return ret; +} - /* get alias from major:minor*/ - alias = dm_mapname(major, minor); - if (!alias){ - condlog(0, "%d:%d failed to get device alias.", major, minor); - return MPATH_PR_DMMP_ERROR; - } +static vector curmp; +static vector pathvec; - condlog(3, "alias = %s", alias); - map_present = dm_map_present(alias); - if (map_present && dm_is_mpath(alias) != 1){ - condlog( 0, "%s: not a multipath device.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out; - } +void mpath_persistent_reserve_free_vecs(void) +{ + free_multipathvec(curmp, KEEP_PATHS); + free_pathvec(pathvec, FREE_PATHS); + curmp = pathvec = NULL; +} + +int mpath_persistent_reserve_init_vecs(int verbose) +{ + struct config *conf = get_multipath_config(); + + conf->verbosity = verbose; + put_multipath_config(conf); + if (curmp) + return MPATH_PR_SUCCESS; /* * allocate core vectors to store paths and multipaths */ @@ -201,70 +206,32 @@ int mpath_persistent_reserve_in (int fd, int rq_servact, pathvec = vector_alloc (); if (!curmp || !pathvec){ - condlog (0, "%s: vector allocation failed.", alias); - ret = MPATH_PR_DMMP_ERROR; - if (curmp) - vector_free(curmp); - if (pathvec) - vector_free(pathvec); - goto out; + condlog (0, "vector allocation failed."); + goto err; } - if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) { - ret = MPATH_PR_DMMP_ERROR; - goto out1; - } + if (dm_get_maps(curmp)) + goto err; - /* get info of all paths from the dm device */ - if (get_mpvec (curmp, pathvec, alias)){ - condlog(0, "%s: failed to get device info.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out1; - } - - mpp = find_mp_by_alias(curmp, alias); - if (!mpp){ - condlog(0, "%s: devmap not registered.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out1; - } - - ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy); + return MPATH_PR_SUCCESS; -out1: - free_multipathvec(curmp, KEEP_PATHS); - free_pathvec(pathvec, FREE_PATHS); -out: - FREE(alias); - return ret; +err: + mpath_persistent_reserve_free_vecs(); + return MPATH_PR_DMMP_ERROR; } -int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, - unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose) +static int mpath_get_map(int fd, char **palias, struct multipath **pmpp) { - + int ret = MPATH_PR_DMMP_ERROR; struct stat info; - - vector curmp = NULL; - vector pathvec = NULL; - - char * alias; - struct multipath * mpp; - int map_present; int major, minor; - int ret; - uint64_t prkey; - struct config *conf; - - conf = get_multipath_config(); - conf->verbosity = verbose; - put_multipath_config(conf); + char *alias; + struct multipath *mpp; - if (fstat( fd, &info) != 0){ + if (fstat(fd, &info) != 0){ condlog(0, "stat error fd=%d", fd); return MPATH_PR_FILE_ERROR; } - if(!S_ISBLK(info.st_mode)){ condlog(3, "Failed to get major:minor. fd=%d", fd); return MPATH_PR_FILE_ERROR; @@ -274,56 +241,72 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, minor = minor(info.st_rdev); condlog(4, "Device %d:%d", major, minor); - /* get WWN of the device from major:minor*/ + /* get alias from major:minor*/ alias = dm_mapname(major, minor); if (!alias){ + condlog(0, "%d:%d failed to get device alias.", major, minor); return MPATH_PR_DMMP_ERROR; } condlog(3, "alias = %s", alias); - map_present = dm_map_present(alias); - if (map_present && dm_is_mpath(alias) != 1){ + if (dm_map_present(alias) && dm_is_mpath(alias) != 1){ condlog(3, "%s: not a multipath device.", alias); - ret = MPATH_PR_DMMP_ERROR; goto out; } - /* - * allocate core vectors to store paths and multipaths - */ - curmp = vector_alloc (); - pathvec = vector_alloc (); - - if (!curmp || !pathvec){ - condlog (0, "%s: vector allocation failed.", alias); - ret = MPATH_PR_DMMP_ERROR; - if (curmp) - vector_free(curmp); - if (pathvec) - vector_free(pathvec); - goto out; - } - - if (path_discovery(pathvec, DI_SYSFS | DI_CHECKER) < 0) { - ret = MPATH_PR_DMMP_ERROR; - goto out1; - } - /* get info of all paths from the dm device */ if (get_mpvec(curmp, pathvec, alias)){ condlog(0, "%s: failed to get device info.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out1; + goto out; } mpp = find_mp_by_alias(curmp, alias); if (!mpp) { condlog(0, "%s: devmap not registered.", alias); - ret = MPATH_PR_DMMP_ERROR; - goto out1; + goto out; + } + + ret = MPATH_PR_SUCCESS; + if (pmpp) + *pmpp = mpp; + if (palias) { + *palias = alias; + alias = NULL; } +out: + FREE(alias); + return ret; +} + +int __mpath_persistent_reserve_in (int fd, int rq_servact, + struct prin_resp *resp, int noisy) +{ + struct multipath *mpp; + int ret; + + ret = mpath_get_map(fd, NULL, &mpp); + if (ret != MPATH_PR_SUCCESS) + return ret; + + ret = mpath_prin_activepath(mpp, rq_servact, resp, noisy); + + return ret; +} + +int __mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, + unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy) +{ + struct multipath *mpp; + char *alias; + int ret; + uint64_t prkey; + struct config *conf; + + ret = mpath_get_map(fd, &alias, &mpp); + if (ret != MPATH_PR_SUCCESS) + return ret; conf = get_multipath_config(); select_reservation_key(conf, mpp); @@ -385,10 +368,6 @@ int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, update_prkey(alias, 0); } out1: - free_multipathvec(curmp, KEEP_PATHS); - free_pathvec(pathvec, FREE_PATHS); - -out: FREE(alias); return ret; } @@ -400,22 +379,22 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid) struct multipath *mpp; char params[PARAMS_SIZE], status[PARAMS_SIZE]; - if (dm_get_maps (curmp)){ - return 1; - } - vector_foreach_slot (curmp, mpp, i){ /* * discard out of scope maps */ - if (mpp->alias && refwwid && - strncmp (mpp->alias, refwwid, WWID_SIZE - 1)){ - free_multipath (mpp, KEEP_PATHS); - vector_del_slot (curmp, i); - i--; + if (!mpp->alias) { + condlog(0, "%s: map with empty alias!", __func__); continue; } + if (mpp->pg != NULL) + /* Already seen this one */ + continue; + + if (refwwid && strncmp (mpp->alias, refwwid, WWID_SIZE - 1)) + continue; + dm_get_map(mpp->alias, &mpp->size, params); condlog(3, "params = %s", params); dm_get_status(mpp->alias, status); @@ -428,7 +407,6 @@ get_mpvec (vector curmp, vector pathvec, char * refwwid) * about them */ updatepaths(mpp); - mpp->bestpg = select_path_group (mpp); disassemble_status (status, mpp); } @@ -519,8 +497,8 @@ int mpath_prout_reg(struct multipath *mpp,int rq_servact, int rq_scope, if (k < count) continue; } - strncpy(thread[count].param.dev, pp->dev, - FILE_NAME_SIZE - 1); + strlcpy(thread[count].param.dev, pp->dev, + FILE_NAME_SIZE); if (count && (thread[count].param.paramp->sa_flags & MPATH_F_SPEC_I_PT_MASK)){ /* @@ -640,7 +618,7 @@ int send_prout_activepath(char * dev, int rq_servact, int rq_scope, int rc; memset(&thread, 0, sizeof(thread)); - strncpy(param.dev, dev, FILE_NAME_SIZE - 1); + strlcpy(param.dev, dev, FILE_NAME_SIZE); /* Initialize and set thread joinable attribute */ pthread_attr_init(&attr); pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE); @@ -708,8 +686,8 @@ int mpath_prout_rel(struct multipath *mpp,int rq_servact, int rq_scope, continue; } - strncpy(thread[count].param.dev, pp->dev, - FILE_NAME_SIZE - 1); + strlcpy(thread[count].param.dev, pp->dev, + FILE_NAME_SIZE); condlog (3, "%s: sending pr out command to %s", mpp->wwid, pp->dev); rc = pthread_create (&thread[count].id, &attr, mpath_prout_pthread_fn, (void *) (&thread[count].param)); diff --git a/libmpathpersist/mpath_persist.h b/libmpathpersist/mpath_persist.h index 9a84bc9..7cf4faf 100644 --- a/libmpathpersist/mpath_persist.h +++ b/libmpathpersist/mpath_persist.h @@ -213,6 +213,15 @@ extern int mpath_lib_exit (struct config *conf); extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp *resp, int noisy, int verbose); +/* + * DESCRIPTION : + * This function is like mpath_persistent_reserve_in(), except that it doesn't call + * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs() + * before and after the actual PR call. + */ +extern int __mpath_persistent_reserve_in(int fd, int rq_servact, + struct prin_resp *resp, int noisy); + /* * DESCRIPTION : * This function sends PROUT command to the DM device and get the response. @@ -238,6 +247,37 @@ extern int mpath_persistent_reserve_in (int fd, int rq_servact, struct prin_resp extern int mpath_persistent_reserve_out ( int fd, int rq_servact, int rq_scope, unsigned int rq_type, struct prout_param_descriptor *paramp, int noisy, int verbose); +/* + * DESCRIPTION : + * This function is like mpath_persistent_reserve_out(), except that it doesn't call + * mpath_persistent_reserve_init_vecs() and mpath_persistent_reserve_free_vecs() + * before and after the actual PR call. + */ +extern int __mpath_persistent_reserve_out( int fd, int rq_servact, int rq_scope, + unsigned int rq_type, struct prout_param_descriptor *paramp, + int noisy); + +/* + * DESCRIPTION : + * This function allocates data structures and performs basic initialization and + * device discovery for later calls of __mpath_persistent_reserve_in() or + * __mpath_persistent_reserve_out(). + * @verbose: Set verbosity level. Input argument. value:0 to 3. 0->disabled, 3->Max verbose + * + * RESTRICTIONS: + * + * RETURNS: MPATH_PR_SUCCESS if successful else returns any of the status specified + * above in RETURN_STATUS. + */ +int mpath_persistent_reserve_init_vecs(int verbose); + +/* + * DESCRIPTION : + * This function frees data structures allocated by + * mpath_persistent_reserve_init_vecs(). + */ +void mpath_persistent_reserve_free_vecs(void); + #ifdef __cplusplus } diff --git a/libmultipath/blacklist.c b/libmultipath/blacklist.c index e0d0279..00e8dbd 100644 --- a/libmultipath/blacklist.c +++ b/libmultipath/blacklist.c @@ -366,7 +366,7 @@ filter_path (struct config * conf, struct path * pp) { int r; - r = filter_property(conf, pp->udev, 3); + r = filter_property(conf, pp->udev, 3, pp->uid_attribute); if (r > 0) return r; r = filter_devnode(conf->blist_devnode, conf->elist_devnode, pp->dev); @@ -384,7 +384,8 @@ filter_path (struct config * conf, struct path * pp) } int -filter_property(struct config *conf, struct udev_device *udev, int lvl) +filter_property(struct config *conf, struct udev_device *udev, int lvl, + const char *uid_attribute) { const char *devname = udev_device_get_sysname(udev); struct udev_list_entry *list_entry; @@ -395,7 +396,21 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl) /* * This is the inverse of the 'normal' matching; * the environment variable _has_ to match. + * But only if the uid_attribute used for determining the WWID + * of the path is is present in the environment + * (uid_attr_seen). If this is not the case, udev probably + * just failed to access the device, which should not cause the + * device to be blacklisted (it won't be used by multipath + * anyway without WWID). + * Likewise, if no uid attribute is defined, udev-based WWID + * determination is effectively off, and devices shouldn't be + * blacklisted by missing properties (check_missing_prop). */ + + bool check_missing_prop = uid_attribute != NULL && + *uid_attribute != '\0'; + bool uid_attr_seen = false; + r = MATCH_PROPERTY_BLIST_MISSING; udev_list_entry_foreach(list_entry, udev_device_get_properties_list_entry(udev)) { @@ -403,6 +418,10 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl) env = udev_list_entry_get_name(list_entry); if (!env) continue; + + if (check_missing_prop && !strcmp(env, uid_attribute)) + uid_attr_seen = true; + if (_blacklist_exceptions(conf->elist_property, env)) { r = MATCH_PROPERTY_BLIST_EXCEPT; break; @@ -413,6 +432,9 @@ filter_property(struct config *conf, struct udev_device *udev, int lvl) } env = NULL; } + if (r == MATCH_PROPERTY_BLIST_MISSING && + (!check_missing_prop || !uid_attr_seen)) + r = MATCH_NOTHING; } log_filter(devname, NULL, NULL, NULL, env, NULL, r, lvl); diff --git a/libmultipath/blacklist.h b/libmultipath/blacklist.h index 4c8ec99..2d721f6 100644 --- a/libmultipath/blacklist.h +++ b/libmultipath/blacklist.h @@ -37,7 +37,7 @@ int filter_devnode (vector, vector, char *); int filter_wwid (vector, vector, char *, char *); int filter_device (vector, vector, char *, char *, char *); int filter_path (struct config *, struct path *); -int filter_property(struct config *, struct udev_device *, int); +int filter_property(struct config *, struct udev_device *, int, const char*); int filter_protocol(vector, vector, struct path *); int store_ble (vector, char *, int); int set_ble_device (vector, char *, char *, int); diff --git a/libmultipath/config.c b/libmultipath/config.c index 141f092..20e3b8b 100644 --- a/libmultipath/config.c +++ b/libmultipath/config.c @@ -585,8 +585,7 @@ free_config (struct config * conf) if (conf->uid_attribute) FREE(conf->uid_attribute); - if (conf->uid_attrs) - FREE(conf->uid_attrs); + vector_reset(&conf->uid_attrs); if (conf->getuid) FREE(conf->getuid); @@ -718,7 +717,6 @@ load_config (char * file) conf->remove_retries = 0; conf->ghost_delay = DEFAULT_GHOST_DELAY; conf->all_tg_pt = DEFAULT_ALL_TG_PT; - /* * preload default hwtable */ @@ -853,3 +851,50 @@ out: free_config(conf); return NULL; } + +char *get_uid_attribute_by_attrs(struct config *conf, + const char *path_dev) +{ + vector uid_attrs = &conf->uid_attrs; + int j; + char *att, *col; + + vector_foreach_slot(uid_attrs, att, j) { + col = strrchr(att, ':'); + if (!col) + continue; + if (!strncmp(path_dev, att, col - att)) + return col + 1; + } + return NULL; +} + +int parse_uid_attrs(char *uid_attrs, struct config *conf) +{ + vector attrs = &conf->uid_attrs; + char *uid_attr_record, *tmp; + int ret = 0, count; + + if (!uid_attrs) + return 1; + + count = get_word(uid_attrs, &uid_attr_record); + while (uid_attr_record) { + tmp = strchr(uid_attr_record, ':'); + if (!tmp) { + condlog(2, "invalid record in uid_attrs: %s", + uid_attr_record); + free(uid_attr_record); + ret = 1; + } else if (!vector_alloc_slot(attrs)) { + free(uid_attr_record); + ret = 1; + } else + vector_set_slot(attrs, uid_attr_record); + if (!count) + break; + uid_attrs += count; + count = get_word(uid_attrs, &uid_attr_record); + } + return ret; +} diff --git a/libmultipath/config.h b/libmultipath/config.h index f5bf5b1..ff2b4e8 100644 --- a/libmultipath/config.h +++ b/libmultipath/config.h @@ -190,7 +190,7 @@ struct config { char * multipath_dir; char * selector; - char * uid_attrs; + struct _vector uid_attrs; char * uid_attribute; char * getuid; char * features; @@ -250,4 +250,8 @@ void free_config (struct config * conf); extern struct config *get_multipath_config(void); extern void put_multipath_config(void *); +int parse_uid_attrs(char *uid_attrs, struct config *conf); +char *get_uid_attribute_by_attrs(struct config *conf, + const char *path_dev); + #endif diff --git a/libmultipath/configure.c b/libmultipath/configure.c index af4d78d..b09ef52 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -73,7 +73,7 @@ int group_by_host_adapter(struct pathgroup *pgp, vector adapters) goto out; agp->pgp = pgp; - strncpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE - 1); + strlcpy(agp->adapter_name, adapter_name1, SLOT_NAME_SIZE); store_adaptergroup(adapters, agp); /* create a new host port group @@ -650,7 +650,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) if (cmpp) { condlog(2, "%s: rename %s to %s", mpp->wwid, cmpp->alias, mpp->alias); - strncpy(mpp->alias_old, cmpp->alias, WWID_SIZE - 1); + strlcpy(mpp->alias_old, cmpp->alias, WWID_SIZE); mpp->action = ACT_RENAME; if (force_reload) { mpp->force_udev_reload = 1; @@ -667,7 +667,7 @@ select_action (struct multipath * mpp, vector curmp, int force_reload) if (!cmpp) { condlog(2, "%s: remove (wwid changed)", mpp->alias); dm_flush_map(mpp->alias); - strncpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE - 1); + strlcpy(cmpp_by_name->wwid, mpp->wwid, WWID_SIZE); drop_multipath(curmp, cmpp_by_name->wwid, KEEP_PATHS); mpp->action = ACT_CREATE; condlog(3, "%s: set ACT_CREATE (map wwid change)", @@ -1257,7 +1257,7 @@ int coalesce_paths (struct vectors * vecs, vector newmp, char * refwwid, if (!deadmap(mpp)) continue; - strncpy(alias, mpp->alias, WWID_SIZE - 1); + strlcpy(alias, mpp->alias, WWID_SIZE); vector_del_slot(newmp, i); i--; @@ -1369,7 +1369,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev, 3) > 0) + filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0) invalid = 1; pthread_cleanup_pop(1); if (invalid) @@ -1409,7 +1409,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev, 3) > 0) + filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0) invalid = 1; pthread_cleanup_pop(1); if (invalid) @@ -1438,7 +1438,7 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); if (pp->udev && pp->uid_attribute && - filter_property(conf, pp->udev, 3) > 0) + filter_property(conf, pp->udev, 3, pp->uid_attribute) > 0) invalid = 1; pthread_cleanup_pop(1); if (invalid) @@ -1451,7 +1451,8 @@ int get_refwwid(enum mpath_cmds cmd, char *dev, enum devtypes dev_type, conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - if (((dm_get_uuid(dev, tmpwwid)) == 0) && (strlen(tmpwwid))) { + if (((dm_get_uuid(dev, tmpwwid, WWID_SIZE)) == 0) + && (strlen(tmpwwid))) { refwwid = tmpwwid; goto check; } diff --git a/libmultipath/debug.c b/libmultipath/debug.c index cbf1e57..4128cb9 100644 --- a/libmultipath/debug.c +++ b/libmultipath/debug.c @@ -4,6 +4,7 @@ #include #include #include +#include #include "log_pthread.h" #include diff --git a/libmultipath/defaults.c b/libmultipath/defaults.c index c20bb0d..082640d 100644 --- a/libmultipath/defaults.c +++ b/libmultipath/defaults.c @@ -7,20 +7,3 @@ #include "memory.h" const char * const default_partition_delim = DEFAULT_PARTITION_DELIM; - -char * -set_default (char * str) -{ - int len; - char * p; - - len = strlen(str); - p = MALLOC(len + 1); - - if (!p) - return NULL; - - strncat(p, str, len); - - return p; -} diff --git a/libmultipath/defaults.h b/libmultipath/defaults.h index 6576939..decc933 100644 --- a/libmultipath/defaults.h +++ b/libmultipath/defaults.h @@ -1,8 +1,11 @@ +#ifndef _DEFAULTS_H +#define _DEFAULTS_H /* * If you add or modify a value also update multipath/multipath.conf.5 * and the TEMPLATE in libmultipath/hwtable.c */ #define DEFAULT_UID_ATTRIBUTE "ID_SERIAL" +#define DEFAULT_NVME_UID_ATTRIBUTE "ID_WWN" #define DEFAULT_UDEVDIR "/dev" #define DEFAULT_MULTIPATHDIR "/" LIB_STRING "/multipath" #define DEFAULT_SELECTOR "service-time 0" @@ -60,5 +63,10 @@ #define DEFAULT_CONFIG_DIR "/etc/multipath/conf.d" #define MULTIPATH_SHM_BASE "/dev/shm/multipath/" -char * set_default (char * str); + +static inline char *set_default(char *str) +{ + return strdup(str); +} extern const char *const default_partition_delim; +#endif /* _DEFAULTS_H */ diff --git a/libmultipath/devmapper.c b/libmultipath/devmapper.c index 2e79667..0f0c3a3 100644 --- a/libmultipath/devmapper.c +++ b/libmultipath/devmapper.c @@ -13,7 +13,9 @@ #include #include #include +#include +#include "util.h" #include "checkers.h" #include "vector.h" #include "structs.h" @@ -554,7 +556,7 @@ out: } static int -dm_get_prefixed_uuid(const char *name, char *uuid) +dm_get_prefixed_uuid(const char *name, char *uuid, int uuid_len) { struct dm_task *dmt; const char *uuidtmp; @@ -572,7 +574,7 @@ dm_get_prefixed_uuid(const char *name, char *uuid) uuidtmp = dm_task_get_uuid(dmt); if (uuidtmp) - strcpy(uuid, uuidtmp); + strlcpy(uuid, uuidtmp, uuid_len); else uuid[0] = '\0'; @@ -582,14 +584,18 @@ uuidout: return r; } -int dm_get_uuid(const char *name, char *uuid) +int dm_get_uuid(const char *name, char *uuid, int uuid_len) { - if (dm_get_prefixed_uuid(name, uuid)) + char tmp[DM_UUID_LEN]; + + if (dm_get_prefixed_uuid(name, tmp, sizeof(tmp))) return 1; - if (!strncmp(uuid, UUID_PREFIX, UUID_PREFIX_LEN)) - memmove(uuid, uuid + UUID_PREFIX_LEN, - strlen(uuid + UUID_PREFIX_LEN) + 1); + if (!strncmp(tmp, UUID_PREFIX, UUID_PREFIX_LEN)) + strlcpy(uuid, tmp + UUID_PREFIX_LEN, uuid_len); + else + uuid[0] = '\0'; + return 0; } @@ -597,12 +603,12 @@ static int is_mpath_part(const char *part_name, const char *map_name) { char *p; - char part_uuid[WWID_SIZE], map_uuid[WWID_SIZE]; + char part_uuid[DM_UUID_LEN], map_uuid[DM_UUID_LEN]; - if (dm_get_prefixed_uuid(part_name, part_uuid)) + if (dm_get_prefixed_uuid(part_name, part_uuid, sizeof(part_uuid))) return 0; - if (dm_get_prefixed_uuid(map_name, map_uuid)) + if (dm_get_prefixed_uuid(map_name, map_uuid, sizeof(map_uuid))) return 0; if (strncmp(part_uuid, "part", 4) != 0) @@ -1066,7 +1072,7 @@ struct multipath *dm_get_multipath(const char *name) if (dm_get_map(name, &mpp->size, NULL)) goto out; - dm_get_uuid(name, mpp->wwid); + dm_get_uuid(name, mpp->wwid, WWID_SIZE); dm_get_info(name, &mpp->dmi); return mpp; diff --git a/libmultipath/devmapper.h b/libmultipath/devmapper.h index db75526..7557a86 100644 --- a/libmultipath/devmapper.h +++ b/libmultipath/devmapper.h @@ -63,7 +63,7 @@ int dm_get_major_minor (const char *name, int *major, int *minor); char * dm_mapname(int major, int minor); int dm_remove_partmaps (const char * mapname, int need_sync, int deferred_remove); -int dm_get_uuid(const char *name, char *uuid); +int dm_get_uuid(const char *name, char *uuid, int uuid_len); int dm_get_info (const char * mapname, struct dm_info ** dmi); int dm_rename (const char * old, char * new, char * delim, int skip_kpartx); int dm_reassign(const char * mapname); diff --git a/libmultipath/dict.c b/libmultipath/dict.c index 96815f8..c6eba0f 100644 --- a/libmultipath/dict.c +++ b/libmultipath/dict.c @@ -374,8 +374,38 @@ declare_ovr_snprint(selector, print_str) declare_mp_handler(selector, set_str) declare_mp_snprint(selector, print_str) -declare_def_handler(uid_attrs, set_str) -declare_def_snprint(uid_attrs, print_str) +static int snprint_uid_attrs(struct config *conf, char *buff, int len, + const void *dummy) +{ + char *p = buff; + int n, j; + const char *att; + + vector_foreach_slot(&conf->uid_attrs, att, j) { + n = snprintf(p, len, "%s%s", j == 0 ? "" : " ", att); + if (n >= len) + return (p - buff) + n; + p += n; + len -= n; + } + return p - buff; +} + +static int uid_attrs_handler(struct config *conf, vector strvec) +{ + char *val; + + vector_reset(&conf->uid_attrs); + val = set_value(strvec); + if (!val) + return 1; + if (parse_uid_attrs(val, conf)) + condlog(1, "error parsing uid_attrs: \"%s\"", val); + condlog(3, "parsed %d uid_attrs", VECTOR_SIZE(&conf->uid_attrs)); + FREE(val); + return 0; +} + declare_def_handler(uid_attribute, set_str) declare_def_snprint_defstr(uid_attribute, print_str, DEFAULT_UID_ATTRIBUTE) declare_ovr_handler(uid_attribute, set_str) @@ -1618,7 +1648,7 @@ init_keywords(vector keywords) install_keyword("multipath_dir", &def_multipath_dir_handler, &snprint_def_multipath_dir); install_keyword("path_selector", &def_selector_handler, &snprint_def_selector); install_keyword("path_grouping_policy", &def_pgpolicy_handler, &snprint_def_pgpolicy); - install_keyword("uid_attrs", &def_uid_attrs_handler, &snprint_def_uid_attrs); + install_keyword("uid_attrs", &uid_attrs_handler, &snprint_uid_attrs); install_keyword("uid_attribute", &def_uid_attribute_handler, &snprint_def_uid_attribute); install_keyword("getuid_callout", &def_getuid_handler, &snprint_def_getuid); install_keyword("prio", &def_prio_name_handler, &snprint_def_prio_name); diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 00ffd06..acca466 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -134,7 +134,8 @@ path_discover (vector pathvec, struct config * conf, pp = find_path_by_devt(pathvec, devt); if (!pp) return store_pathinfo(pathvec, conf, - udevice, flag, NULL); + udevice, flag | DI_BLACKLIST, + NULL); } return pathinfo(pp, conf, flag); } @@ -296,8 +297,8 @@ sysfs_get_timeout(const struct path *pp, unsigned int *timeout) return 1; } -int -sysfs_get_tgt_nodename (struct path *pp, char * node) +static int +sysfs_get_tgt_nodename(struct path *pp, char *node) { const char *tgtname, *value; struct udev_device *parent, *tgtdev; @@ -321,7 +322,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) if (tgtid >= 0) { pp->sg_id.proto_id = SCSI_PROTOCOL_SAS; pp->sg_id.transport_id = tgtid; - strncpy(node, value, NODE_NAME_SIZE); + strlcpy(node, value, NODE_NAME_SIZE); return 0; } } @@ -333,7 +334,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) if (value && !strcmp(value, "usb")) { pp->sg_id.proto_id = SCSI_PROTOCOL_UNSPEC; tgtname = udev_device_get_sysname(tgtdev); - strncpy(node, tgtname, strlen(tgtname)); + strlcpy(node, tgtname, NODE_NAME_SIZE); condlog(3, "%s: skip USB device %s", pp->dev, node); return 1; } @@ -360,7 +361,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_FCP; pp->sg_id.transport_id = tgtid; - strncpy(node, value, NODE_NAME_SIZE); + strlcpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } else @@ -389,7 +390,7 @@ sysfs_get_tgt_nodename (struct path *pp, char * node) if (value) { pp->sg_id.proto_id = SCSI_PROTOCOL_ISCSI; pp->sg_id.transport_id = tgtid; - strncpy(node, value, NODE_NAME_SIZE); + strlcpy(node, value, NODE_NAME_SIZE); udev_device_unref(tgtdev); return 0; } @@ -906,27 +907,27 @@ get_geometry(struct path *pp) static int parse_vpd_pg80(const unsigned char *in, char *out, size_t out_len) { - char *p = NULL; int len = get_unaligned_be16(&in[2]); + /* + * Strip leading and trailing whitespace + */ + while (len > 0 && in[len + 3] == ' ') + --len; + while (len > 0 && in[4] == ' ') { + ++in; + --len; + } + if (len >= out_len) { condlog(2, "vpd pg80 overflow, %d/%d bytes required", - len, (int)out_len); - len = out_len; + len + 1, (int)out_len); + len = out_len - 1; } if (len > 0) { memcpy(out, in + 4, len); out[len] = '\0'; } - /* - * Strip trailing whitspaces - */ - p = out + len - 1; - while (p > out && *p == ' ') { - *p = '\0'; - p--; - len --; - } return len; } @@ -1010,51 +1011,45 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, if (vpd_type == 0x2 || vpd_type == 0x3) { int i; + assert(out_len >= 2); len = sprintf(out, "%d", vpd_type); - for (i = 0; i < vpd_len; i++) { + if (2 * vpd_len >= out_len - len) { + condlog(1, "%s: WWID overflow, type %d, %d/%lu bytes required", + __func__, vpd_type, + 2 * vpd_len + len + 1, out_len); + vpd_len = (out_len - len - 1) / 2; + } + for (i = 0; i < vpd_len; i++) len += sprintf(out + len, "%02x", vpd[i]); - if (len >= out_len) - break; - } } else if (vpd_type == 0x8) { - if (!memcmp("eui.", vpd, 4)) { + if (!memcmp("eui.", vpd, 4)) out[0] = '2'; - len = 1; - vpd += 4; - vpd_len -= 4; - for (i = 0; i < vpd_len; i++) { - len += sprintf(out + len, "%c", - tolower(vpd[i])); - if (len >= out_len) - break; - } - len = vpd_len + 1; - out[len] = '\0'; - } else if (!memcmp("naa.", vpd, 4)) { + else if (!memcmp("naa.", vpd, 4)) out[0] = '3'; - len = 1; - vpd += 4; - vpd_len -= 4; - for (i = 0; i < vpd_len; i++) { - len += sprintf(out + len, "%c", - tolower(vpd[i])); - if (len >= out_len) - break; - } - len = vpd_len + 1; - out[len] = '\0'; - } else { + else out[0] = '8'; - len = 1; - vpd += 4; - vpd_len -= 4; - if (vpd_len > out_len + 2) - vpd_len = out_len - 2; - memcpy(out, vpd, vpd_len); - len = vpd_len + 1; - out[len] = '\0'; + + vpd += 4; + len = vpd_len - 4; + while (len > 2 && vpd[len - 2] == '\0') + --len; + if (len > out_len - 1) { + condlog(1, "%s: WWID overflow, type 8/%c, %d/%lu bytes required", + __func__, out[0], len + 1, out_len); + len = out_len - 1; } + + if (out[0] == '8') + for (i = 0; i < len; ++i) + out[1 + i] = vpd[i]; + else + for (i = 0; i < len; ++i) + out[1 + i] = tolower(vpd[i]); + + /* designator should be 0-terminated, but let's make sure */ + out[len] = '\0'; + } else if (vpd_type == 0x1) { const unsigned char *p; int p_len; @@ -1064,8 +1059,11 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, p = vpd; while ((p = memchr(vpd, ' ', vpd_len))) { p_len = p - vpd; - if (len + p_len > out_len - 1) - p_len = out_len - len - 2; + if (len + p_len > out_len - 1) { + condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required", + __func__, len + p_len, out_len); + p_len = out_len - len - 1; + } memcpy(out + len, vpd, p_len); len += p_len; if (len >= out_len - 1) { @@ -1074,6 +1072,10 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, } out[len] = '_'; len ++; + if (len >= out_len - 1) { + out[len] = '\0'; + break; + } vpd = p; vpd_len -= p_len; while (vpd && *vpd == ' ') { @@ -1081,6 +1083,17 @@ parse_vpd_pg83(const unsigned char *in, size_t in_len, vpd_len --; } } + p_len = vpd_len; + if (p_len > 0 && len < out_len - 1) { + if (len + p_len > out_len - 1) { + condlog(1, "%s: WWID overflow, type 1, %d/%lu bytes required", + __func__, len + p_len + 1, out_len); + p_len = out_len - len - 1; + } + memcpy(out + len, vpd, p_len); + len += p_len; + out[len] = '\0'; + } if (len > 1 && out[len - 1] == '_') { out[len - 1] = '\0'; len--; @@ -1645,8 +1658,18 @@ get_prio (struct path * pp) old_prio = pp->priority; pp->priority = prio_getprio(p, pp, checker_timeout); if (pp->priority < 0) { - condlog(3, "%s: %s prio error", pp->dev, prio_name(p)); - pp->priority = PRIO_UNDEF; + /* this changes pp->offline, but why not */ + int state = path_offline(pp); + + if (state == PATH_DOWN || state == PATH_PENDING) { + pp->priority = old_prio; + condlog(3, "%s: %s prio error in state %d, keeping prio = %d", + pp->dev, prio_name(p), state, pp->priority); + } else { + condlog(3, "%s: %s prio error in state %d", + pp->dev, prio_name(p), state); + pp->priority = PRIO_UNDEF; + } return 1; } condlog((old_prio == pp->priority ? 4 : 3), "%s: %s prio = %u", @@ -1779,8 +1802,7 @@ static ssize_t uid_fallback(struct path *pp, int path_state, { ssize_t len = -1; - if (pp->bus == SYSFS_BUS_SCSI && - !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) { + if (pp->bus == SYSFS_BUS_SCSI) { len = get_vpd_uid(pp); *origin = "sysfs"; if (len < 0 && path_state == PATH_UP) { @@ -1810,11 +1832,22 @@ static ssize_t uid_fallback(struct path *pp, int path_state, return len; } -static int has_uid_fallback(struct path *pp) +static bool has_uid_fallback(struct path *pp) { + /* + * Falling back to direct WWID determination is dangerous + * if uid_attribute is set to something non-standard. + * Allow it only if it's either the default, or if udev + * has been disabled by setting 'uid_attribute ""'. + */ + if (!pp->uid_attribute) + return false; return ((pp->bus == SYSFS_BUS_SCSI && - !strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE)) || - pp->bus == SYSFS_BUS_NVME); + (!strcmp(pp->uid_attribute, DEFAULT_UID_ATTRIBUTE) || + !strcmp(pp->uid_attribute, ""))) || + (pp->bus == SYSFS_BUS_NVME && + (!strcmp(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE) || + !strcmp(pp->uid_attribute, "")))); } int @@ -1853,20 +1886,20 @@ get_uid (struct path * pp, int path_state, struct udev_device *udev, len = strlen(pp->wwid); origin = "callout"; } else { + bool udev_available = udev && pp->uid_attribute + && *pp->uid_attribute; - if (udev && pp->uid_attribute) { + if (udev_available) { len = get_udev_uid(pp, pp->uid_attribute, udev); - origin = "udev"; if (len <= 0) condlog(1, "%s: failed to get udev uid: %s", pp->dev, strerror(-len)); - - } else if (pp->bus == SYSFS_BUS_SCSI) { - len = get_vpd_uid(pp); - origin = "sysfs"; + else + origin = "udev"; } - if (len <= 0 && allow_fallback && has_uid_fallback(pp)) { + if ((!udev_available || (len <= 0 && allow_fallback)) + && has_uid_fallback(pp)) { used_fallback = 1; len = uid_fallback(pp, path_state, &origin); } @@ -1912,7 +1945,7 @@ int pathinfo(struct path *pp, struct config *conf, int mask) return PATHINFO_SKIPPED; } if (is_claimed_by_foreign(pp->udev) || - filter_property(conf, pp->udev, 4) > 0) + filter_property(conf, pp->udev, 4, pp->uid_attribute) > 0) return PATHINFO_SKIPPED; } @@ -2002,9 +2035,6 @@ int pathinfo(struct path *pp, struct config *conf, int mask) } else { condlog(3, "%s: path inaccessible", pp->dev); pp->chkrstate = pp->state = path_state; - if (path_state == PATH_PENDING || - path_state == PATH_DOWN) - pp->priority = 0; } } diff --git a/libmultipath/dmparser.c b/libmultipath/dmparser.c index ac13ec0..b856a07 100644 --- a/libmultipath/dmparser.c +++ b/libmultipath/dmparser.c @@ -18,30 +18,24 @@ #define WORD_SIZE 64 static int -merge_words (char ** dst, char * word, int space) +merge_words(char **dst, char *word) { char * p = *dst; - int len; + int len, dstlen; - len = strlen(*dst) + strlen(word) + space; - *dst = REALLOC(*dst, len + 1); + dstlen = strlen(*dst); + len = dstlen + strlen(word) + 2; + *dst = REALLOC(*dst, len); if (!*dst) { free(p); return 1; } - p = *dst; - - while (*p != '\0') - p++; - - while (space) { - *p = ' '; - p++; - space--; - } - strncpy(p, word, strlen(word) + 1); + p = *dst + dstlen; + *p = ' '; + ++p; + strncpy(p, word, len - dstlen - 1); return 0; } @@ -163,7 +157,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, if (!word) return 1; - if (merge_words(&mpp->features, word, 1)) { + if (merge_words(&mpp->features, word)) { FREE(word); return 1; } @@ -187,7 +181,7 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, if (!word) return 1; - if (merge_words(&mpp->hwhandler, word, 1)) { + if (merge_words(&mpp->hwhandler, word)) { FREE(word); return 1; } @@ -248,9 +242,8 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, num_pg_args = atoi(word); - if (merge_words(&mpp->selector, word, 1)) { + if (merge_words(&mpp->selector, word)) goto out1; - } FREE(word); } else { p += get_word(p, NULL); @@ -315,11 +308,11 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, if (!pp) goto out1; - strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1); - strncpy(pp->dev, devname, FILE_NAME_SIZE - 1); + strlcpy(pp->dev_t, word, BLK_DEV_SIZE); + strlcpy(pp->dev, devname, FILE_NAME_SIZE); if (strlen(mpp->wwid)) { - strncpy(pp->wwid, mpp->wwid, - WWID_SIZE - 1); + strlcpy(pp->wwid, mpp->wwid, + WWID_SIZE); } /* Only call this in multipath client mode */ if (!is_daemon && store_path(pathvec, pp)) @@ -327,8 +320,8 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, } else { if (!strlen(pp->wwid) && strlen(mpp->wwid)) - strncpy(pp->wwid, mpp->wwid, - WWID_SIZE - 1); + strlcpy(pp->wwid, mpp->wwid, + WWID_SIZE); } FREE(word); @@ -340,23 +333,21 @@ int disassemble_map(vector pathvec, char *params, struct multipath *mpp, * in the get_dm_mpvec() code path */ if (!strlen(mpp->wwid)) - strncpy(mpp->wwid, pp->wwid, - WWID_SIZE - 1); + strlcpy(mpp->wwid, pp->wwid, WWID_SIZE); /* * Update wwid for paths which may not have been * active at the time the getuid callout was run */ else if (!strlen(pp->wwid)) - strncpy(pp->wwid, mpp->wwid, - WWID_SIZE - 1); + strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); /* * Do not allow in-use patch to change wwid */ else if (strcmp(pp->wwid, mpp->wwid) != 0) { condlog(0, "%s: path wwid appears to have changed. Using map wwid.\n", pp->dev_t); - strncpy(pp->wwid, mpp->wwid, WWID_SIZE); + strlcpy(pp->wwid, mpp->wwid, WWID_SIZE); } pgp->id ^= (long)pp; diff --git a/libmultipath/hwtable.c b/libmultipath/hwtable.c index 1d96433..96e8b25 100644 --- a/libmultipath/hwtable.c +++ b/libmultipath/hwtable.c @@ -38,7 +38,7 @@ * * COMPANY_NAME * - * Maintainer: XXX + * Maintainer: NAME */ { /* Product Name */ @@ -88,7 +88,7 @@ static struct hwentry default_hw[] = { /* Generic NVMe */ .vendor = "NVME", .product = ".*", - .uid_attribute = "ID_WWN", + .uid_attribute = DEFAULT_NVME_UID_ATTRIBUTE, .checker_name = NONE, .retain_hwhandler = RETAIN_HWHANDLER_OFF, }, @@ -708,7 +708,7 @@ static struct hwentry default_hw[] = { /* * DE Series * - * Maintainer: ng-eseries-upstream-maintainers@netapp.com + * Maintainer: NetApp RDAC team */ .vendor = "LENOVO", .product = "DE_Series", diff --git a/libmultipath/parser.c b/libmultipath/parser.c index 92ef7cf..e00c5ff 100644 --- a/libmultipath/parser.c +++ b/libmultipath/parser.c @@ -345,17 +345,13 @@ set_value(vector strvec) if (alloc) memcpy(alloc, str, size); else - condlog(0, "can't allocate memeory for option '%s'", - (char *)VECTOR_SLOT(strvec, 0)); + goto oom; return alloc; } /* Even empty quotes counts as a value (An empty string) */ alloc = (char *) MALLOC(sizeof (char)); - if (!alloc) { - condlog(0, "can't allocate memeory for option '%s'", - (char *)VECTOR_SLOT(strvec, 0)); - return NULL; - } + if (!alloc) + goto oom; for (i = 2; i < VECTOR_SIZE(strvec); i++) { str = VECTOR_SLOT(strvec, i); if (!str) { @@ -373,15 +369,17 @@ set_value(vector strvec) alloc = REALLOC(alloc, sizeof (char) * len); if (!alloc) { FREE(tmp); - condlog(0, "can't allocate memeory for option '%s'", - (char *)VECTOR_SLOT(strvec, 0)); - return NULL; + goto oom; } if (*alloc != '\0') strncat(alloc, " ", 1); - strncat(alloc, str, strlen(str)); + strncat(alloc, str, len - strlen(alloc) - 1); } return alloc; +oom: + condlog(0, "can't allocate memory for option '%s'", + (char *)VECTOR_SLOT(strvec, 0)); + return NULL; } /* non-recursive configuration stream handler */ diff --git a/libmultipath/pgpolicies.c b/libmultipath/pgpolicies.c index ac2596a..660768a 100644 --- a/libmultipath/pgpolicies.c +++ b/libmultipath/pgpolicies.c @@ -312,7 +312,7 @@ out: int group_by_prio(struct multipath *mp) { int i; - unsigned int prio; + int prio; struct path * pp; struct pathgroup * pgp; vector pathvec = NULL; diff --git a/libmultipath/prio.c b/libmultipath/prio.c index 0590218..87de1f9 100644 --- a/libmultipath/prio.c +++ b/libmultipath/prio.c @@ -5,6 +5,7 @@ #include #include "debug.h" +#include "util.h" #include "prio.h" static LIST_HEAD(prioritizers); @@ -166,7 +167,7 @@ void prio_get (char *multipath_dir, struct prio * dst, char * name, char * args) strncpy(dst->name, src->name, PRIO_NAME_LEN); if (args) - strncpy(dst->args, args, PRIO_ARGS_LEN - 1); + strlcpy(dst->args, args, PRIO_ARGS_LEN); dst->getprio = src->getprio; dst->handle = NULL; diff --git a/libmultipath/prioritizers/ana.c b/libmultipath/prioritizers/ana.c index 990d935..2673d9d 100644 --- a/libmultipath/prioritizers/ana.c +++ b/libmultipath/prioritizers/ana.c @@ -164,7 +164,7 @@ int get_ana_info(struct path * pp, unsigned int timeout) ana_log, ana_log_len); pthread_cleanup_pop(1); if (rc >= 0) - condlog(3, "%s: ana state = %02x [%s]", pp->dev, rc, + condlog(4, "%s: ana state = %02x [%s]", pp->dev, rc, aas_print_string(rc)); return rc; } diff --git a/libmultipath/propsel.c b/libmultipath/propsel.c index e6263e9..6af2513 100644 --- a/libmultipath/propsel.c +++ b/libmultipath/propsel.c @@ -536,9 +536,9 @@ int select_getuid(struct config *conf, struct path *pp) { const char *origin; - pp->uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, pp->dev); + pp->uid_attribute = get_uid_attribute_by_attrs(conf, pp->dev); if (pp->uid_attribute) { - origin = "(setting: multipath.conf defaults section)"; + origin = "(setting: multipath.conf defaults section / uid_attrs)"; goto out; } diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index db5d19d..c43b58f 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -2,6 +2,7 @@ #include #include +#include "util.h" #include "checkers.h" #include "vector.h" #include "defaults.h" @@ -350,7 +351,7 @@ find_existing_alias (struct multipath * mpp, vector_foreach_slot (vecs->mpvec, mp, i) if (strncmp(mp->wwid, mpp->wwid, WWID_SIZE - 1) == 0) { - strncpy(mpp->alias_old, mp->alias, WWID_SIZE - 1); + strlcpy(mpp->alias_old, mp->alias, WWID_SIZE); return; } } diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index f73de8c..8f7b2ef 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -163,13 +163,12 @@ uevent_get_wwid(struct uevent *uev) conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); - uid_attribute = parse_uid_attribute_by_attrs(conf->uid_attrs, uev->kernel); + uid_attribute = get_uid_attribute_by_attrs(conf, uev->kernel); pthread_cleanup_pop(1); val = uevent_get_env_var(uev, uid_attribute); if (val) uev->wwid = val; - FREE(uid_attribute); } bool @@ -179,7 +178,7 @@ uevent_need_merge(void) bool need_merge = false; conf = get_multipath_config(); - if (conf->uid_attrs) + if (VECTOR_SIZE(&conf->uid_attrs) > 0) need_merge = true; put_multipath_config(conf); diff --git a/libmultipath/util.c b/libmultipath/util.c index 5b838d5..28cbf4b 100644 --- a/libmultipath/util.c +++ b/libmultipath/util.c @@ -188,7 +188,7 @@ int devt2devname(char *devname, int devname_len, char *devt) return 1; } p++; - strncpy(devname, p, devname_len); + strlcpy(devname, p, devname_len); return 0; } } @@ -273,48 +273,6 @@ dev_t parse_devt(const char *dev_t) return makedev(maj, min); } -char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev) -{ - char *uid_attribute; - char *uid_attr_record; - char *dev; - char *attr; - char *tmp; - int count; - - if(!uid_attrs || !path_dev) - return NULL; - - count = get_word(uid_attrs, &uid_attr_record); - while (uid_attr_record) { - tmp = strrchr(uid_attr_record, ':'); - if (!tmp) { - free(uid_attr_record); - if (!count) - break; - uid_attrs += count; - count = get_word(uid_attrs, &uid_attr_record); - continue; - } - dev = uid_attr_record; - attr = tmp + 1; - *tmp = '\0'; - - if(!strncmp(path_dev, dev, strlen(dev))) { - uid_attribute = STRDUP(attr); - free(uid_attr_record); - return uid_attribute; - } - - free(uid_attr_record); - if (!count) - break; - uid_attrs += count; - count = get_word(uid_attrs, &uid_attr_record); - } - return NULL; -} - void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached) { diff --git a/libmultipath/util.h b/libmultipath/util.h index 1e0d832..693991c 100644 --- a/libmultipath/util.h +++ b/libmultipath/util.h @@ -15,7 +15,6 @@ size_t strlcat(char *dst, const char *src, size_t size); int devt2devname (char *, int, char *); dev_t parse_devt(const char *dev_t); char *convert_dev(char *dev, int is_path_device); -char *parse_uid_attribute_by_attrs(char *uid_attrs, char *path_dev); void setup_thread_attr(pthread_attr_t *attr, size_t stacksize, int detached); int systemd_service_enabled(const char *dev); int get_linux_version_code(void); diff --git a/libmultipath/uxsock.c b/libmultipath/uxsock.c index 7e5a144..9b4e978 100644 --- a/libmultipath/uxsock.c +++ b/libmultipath/uxsock.c @@ -66,9 +66,12 @@ int ux_socket_listen(const char *name) memset(&addr, 0, sizeof(addr)); addr.sun_family = AF_LOCAL; addr.sun_path[0] = '\0'; - len = strlen(name) + 1 + sizeof(sa_family_t); - strncpy(&addr.sun_path[1], name, len); + len = strlen(name) + 1; + if (len >= sizeof(addr.sun_path)) + len = sizeof(addr.sun_path) - 1; + memcpy(&addr.sun_path[1], name, len); + len += sizeof(sa_family_t); if (bind(fd, (struct sockaddr *)&addr, len) == -1) { condlog(3, "Couldn't bind to ux_socket, error %d", errno); close(fd); diff --git a/libmultipath/version.h b/libmultipath/version.h index 1edd65f..f751c30 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -20,8 +20,8 @@ #ifndef _VERSION_H #define _VERSION_H -#define VERSION_CODE 0x000801 -#define DATE_CODE 0x041213 +#define VERSION_CODE 0x000802 +#define DATE_CODE 0x070313 #define PROG "multipath-tools" diff --git a/libmultipath/wwids.c b/libmultipath/wwids.c index 53e7951..ef74812 100644 --- a/libmultipath/wwids.c +++ b/libmultipath/wwids.c @@ -294,7 +294,8 @@ should_multipath(struct path *pp1, vector pathvec, vector mpvec) char tmp_wwid[WWID_SIZE]; struct multipath *mp = find_mp_by_wwid(mpvec, pp1->wwid); - if (mp != NULL && dm_get_uuid(mp->alias, tmp_wwid) == 0 && + if (mp != NULL && + dm_get_uuid(mp->alias, tmp_wwid, WWID_SIZE) == 0 && !strncmp(tmp_wwid, pp1->wwid, WWID_SIZE)) { condlog(3, "wwid %s is already multipathed, keeping it", pp1->wwid); diff --git a/mpathpersist/main.c b/mpathpersist/main.c index 10cba45..5ad06a9 100644 --- a/mpathpersist/main.c +++ b/mpathpersist/main.c @@ -15,6 +15,8 @@ #include #include #include +#include +#include "version.h" static const char * pr_type_strs[] = { "obsolete [0]", @@ -59,9 +61,101 @@ void rcu_unregister_thread_memb(void) {} struct udev *udev; -int main (int argc, char * argv[]) +static int verbose, loglevel, noisy; + +static int handle_args(int argc, char * argv[], int line); + +static int do_batch_file(const char *batch_fn) +{ + char command[] = "mpathpersist"; + const int ARGV_CHUNK = 2; + const char delims[] = " \t\n"; + size_t len = 0; + char *line = NULL; + ssize_t n; + int nline = 0; + int argl = ARGV_CHUNK; + FILE *fl; + char **argv = calloc(argl, sizeof(*argv)); + int ret = MPATH_PR_SUCCESS; + + if (argv == NULL) + return MPATH_PR_OTHER; + + fl = fopen(batch_fn, "r"); + if (fl == NULL) { + fprintf(stderr, "unable to open %s: %s\n", + batch_fn, strerror(errno)); + free(argv); + return MPATH_PR_SYNTAX_ERROR; + } else { + if (verbose >= 2) + fprintf(stderr, "running batch file %s\n", + batch_fn); + } + + while ((n = getline(&line, &len, fl)) != -1) { + char *_token, *token; + int argc = 0; + int rv; + + nline++; + argv[argc++] = command; + + if (line[n-1] == '\n') + line[n-1] = '\0'; + if (verbose >= 3) + fprintf(stderr, "processing line %d: %s\n", + nline, line); + + for (token = strtok_r(line, delims, &_token); + token != NULL && *token != '#'; + token = strtok_r(NULL, delims, &_token)) { + + if (argc >= argl) { + int argn = argl + ARGV_CHUNK; + char **tmp; + + tmp = realloc(argv, argn * sizeof(*argv)); + if (tmp == NULL) + break; + argv = tmp; + argl = argn; + } + + if (argc == 1 && !strcmp(token, command)) + continue; + + argv[argc++] = token; + } + + if (argc <= 1) + continue; + + if (verbose >= 2) { + int i; + + fprintf(stderr, "## file %s line %d:", batch_fn, nline); + for (i = 0; i < argc; i++) + fprintf(stderr, " %s", argv[i]); + fprintf(stderr, "\n"); + } + + optind = 0; + rv = handle_args(argc, argv, nline); + if (rv != MPATH_PR_SUCCESS) + ret = rv; + } + + fclose(fl); + free(argv); + free(line); + return ret; +} + +static int handle_args(int argc, char * argv[], int nline) { - int fd, c, res; + int fd, c; const char *device_name = NULL; int num_prin_sa = 0; int num_prout_sa = 0; @@ -82,51 +176,41 @@ int main (int argc, char * argv[]) int prin = 1; int prin_sa = -1; int prout_sa = -1; - int verbose = 0; - int loglevel = 0; - int noisy = 0; int num_transport =0; + char *batch_fn = NULL; void *resp = NULL; struct transportid * tmp; - struct config *conf; - - if (optind == argc) - { - - fprintf (stderr, "No parameter used\n"); - usage (); - exit (1); - } - - if (getuid () != 0) - { - fprintf (stderr, "need to be root\n"); - exit (1); - } - - udev = udev_new(); - conf = mpath_lib_init(); - if(!conf) { - udev_unref(udev); - exit(1); - } memset(transportids, 0, MPATH_MX_TIDS * sizeof(struct transportid)); - multipath_conf = conf; while (1) { int option_index = 0; - c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:", + c = getopt_long (argc, argv, "v:Cd:hHioYZK:S:PAT:skrGILcRX:l:f:", long_options, &option_index); if (c == -1) break; switch (c) { + case 'f': + if (nline != 0) { + fprintf(stderr, + "ERROR: -f option not allowed in batch file\n"); + ret = MPATH_PR_SYNTAX_ERROR; + goto out; + } + if (batch_fn != NULL) { + fprintf(stderr, + "ERROR: -f option can be used at most once\n"); + ret = MPATH_PR_SYNTAX_ERROR; + goto out; + } + batch_fn = strdup(optarg); + break; case 'v': - if (1 != sscanf (optarg, "%d", &loglevel)) + if (nline == 0 && 1 != sscanf (optarg, "%d", &loglevel)) { fprintf (stderr, "bad argument to '--verbose'\n"); return MPATH_PR_SYNTAX_ERROR; @@ -265,7 +349,6 @@ int main (int argc, char * argv[]) default: fprintf(stderr, "unrecognised switch " "code 0x%x ??\n", c); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } @@ -283,27 +366,29 @@ int main (int argc, char * argv[]) { for (; optind < argc; ++optind) fprintf (stderr, "Unexpected extra argument: %s\n", argv[optind]); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } } - /* set verbosity */ - noisy = (loglevel >= 3) ? 1 : hex; - verbose = (loglevel >= 3)? 3: loglevel; + if (nline == 0) { + /* set verbosity */ + noisy = (loglevel >= 3) ? 1 : hex; + verbose = (loglevel >= 3)? 3: loglevel; + ret = mpath_persistent_reserve_init_vecs(verbose); + if (ret != MPATH_PR_SUCCESS) + goto out; + } - if ((prout_flag + prin_flag) == 0) + if ((prout_flag + prin_flag) == 0 && batch_fn == NULL) { fprintf (stderr, "choose either '--in' or '--out' \n"); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } if ((prout_flag + prin_flag) > 1) { fprintf (stderr, "choose either '--in' or '--out' \n"); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } @@ -334,21 +419,19 @@ int main (int argc, char * argv[]) { fprintf (stderr, " No service action given for Persistent Reserve IN\n"); - usage(); ret = MPATH_PR_SYNTAX_ERROR; } else if (num_prin_sa > 1) { fprintf (stderr, " Too many service actions given; choose " "one only\n"); - usage(); ret = MPATH_PR_SYNTAX_ERROR; } } else { - usage (); - ret = MPATH_PR_SYNTAX_ERROR; + if (batch_fn == NULL) + ret = MPATH_PR_SYNTAX_ERROR; goto out; } @@ -356,7 +439,6 @@ int main (int argc, char * argv[]) { fprintf (stderr, " --relative-target-port" " only useful with --register-move\n"); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } @@ -378,7 +460,6 @@ int main (int argc, char * argv[]) if (device_name == NULL) { fprintf (stderr, "No device name given \n"); - usage (); ret = MPATH_PR_SYNTAX_ERROR; goto out; } @@ -403,7 +484,7 @@ int main (int argc, char * argv[]) goto out; } - ret = mpath_persistent_reserve_in (fd, prin_sa, resp, noisy, verbose); + ret = __mpath_persistent_reserve_in (fd, prin_sa, resp, noisy); if (ret != MPATH_PR_SUCCESS ) { fprintf (stderr, "Persistent Reserve IN command failed\n"); @@ -463,8 +544,8 @@ int main (int argc, char * argv[]) } /* PROUT commands other than 'register and move' */ - ret = mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type, - paramp, noisy, verbose); + ret = __mpath_persistent_reserve_out (fd, prout_sa, 0, prout_type, + paramp, noisy); for (j = 0 ; j < num_transport; j++) { tmp = paramp->trnptid_list[j]; @@ -487,17 +568,57 @@ int main (int argc, char * argv[]) printf("PR out: command failed\n"); } - res = close (fd); - if (res < 0) + close (fd); + +out : + if (ret == MPATH_PR_SYNTAX_ERROR) { + free(batch_fn); + if (nline == 0) + usage(); + else + fprintf(stderr, "syntax error on line %d in batch file\n", + nline); + } else if (batch_fn != NULL) { + int rv = do_batch_file(batch_fn); + + free(batch_fn); + ret = ret == 0 ? rv : ret; + } + if (nline == 0) + mpath_persistent_reserve_free_vecs(); + return (ret >= 0) ? ret : MPATH_PR_OTHER; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (optind == argc) + { + + fprintf (stderr, "No parameter used\n"); + usage (); + exit (1); + } + + if (getuid () != 0) { - mpath_lib_exit(conf); + fprintf (stderr, "need to be root\n"); + exit (1); + } + + udev = udev_new(); + multipath_conf = mpath_lib_init(); + if(!multipath_conf) { udev_unref(udev); - return MPATH_PR_FILE_ERROR; + exit(1); } -out : - mpath_lib_exit(conf); + ret = handle_args(argc, argv, 0); + + mpath_lib_exit(multipath_conf); udev_unref(udev); + return (ret >= 0) ? ret : MPATH_PR_OTHER; } @@ -687,6 +808,7 @@ void mpath_print_buf_readfullstat( struct prin_resp *pr_buff) static void usage(void) { + fprintf(stderr, VERSION_STRING); fprintf(stderr, "Usage: mpathpersist [OPTIONS] [DEVICE]\n" " Options:\n" @@ -698,6 +820,7 @@ static void usage(void) " 4 Informational messages with trace enabled\n" " --clear|-C PR Out: Clear\n" " --device=DEVICE|-d DEVICE query or change DEVICE\n" + " --batch-file|-f FILE run commands from FILE\n" " --help|-h output this usage message\n" " --hex|-H output response in hex\n" " --in|-i request PR In command \n" @@ -706,10 +829,8 @@ static void usage(void) " --param-aptpl|-Z PR Out parameter 'APTPL'\n" " --read-keys|-k PR In: Read Keys\n" " --param-rk=RK|-K RK PR Out parameter reservation key\n" - " --param-sark=SARK|-S SARK PR Out parameter service " - "action\n" - " reservation key (SARK is in " - "hex)\n" + " --param-sark=SARK|-S SARK PR Out parameter service action\n" + " reservation key (SARK is in hex)\n" " --preempt|-P PR Out: Preempt\n" " --preempt-abort|-A PR Out: Preempt and Abort\n" " --prout-type=TYPE|-T TYPE PR Out command type\n" @@ -721,11 +842,14 @@ static void usage(void) " --release|-L PR Out: Release\n" " --report-capabilities|-c PR In: Report Capabilities\n" " --reserve|-R PR Out: Reserve\n" - " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned \n" - " in several forms\n" + " --transport-id=TIDS|-X TIDS TransportIDs can be mentioned\n" + " in several forms\n" + " --alloc-length=LEN|-l LEN PR In: maximum allocation length\n" " Examples:\n" " mpathpersist --out --register --param-sark=123abc --prout-type=5 /dev/mapper/mpath9\n" - " mpathpersist -i -k /dev/mapper/mpath9\n" ); + " mpathpersist -i -k /dev/mapper/mpath9\n" + " mpathpersist --out --reserve --param-sark=123abc --prout-type=8 -d /dev/mapper/mpath9\n" + " mpathpersist -i -s -d /dev/mapper/mpath9\n"); } void diff --git a/mpathpersist/main.h b/mpathpersist/main.h index beb8a21..bfbb82e 100644 --- a/mpathpersist/main.h +++ b/mpathpersist/main.h @@ -2,6 +2,7 @@ static struct option long_options[] = { {"verbose", 1, NULL, 'v'}, {"clear", 0, NULL, 'C'}, {"device", 1, NULL, 'd'}, + {"batch-file", 1, NULL, 'f' }, {"help", 0, NULL, 'h'}, {"hex", 0, NULL, 'H'}, {"in", 0, NULL, 'i'}, diff --git a/mpathpersist/mpathpersist.8 b/mpathpersist/mpathpersist.8 index 885491d..882043a 100644 --- a/mpathpersist/mpathpersist.8 +++ b/mpathpersist/mpathpersist.8 @@ -5,7 +5,7 @@ .\" .\" ---------------------------------------------------------------------------- . -.TH MPATHPERSIST 8 2016-10-30 "Linux" +.TH MPATHPERSIST 8 2019-05-27 "Linux" . . .\" ---------------------------------------------------------------------------- @@ -71,6 +71,11 @@ Informational messages with trace enabled. Query or change DEVICE. . .TP +.BI \--batch-file=\fIDEVICE\fB|\-f " FILE" +Read commands from \fIFILE\fR. See section \(dqBATCH FILES\(dq below. This +option can be given at most once. +. +.TP .B \--help|\-h Output this usage message. . @@ -167,18 +172,114 @@ PR In: maximum allocation length. LEN is a decimal number between 0 and 8192. .SH EXAMPLE .\" ---------------------------------------------------------------------------- . -.TP -Register the Service Action Reservation Key for the /dev/mapper/mpath9 device: -\fBmpathpersist --out --register --param-sark=\fI123abc \fB--prout-type=\fI5 /dev/mapper/mpath9\fR -.TP -Read the Service Action Reservation Key for the /dev/mapper/mpath9 device: +.PP +Register the key \(dq123abc\(dq for the /dev/mapper/mpath9 device: +.RS +\fBmpathpersist --out --register --param-sark=\fI123abc /dev/mapper/mpath9\fR +.RE +.PP +Read registered reservation keys for the /dev/mapper/mpath9 device: +.RS \fBmpathpersist -i -k \fI/dev/mapper/mpath9\fR -.TP -Reserve the Service Action Reservation Key for the /dev/mapper/mpath9 device: -\fBmpathpersist --out --reserve --param-sark=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR -.TP +.RE +.PP +Create a reservation for the /dev/mapper/mpath9 device with the given +reservation key: +.RS +\fBmpathpersist --out --reserve --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR +.RE +.PP Read the reservation status of the /dev/mapper/mpath9 device: +.RS \fBmpathpersist -i -s -d \fI/dev/mapper/mpath9\fR +.RE +.PP +Release the previously created reservation (note that the prout-type needs to +be the same as above): +.RS +\fBmpathpersist --out --release --param-rk=\fI123abc \fB--prout-type=\fI8 \fB-d \fI/dev/mapper/mpath9\fR +.RE +.PP +Remove the current key registered for this host (i.e. reset it to 0): +.RS +\fBmpathpersist --out --register-ignore -K \fI123abc\fB -S \fI0\fB \fI/dev/mapper/mpath9\fR +.RE +.PP +Remove current reservation, and unregister all registered keys from all I_T nexuses: +.RS +\fBmpathpersist -oCK \fI123abc \fI/dev/mapper/mpath9\fR +.RE +. +. +.\" ---------------------------------------------------------------------------- +.SH BATCH FILES +.\" ---------------------------------------------------------------------------- +. +.PP +The option \fI--batch-file\fR (\fI-f\fR) sets an input file to be processed +by \fBmpathpersist\fR. Grouping commands in batch files can provide a speed +improvement in particular on large installments, because \fBmpathpersist\fR +needs to scan existing paths and maps only once during startup. +. +.PP +The input file is a text file that is parsed +line by line. Every line of the file is interpreted as a command line +(i.e. list of options and parameters) for \fBmpathpersist\fR. Options +and parameters are separated by one or more whitespace characters (space or TAB). +Lines can, but do not have to, begin with the word \(dqmpathpersist\(dq. +The \(dq#\(dq character, either at the beginning of the line or following +some whitespace, denotes the start of a comment that lasts until the end of the +line. Empty lines are allowed. Continuation of mpathpersist commands over +multiple lines is not supported. +. +.PP +All options listed in this man page, except \fI-f\fR and +\fI-v\fR, are allowed in batch files. Both short and long option formats may be used. +Using the \fI-f\fR option inside the batch file is an error. The \fI-v\fR +option is ignored in batch files. +. +.PP +The multipath map on which to act must be specified on every input line, e.g. using the \fI-d\fR option. +Commands acting on different multipath maps may be combined in a +batch file, and multiple commands may act on the same multipath +map. Commands are executed one by one, so +that commands further down in the file see status changes caused by previous +commands. +If \fBmpathpersist\fR encounters an error while processing a line in the +batch file, batch file processing is \fBnot\fR aborted; subsequent commands +are executed nonetheless. The exit status of \fBmpathpersist\fR is the status +of the first failed command, or 0 if all commands succeeded. +. +.PP +If other options and parameters are used along with +\fI-f\fR on the \fBmpathpersist\fR command line, the command line will be executed first, followed +by the commands from the the batch file. +. +.PP +Below is an example of a valid batch input file. +. +.PP +.RS +.EX +# This is an mpathpersist input file. +# Short and long forms of the same command +-i -k /dev/dm-1 # short form, this comment is ignored +mpathpersist --in --read-keys --device=/dev/dm-1 + +# Mixing of long and short options, variable white space + --out --register -S abcde /dev/dm-1 + +# Mixing of commands for different maps +-ir /dev/dm-0 +-ir /dev/dm-1 + +mpathpersist --out --param-rk abcde --reserve --prout-type 5 /dev/dm-1 +# This should now show a reservation +-ir /dev/dm-1 +-oCK abcde /dev/dm-1 +--in --read-reservation /dev/dm-1 +.EE +.RE . . .\" ---------------------------------------------------------------------------- diff --git a/multipath/main.c b/multipath/main.c index 69141db..96a1146 100644 --- a/multipath/main.c +++ b/multipath/main.c @@ -133,11 +133,15 @@ usage (char * progname) { fprintf (stderr, VERSION_STRING); fprintf (stderr, "Usage:\n"); - fprintf (stderr, " %s [-a|-c|-w|-W] [-d] [-r] [-i] [-v lvl] [-p pol] [-b fil] [-q] [dev]\n", progname); - fprintf (stderr, " %s -l|-ll|-f [-v lvl] [-b fil] [-R num] [dev]\n", progname); - fprintf (stderr, " %s -F [-v lvl] [-R num]\n", progname); - fprintf (stderr, " %s [-t|-T]\n", progname); - fprintf (stderr, " %s -h\n", progname); + fprintf (stderr, " %s [-v level] [-B|-d|-i|-q|-r] [-b file] [-p policy] [device]\n", progname); + fprintf (stderr, " %s [-v level] [-R retries] -f device\n", progname); + fprintf (stderr, " %s [-v level] [-R retries] -F\n", progname); + fprintf (stderr, " %s [-v level] [-l|-ll] [device]\n", progname); + fprintf (stderr, " %s [-v level] [-a|-w] device\n", progname); + fprintf (stderr, " %s [-v level] -W\n", progname); + fprintf (stderr, " %s [-v level] [-i] [-c|-C] device\n", progname); + fprintf (stderr, " %s [-v level] [-i] [-u|-U]\n", progname); + fprintf (stderr, " %s [-h|-t|-T]\n", progname); fprintf (stderr, "\n" "Where:\n" @@ -159,23 +163,28 @@ usage (char * progname) " -b fil bindings file location\n" " -w remove a device from the wwids file\n" " -W reset the wwids file include only the current devices\n" - " -p pol force all maps to specified path grouping policy :\n" + " -R num number of times to retry removes of in-use devices\n" + " -u check if the device specified in the program environment should be a\n" + " path in a multipath device\n" + " -U check if the device specified in the program environment is a\n" + " multipath device with usable paths, see -C flag\n" + " -p pol force all maps to specified path grouping policy:\n" " . failover one path per priority group\n" " . multibus all paths in one priority group\n" " . group_by_serial one priority group per serial\n" " . group_by_prio one priority group per priority lvl\n" " . group_by_node_name one priority group per target node\n" - " -v lvl verbosity level\n" + " -v lvl verbosity level:\n" " . 0 no output\n" " . 1 print created devmap names only\n" " . 2 default verbosity\n" " . 3 print debug information\n" - " -R num number of times to retry removes of in-use devices\n" - " dev action limited to:\n" - " . multipath named 'dev' (ex: mpath0) or\n" - " . multipath whose wwid is 'dev' (ex: 60051..)\n" - " . multipath including the path named 'dev' (ex: /dev/sda)\n" - " . multipath including the path with maj:min 'dev' (ex: 8:0)\n" + " device action limited to:\n" + " . multipath named 'device' (ex: mpath0)\n" + " . multipath whose wwid is 'device' (ex: 60051...)\n" + " . multipath including the path named 'device' (ex: /dev/sda or\n" + " /dev/dm-0)\n" + " . multipath including the path with maj:min 'device' (ex: 8:0)\n" ); } @@ -850,6 +859,32 @@ out: return r; } +static int test_multipathd_socket(void) +{ + int fd; + /* + * "multipath -u" may be run before the daemon is started. In this + * case, systemd might own the socket but might delay multipathd + * startup until some other unit (udev settle!) has finished + * starting. With many LUNs, the listen backlog may be exceeded, which + * would cause connect() to block. This causes udev workers calling + * "multipath -u" to hang, and thus creates a deadlock, until "udev + * settle" times out. To avoid this, call connect() in non-blocking + * mode here, and take EAGAIN as indication for a filled-up systemd + * backlog. + */ + + fd = __mpath_connect(1); + if (fd == -1) { + if (errno == EAGAIN) + condlog(3, "daemon backlog exceeded"); + else + return 0; + } else + close(fd); + return 1; +} + int main (int argc, char *argv[]) { @@ -1028,17 +1063,13 @@ main (int argc, char *argv[]) } if (cmd == CMD_VALID_PATH && dev_type == DEV_UEVENT) { - int fd; - - fd = mpath_connect(); - if (fd == -1) { + if (!test_multipathd_socket()) { condlog(3, "%s: daemon is not running", dev); if (!systemd_service_enabled(dev)) { r = print_cmd_valid(RTVL_NO, NULL, conf); goto out; } - } else - mpath_disconnect(fd); + } } if (cmd == CMD_REMOVE_WWID && !dev) { diff --git a/multipath/multipath.conf.5 b/multipath/multipath.conf.5 index 6f08980..f7d21b4 100644 --- a/multipath/multipath.conf.5 +++ b/multipath/multipath.conf.5 @@ -241,23 +241,32 @@ The default is: \fBfailover\fR . .TP .B uid_attrs -The udev attribute providing a unique path identifier for corresponding -type of path devices. If this field is configured and matched with type -of device, it would override any other methods providing for device -unique identifier in config file, and it would activate merging uevents -according to the identifier to promote effiecncy in processing uevents. -It has no default value, if you want to identify path by udev attribute -and to activate merging uevents for SCSI and DASD devices, you can set -its value as: \fIuid_attrs "sd:ID_SERIAL dasd:ID_UID"\fR. +. +Setting this option activates \fBmerging uevents\fR by WWID, which may improve +uevent processing effiency. Moreover, it's an alternative method to configure +the udev properties to use for determining unique path identifiers (WWIDs). .RS -.TP -The default is: \fB\fR +.PP +The value of this option is a space separated list of records like +\(dq\fItype:ATTR\fR\(dq, where \fItype\fR is matched against the beginning +of the device node name (e.g. \fIsd:ATTR\fR matches \fIsda\fR), and +\fIATTR\fR is the name of the udev property to use for matching devices. +.PP +If this option is configured and matches the device +node name of a device, it overrides any other configured methods for +determining the WWID for this device. +.PP +The default is: \fB\fR. To enable uevent merging, set it e.g. to +\(dqsd:ID_SERIAL dasd:ID_UID nvme:ID_WWN\(dq. .RE . . .TP .B uid_attribute -The udev attribute providing a unique path identifier. +The udev attribute providing a unique path identifier (WWID). If +\fIuid_attribute\fR is set to the empty string, WWID determination is done +using the \fIsysfs\fR method rather then using udev (not recommended in +production; see \fBWWID generation\fR below). .RS .TP The default is: \fBID_SERIAL\fR, for SCSI devices @@ -1241,6 +1250,14 @@ otherwise they're treated as blacklisted, and the message . .RS .PP +.B Note: +The behavior of this option has changed in \fBmultipath-tools\fR 0.8.2 +compared to previous versions. +Blacklisting by missing properties is only applied to devices which do have the +property specified by \fIuid_attribute\fR (e.g. \fIID_SERIAL\fR) +set. Previously, it was applied to every device, possibly causing devices to be +blacklisted because of temporary I/O error conditions. +.PP The default \fIblacklist exception\fR is: \fB(SCSI_IDENT_|ID_WWN)\fR, causing well-behaved SCSI devices and devices that provide a WWN (World Wide Number) to be included, and all others to be excluded. @@ -1627,8 +1644,8 @@ WWID is assumed to point to the same device. The WWID is generated by four methods (in the order of preference): .TP 17 .B uid_attrs -The WWID is derived from udev attributes by matching the device node name. See -description of \fIuid_attrs\fR in the defaults section above. +The WWID is derived from udev attributes by matching the device node name; cf +\fIuid_attrs\fR above. .TP .B getuid_callout Use the specified external program; cf \fIgetuid_callout\fR above. @@ -1643,10 +1660,15 @@ does not need to call any external programs here. However, under certain circumstances udev might not be able to generate the requested variable. .TP -.B vpd_pg83 -If none of the \fIgetuid_callout\fR or \fIuid_attribute\fR parameters -are present multipath will try to use the sysfs attribute -\fIvpd_pg83\fR to generate the WWID. +.B sysfs +Try to determine the WWID from sysfs attributes. +For SCSI devices, this means reading the Vital Product Data (VPD) page +\(dqDevice Identification\(dq (0x83). +.PP +The default settings (using udev and \fBuid_attribute\fR configured from +the built-in hardware table) should work fine +in most scenarios. Users who want to enable uevent merging must set +\fBuid_attrs\fR. . . .\" ---------------------------------------------------------------------------- diff --git a/multipathd/cli.c b/multipathd/cli.c index ca176a9..17795b6 100644 --- a/multipathd/cli.c +++ b/multipathd/cli.c @@ -467,6 +467,8 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) if (r) { *reply = genhelp_handler(cmd, r); + if (*reply == NULL) + return EINVAL; *len = strlen(*reply) + 1; return 0; } @@ -474,9 +476,11 @@ parse_cmd (char * cmd, char ** reply, int * len, void * data, int timeout ) h = find_handler(fingerprint(cmdvec)); if (!h || !h->fn) { + free_keys(cmdvec); *reply = genhelp_handler(cmd, EINVAL); + if (*reply == NULL) + return EINVAL; *len = strlen(*reply) + 1; - free_keys(cmdvec); return 0; } diff --git a/multipathd/cli.h b/multipathd/cli.h index f3fa077..32dcffa 100644 --- a/multipathd/cli.h +++ b/multipathd/cli.h @@ -100,15 +100,16 @@ enum { if (m >= MAX_REPLY_LEN) { \ condlog(1, "Warning: max reply length exceeded"); \ free(tmp); \ - r = NULL; \ + (r) = NULL; \ + } else { \ + (r) = REALLOC((r), (m) * 2); \ + if ((r)) { \ + memset((r) + (m), 0, (m)); \ + (m) *= 2; \ + } \ + else \ + free(tmp); \ } \ - (r) = REALLOC((r), (m) * 2); \ - if ((r)) { \ - memset((r) + (m), 0, (m)); \ - (m) *= 2; \ - } \ - else \ - free(tmp); \ } \ } while (0) diff --git a/multipathd/cli_handlers.c b/multipathd/cli_handlers.c index 60e17d6..4c32d95 100644 --- a/multipathd/cli_handlers.c +++ b/multipathd/cli_handlers.c @@ -730,7 +730,7 @@ cli_add_path (void * v, char ** reply, int * len, void * data) conf = get_multipath_config(); pthread_cleanup_push(put_multipath_config, conf); r = store_pathinfo(vecs->pathvec, conf, - udevice, DI_ALL, &pp); + udevice, DI_ALL | DI_BLACKLIST, &pp); pthread_cleanup_pop(1); udev_device_unref(udevice); if (!pp) { diff --git a/multipathd/main.c b/multipathd/main.c index f203d77..7a5cd11 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -220,6 +220,33 @@ static void config_cleanup(void *arg) pthread_mutex_unlock(&config_lock); } +/* + * If the current status is @oldstate, wait for at most @ms milliseconds + * for the state to change, and return the new state, which may still be + * @oldstate. + */ +enum daemon_status wait_for_state_change_if(enum daemon_status oldstate, + unsigned long ms) +{ + enum daemon_status st; + struct timespec tmo; + + if (oldstate == DAEMON_SHUTDOWN) + return DAEMON_SHUTDOWN; + + pthread_mutex_lock(&config_lock); + pthread_cleanup_push(config_cleanup, NULL); + st = running_state; + if (st == oldstate && clock_gettime(CLOCK_MONOTONIC, &tmo) == 0) { + tmo.tv_nsec += ms * 1000 * 1000; + normalize_timespec(&tmo); + (void)pthread_cond_timedwait(&config_cond, &config_lock, &tmo); + st = running_state; + } + pthread_cleanup_pop(1); + return st; +} + /* must be called with config_lock held */ static void __post_config_state(enum daemon_status state) { @@ -380,7 +407,7 @@ set_multipath_wwid (struct multipath * mpp) if (strlen(mpp->wwid)) return; - dm_get_uuid(mpp->alias, mpp->wwid); + dm_get_uuid(mpp->alias, mpp->wwid, WWID_SIZE); } static void set_no_path_retry(struct multipath *mpp) diff --git a/multipathd/main.h b/multipathd/main.h index e5c1398..7bb8463 100644 --- a/multipathd/main.h +++ b/multipathd/main.h @@ -20,6 +20,8 @@ extern int uxsock_timeout; void exit_daemon(void); const char * daemon_status(void); +enum daemon_status wait_for_state_change_if(enum daemon_status oldstate, + unsigned long ms); int need_to_delay_reconfig (struct vectors *); int reconfigure (struct vectors *); int ev_add_path (struct path *, struct vectors *, int); diff --git a/multipathd/multipathd.8 b/multipathd/multipathd.8 index 94c3f97..edac7a9 100644 --- a/multipathd/multipathd.8 +++ b/multipathd/multipathd.8 @@ -25,6 +25,7 @@ multipathd \- Multipath daemon. .RB [\| \-v\ \c .IR verbosity \|] .RB [\| \-B \|] +.RB [\| \-w \|] . . .\" ---------------------------------------------------------------------------- @@ -77,6 +78,12 @@ be viewed by entering '\fIhelp\fR'. When you are finished entering commands, pre multipathd. See .BR multipath.conf(5). . +.TP +.B \-w +Since kernel 4.14 a new device-mapper event polling interface is used for updating +multipath devices on dmevents. Use this flag to force it to use the old event +waiting method, based on creating a seperate thread for each device. +. . . .\" ---------------------------------------------------------------------------- diff --git a/multipathd/uxlsnr.c b/multipathd/uxlsnr.c index 773bc87..04cbb7c 100644 --- a/multipathd/uxlsnr.c +++ b/multipathd/uxlsnr.c @@ -249,6 +249,18 @@ void * uxsock_listen(uxsock_trigger_fn uxsock_trigger, long ux_sock, continue; } + /* + * Client connection. We shouldn't answer while we're + * configuring - nothing may be configured yet. + * But we can't wait forever either, because this thread + * must handle signals. So wait a short while only. + */ + if (wait_for_state_change_if(DAEMON_CONFIGURE, 10) + == DAEMON_CONFIGURE) { + handle_signals(false); + continue; + } + /* see if a client wants to speak to us */ for (i = 1; i < num_clients + 1; i++) { if (polls[i].revents & POLLIN) { diff --git a/multipathd/waiter.c b/multipathd/waiter.c index 5895f15..eb8d699 100644 --- a/multipathd/waiter.c +++ b/multipathd/waiter.c @@ -11,6 +11,7 @@ #include #include +#include "util.h" #include "vector.h" #include "memory.h" #include "checkers.h" @@ -210,7 +211,7 @@ int start_waiter_thread (struct multipath *mpp, struct vectors *vecs) if (!wp) goto out; - strncpy(wp->mapname, mpp->alias, WWID_SIZE - 1); + strlcpy(wp->mapname, mpp->alias, WWID_SIZE); wp->vecs = vecs; if (pthread_create(&wp->thread, &waiter_attr, waitevent, wp)) { diff --git a/tests/Makefile b/tests/Makefile index ef90086..bf159b2 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -3,7 +3,7 @@ include ../Makefile.inc CFLAGS += $(BIN_CFLAGS) -I$(multipathdir) -I$(mpathcmddir) LIBDEPS += -L$(multipathdir) -lmultipath -lcmocka -TESTS := uevent parser util dmevents hwtable blacklist unaligned +TESTS := uevent parser util dmevents hwtable blacklist unaligned vpd .SILENT: $(TESTS:%=%.o) .PRECIOUS: $(TESTS:%=%-test) @@ -25,6 +25,8 @@ hwtable-test_OBJDEPS := ../libmultipath/discovery.o ../libmultipath/blacklist.o hwtable-test_LIBDEPS := -ludev -lpthread -ldl blacklist-test_OBJDEPS := ../libmultipath/blacklist.o blacklist-test_LIBDEPS := -ludev +vpd-test_OBJDEPS := ../libmultipath/discovery.o +vpd-test_LIBDEPS := -ludev -lpthread -ldl lib/libchecktur.so: mkdir lib diff --git a/tests/blacklist.c b/tests/blacklist.c index 54d568f..362c44d 100644 --- a/tests/blacklist.c +++ b/tests/blacklist.c @@ -267,7 +267,7 @@ static void test_property_blacklist(void **state) static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; conf.blist_property = blist_property_wwn; expect_condlog(3, "sdb: udev property ID_WWN blacklisted\n"); - assert_int_equal(filter_property(&conf, &udev, 3), + assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), MATCH_PROPERTY_BLIST); } @@ -281,17 +281,23 @@ static void test_property_whitelist(void **state) static struct udev_device udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; conf.elist_property = blist_property_wwn; expect_condlog(3, "sdb: udev property ID_WWN whitelisted\n"); - assert_int_equal(filter_property(&conf, &udev, 3), + assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), MATCH_PROPERTY_BLIST_EXCEPT); } static void test_property_missing(void **state) { - static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } }; + static struct udev_device udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } }; conf.blist_property = blist_property_wwn; expect_condlog(3, "sdb: blacklisted, udev property missing\n"); - assert_int_equal(filter_property(&conf, &udev, 3), + assert_int_equal(filter_property(&conf, &udev, 3, "ID_SERIAL"), MATCH_PROPERTY_BLIST_MISSING); + assert_int_equal(filter_property(&conf, &udev, 3, "ID_BLAH"), + MATCH_NOTHING); + assert_int_equal(filter_property(&conf, &udev, 3, ""), + MATCH_NOTHING); + assert_int_equal(filter_property(&conf, &udev, 3, NULL), + MATCH_NOTHING); } struct udev_device test_udev = { "sdb", { "ID_FOO", "ID_WWN", "ID_BAR", NULL } }; @@ -347,16 +353,25 @@ static void test_filter_path_wwid(void **state) assert_int_equal(filter_path(&conf, &test_pp), MATCH_WWID_BLIST); } -struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", NULL } }; +struct udev_device miss_udev = { "sdb", { "ID_FOO", "ID_BAZ", "ID_BAR", "ID_SERIAL", NULL } }; struct path miss1_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI, .udev = &miss_udev, + .uid_attribute = "ID_SERIAL", .sg_id.proto_id = SCSI_PROTOCOL_ISCSI, .vendor_id = "foo", .product_id = "baz", .wwid = "plugh" }; struct path miss2_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI, .udev = &test_udev, + .uid_attribute = "ID_SERIAL", + .sg_id.proto_id = SCSI_PROTOCOL_ISCSI, + .vendor_id = "foo", .product_id = "baz", + .wwid = "plugh" }; + +struct path miss3_pp = { .dev = "sdc", .bus = SYSFS_BUS_SCSI, + .udev = &miss_udev, + .uid_attribute = "ID_EGGS", .sg_id.proto_id = SCSI_PROTOCOL_ISCSI, .vendor_id = "foo", .product_id = "baz", .wwid = "plugh" }; @@ -387,6 +402,19 @@ static void test_filter_path_missing2(void **state) MATCH_NOTHING); } +/* Here we use a different uid_attribute which is also missing, thus + the path is not blacklisted */ +static void test_filter_path_missing3(void **state) +{ + conf.blist_property = blist_property_wwn; + conf.blist_devnode = blist_devnode_sdb; + conf.blist_device = blist_device_foo_bar; + conf.blist_protocol = blist_protocol_fcp; + conf.blist_wwid = blist_wwid_xyzzy; + assert_int_equal(filter_path(&conf, &miss3_pp), + MATCH_NOTHING); +} + static void test_filter_path_whitelist(void **state) { conf.elist_property = blist_property_wwn; @@ -495,6 +523,7 @@ int test_blacklist(void) test_and_reset(test_filter_path_wwid), test_and_reset(test_filter_path_missing1), test_and_reset(test_filter_path_missing2), + test_and_reset(test_filter_path_missing3), test_and_reset(test_filter_path_whitelist), test_and_reset(test_filter_path_whitelist_property), test_and_reset(test_filter_path_whitelist_devnode), diff --git a/tests/globals.c b/tests/globals.c index 07d970e..8add5eb 100644 --- a/tests/globals.c +++ b/tests/globals.c @@ -3,9 +3,8 @@ /* Required globals */ struct udev *udev; -int logsink = 0; +int logsink = -1; struct config conf = { - .uid_attrs = "sd:ID_BOGUS", .verbosity = 4, }; diff --git a/tests/hwtable.c b/tests/hwtable.c index ad863b0..977a566 100644 --- a/tests/hwtable.c +++ b/tests/hwtable.c @@ -54,7 +54,7 @@ struct hwt_state { static struct config *_conf; struct udev *udev; -int logsink; +int logsink = -1; struct config *get_multipath_config(void) { @@ -457,7 +457,7 @@ static void replicate_config(const struct hwt_state *hwt, bool local) vector hwtable; struct config *conf; - condlog(1, "--- %s: replicating %s configuration", __func__, + condlog(3, "--- %s: replicating %s configuration", __func__, local ? "local" : "full"); conf = get_multipath_config(); @@ -571,7 +571,7 @@ static void test_internal_nvme(const struct hwt_state *hwt) mp = mock_multipath(pp); assert_ptr_not_equal(mp, NULL); TEST_PROP(checker_name(&pp->checker), NONE); - TEST_PROP(pp->uid_attribute, "ID_WWN"); + TEST_PROP(pp->uid_attribute, DEFAULT_NVME_UID_ATTRIBUTE); assert_int_equal(mp->pgpolicy, DEFAULT_PGPOLICY); assert_int_equal(mp->no_path_retry, DEFAULT_NO_PATH_RETRY); assert_int_equal(mp->retain_hwhandler, RETAIN_HWHANDLER_OFF); diff --git a/tests/uevent.c b/tests/uevent.c index b0d0bfd..f4afd9b 100644 --- a/tests/uevent.c +++ b/tests/uevent.c @@ -43,7 +43,11 @@ void uevent_get_wwid(struct uevent *uev); static int setup_uev(void **state) { + static char test_uid_attrs[] = + "dasd:ID_SPAM sd:ID_BOGUS nvme:ID_EGGS "; + struct uevent *uev = alloc_uevent(); + struct config *conf; if (uev == NULL) return -1; @@ -51,11 +55,16 @@ static int setup_uev(void **state) *state = uev; uev->kernel = "sdo"; uev->envp[0] = "MAJOR=" str(MAJOR); + uev->envp[1] = "ID_SPAM=nonsense"; uev->envp[1] = "ID_BOGUS=" WWID; uev->envp[2] = "MINOR=" str(MINOR); uev->envp[3] = "DM_NAME=" DM_NAME; uev->envp[4] = "DISK_RO=" str(DISK_RO); uev->envp[5] = NULL; + + conf = get_multipath_config(); + parse_uid_attrs(test_uid_attrs, conf); + put_multipath_config(conf); return 0; } @@ -86,6 +95,23 @@ static void test_ro_good(void **state) assert_int_equal(uevent_get_disk_ro(uev), DISK_RO); } +static void test_uid_attrs(void **state) +{ + /* see test_uid_attrs above */ + struct config *conf = get_multipath_config(); + vector attrs = &conf->uid_attrs; + + assert_int_equal(VECTOR_SIZE(attrs), 3); + assert_null(get_uid_attribute_by_attrs(conf, "hda")); + assert_string_equal("ID_BOGUS", + get_uid_attribute_by_attrs(conf, "sdaw")); + assert_string_equal("ID_SPAM", + get_uid_attribute_by_attrs(conf, "dasdu")); + assert_string_equal("ID_EGGS", + get_uid_attribute_by_attrs(conf, "nvme2n4")); + put_multipath_config(conf); +} + static void test_wwid(void **state) { struct uevent *uev = *state; @@ -271,6 +297,7 @@ int test_uevent_get_XXX(void) cmocka_unit_test(test_minor_good), cmocka_unit_test(test_ro_good), cmocka_unit_test(test_dm_name_good), + cmocka_unit_test(test_uid_attrs), cmocka_unit_test(test_wwid), cmocka_unit_test(test_major_bad_0), cmocka_unit_test(test_major_bad_1), diff --git a/tests/util.c b/tests/util.c index e6d4b9a..4e04a48 100644 --- a/tests/util.c +++ b/tests/util.c @@ -258,10 +258,152 @@ int test_basenamecpy(void) return cmocka_run_group_tests(tests, NULL, NULL); } +static const char src_str[] = "Hello"; + +/* strlcpy with length 0 */ +static void test_strlcpy_0(void **state) +{ + char tst[] = "word"; + int rc; + + rc = strlcpy(tst, src_str, 0); + assert_int_equal(rc, strlen(src_str)); + assert_string_equal(tst, "word"); +} + +/* strlcpy with length 1 */ +static void test_strlcpy_1(void **state) +{ + char tst[] = "word"; + int rc; + + rc = strlcpy(tst, src_str, 1); + assert_int_equal(rc, strlen(src_str)); + assert_int_equal(tst[0], '\0'); + assert_string_equal(tst + 1, "ord"); +} + +/* strlcpy with length 2 */ +static void test_strlcpy_2(void **state) +{ + char tst[] = "word"; + int rc; + + rc = strlcpy(tst, src_str, 2); + assert_int_equal(rc, strlen(src_str)); + assert_int_equal(tst[0], src_str[0]); + assert_int_equal(tst[1], '\0'); + assert_string_equal(tst + 2, "rd"); +} + +/* strlcpy with dst length < src length */ +static void test_strlcpy_3(void **state) +{ + char tst[] = "word"; + int rc; + + rc = strlcpy(tst, src_str, sizeof(tst)); + assert_int_equal(rc, strlen(src_str)); + assert_int_equal(sizeof(tst) - 1, strlen(tst)); + assert_true(strncmp(tst, src_str, sizeof(tst) - 1) == 0); +} + +/* strlcpy with dst length > src length */ +static void test_strlcpy_4(void **state) +{ + static const char old[] = "0123456789"; + char *tst; + int rc; + + tst = strdup(old); + rc = strlcpy(tst, src_str, sizeof(old)); + assert_int_equal(rc, strlen(src_str)); + assert_string_equal(src_str, tst); + assert_string_equal(tst + sizeof(src_str), old + sizeof(src_str)); + free(tst); +} + +/* strlcpy with dst length = src length, dst not terminated */ +static void test_strlcpy_5(void **state) +{ + char *tst; + int rc; + + tst = malloc(sizeof(src_str)); + memset(tst, 'f', sizeof(src_str)); + + rc = strlcpy(tst, src_str, sizeof(src_str)); + assert_int_equal(rc, strlen(src_str)); + assert_string_equal(src_str, tst); + + free(tst); +} + +/* strlcpy with dst length > src length, dst not terminated */ +static void test_strlcpy_6(void **state) +{ + char *tst; + int rc; + + tst = malloc(sizeof(src_str) + 2); + memset(tst, 'f', sizeof(src_str) + 2); + + rc = strlcpy(tst, src_str, sizeof(src_str) + 2); + assert_int_equal(rc, strlen(src_str)); + assert_string_equal(src_str, tst); + assert_int_equal(tst[sizeof(src_str)], 'f'); + assert_int_equal(tst[sizeof(src_str) + 1], 'f'); + + free(tst); +} + +/* strlcpy with empty src */ +static void test_strlcpy_7(void **state) +{ + char tst[] = "word"; + static const char empty[] = ""; + int rc; + + rc = strlcpy(tst, empty, sizeof(tst)); + assert_int_equal(rc, strlen(empty)); + assert_string_equal(empty, tst); + assert_string_equal(tst + 1, "ord"); +} + +/* strlcpy with empty src, length 0 */ +static void test_strlcpy_8(void **state) +{ + char tst[] = "word"; + static const char empty[] = ""; + int rc; + + rc = strlcpy(tst, empty, 0); + assert_int_equal(rc, strlen(empty)); + assert_string_equal("word", tst); +} + +static int test_strlcpy(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_strlcpy_0), + cmocka_unit_test(test_strlcpy_1), + cmocka_unit_test(test_strlcpy_2), + cmocka_unit_test(test_strlcpy_3), + cmocka_unit_test(test_strlcpy_4), + cmocka_unit_test(test_strlcpy_5), + cmocka_unit_test(test_strlcpy_6), + cmocka_unit_test(test_strlcpy_7), + cmocka_unit_test(test_strlcpy_8), + }; + + return cmocka_run_group_tests(tests, NULL, NULL); +} + int main(void) { int ret = 0; ret += test_basenamecpy(); + ret += test_strlcpy(); return ret; } diff --git a/tests/vpd.c b/tests/vpd.c new file mode 100644 index 0000000..d9f80ea --- /dev/null +++ b/tests/vpd.c @@ -0,0 +1,790 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (c) 2019 Martin Wilck, SUSE Linux GmbH, Nuremberg */ + +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "unaligned.h" +#include "debug.h" +#include "vector.h" +#include "structs.h" +#include "discovery.h" +#include "globals.c" + +#define VPD_BUFSIZ 4096 + +struct vpdtest { + unsigned char vpdbuf[VPD_BUFSIZ]; + char wwid[WWID_SIZE]; +}; + +static int setup(void **state) +{ + struct vpdtest *vt = malloc(sizeof(*vt)); + + if (vt == NULL) + return -1; + *state = vt; + return 0; +} + +static int teardown(void **state) +{ + struct vpdtest *vt = *state; + + free(vt); + *state = NULL; + return 0; +} + +/* vendor_id should have less than 8 chars to test space handling */ +static const char vendor_id[] = "Linux"; +static const char test_id[] = + "A123456789AbcDefB123456789AbcDefC123456789AbcDefD123456789AbcDef"; + +int __wrap_ioctl(int fd, unsigned long request, void *param) +{ + int len; + struct sg_io_hdr *io_hdr; + unsigned char *val; + + len = mock(); + io_hdr = (struct sg_io_hdr *)param; + assert_in_range(len, 0, io_hdr->dxfer_len); + val = mock_ptr_type(unsigned char *); + io_hdr->status = 0; + memcpy(io_hdr->dxferp, val, len); + return 0; +} + + +/** + * create_vpd80() - create a "unit serial number" VPD page. + * @buf: VPD buffer + * @bufsiz: length of VPD buffer + * @id: input ID + * @size: value for the "page length" field + * @len: actual number of characters to use from @id + * + * If len < size, the content will be right aligned, as mandated by the + * SPC spec. + * + * Return: VPD length. + */ +static int create_vpd80(unsigned char *buf, size_t bufsiz, const char *id, + int size, int len) +{ + assert_true(len <= size); + + memset(buf, 0, bufsiz); + buf[1] = 0x80; + put_unaligned_be16(size, buf + 2); + + memset(buf + 4, ' ', size - len); + memcpy(buf + 4 + size - len, id, len); + return size + 4; +} + +static int _hex2bin(const char hx) +{ + assert_true(isxdigit(hx)); + if (hx >= '0' && hx <= '9') + return hx - '0'; + if (hx >= 'a' && hx <= 'f') + return hx - 'a' + 10; + if (hx >= 'A' && hx <= 'F') + return hx - 'A' + 10; + return -1; +} + +static void hex2bin(unsigned char *dst, const char *src, + size_t dstlen, size_t srclen) +{ + const char *sc; + unsigned char *ds; + + assert(srclen % 2 == 0); + for (sc = src, ds = dst; + sc < src + srclen && ds < dst + dstlen; + sc += 2, ++ds) + *ds = 16 * _hex2bin(sc[0]) + _hex2bin(sc[1]); +} + +/** + * create_t10_vendor_id_desc() - Create T10 vendor ID + * @desc: descriptor buffer + * @id: input ID + * @n: number of characters to use for ID + * (includes 8 bytes for vendor ID!) + * + * Create a "T10 vendor specific ID" designation descriptor. + * The vendor field (8 bytes) is filled with vendor_id (above). + * + * Return: descriptor length. + */ +static int create_t10_vendor_id_desc(unsigned char *desc, + const char *id, size_t n) +{ + int vnd_len = sizeof(vendor_id) - 1; + + /* code set: ascii */ + desc[0] = 2; + /* type: 10 vendor ID */ + desc[1] = 1; + desc[2] = 0; + desc[3] = n; + + memcpy(desc + 4, (const unsigned char *)vendor_id, vnd_len); + memset(desc + 4 + vnd_len, ' ', 8 - vnd_len); + memcpy(desc + 4 + 8, (const unsigned char *)id, n - 8); + + return n + 4; +} + +/** + * create_eui64_desc() - create EUI64 descriptor. + * @desc, @id: see above. + * @n: number of bytes (8, 12, or 16). + * + * Create an EUI64 designation descriptor. + * + * Return: descriptor length. + */ +static int create_eui64_desc(unsigned char *desc, + const char *id, size_t n) +{ + assert_true(n == 8 || n == 12 || n == 16); + + /* code set: binary */ + desc[0] = 1; + /* type: EUI64 */ + desc[1] = 2; + desc[2] = 0; + desc[3] = n; + + hex2bin(desc + 4, id, n, 2 * n); + return n + 4; +} + +/** + * create_naa_desc() - create an NAA designation descriptor + * @desc, @id: see above. + * @naa: Name Address Authority field (2, 3, 5, or 6). + * + * Return: descriptor length. + */ +static int create_naa_desc(unsigned char *desc, + const char *id, int naa) +{ + assert_true(naa == 2 || naa == 3 || naa == 5 || naa == 6); + + /* code set: binary */ + desc[0] = 1; + /* type: NAA */ + desc[1] = 3; + desc[2] = 0; + desc[4] = _hex2bin(id[0]) | (naa << 4); + switch (naa) { + case 2: + case 3: + case 5: + hex2bin(desc + 5, id + 1, 7, 14); + desc[3] = 8; + return 12; + case 6: + hex2bin(desc + 5, id + 1, 15, 30); + desc[3] = 16; + return 20; + default: + return 0; + } +} + +/* type and flags for SCSI name string designation descriptor */ +enum { + STR_EUI = 0, + STR_NAA, + STR_IQN, + STR_MASK = 0xf, + ZERO_LAST = 0x10, /* flag to zero out some bytes at string end */ +}; + +static const char * const str_prefix[] = { + [STR_EUI] = "eui.", + [STR_NAA] = "naa.", + [STR_IQN] = "iqn.", +}; + +static const char byte0[] = { + [STR_EUI] = '2', + [STR_NAA] = '3', + [STR_IQN] = '8', +}; + +/** + * create_scsi_string_desc() - create a SCSI name string descriptor. + * @desc, @id: see above. + * @typ: one of STR_EUI, STR_NAA, STR_IQN, possibly ORd with ZERO_LAST + * @maxlen: number of characters to use from input ID. + * + * If ZERO_LAST is set, zero out the last byte. + * + * Return: descriptor length. + */ +static int create_scsi_string_desc(unsigned char *desc, + const char *id, int typ, int maxlen) +{ + int len, plen; + int type = typ & STR_MASK; + + /* code set: UTF-8 */ + desc[0] = 3; + /* type: SCSI string */ + desc[1] = 8; + desc[2] = 0; + + assert_in_range(type, STR_EUI, STR_IQN); + assert_true(maxlen % 4 == 0); + len = snprintf((char *)(desc + 4), maxlen, "%s%s", + str_prefix[type], id); + if (len > maxlen) + len = maxlen; + /* zero-pad */ + if (typ & ZERO_LAST) + len -= 2; + plen = 4 * ((len - 1) / 4) + 4; + memset(desc + 4 + len, '\0', plen - len); + desc[3] = plen; + return plen + 4; +} + +/** + * create_vpd83() - create "device identification" VPD page + * @buf, @bufsiz, @id: see above. + * @type: descriptor type to use (1, 2, 3, 8) + * @parm: opaque parameter (e.g. means "naa" for NAA type) + * @len: designator length (exact meaning depends on designator type) + * + * Create a "device identification" VPD page with a single + * designation descriptor. + * + * Return: VPD page length. + */ +static int create_vpd83(unsigned char *buf, size_t bufsiz, const char *id, + uint8_t type, int parm, int len) +{ + unsigned char *desc; + int n = 0; + + memset(buf, 0, bufsiz); + buf[1] = 0x83; + + desc = buf + 4; + switch (type) { + case 1: + n = create_t10_vendor_id_desc(desc, id, len); + break; + case 2: + n = create_eui64_desc(desc, id, len); + break; + case 3: + n = create_naa_desc(desc, id, parm); + break; + case 8: + n = create_scsi_string_desc(desc, id, parm, len); + break; + default: + break; + } + put_unaligned_be16(n, buf + 2); + return n + 4; +} + +/** + * assert_correct_wwid() - test that a retrieved WWID matches expectations + * @test: test name + * @expected: expected WWID length + * @returned: WWID length as returned by code under test + * @byte0, @byte1: leading chars that our code prepends to the ID + * (e.g. "36" for "NAA registered extended" type) + * @lowercase: set if lower case WWID is expected + * @orig: original ID string, may be longer than wwid + * @wwid: WWID as returned by code under test + */ +static void assert_correct_wwid(const char *test, + int expected, int returned, + int byte0, int byte1, bool lowercase, + const char *orig, + const char *wwid) +{ + int ofs = 0, i; + + condlog(2, "%s: exp/ret: %d/%d, wwid: %s", test, + expected, returned, wwid); + /* + * byte0 and byte1 are the leading chars that our code prepends + * to the ID to indicate the designation descriptor type, . + */ + if (byte0 != 0) { + assert_int_equal(byte0, wwid[0]); + ++ofs; + } + if (byte1 != 0) { + assert_int_equal(byte1, wwid[1]); + ++ofs; + } + /* check matching length, and length of WWID string */ + assert_int_equal(expected, returned); + assert_int_equal(returned, strlen(wwid)); + /* check expected string value up to expected length */ + for (i = 0; i < returned - ofs; i++) + assert_int_equal(wwid[ofs + i], + lowercase ? tolower(orig[i]) : orig[i]); +} + +/* + * For T10 vendor ID - replace sequences of spaces with a single underscore. + * Use a different implementation then libmultipath, deliberately. + */ +static char *subst_spaces(const char *src) +{ + char *dst = calloc(1, strlen(src) + 1); + char *p; + static regex_t *re; + regmatch_t match; + int rc; + + assert_non_null(dst); + if (re == NULL) { + re = calloc(1, sizeof(*re)); + assert_non_null(re); + rc = regcomp(re, " +", REG_EXTENDED); + assert_int_equal(rc, 0); + } + + for (rc = regexec(re, src, 1, &match, 0), p = dst; + rc == 0; + src += match.rm_eo, rc = regexec(re, src, 1, &match, 0)) { + memcpy(p, src, match.rm_so); + p += match.rm_so; + *p = '_'; + ++p; + } + assert_int_equal(rc, REG_NOMATCH); + strcpy(p, src); + return dst; +} + +/** + * test_vpd_vnd_LEN_WLEN() - test code for VPD 83, T10 vendor ID + * @LEN: ID length in the VPD page (includes 8 byte vendor ID) + * @WLEN: WWID buffer size + * + * The input ID is modified by inserting some spaces, to be able to + * test the handling of spaces by the code. This is relevant only for + * a minimum input length of 24. + * The expected result must be adjusted accordingly. + */ +#define make_test_vpd_vnd(len, wlen) \ +static void test_vpd_vnd_ ## len ## _ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret, rc; \ + int exp_len; \ + char *exp_wwid, *exp_subst, *input; \ + \ + input = strdup(test_id); \ + /* 8 vendor bytes collapsed to actual vendor ID length + 1 */ \ + /* and one '1' prepended */ \ + exp_len = len - 8 + sizeof(vendor_id) + 1; \ + \ + /* insert some spaces to test space collapsing */ \ + input[15] = input[17] = input[18] = ' '; \ + /* adjust expectation for space treatment */ \ + /* drop char for 2nd space on offset 17/18 */ \ + if (len >= 18 + 9) \ + --exp_len; \ + /* drop trailing single '_' if input ends with space */ \ + if (len == 15 + 9 || len == 17 + 9 || len == 18 + 9) \ + --exp_len; \ + if (exp_len >= wlen) \ + exp_len = wlen - 1; \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), input, \ + 1, 0, len); \ + rc = asprintf(&exp_wwid, "%s_%s", vendor_id, input); \ + assert_int_not_equal(rc, -1); \ + free(input); \ + /* Replace spaces, like code under test */ \ + exp_subst = subst_spaces(exp_wwid); \ + free(exp_wwid); \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd_vnd_" #len "_" #wlen, \ + exp_len, ret, '1', 0, false, \ + exp_subst, vt->wwid); \ + free(exp_subst); \ +} + +/** + * test_vpd_str_TYP_LEN_WLEN() - test code for VPD 83, SCSI name string + * @TYP: numeric value of STR_EUI, STR_NAA, STR_IQN above + * @LEN: ID length the VPD page + * @WLEN: WWID buffer size + */ +#define make_test_vpd_str(typ, len, wlen) \ +static void test_vpd_str_ ## typ ## _ ## len ## _ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + int exp_len; \ + int type = typ & STR_MASK; \ + \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \ + 8, typ, len); \ + exp_len = len - strlen(str_prefix[type]); \ + if (typ & ZERO_LAST) \ + exp_len--; \ + if (exp_len >= wlen) \ + exp_len = wlen - 1; \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd_str_" #typ "_" #len "_" #wlen, \ + exp_len, ret, byte0[type], 0, \ + type != STR_IQN, \ + test_id, vt->wwid); \ +} + +/** + * test_vpd_naa_NAA_WLEN() - test code for VPD 83 NAA designation + * @NAA: Network Name Authority (2, 3, 5, or 6) + * @WLEN: WWID buffer size + */ +#define make_test_vpd_naa(naa, wlen) \ +static void test_vpd_naa_ ## naa ## _ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + int len, exp_len; \ + \ + switch (naa) { \ + case 2: \ + case 3: \ + case 5: \ + len = 17; \ + break; \ + case 6: \ + len = 33; \ + break; \ + } \ + /* returned size is always uneven */ \ + exp_len = wlen > len ? len : \ + wlen % 2 == 0 ? wlen - 1 : wlen - 2; \ + \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \ + 3, naa, 0); \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd_naa_" #naa "_" #wlen, \ + exp_len, ret, '3', '0' + naa, true, \ + test_id, vt->wwid); \ +} + +/** + * test_vpd_eui_LEN_WLEN() - test code for VPD 83, EUI64 + * @LEN: EUI64 length (8, 12, or 16) + * @WLEN: WWID buffer size + */ +#define make_test_vpd_eui(len, wlen) \ +static void test_vpd_eui_ ## len ## _ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + /* returned size is always uneven */ \ + int exp_len = wlen > 2 * len + 1 ? 2 * len + 1 : \ + wlen % 2 == 0 ? wlen - 1 : wlen - 2; \ + \ + n = create_vpd83(vt->vpdbuf, sizeof(vt->vpdbuf), test_id, \ + 2, 0, len); \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x83, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd_eui_" #len "_" #wlen, \ + exp_len, ret, '2', 0, true, \ + test_id, vt->wwid); \ +} + +/** + * test_vpd80_SIZE_LEN_WLEN() - test code for VPD 80 + * @SIZE, @LEN: see create_vpd80() + * @WLEN: WWID buffer size + */ +#define make_test_vpd80(size, len, wlen) \ +static void test_vpd80_ ## size ## _ ## len ## _ ## wlen(void **state) \ +{ \ + struct vpdtest *vt = *state; \ + int n, ret; \ + int exp_len = len > 20 ? 20 : len; \ + char *input = strdup(test_id); \ + \ + /* insert trailing whitespace after pos 20 */ \ + memset(input + 20, ' ', sizeof(test_id) - 20); \ + if (exp_len >= wlen) \ + exp_len = wlen - 1; \ + n = create_vpd80(vt->vpdbuf, sizeof(vt->vpdbuf), input, \ + size, len); \ + will_return(__wrap_ioctl, n); \ + will_return(__wrap_ioctl, vt->vpdbuf); \ + ret = get_vpd_sgio(10, 0x80, vt->wwid, wlen); \ + assert_correct_wwid("test_vpd80_" #size "_" #len "_" #wlen, \ + exp_len, ret, 0, 0, false, \ + input, vt->wwid); \ + free(input); \ +} + +/* VPD 80 */ +/* Tests without trailing whitespace: 21 WWID bytes required */ +make_test_vpd80(20, 20, 30); +make_test_vpd80(20, 20, 21); +make_test_vpd80(20, 20, 20); +make_test_vpd80(20, 20, 10); + +/* Tests with 4 byte trailing whitespace: 21 WWID bytes required */ +make_test_vpd80(24, 24, 30); +make_test_vpd80(24, 24, 25); +make_test_vpd80(24, 24, 24); +make_test_vpd80(24, 24, 21); +make_test_vpd80(24, 24, 20); + +/* Tests with 4 byte leading whitespace: 17 WWID bytes required */ +make_test_vpd80(20, 16, 30); +make_test_vpd80(20, 16, 17); +make_test_vpd80(20, 16, 16); + +/* Tests with 4 byte leading whitespace: 21 WWID bytes required */ +make_test_vpd80(24, 20, 21); +make_test_vpd80(24, 20, 20); + +/* Tests with leading and trailing whitespace: 21 WWID bytes required */ +make_test_vpd80(30, 24, 30); +make_test_vpd80(30, 24, 21); +make_test_vpd80(30, 24, 20); + +/* VPD 83, T10 vendor ID */ +make_test_vpd_vnd(40, 40); +make_test_vpd_vnd(40, 30); +make_test_vpd_vnd(30, 20); +make_test_vpd_vnd(29, 30); +make_test_vpd_vnd(28, 30); +make_test_vpd_vnd(27, 30); /* space at end */ +make_test_vpd_vnd(26, 30); /* space at end */ +make_test_vpd_vnd(25, 30); +make_test_vpd_vnd(24, 30); /* space at end */ +make_test_vpd_vnd(23, 30); +make_test_vpd_vnd(24, 20); +make_test_vpd_vnd(23, 20); +make_test_vpd_vnd(22, 20); +make_test_vpd_vnd(21, 20); +make_test_vpd_vnd(20, 20); +make_test_vpd_vnd(19, 20); +make_test_vpd_vnd(20, 10); +make_test_vpd_vnd(10, 10); + +/* EUI64 tests */ +/* 64bit, WWID size: 18 */ +make_test_vpd_eui(8, 32); +make_test_vpd_eui(8, 18); +make_test_vpd_eui(8, 17); +make_test_vpd_eui(8, 16); +make_test_vpd_eui(8, 10); + +/* 96 bit, WWID size: 26 */ +make_test_vpd_eui(12, 32); +make_test_vpd_eui(12, 26); +make_test_vpd_eui(12, 25); +make_test_vpd_eui(12, 20); +make_test_vpd_eui(12, 10); + +/* 128 bit, WWID size: 34 */ +make_test_vpd_eui(16, 40); +make_test_vpd_eui(16, 34); +make_test_vpd_eui(16, 33); +make_test_vpd_eui(16, 20); + +/* NAA IEEE registered extended (36), WWID size: 34 */ +make_test_vpd_naa(6, 40); +make_test_vpd_naa(6, 34); +make_test_vpd_naa(6, 33); +make_test_vpd_naa(6, 32); +make_test_vpd_naa(6, 20); + +/* NAA IEEE registered (35), WWID size: 18 */ +make_test_vpd_naa(5, 20); +make_test_vpd_naa(5, 18); +make_test_vpd_naa(5, 17); +make_test_vpd_naa(5, 16); + +/* NAA local (33), WWID size: 18 */ +make_test_vpd_naa(3, 20); +make_test_vpd_naa(3, 18); +make_test_vpd_naa(3, 17); +make_test_vpd_naa(3, 16); + +/* NAA IEEE extended (32), WWID size: 18 */ +make_test_vpd_naa(2, 20); +make_test_vpd_naa(2, 18); +make_test_vpd_naa(2, 17); +make_test_vpd_naa(2, 16); + +/* SCSI Name string: EUI64, WWID size: 17 */ +make_test_vpd_str(0, 20, 18) +make_test_vpd_str(0, 20, 17) +make_test_vpd_str(0, 20, 16) +make_test_vpd_str(0, 20, 15) + +/* SCSI Name string: EUI64, zero padded, WWID size: 16 */ +make_test_vpd_str(16, 20, 18) +make_test_vpd_str(16, 20, 17) +make_test_vpd_str(16, 20, 16) +make_test_vpd_str(16, 20, 15) + +/* SCSI Name string: NAA, WWID size: 17 */ +make_test_vpd_str(1, 20, 18) +make_test_vpd_str(1, 20, 17) +make_test_vpd_str(1, 20, 16) +make_test_vpd_str(1, 20, 15) + +/* SCSI Name string: NAA, zero padded, WWID size: 16 */ +make_test_vpd_str(17, 20, 18) +make_test_vpd_str(17, 20, 17) +make_test_vpd_str(17, 20, 16) +make_test_vpd_str(17, 20, 15) + +/* SCSI Name string: IQN, WWID size: 17 */ +make_test_vpd_str(2, 20, 18) +make_test_vpd_str(2, 20, 17) +make_test_vpd_str(2, 20, 16) +make_test_vpd_str(2, 20, 15) + +/* SCSI Name string: IQN, zero padded, WWID size: 16 */ +make_test_vpd_str(18, 20, 18) +make_test_vpd_str(18, 20, 17) +make_test_vpd_str(18, 20, 16) +make_test_vpd_str(18, 20, 15) + +static int test_vpd(void) +{ + const struct CMUnitTest tests[] = { + cmocka_unit_test(test_vpd80_20_20_30), + cmocka_unit_test(test_vpd80_20_20_21), + cmocka_unit_test(test_vpd80_20_20_20), + cmocka_unit_test(test_vpd80_20_20_10), + cmocka_unit_test(test_vpd80_24_24_30), + cmocka_unit_test(test_vpd80_24_24_25), + cmocka_unit_test(test_vpd80_24_24_24), + cmocka_unit_test(test_vpd80_24_24_21), + cmocka_unit_test(test_vpd80_24_24_20), + cmocka_unit_test(test_vpd80_20_16_30), + cmocka_unit_test(test_vpd80_20_16_17), + cmocka_unit_test(test_vpd80_20_16_16), + cmocka_unit_test(test_vpd80_24_20_21), + cmocka_unit_test(test_vpd80_24_20_20), + cmocka_unit_test(test_vpd80_30_24_30), + cmocka_unit_test(test_vpd80_30_24_21), + cmocka_unit_test(test_vpd80_30_24_20), + cmocka_unit_test(test_vpd_vnd_40_40), + cmocka_unit_test(test_vpd_vnd_40_30), + cmocka_unit_test(test_vpd_vnd_30_20), + cmocka_unit_test(test_vpd_vnd_29_30), + cmocka_unit_test(test_vpd_vnd_28_30), + cmocka_unit_test(test_vpd_vnd_27_30), + cmocka_unit_test(test_vpd_vnd_26_30), + cmocka_unit_test(test_vpd_vnd_25_30), + cmocka_unit_test(test_vpd_vnd_24_30), + cmocka_unit_test(test_vpd_vnd_23_30), + cmocka_unit_test(test_vpd_vnd_24_20), + cmocka_unit_test(test_vpd_vnd_23_20), + cmocka_unit_test(test_vpd_vnd_22_20), + cmocka_unit_test(test_vpd_vnd_21_20), + cmocka_unit_test(test_vpd_vnd_20_20), + cmocka_unit_test(test_vpd_vnd_19_20), + cmocka_unit_test(test_vpd_vnd_20_10), + cmocka_unit_test(test_vpd_vnd_10_10), + cmocka_unit_test(test_vpd_eui_8_32), + cmocka_unit_test(test_vpd_eui_8_18), + cmocka_unit_test(test_vpd_eui_8_17), + cmocka_unit_test(test_vpd_eui_8_16), + cmocka_unit_test(test_vpd_eui_8_10), + cmocka_unit_test(test_vpd_eui_12_32), + cmocka_unit_test(test_vpd_eui_12_26), + cmocka_unit_test(test_vpd_eui_12_25), + cmocka_unit_test(test_vpd_eui_12_20), + cmocka_unit_test(test_vpd_eui_12_10), + cmocka_unit_test(test_vpd_eui_16_40), + cmocka_unit_test(test_vpd_eui_16_34), + cmocka_unit_test(test_vpd_eui_16_33), + cmocka_unit_test(test_vpd_eui_16_20), + cmocka_unit_test(test_vpd_naa_6_40), + cmocka_unit_test(test_vpd_naa_6_34), + cmocka_unit_test(test_vpd_naa_6_33), + cmocka_unit_test(test_vpd_naa_6_32), + cmocka_unit_test(test_vpd_naa_6_20), + cmocka_unit_test(test_vpd_naa_5_20), + cmocka_unit_test(test_vpd_naa_5_18), + cmocka_unit_test(test_vpd_naa_5_17), + cmocka_unit_test(test_vpd_naa_5_16), + cmocka_unit_test(test_vpd_naa_3_20), + cmocka_unit_test(test_vpd_naa_3_18), + cmocka_unit_test(test_vpd_naa_3_17), + cmocka_unit_test(test_vpd_naa_3_16), + cmocka_unit_test(test_vpd_naa_2_20), + cmocka_unit_test(test_vpd_naa_2_18), + cmocka_unit_test(test_vpd_naa_2_17), + cmocka_unit_test(test_vpd_naa_2_16), + cmocka_unit_test(test_vpd_str_0_20_18), + cmocka_unit_test(test_vpd_str_0_20_17), + cmocka_unit_test(test_vpd_str_0_20_16), + cmocka_unit_test(test_vpd_str_0_20_15), + cmocka_unit_test(test_vpd_str_16_20_18), + cmocka_unit_test(test_vpd_str_16_20_17), + cmocka_unit_test(test_vpd_str_16_20_16), + cmocka_unit_test(test_vpd_str_16_20_15), + cmocka_unit_test(test_vpd_str_1_20_18), + cmocka_unit_test(test_vpd_str_1_20_17), + cmocka_unit_test(test_vpd_str_1_20_16), + cmocka_unit_test(test_vpd_str_1_20_15), + cmocka_unit_test(test_vpd_str_17_20_18), + cmocka_unit_test(test_vpd_str_17_20_17), + cmocka_unit_test(test_vpd_str_17_20_16), + cmocka_unit_test(test_vpd_str_17_20_15), + cmocka_unit_test(test_vpd_str_2_20_18), + cmocka_unit_test(test_vpd_str_2_20_17), + cmocka_unit_test(test_vpd_str_2_20_16), + cmocka_unit_test(test_vpd_str_2_20_15), + cmocka_unit_test(test_vpd_str_18_20_18), + cmocka_unit_test(test_vpd_str_18_20_17), + cmocka_unit_test(test_vpd_str_18_20_16), + cmocka_unit_test(test_vpd_str_18_20_15), + }; + return cmocka_run_group_tests(tests, setup, teardown); +} + +int main(void) +{ + int ret = 0; + + ret += test_vpd(); + return ret; +}