]> www.infradead.org Git - mtd-utils.git/commitdiff
UBI-utils: add compare feature
authorAlexander Schmidt <alexs@linux.vnet.ibm.com>
Tue, 10 Jul 2007 15:53:54 +0000 (17:53 +0200)
committerArtem Bityutskiy <Artem.Bityutskiy@nokia.com>
Tue, 10 Jul 2007 18:00:43 +0000 (21:00 +0300)
This is a new feature for pfiflash, called "--compare". It allows the user
to simulate a pfiflash session without actually changing the flash
content. If the flash content is equal to the data in the pfif file,
pfiflash returns zero. A positive value is returned when the flash
content differs from the pfi file, which indicates that an update is
necessary. This feature is useful when a controller mounts an NFS share
during boot and has to determine if a pfi file stored on this share
contains a code update. Modified PDD values are also registered by the
compare feature.

Signed-off-by: Alexander Schmidt <alexs@linux.vnet.ibm.com>
ubi-utils/src/bootenv.c
ubi-utils/src/bootenv.h
ubi-utils/src/libpfiflash.c
ubi-utils/src/pfiflash.c
ubi-utils/src/pfiflash.h
ubi-utils/src/pfiflash_error.h

index ed15dc771438cfc1183310323284e9bd1b340b04..561d4734984b321d9d4bc8f3a7af40ea90391fa3 100644 (file)
@@ -479,6 +479,54 @@ bootenv_write(FILE* fp, bootenv_t env)
        return bootenv_write_crc(fp, env, NULL);
 }
 
+int
+bootenv_compare(bootenv_t first, bootenv_t second)
+{
+       int rc;
+       size_t written_first, written_second;
+       char *buf_first, *buf_second;
+
+       if (first == NULL || second == NULL)
+               return -EINVAL;
+
+       buf_first = malloc(BOOTENV_MAXSIZE);
+       if (!buf_first)
+               return -ENOMEM;
+       buf_second = malloc(BOOTENV_MAXSIZE);
+       if (!buf_second) {
+               rc = -ENOMEM;
+               goto err;
+       }
+
+       rc = fill_output_buffer(first, buf_first, BOOTENV_MAXSIZE,
+                       &written_first);
+       if (rc < 0)
+               goto err;
+       rc = fill_output_buffer(second, buf_second, BOOTENV_MAXSIZE,
+                       &written_second);
+       if (rc < 0)
+               goto err;
+
+       if (written_first != written_second) {
+               rc = 1;
+               goto err;
+       }
+
+       rc = memcmp(buf_first, buf_second, written_first);
+       if (rc != 0) {
+               rc = 2;
+               goto err;
+       }
+
+err:
+       if (buf_first)
+               free(buf_first);
+       if (buf_second)
+               free(buf_second);
+
+       return rc;
+}
+
 int
 bootenv_size(bootenv_t env, size_t *size)
 {
index 9003d705e14476a21c0ef9b21bcf28d37b0d5353..8fecdbf41797ddeb53fc7593453a8cebfb6e9d3e 100644 (file)
@@ -267,6 +267,16 @@ int bootenv_write_crc(FILE* fp, bootenv_t env, uint32_t* ret_crc);
  */
 int bootenv_write_txt(FILE* fp, bootenv_t env);
 
+/**
+ * @brief Compare bootenvs using memcmp().
+ * @param first        First bootenv.
+ * @param second       Second bootenv.
+ * @return 0 if bootenvs are equal
+ * @return < 0 if error
+ * @return > 0 if unequal
+ */
+int bootenv_compare(bootenv_t first, bootenv_t second);
+
 /**
  * @brief Prototype for a PDD handling funtion
  */
index 4f1f5cdb5753f8741b4c04b5e462a703c712abbf..e60290a7f4e93a1a9b84ac804b4ddc763b03bff4 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) International Business Machines Corp., 2006
+ * Copyright International Business Machines Corp., 2006, 2007
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
 
 #define __unused __attribute__((unused))
 
+#define COMPARE_BUFFER_SIZE 2048
+
 static const char copyright [] __unused =
-       "Copyright (c) International Business Machines Corp., 2006";
+       "Copyright International Business Machines Corp., 2006, 2007";
 
 /* simply clear buffer, then write into front of it */
 #define EBUF(fmt...)                                                   \
