multipathd: fix reservation_key check
[multipath-tools/.git] / libmultipath / dmparser.c
index 2562ba1..620f507 100644 (file)
 #include "structs.h"
 #include "util.h"
 #include "debug.h"
-#include "config.h"
+#include "dmparser.h"
 
 #define WORD_SIZE 64
 
 static int
 merge_words (char ** dst, char * word, int space)
 {
-       char * p;
+       char * p = *dst;
        int len;
 
        len = strlen(*dst) + strlen(word) + space;
        *dst = REALLOC(*dst, len + 1);
 
-       if (!*dst)
+       if (!*dst) {
+               free(p);
                return 1;
+       }
 
        p = *dst;
 
@@ -44,6 +46,22 @@ merge_words (char ** dst, char * word, int space)
        return 0;
 }
 
+#define APPEND(p, end, args...)                                                \
+({                                                                     \
+       int ret;                                                        \
+                                                                       \
+       ret = snprintf(p, end - p, ##args);                             \
+       if (ret < 0) {                                                  \
+               condlog(0, "%s: conversion error", mp->alias);          \
+               goto err;                                               \
+       }                                                               \
+       p += ret;                                                       \
+       if (p >= end) {                                                 \
+               condlog(0, "%s: params too small", mp->alias);          \
+               goto err;                                               \
+       }                                                               \
+})
+
 /*
  * Transforms the path group vector into a proper device map string
  */
@@ -51,10 +69,10 @@ int
 assemble_map (struct multipath * mp, char * params, int len)
 {
        int i, j;
-       int shift, freechar;
        int minio;
        int nr_priority_groups, initial_pg_nr;
        char * p, * f;
+       const char *const end = params + len;
        char no_path_retry[] = "queue_if_no_path";
        char retain_hwhandler[] = "retain_attached_hw_handler";
        struct pathgroup * pgp;
@@ -62,52 +80,27 @@ assemble_map (struct multipath * mp, char * params, int len)
 
        minio = mp->minio;
        p = params;
-       freechar = len;
 
        nr_priority_groups = VECTOR_SIZE(mp->pg);
        initial_pg_nr = (nr_priority_groups ? mp->bestpg : 0);
 
-       f = STRDUP(mp->features);
-
-       /*
-        * We have to set 'queue_if_no_path' here even
-        * to avoid path failures during map reload.
-        */
-       if (mp->no_path_retry == NO_PATH_RETRY_UNDEF ||
-           mp->no_path_retry == NO_PATH_RETRY_FAIL) {
-               /* remove queue_if_no_path settings */
-               condlog(3, "%s: remove queue_if_no_path from '%s'",
-                       mp->alias, mp->features);
-               remove_feature(&f, no_path_retry);
-       } else {
-               add_feature(&f, no_path_retry);
+       if (mp->no_path_retry != NO_PATH_RETRY_UNDEF  &&
+           mp->no_path_retry != NO_PATH_RETRY_FAIL) {
+               add_feature(&mp->features, no_path_retry);
        }
-       if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON)
-               add_feature(&f, retain_hwhandler);
+       if (mp->retain_hwhandler == RETAIN_HWHANDLER_ON &&
+           get_linux_version_code() < KERNEL_VERSION(4, 3, 0))
+               add_feature(&mp->features, retain_hwhandler);
 
-       shift = snprintf(p, freechar, "%s %s %i %i",
-                        f, mp->hwhandler,
-                        nr_priority_groups, initial_pg_nr);
+       f = STRDUP(mp->features);
 
-       FREE(f);
-
-       if (shift >= freechar) {
-               condlog(0, "%s: params too small", mp->alias);
-               return 1;
-       }
-       p += shift;
-       freechar -= shift;
+       APPEND(p, end, "%s %s %i %i", f, mp->hwhandler, nr_priority_groups,
+              initial_pg_nr);
 
        vector_foreach_slot (mp->pg, pgp, i) {
                pgp = VECTOR_SLOT(mp->pg, i);
-               shift = snprintf(p, freechar, " %s %i 1", mp->selector,
-                                VECTOR_SIZE(pgp->paths));
-               if (shift >= freechar) {
-                       condlog(0, "%s: params too small", mp->alias);
-                       return 1;
-               }
-               p += shift;
-               freechar -= shift;
+               APPEND(p, end, " %s %i 1", mp->selector,
+                      VECTOR_SIZE(pgp->paths));
 
                vector_foreach_slot (pgp->paths, pp, j) {
                        int tmp_minio = minio;
@@ -117,30 +110,25 @@ assemble_map (struct multipath * mp, char * params, int len)
                                tmp_minio = minio * pp->priority;
                        if (!strlen(pp->dev_t) ) {
                                condlog(0, "dev_t not set for '%s'", pp->dev);
-                               return 1;
+                               goto err;
                        }
-                       shift = snprintf(p, freechar, " %s %d",
-                                        pp->dev_t, tmp_minio);
-                       if (shift >= freechar) {
-                               condlog(0, "%s: params too small", mp->alias);
-                               return 1;
-                       }
-                       p += shift;
-                       freechar -= shift;
+                       APPEND(p, end, " %s %d", pp->dev_t, tmp_minio);
                }
        }
-       if (freechar < 1) {
-               condlog(0, "%s: params too small", mp->alias);
-               return 1;
-       }
-       snprintf(p, 1, "\n");
 
+       FREE(f);
        condlog(3, "%s: assembled map [%s]", mp->alias, params);
        return 0;
+
+err:
+       FREE(f);
+       return 1;
 }
 
-extern int
-disassemble_map (vector pathvec, char * params, struct multipath * mpp)
+#undef APPEND
+
+int disassemble_map(vector pathvec, char *params, struct multipath *mpp,
+                   int is_daemon)
 {
        char * word;
        char * p;
@@ -152,7 +140,6 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
        int num_paths = 0;
        int num_paths_args = 0;
        int def_minio = 0;
-       int no_path_retry = NO_PATH_RETRY_UNDEF;
        struct path * pp;
        struct pathgroup * pgp;
 
@@ -169,8 +156,6 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                return 1;
 
        num_features = atoi(mpp->features);
-       no_path_retry = mpp->no_path_retry;
-       mpp->no_path_retry = NO_PATH_RETRY_UNDEF;
 
        for (i = 0; i < num_features; i++) {
                p += get_word(p, &word);
@@ -182,25 +167,10 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                        FREE(word);
                        return 1;
                }
-               if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF) ||
-                       (mpp->no_path_retry == NO_PATH_RETRY_FAIL) ||
-                       (mpp->no_path_retry == NO_PATH_RETRY_QUEUE))
-                       setup_feature(mpp, word);
 
                FREE(word);
        }
 
