update_engine: Make reading the size of a block device file work as expected.

When stating a block device file, the size is always returned as zero since,
presumably, there isn't a universally applicable way to determine the size of
device data, if such a concept even makes sense. Instead, block devices have
an ioctl which returns their size.

If utils::FileSize is called on a block device, it will now fall back to the
new function utils::BlockDevSize which will return the right answer. The error
reporting in FileSize has also been beefed up slightly so that when a call to
a standard library function fails (for instance stat), some information about
the error is printed. Finally, those functions have been reorganized so that
they can be called on file descriptors as well as file paths.

BUG=chromium:415867
TEST=Ran the delta generator using device files as input and saw that their
size was detected properly. Ran a tryjob on butterfly-paladin with --hwtest.
Ran the update_engine unittests for lumpy and for the host (with a hack around
crbug.com/412630) with another change which makes the rest of the update engine
use these functions to get file sizes.

Change-Id: I2ec8c1a0d134be76dffae730ef2ee2d78e4e887e
Reviewed-on: https://chromium-review.googlesource.com/216884
Reviewed-by: Don Garrett <dgarrett@chromium.org>
Commit-Queue: Gabe Black <gabeblack@chromium.org>
Tested-by: Gabe Black <gabeblack@chromium.org>
diff --git a/utils.cc b/utils.cc
index 05f7b07..ab5b7f3 100644
--- a/utils.cc
+++ b/utils.cc
@@ -282,13 +282,58 @@
   return ReadPipeAndAppend(cmd, out_p);
 }
 
-off_t FileSize(const string& path) {
+off_t BlockDevSize(int fd) {
+  uint64_t dev_size;
+  int rc = ioctl(fd, BLKGETSIZE64, &dev_size);
+  if (rc == -1) {
+    dev_size = -1;
+    PLOG(ERROR) << "Error running ioctl(BLKGETSIZE64) on " << fd;
+  }
+  return dev_size;
+}
+
+off_t BlockDevSize(const string& path) {
+  int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error opening " << path;
+    return fd;
+  }
+
+  off_t dev_size = BlockDevSize(fd);
+  if (dev_size == -1)
+    PLOG(ERROR) << "Error getting block device size on " << path;
+
+  close(fd);
+  return dev_size;
+}
+
+off_t FileSize(int fd) {
   struct stat stbuf;
-  int rc = stat(path.c_str(), &stbuf);
+  int rc = fstat(fd, &stbuf);
   CHECK_EQ(rc, 0);
-  if (rc < 0)
+  if (rc < 0) {
+    PLOG(ERROR) << "Error stat-ing " << fd;
     return rc;
-  return stbuf.st_size;
+  }
+  if (S_ISREG(stbuf.st_mode))
+    return stbuf.st_size;
+  if (S_ISBLK(stbuf.st_mode))
+    return BlockDevSize(fd);
+  LOG(ERROR) << "Couldn't determine the type of " << fd;
+  return -1;
+}
+
+off_t FileSize(const string& path) {
+  int fd = open(path.c_str(), O_RDONLY | O_CLOEXEC);
+  if (fd == -1) {
+    PLOG(ERROR) << "Error opening " << path;
+    return fd;
+  }
+  off_t size = FileSize(fd);
+  if (size == -1)
+    PLOG(ERROR) << "Error getting file size of " << path;
+  close(fd);
+  return size;
 }
 
 void HexDumpArray(const unsigned char* const arr, const size_t length) {