@@ -82,6 +84,7 @@ static pdd_func_t pdd_funcs[PDD_HANDLING_NUM]  =
 typedef enum ubi_update_process_t {
        UBI_REMOVE = 0,
        UBI_WRITE,
+       UBI_COMPARE,
 } ubi_update_process_t;
 
 
@@ -556,6 +559,170 @@ write_normal_volume(int devno, uint32_t id, size_t update_size, FILE* fp_in,
        return rc;
 }
 
+static int compare_bootenv(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+               uint32_t data_size, pdd_func_t pdd_f, char *err_buf,
+               size_t err_buf_size)
+{
+       int rc, warnings = 0;
+       unsigned int i;
+       bootenv_t bootenv_pfi, bootenv_res = NULL, bootenv_flash = NULL;
+
+       rc = bootenv_create(&bootenv_pfi);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               goto err;
+       }
+
+       rc = bootenv_create(&bootenv_res);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+               goto err;
+       }
+
+       rc = bootenv_read(fp_pfi, bootenv_pfi, data_size);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_BOOTENV_READ;
+               goto err;
+       }
+
+       for (i = 0; i < ids_size; i++) {
+               rc = bootenv_create(&bootenv_flash);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_BOOTENV_CREATE;
+                       goto err;
+               }
+
+               rc = bootenv_read(fp_flash[i], bootenv_flash, BOOTENV_MAXSIZE);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_BOOTENV_READ;
+                       goto err;
+               }
+
+               rc = pdd_f(bootenv_flash, bootenv_pfi, &bootenv_res,
+                               &warnings, err_buf, err_buf_size);
+               if (rc != 0) {
+                       rc = -PFIFLASH_ERR_PDD_UNKNOWN;
+                       goto err;
+               }
+
+               rc = bootenv_compare(bootenv_flash, bootenv_res);
+               if (rc > 0) {
+                       rc = -PFIFLASH_CMP_DIFF;
+                       goto err;
+               } else if (rc < 0) {
+                       rc = -PFIFLASH_ERR_COMPARE;
+                       goto err;
+               }
+
+               bootenv_destroy(&bootenv_flash);
+               bootenv_flash = NULL;
+       }
+
+err:
+       if (bootenv_pfi)
+               bootenv_destroy(&bootenv_pfi);
+       if (bootenv_res)
+               bootenv_destroy(&bootenv_res);
+       if (bootenv_flash)
+               bootenv_destroy(&bootenv_flash);
+
+       return rc;
+}
+
+static int compare_data(FILE *fp_pfi, FILE **fp_flash, uint32_t ids_size,
+               uint32_t bytes_left)
+{
+       unsigned int i;
+       size_t read_bytes, rc = 0;
+       char buf_pfi[COMPARE_BUFFER_SIZE];
+       char *buf_flash[ids_size];
+
+       for (i = 0; i < ids_size; i++) {
+               buf_flash[i] = malloc(COMPARE_BUFFER_SIZE);
+               if (!buf_flash[i])
+                       return -PFIFLASH_ERR_COMPARE;
+       }
+
+       while (bytes_left) {
+               if (bytes_left > COMPARE_BUFFER_SIZE)
+                       read_bytes = COMPARE_BUFFER_SIZE;
+               else
+                       read_bytes = bytes_left;
+
+               rc = fread(buf_pfi, 1, read_bytes, fp_pfi);
+               if (rc != read_bytes) {
+                       rc = -PFIFLASH_ERR_COMPARE;
+                       goto err;
+               }
+
+               for (i = 0; i < ids_size; i++) {
+                       rc = fread(buf_flash[i], 1, read_bytes, fp_flash[i]);
+                       if (rc != read_bytes) {
+                               rc = -PFIFLASH_CMP_DIFF;
+                               goto err;
+                       }
+
+                       rc = memcmp(buf_pfi, buf_flash[i], read_bytes);
+                       if (rc != 0) {
+                               rc = -PFIFLASH_CMP_DIFF;
+                               goto err;
+                       }
+               }
+
+               bytes_left -= read_bytes;
+       }
+
+err:
+       for (i = 0; i < ids_size; i++)
+               free(buf_flash[i]);
+
+       return rc;
+}
+
+static int compare_volumes(int devno, pfi_ubi_t u, FILE *fp_pfi,
+               pdd_func_t pdd_f, char *err_buf, size_t err_buf_size)
+{
+       int rc, is_bootenv = 0;
+       unsigned int i;
+       ubi_lib_t ulib = NULL;
+       FILE *fp_flash[u->ids_size];
+
+       rc = ubi_open(&ulib);
+       if (rc != 0) {
+               rc = -PFIFLASH_ERR_UBI_OPEN;
+               goto err;
+       }
+
+       for (i = 0; i < u->ids_size; i++) {
+               if (u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_1 ||
+                   u->ids[i] == EXAMPLE_BOOTENV_VOL_ID_2)
+                       is_bootenv = 1;
+
+               fp_flash[i] = ubi_vol_fopen_read(ulib, devno, u->ids[i]);
+               if (fp_flash[i] == NULL) {
+                       rc = -PFIFLASH_ERR_UBI_OPEN;
+                       goto err;
+               }
+       }
+
+       if (is_bootenv)
+               rc = compare_bootenv(fp_pfi, fp_flash, u->ids_size,
+                               u->data_size, pdd_f, err_buf, err_buf_size);
+       else
+               rc = compare_data(fp_pfi, fp_flash, u->ids_size, u->data_size);
+
+err:
+       if (rc < 0)
+               EBUF(PFIFLASH_ERRSTR[-rc]);
+
+       for (i = 0; i < u->ids_size; i++)
+               fclose(fp_flash[i]);
+       if (ulib)
+               ubi_close(&ulib);
+
+       return rc;
+}
+
 static int
 erase_mtd_region(FILE* file_p, int start, int length)
 {
@@ -864,6 +1031,15 @@ process_ubi_volumes(FILE* pfi, int seqnum, list_t pfi_ubis,
                        if (rc != 0)
                                goto err;
 
+                       break;
+               case UBI_COMPARE:
+                       rc = compare_volumes(EXAMPLE_UBI_DEVICE, u, pfi, pdd_f,
+                                       err_buf, err_buf_size);
+                       if (rc != 0) {
+                               EBUF_PREPEND("compare volume");
+                               goto err;
+                       }
+
                        break;
                default:
                        rc = -PFIFLASH_ERR_UBI_UNKNOWN;
@@ -949,10 +1125,11 @@ mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
 
 
 /**
- * pfiflash_with_raw - exposed func to flash memory with a PFI file
+ * pfiflash_with_options - exposed func to flash memory with a PFI file
  * @pfi                        PFI data file pointer
  * @complete           flag to erase unmapped volumes
  * @seqnum             sequence number
+ * @compare            flag to compare
  * @pdd_handling       method to handle pdd (keep, merge, overwrite...)
  *
  * Error handling:
@@ -967,7 +1144,7 @@ mirror_ubi_volumes(uint32_t devno, list_t pfi_ubis,
  *     - passes rc, prepends err_buf with contextual aid
  **/
 int
-pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
+pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
                  pdd_handling_t pdd_handling, const char* rawdev,
                  char *err_buf, size_t err_buf_size)
 {
@@ -1001,7 +1178,7 @@ pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
                goto err;
        }
 
-       if (rawdev == NULL)
+       if (rawdev == NULL || compare)
                rc = skip_raw_volumes(pfi, pfi_raws, err_buf, err_buf_size);
        else
                rc = process_raw_volumes(pfi, pfi_raws, rawdev, err_buf,
@@ -1011,7 +1188,7 @@ pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
                goto err;
        }
 
-       if (complete) {
+       if (complete && !compare) {
                rc = erase_unmapped_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
                                                err_buf, err_buf_size);
                if (rc != 0) {
@@ -1029,25 +1206,40 @@ pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
                goto err;
        }
 
-       rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f,
-                                UBI_REMOVE, err_buf, err_buf_size);
-       if (rc != 0) {
-               EBUF_PREPEND("removing UBI volumes");
-               goto err;
-       }
+       if (!compare) {
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_REMOVE, err_buf, err_buf_size);
+               if (rc != 0) {
+                       EBUF_PREPEND("removing UBI volumes");
+                       goto err;
+               }
 
-       rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv, pdd_f,
-                                UBI_WRITE, err_buf, err_buf_size);
-       if  (rc != 0) {
-               EBUF_PREPEND("writing UBI volumes");
-               goto err;
-       }
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_WRITE, err_buf, err_buf_size);
+               if  (rc != 0) {
+                       EBUF_PREPEND("writing UBI volumes");
+                       goto err;
+               }
 