-       /*
-        * Reset no_path_retry.
-        * - if not set from features
-        * - if queue_if_no_path is set from features but
-        *   no_path_retry > 0 is selected.
-        */
-       if ((mpp->no_path_retry == NO_PATH_RETRY_UNDEF ||
-            mpp->no_path_retry == NO_PATH_RETRY_QUEUE) &&
-           mpp->no_path_retry != no_path_retry)
-               mpp->no_path_retry = no_path_retry;
-
        /*
         * hwhandler
         */
@@ -298,7 +268,7 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                if (!pgp)
                        goto out;
 
-               if (store_pathgroup(mpp->pg, pgp))
+               if (add_pathgroup(mpp, pgp))
                        goto out;
 
                p += get_word(p, &word);
@@ -318,14 +288,26 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                FREE(word);
 
                for (j = 0; j < num_paths; j++) {
+                       char devname[FILE_NAME_SIZE];
+
                        pp = NULL;
                        p += get_word(p, &word);
 
                        if (!word)
                                goto out;
 
-                       if (pathvec)
-                               pp = find_path_by_devt(pathvec, word);
+                       if (devt2devname(devname, FILE_NAME_SIZE, word)) {
+                               condlog(2, "%s: cannot find block device",
+                                       word);
+                               devname[0] = '\0';
+                       }
+
+                       if (pathvec) {
+                               if (strlen(devname))
+                                       pp = find_path_by_dev(pathvec, devname);
+                               else
+                                       pp = find_path_by_devt(pathvec, word);
+                       }
 
                        if (!pp) {
                                pp = alloc_path();
@@ -333,11 +315,20 @@ disassemble_map (vector pathvec, char * params, struct multipath * mpp)
                                if (!pp)
                                        goto out1;
 
-                               strncpy(pp->dev_t, word, BLK_DEV_SIZE);
-
+                               strncpy(pp->dev_t, word, BLK_DEV_SIZE - 1);
+                               strncpy(pp->dev, devname, FILE_NAME_SIZE - 1);
+                               if (strlen(mpp->wwid)) {
+                                       strncpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE - 1);
+                               }
                                /* Only call this in multipath client mode */
-                               if (!conf->daemon && store_path(pathvec, pp))
+                               if (!is_daemon && store_path(pathvec, pp))
                                        goto out1;
+                       } else {
+                               if (!strlen(pp->wwid) &&
+                                   strlen(mpp->wwid))
+                                       strncpy(pp->wwid, mpp->wwid,
+                                               WWID_SIZE - 1);
                        }
                        FREE(word);
 
@@ -349,33 +340,41 @@ 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);
+                               strncpy(mpp->wwid, pp->wwid,
+                                       WWID_SIZE - 1);
 
                        /*
                         * 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);
+
+                       /*
+                        * 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);
+                       }
 
                        pgp->id ^= (long)pp;
                        pp->pgindex = i + 1;
 
                        for (k = 0; k < num_paths_args; k++)
                                if (k == 0) {
+                                       p += get_word(p, &word);
+                                       def_minio = atoi(word);
+                                       FREE(word);
+
                                        if (!strncmp(mpp->selector,
                                                     "round-robin", 11)) {
-                                               p += get_word(p, &word);
-                                               def_minio = atoi(word);
 
                                                if (mpp->rr_weight == RR_WEIGHT_PRIO
                                                    && pp->priority > 0)
                                                        def_minio /= pp->priority;
 
-                                               FREE(word);
-                                       } else {
-                                               p += get_word(p, NULL);
-                                               def_minio = 0;
                                        }
 
                                        if (def_minio != mpp->minio)
@@ -395,8 +394,7 @@ out:
        return 1;
 }
 
-extern int
-disassemble_status (char * params, struct multipath * mpp)
+int disassemble_status(char *params, struct multipath *mpp)
 {
        char * word;
        char * p;