diff --git a/.github/actions/spelling/patterns.txt b/.github/actions/spelling/patterns.txt index 55d4742eb..b6a7aaeb7 100644 --- a/.github/actions/spelling/patterns.txt +++ b/.github/actions/spelling/patterns.txt @@ -15,6 +15,9 @@ \b0x[0-9a-f]{8}\b \b0x[0-9a-f]{16}\b +# Commit SHAs +\b[0-9a-f]{7}\b + # WWNN/WWPN (NAA identifiers) \b(?:0x)?10[0-9a-f]{14}\b \b(?:0x|3)?[25][0-9a-f]{15}\b diff --git a/.github/workflows/spelling.yml b/.github/workflows/spelling.yml index 7943ebb8e..bfce4e662 100644 --- a/.github/workflows/spelling.yml +++ b/.github/workflows/spelling.yml @@ -86,6 +86,23 @@ on: - 'opened' - 'reopened' - 'synchronize' + paths: + - '.github/workflows/spelling.yml' + - 'README*' + - 'NEWS.md' + - '**.3' + - '**.5' + - '**.8' + - '**.8' + - '**.in' + - '**.service' + - '**.socket' + - '**.rules' + - '**/libdmmp.h' + - '**/mpath_valid.h' + - '**/mpath_cmd.h' + - '**/mpath_persist.h' + - '.github/actions/spelling/*' jobs: spelling: diff --git a/NEWS.md b/NEWS.md index a842648e4..7aa94cd30 100644 --- a/NEWS.md +++ b/NEWS.md @@ -9,6 +9,28 @@ release. These bug fixes will be tracked in stable branches. See [README.md](README.md) for additional information. +## multipath-tools 0.14.1, 2026/01 + +### Bug fixes + +* kpartx: Fix freeing static buffer when operating on regular files. + Fixes 0.14.0. Commit fab5d44. + Fixes [#139](https://github.com/opensvc/multipath-tools/issues/139). +* Fix initialization of paths that were offline during path detection. + Commit 1942fb1. +* Fix printing the "path offline" log message for offline paths that don't + have a path checker configured. Commit 1a364a1. + +### Other changes + +* If path devices that are members of multipath maps aren't detected during + multipathd startup or `reconfigure`, don't add them to newly created maps + in the first place. In previous versions, such paths would be added to the + maps, only to be removed later. Commit a04be55. +* Improve the detection of "busy" state in the `show status` command, such + that reading udev events from udevd is also counted as busy. + Commit 7fdd93b. + ## multipath-tools 0.14.0, 2026/01 ### User-visible changes diff --git a/kpartx/devmapper.c b/kpartx/devmapper.c index d49c6806a..45dac585e 100644 --- a/kpartx/devmapper.c +++ b/kpartx/devmapper.c @@ -1,6 +1,7 @@ /* * Copyright (c) 2004, 2005 Christophe Varoqui */ +#define _GNU_SOURCE #include #include #include @@ -699,14 +700,13 @@ int dm_find_part(const char *parent, const char *delim, int part, char *nondm_create_uuid(dev_t devt) { -#define NONDM_UUID_BUFLEN (34 + sizeof(NONDM_UUID_PREFIX) + \ - sizeof(NONDM_UUID_SUFFIX)) - static char uuid_buf[NONDM_UUID_BUFLEN]; - snprintf(uuid_buf, sizeof(uuid_buf), "%s_%u:%u_%s", - NONDM_UUID_PREFIX, major(devt), minor(devt), - NONDM_UUID_SUFFIX); - uuid_buf[NONDM_UUID_BUFLEN-1] = '\0'; - return uuid_buf; + char *uuid; + + if (asprintf(&uuid, "%s_%u:%u_%s", NONDM_UUID_PREFIX, major(devt), + minor(devt), NONDM_UUID_SUFFIX) >= 0) + return uuid; + else + return NULL; } int nondm_parse_uuid(const char *uuid, unsigned int *major, unsigned int *minor) diff --git a/kpartx/kpartx.c b/kpartx/kpartx.c index 9bdd20409..cfd821284 100644 --- a/kpartx/kpartx.c +++ b/kpartx/kpartx.c @@ -334,8 +334,11 @@ main(int argc, char **argv){ * This allows deletion of partitions created with older kpartx * versions which didn't use the fake UUID during creation. */ - if (!uuid && !(what == DELETE && force_devmap)) + if (!uuid && !(what == DELETE && force_devmap)) { uuid = nondm_create_uuid(buf.st_rdev); + if (!uuid) + exit(1); + } if (delim == NULL) { delim = xmalloc(DELIM_SIZE); diff --git a/libmultipath/configure.c b/libmultipath/configure.c index 0bcec0890..a713ddc45 100644 --- a/libmultipath/configure.c +++ b/libmultipath/configure.c @@ -1113,7 +1113,17 @@ int coalesce_paths (struct vectors *vecs, vector mpvec, char *refwwid, continue; } - /* 4. path is out of scope */ + /* + * 4. The path wasn't found in path_discovery. It only exists + * in an old map. + */ + if (pp1->initialized == INIT_PARTIAL || + pp1->initialized == INIT_REMOVED) { + orphan_path(pp1, "path not found"); + continue; + } + + /* 5. path is out of scope */ if (refwwid && strncmp(pp1->wwid, refwwid, WWID_SIZE - 1)) continue; diff --git a/libmultipath/discovery.c b/libmultipath/discovery.c index 0c5e5ca69..0efb8213d 100644 --- a/libmultipath/discovery.c +++ b/libmultipath/discovery.c @@ -2585,6 +2585,8 @@ int pathinfo(struct path *pp, struct config *conf, int mask) * Recoverable error, for example faulty or offline path */ pp->chkrstate = pp->state = PATH_DOWN; + if (mask & DI_IOCTL && pp->ioctl_info == IOCTL_INFO_NOT_REQUESTED) + pp->ioctl_info = IOCTL_INFO_SKIPPED; if (pp->initialized == INIT_NEW || pp->initialized == INIT_FAILED) memset(pp->wwid, 0, WWID_SIZE); diff --git a/libmultipath/structs_vec.c b/libmultipath/structs_vec.c index 9384de017..3e63e9c3f 100644 --- a/libmultipath/structs_vec.c +++ b/libmultipath/structs_vec.c @@ -315,7 +315,8 @@ int adopt_paths(vector pathvec, struct multipath *mpp, pp->dev, mpp->alias); continue; } - if (pp->initialized == INIT_REMOVED) + if (pp->initialized == INIT_REMOVED || + pp->initialized == INIT_PARTIAL) continue; if (mpp->queue_mode == QUEUE_MODE_RQ && pp->bus == SYSFS_BUS_NVME && diff --git a/libmultipath/uevent.c b/libmultipath/uevent.c index be199af00..b0c433403 100644 --- a/libmultipath/uevent.c +++ b/libmultipath/uevent.c @@ -52,6 +52,7 @@ static pthread_cond_t *uev_condp = &uev_cond; static uev_trigger *my_uev_trigger; static void *my_trigger_data; static int servicing_uev; +static int adding_uev; /* uatomic access only */ struct uevent_filter_state { struct list_head uevq; @@ -70,13 +71,14 @@ static void reset_filter_state(struct uevent_filter_state *st) int is_uevent_busy(void) { - int empty, servicing; + int empty, servicing, adding; pthread_mutex_lock(uevq_lockp); empty = list_empty(&uevq); servicing = servicing_uev; + adding = uatomic_read(&adding_uev); pthread_mutex_unlock(uevq_lockp); - return (!empty || servicing); + return (!empty || servicing || adding); } struct uevent * alloc_uevent (void) @@ -730,6 +732,7 @@ int uevent_listen(struct udev *udev) int fdcount, events; struct pollfd ev_poll = { .fd = fd, .events = POLLIN, }; + uatomic_set(&adding_uev, 0); fdcount = poll(&ev_poll, 1, -1); if (fdcount < 0) { if (errno == EINTR) @@ -739,6 +742,8 @@ int uevent_listen(struct udev *udev) err = -errno; break; } + uatomic_set(&adding_uev, 1); + events = uevent_receive_events(fd, &uevlisten_tmp, monitor); if (events <= 0) continue; diff --git a/libmultipath/version.h b/libmultipath/version.h index 445ba9093..ddc7591fb 100644 --- a/libmultipath/version.h +++ b/libmultipath/version.h @@ -11,9 +11,9 @@ #ifndef VERSION_H_INCLUDED #define VERSION_H_INCLUDED -#define VERSION_CODE 0x000E00 +#define VERSION_CODE 0x000E01 /* MMDDYY, in hex */ -#define DATE_CODE 0x01101A +#define DATE_CODE 0x01171A #define PROG "multipath-tools" diff --git a/multipathd/main.c b/multipathd/main.c index 61e0ea346..05dd65e6a 100644 --- a/multipathd/main.c +++ b/multipathd/main.c @@ -96,12 +96,11 @@ mpath_pr_event_handle(struct path *pp, unsigned int nr_keys_needed, #define LOG_MSG(lvl, pp) \ do { \ - if (pp->mpp && checker_selected(&pp->checker) && \ - lvl <= libmp_verbosity) { \ - if (pp->sysfs_state == PATH_DOWN) \ + if (pp->mpp && lvl <= libmp_verbosity) { \ + if (pp->sysfs_state != PATH_UP) \ condlog(lvl, "%s: %s - path offline", \ pp->mpp->alias, pp->dev); \ - else { \ + else if (checker_selected(&pp->checker)) { \ const char *__m = \ checker_message(&pp->checker); \ \ @@ -2572,6 +2571,26 @@ static int sync_mpp(struct vectors *vecs, struct multipath *mpp, unsigned int ti return do_sync_mpp(vecs, mpp); } +/* + * pp->wwid should never be empty when this function is called, but if it + * is, this function can set it. + */ +static bool new_path_wwid_changed(struct path *pp, int state) +{ + char wwid[WWID_SIZE]; + + strlcpy(wwid, pp->wwid, WWID_SIZE); + if (get_uid(pp, state, pp->udev, 1) != 0) { + strlcpy(pp->wwid, wwid, WWID_SIZE); + return false; + } + if (strlen(wwid) && strncmp(wwid, pp->wwid, WWID_SIZE) != 0) { + strlcpy(pp->wwid, wwid, WWID_SIZE); + return true; + } + return false; +} + static int update_path_state (struct vectors * vecs, struct path * pp) { @@ -2601,14 +2620,33 @@ update_path_state (struct vectors * vecs, struct path * pp) return CHECK_PATH_SKIPPED; } - if (pp->recheck_wwid == RECHECK_WWID_ON && - (newstate == PATH_UP || newstate == PATH_GHOST) && + if ((newstate == PATH_UP || newstate == PATH_GHOST) && ((pp->state != PATH_UP && pp->state != PATH_GHOST) || - pp->dmstate == PSTATE_FAILED) && - check_path_wwid_change(pp)) { - condlog(0, "%s: path wwid change detected. Removing", pp->dev); - return handle_path_wwid_change(pp, vecs)? CHECK_PATH_REMOVED : - CHECK_PATH_SKIPPED; + pp->dmstate == PSTATE_FAILED)) { + bool wwid_changed = false; + + if (pp->initialized == INIT_NEW) { + /* + * Path was added to map while offline, mark it as + * initialized. + * DI_SYSFS was checked when the path was added + * DI_IOCTL was checked when the checker was selected + * DI_CHECKER just got checked + * DI_WWID is about to be checked + * DI_PRIO will get checked at the end of this checker + * loop + */ + pp->initialized = INIT_OK; + wwid_changed = new_path_wwid_changed(pp, newstate); + } else if (pp->recheck_wwid == RECHECK_WWID_ON) + wwid_changed = check_path_wwid_change(pp); + if (wwid_changed) { + condlog(0, "%s: path wwid change detected. Removing", + pp->dev); + return handle_path_wwid_change(pp, vecs) + ? CHECK_PATH_REMOVED + : CHECK_PATH_SKIPPED; + } } if ((newstate != PATH_UP && newstate != PATH_GHOST && newstate != PATH_PENDING) && (pp->state == PATH_DELAYED)) {