-       if (seqnum < 0) { /* mirror redundant pairs */
-               rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
+               if (seqnum < 0) { /* mirror redundant pairs */
+                       rc = mirror_ubi_volumes(EXAMPLE_UBI_DEVICE, pfi_ubis,
                                        err_buf, err_buf_size);
-               if (rc != 0) {
-                       EBUF_PREPEND("mirroring UBI volumes");
+                       if (rc != 0) {
+                               EBUF_PREPEND("mirroring UBI volumes");
+                               goto err;
+                       }
+               }
+       } else {
+               /* only compare volumes, don't alter the content */
+               rc = process_ubi_volumes(pfi, curr_seqnum, pfi_ubis, bootenv,
+                               pdd_f, UBI_COMPARE, err_buf, err_buf_size);
+
+               if (rc == -PFIFLASH_CMP_DIFF)
+                       /* update is necessary, return positive value */
+                       rc = 1;
+
+               if (rc < 0) {
+                       EBUF_PREPEND("comparing UBI volumes");
                        goto err;
                }
        }
@@ -1061,7 +1253,7 @@ pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
 
 
 /**
- * pfiflash - passes to pfiflash_with_raw
+ * pfiflash - passes to pfiflash_with_options
  * @pfi                        PFI data file pointer
  * @complete           flag to erase unmapped volumes
  * @seqnum             sequence number
@@ -1069,8 +1261,8 @@ pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
  **/
 int
 pfiflash(FILE* pfi, int complete, int seqnum, pdd_handling_t pdd_handling,
-        char *err_buf, size_t err_buf_size)
+               char *err_buf, size_t err_buf_size)
 {
-       return pfiflash_with_raw(pfi, complete, seqnum, pdd_handling,
+       return pfiflash_with_options(pfi, complete, seqnum, 0, pdd_handling,
                                 NULL, err_buf, err_buf_size);
 }
index d5b54065faf755e7f45dfa0deb3809f509291ba9..bac62e638c0d5b02450999b4c0fe77d1a9e43e90 100644 (file)
@@ -63,6 +63,9 @@ static const char *optionsstr =
 "                             'keep', 'merge' or 'overwrite'.\n"
 "  -r, --raw-flash=<dev>      Flash the raw data. Use the specified mtd device.\n"
 "  -s, --side=<seqnum>        Select the side which shall be updated.\n"
+"  -x, --compare              Only compare on-flash and pfi data, print info if\n"
+"                             an update is neccessary and return appropriate\n"
+"                             error code.\n"
 "\n"
 "  -?, --help                 Give this help list\n"
 "      --usage                Give a short usage message\n"
@@ -72,7 +75,7 @@ static const char *usage =
 "Usage: pfiflash [-cvC?V] [-l <file>] [-p <type>] [-r <dev>] [-s <seqnum>]\n"
 "            [--copyright] [--logfile=<file>] [--verbose] [--complete]\n"
 "            [--pdd-update=<type>] [--raw-flash=<dev>] [--side=<seqnum>]\n"
-"            [--help] [--usage] [--version] [pfifile]\n";
+"            [--compare] [--help] [--usage] [--version] [pfifile]\n";
 
 static const char copyright [] __attribute__((unused)) =
        "Copyright IBM Corp 2006";
@@ -85,6 +88,7 @@ struct option long_options[] = {
        { .name = "pdd-update", .has_arg = 1, .flag = NULL, .val = 'p' },
        { .name = "raw-flash", .has_arg = 1, .flag = NULL, .val = 'r' },
        { .name = "side", .has_arg = 1, .flag = NULL, .val = 's' },
+       { .name = "compare", .has_arg = 0, .flag = NULL, .val = 'x' },
        { .name = "help", .has_arg = 0, .flag = NULL, .val = '?' },
        { .name = "usage", .has_arg = 0, .flag = NULL, .val = 0 },
        { .name = "version", .has_arg = 0, .flag = NULL, .val = 'V' },
@@ -98,6 +102,7 @@ typedef struct myargs {
 
        pdd_handling_t pdd_handling;
        int seqnum;
+       int compare;
        int complete;
 
        FILE* fp_in;
@@ -142,7 +147,7 @@ parse_opt(int argc, char **argv, myargs *args)
        while (1) {
                int key;
 
-               key = getopt_long(argc, argv, "cl:vCp:r:s:?V",
+               key = getopt_long(argc, argv, "cl:vCp:r:s:x?V",
                                  long_options, NULL);
                if (key == -1)
                        break;
@@ -180,6 +185,9 @@ parse_opt(int argc, char **argv, myargs *args)
                                                 "and '1'\n", optarg);
                                }
                                break;
+                       case 'x':
+                               args->compare = 1;
+                               break;
                        case 'r':
                                args->raw_dev = optarg;
                                break;
@@ -222,6 +230,7 @@ int main (int argc, char** argv)
        myargs args = {
                .verbose    = 0,
                .seqnum     = -1,
+               .compare    = 0,
                .complete   = 0,
                .logfile    = NULL, /* "/tmp/pfiflash.log", */
                .pdd_handling = PDD_KEEP,
@@ -239,17 +248,10 @@ int main (int argc, char** argv)
                goto err;
        }
 
-       if (!args.raw_dev) {
-               rc = pfiflash(args.fp_in, args.complete, args.seqnum,
-                             args.pdd_handling, err_buf,
-                             PFIFLASH_MAX_ERR_BUF_SIZE);
-       } else {
-               rc = pfiflash_with_raw(args.fp_in, args.complete, args.seqnum,
-                             args.pdd_handling, args.raw_dev, err_buf,
-                             PFIFLASH_MAX_ERR_BUF_SIZE);
-       }
-
-       if (rc != 0) {
+       rc = pfiflash_with_options(args.fp_in, args.complete, args.seqnum,
+                       args.compare, args.pdd_handling, args.raw_dev, err_buf,
+                       PFIFLASH_MAX_ERR_BUF_SIZE);
+       if (rc < 0) {
                goto err_fp;
        }
 
index a063e7f7fd16e2ad73c3c23007bfb23ddaf0f035..039705de0d3805bf1380dc27df843869a13a7675 100644 (file)
@@ -48,12 +48,13 @@ typedef enum pdd_handling_t
  * @brief Flashes a PFI file to UBI Device 0.
  * @param complete     [0|1] Do a complete system update.
  * @param seqnum       Index in a redundant group.
+ * @param compare      [0|1] Compare contents.
  * @param pdd_handling The PDD handling algorithm.
  * @param rawdev       Device to use for raw flashing
  * @param err_buf      An error buffer.
  * @param err_buf_size Size of the error buffer.
  */
-int pfiflash_with_raw(FILE* pfi, int complete, int seqnum,
+int pfiflash_with_options(FILE* pfi, int complete, int seqnum, int compare,
                pdd_handling_t pdd_handling, const char* rawdev,
                char *err_buf, size_t err_buf_size);
 
index c06232af8fb6a9be18542e3e074e9a4c614aba7c..0f27f4ad5ff64d8812e8351b7ff4f52189122e53 100644 (file)
@@ -42,7 +42,9 @@ enum pfiflash_err {
        PFIFLASH_ERR_MTD_OPEN,
        PFIFLASH_ERR_MTD_CLOSE,
        PFIFLASH_ERR_CRC_CHECK,
-       PFIFLASH_ERR_MTD_ERASE
+       PFIFLASH_ERR_MTD_ERASE,
+       PFIFLASH_ERR_COMPARE,
+       PFIFLASH_CMP_DIFF
 };
 
 const char *const PFIFLASH_ERRSTR[] = {
@@ -65,7 +67,9 @@ const char *const PFIFLASH_ERRSTR[] = {
        "couldn't open MTD device %s",
        "couldn't close MTD device %s",
        "CRC check failed: given=0x%08x, calculated=0x%08x",
-       "couldn't erase raw mtd region"
+       "couldn't erase raw mtd region",
+       "couldn't compare volumes",
+       "on-flash data differ from pfi data, update is neccessary"
 };
 
 #endif /* __PFIFLASH_ERROR_H__ */