changeset 992:910f35ff76be

Achille Fouilleul pointed out that fdlength wasn't returning the right length in the binary search case. (This code was originally written for mke2fs, and applies to block devices. The regular file case should just return the length from stat. The ioctl is left commented out in case I want to add back code to check the size of CDROMs without spinning them up again; not sure the sector size is always right these days.)
author Rob Landley <rob@landley.net>
date Thu, 08 Aug 2013 02:46:45 -0500
parents 252caf3d2b88
children d5a1174ff88a
files lib/lib.c
diffstat 1 files changed, 18 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/lib/lib.c	Wed Aug 07 12:19:51 2013 -0500
+++ b/lib/lib.c	Thu Aug 08 02:46:45 2013 -0500
@@ -277,42 +277,42 @@
 // Return how long the file at fd is, if there's any way to determine it.
 off_t fdlength(int fd)
 {
-  off_t bottom = 0, top = 0, pos, old;
-  int size;
+  struct stat st;
+  off_t base = 0, range = 1, expand = 1, old;
+
+  if (!fstat(fd, &st) && S_ISREG(st.st_mode)) return st.st_size;
 
   // If the ioctl works for this, return it.
-
-  if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
+  // TODO: is blocksize still always 512, or do we stat for it?
+  // unsigned int size;
+  // if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
 
   // If not, do a binary search for the last location we can read.  (Some
   // block devices don't do BLKGETSIZE right.)  This should probably have
   // a CONFIG option...
 
+  // If not, do a binary search for the last location we can read.
+
   old = lseek(fd, 0, SEEK_CUR);
   do {
     char temp;
-
-    pos = bottom + (top - bottom) / 2;
-
-    // If we can read from the current location, it's bigger.
+    off_t pos = base + range / 2;
 
     if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) {
-      if (bottom == top) bottom = top = (top+1) * 2;
-      else bottom = pos;
+      off_t delta = (pos + 1) - base;
 
-      // If we can't, it's smaller.
-
+      base += delta;
+      if (expand) range = (expand <<= 1) - base;
+      else range -= delta;
     } else {
-      if (bottom == top) {
-        if (!top) return 0;
-        bottom = top/2;
-      } else top = pos;
+      expand = 0;
+      range = pos - base;
     }
-  } while (bottom + 1 != top);
+  } while (range > 0);
 
   lseek(fd, old, SEEK_SET);
 
-  return pos + 1;
+  return base;
 }
 
 // Read contents of file as a single freshly allocated nul-terminated string.