AU: Optionally limit the size of delta update operations.

Add a --chunk_size flag to delta_generator. If it's not -1, files will
be split into chunks of this size when generating delta payloads. This
effectively limits the size of each delta operation.

BUG=chromium:229797
TEST=unit tests; generated delta payloads and checked them through
paycheck.py.

Change-Id: I21502118088bfbac75aa8009eb144f6aaf23a83a
Reviewed-on: https://gerrit.chromium.org/gerrit/48357
Commit-Queue: Darin Petkov <petkov@chromium.org>
Reviewed-by: Darin Petkov <petkov@chromium.org>
Tested-by: Darin Petkov <petkov@chromium.org>
diff --git a/extent_mapper.cc b/extent_mapper.cc
index e02f5a2..1cfd8bb 100644
--- a/extent_mapper.cc
+++ b/extent_mapper.cc
@@ -31,40 +31,55 @@
 const int kBlockSize = 4096;
 }
 
-bool ExtentsForFileFibmap(const std::string& path, std::vector<Extent>* out) {
+bool ExtentsForFileChunkFibmap(const std::string& path,
+                               off_t chunk_offset,
+                               off_t chunk_size,
+                               std::vector<Extent>* out) {
   CHECK(out);
+  CHECK_EQ(0, chunk_offset % kBlockSize);
+  CHECK(chunk_size == -1 || chunk_size >= 0);
   struct stat stbuf;
   int rc = stat(path.c_str(), &stbuf);
   TEST_AND_RETURN_FALSE_ERRNO(rc == 0);
   TEST_AND_RETURN_FALSE(S_ISREG(stbuf.st_mode));
-  
+
   int fd = open(path.c_str(), O_RDONLY, 0);
   TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);
   ScopedFdCloser fd_closer(&fd);
-  
+
   // Get file size in blocks
   rc = fstat(fd, &stbuf);
   if (rc < 0) {
     perror("fstat");
     return false;
   }
-  const int block_count = (stbuf.st_size + kBlockSize - 1) / kBlockSize;
+  CHECK_LE(chunk_offset, stbuf.st_size);
+  off_t size = stbuf.st_size - chunk_offset;
+  if (chunk_size != -1) {
+    size = std::min(size, chunk_size);
+  }
+  const int block_count = (size + kBlockSize - 1) / kBlockSize;
+  const int start_block = chunk_offset / kBlockSize;
   Extent current;
   current.set_start_block(0);
   current.set_num_blocks(0);
 
-  for (int i = 0; i < block_count; i++) {
+  for (int i = start_block; i < start_block + block_count; i++) {
     unsigned int block32 = i;
     rc = ioctl(fd, FIBMAP, &block32);
     TEST_AND_RETURN_FALSE_ERRNO(rc == 0);
-    
+
     const uint64_t block = (block32 == 0 ? kSparseHole : block32);
-    
+
     graph_utils::AppendBlockToExtents(out, block);
   }
   return true;
 }
 
+bool ExtentsForFileFibmap(const std::string& path, std::vector<Extent>* out) {
+  return ExtentsForFileChunkFibmap(path, 0, -1, out);
+}
+
 bool GetFilesystemBlockSize(const std::string& path, uint32_t* out_blocksize) {
   int fd = open(path.c_str(), O_RDONLY, 0);
   TEST_AND_RETURN_FALSE_ERRNO(fd >= 0);