hnvram: fix hnvram partition write flushing

On Chimera with Linux 4.4, hnvram write operations are failing:

NPAPID1600X0002# hnvram -w PLATFORM_NAME=GFCH100
ioctl(hnvram, BLKFLSBUF, 0): Operation not permitted
ioctl(hnvram, BLKFLSBUF, 0): Operation not permitted
[HMX_NVRAM_SetField] field(16)-PLATFORM_NAME error(00000006) : HMX_NVRAM_Write
Unable to write PLATFORM_NAME

This happens because:
1. On Chimera, hnvram partition is on eMMC
2. On Linux 4.4, mmc_blk_ioctl() does not allow write buffer flushes
   for MMC partitions:
   /*
    * The caller must have CAP_SYS_RAWIO, and must be calling this on the
    * whole block device, not on a partition.  This prevents overspray
    * between sibling partitions.
    */
   if ((!capable(CAP_SYS_RAWIO)) || (bdev != bdev->bd_contains))
           return -EPERM;
   }

Fix:
When hnvram lives on MMC partition, flush the entire MMC device.

Change-Id: Idea3c35c8670c78212e940bc9417ab04080d10c2
diff --git a/libupgrade/hmx_upgrade_flash.c b/libupgrade/hmx_upgrade_flash.c
index ce9268c..86833af 100644
--- a/libupgrade/hmx_upgrade_flash.c
+++ b/libupgrade/hmx_upgrade_flash.c
@@ -104,8 +104,6 @@
 /************************ static variables *************************/
 /*******************************************************************/
 /* Start static variable */
-int fd_nvram = -1;
-
 /* End static variable */
 
 /*******************************************************************/
@@ -116,101 +114,148 @@
 {
   int ret;
   void *buf;
+  char *rpath;
+  char *p;
+  int fd_nvram;
 
   if (libupgrade_verbose) printf("[%s] offset %08lx, size %d, data = %02x\n",
                                  __FUNCTION__, offset, size, data[0] );
 
   buf = malloc(size);
-  if(!buf) {
+  if (!buf) {
     perror("malloc");
     return -1;
   }
 
-  if ( fd_nvram < 0 ) {
-    fd_nvram = open(TARGET_NVRAM_MTD, (O_RDWR | O_SYNC) );
-    if( fd_nvram < 0 ) {
-      perror(TARGET_NVRAM_MTD);
-      free(buf);
-      return -1;
-    }
+  rpath = realpath(TARGET_NVRAM_MTD, NULL);
+  if (!rpath) {
+    perror("realpath");
+    free(buf);
+    return -1;
+  }
+
+  fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+  if (fd_nvram < 0) {
+    ret = -1;
+    perror(rpath);
+    goto out_free;
   }
 
   ret = lseek(fd_nvram, offset, SEEK_SET);
-  if(ret < 0) {
+  if (ret < 0) {
     perror("lseek");
-    free(buf);
-    return -1;
+    goto out_close;
+  }
+
+  ret = read(fd_nvram, buf, size);
+  if (ret < 0) {
+    perror("read");
+    goto out_close;
   }
 
   /* Only write to flash if data is different from what is already there. */
-  ret = read(fd_nvram, buf, size);
-  if(ret < 0) {
-    perror("read");
-    free(buf);
-    return -1;
+  if (ret == size && !memcmp(data, buf, size)) {
+    ret = 0;
+    goto out_close;
   }
-  if (ret!=size || memcmp(data, buf, size)) {
-    ret = lseek(fd_nvram, offset, SEEK_SET);
-    if(ret < 0) {
-      perror("lseek");
-      free(buf);
-      return -1;
-    }
 
-    ret = write(fd_nvram, data, size);
-    if(ret < 0) {
-      perror("write");
-      free(buf);
-      return -1;
+  ret = lseek(fd_nvram, offset, SEEK_SET);
+  if (ret < 0) {
+    perror("lseek");
+    goto out_close;
+  }
+
+  ret = write(fd_nvram, data, size);
+  if (ret < 0) {
+    perror("write");
+    goto out_close;
+  }
+
+  /*
+   * If hnvram is on MMC partition, we cannot flush the partition.
+   * Instead, we need to flush the entire MMC device.
+   * For example, GFCH100 hnvram partition is at /dev/mmcblk0p1,
+   * so we need to flush /dev/mmcblk0.
+   */
+  p = strstr(rpath, "mmcblk");
+  if (p) {
+    p = strchr(p + 6, 'p');
+    if (p) {
+      *p = '\0';
+      close(fd_nvram);
+      fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+      if (fd_nvram < 0) {
+        ret = -1;
+        perror(rpath);
+        goto out_free;
+      }
     }
   }
 
-  /* Writes to /dev/mtdblockX are cached indefinitely until the last file
+  /*
+   * Writes to /dev/mtdblockX are cached indefinitely until the last file
    * descriptor is closed. Flush this cache after writing. The user depends on
    * data being physically written to flash when this function returns.
-   * Requires CAP_SYS_ADMIN. */
+   * Requires CAP_SYS_ADMIN.
+   */
   ret = ioctl(fd_nvram, BLKFLSBUF, 0);
-  if(ret < 0) {
+  if (ret < 0) {
     perror("ioctl(hnvram, BLKFLSBUF, 0)");
-    free(buf);
-    return -1;
+    goto out_close;
   }
 
+  ret = 0;
+
+out_close:
+  close(fd_nvram);
+out_free:
+  free(rpath);
   free(buf);
-  return 0;
+  return ret;
 }
 
 int HMX_UPGRADE_NVRAM_Read(unsigned long offset, unsigned char * data,
                            unsigned int size )
 {
+  char *rpath;
+  int fd_nvram;
   int ret;
 
   if (libupgrade_verbose) printf("[%s] offset %08lx, size %d, data = %02x\n",
                                  __FUNCTION__, offset, size, data[0] );
 
-  /* */
-  if ( fd_nvram < 0 ) {
-    fd_nvram = open(TARGET_NVRAM_MTD, (O_RDWR | O_SYNC) );
-    if( fd_nvram < 0 ) {
-      perror(TARGET_NVRAM_MTD);
-      return -1;
-    }
-  }
-
-
-  ret = lseek(fd_nvram, offset, SEEK_SET);
-  if(ret < 0) {
-    perror("lseek");
+  rpath = realpath(TARGET_NVRAM_MTD, NULL);
+  if (!rpath) {
+    perror("realpath");
     return -1;
   }
 
-  ret = read(fd_nvram, data, size);
-  if(ret < 0) {
-    perror("read");
-    return ret;
+  fd_nvram = open(rpath, (O_RDWR | O_SYNC));
+  if (fd_nvram < 0) {
+    ret = -1;
+    perror(rpath);
+    goto out_free;
   }
 
-  return 0;
+  ret = lseek(fd_nvram, offset, SEEK_SET);
+  if (ret < 0) {
+    perror("lseek");
+    goto out_close;
+  }
+
+  ret = read(fd_nvram, data, size);
+  if (ret < 0) {
+    perror("read");
+    goto out_close;
+  }
+
+  ret = 0;
+
+out_close:
+  close(fd_nvram);
+out_free:
+  free(rpath);
+  return ret;
 }