aboutsummaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorRob Landley <rob@landley.net>2013-08-08 02:46:45 -0500
committerRob Landley <rob@landley.net>2013-08-08 02:46:45 -0500
commit035f27ae4df2b973b8974730bb1b23a25f6160e8 (patch)
tree12d1520dec29c404882fe915ea94ce76df8e4467 /lib
parent1aa75118c46c8fbe0eeead18974d25e5f13274d5 (diff)
downloadtoybox-035f27ae4df2b973b8974730bb1b23a25f6160e8.tar.gz
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.)
Diffstat (limited to 'lib')
-rw-r--r--lib/lib.c36
1 files changed, 18 insertions, 18 deletions
diff --git a/lib/lib.c b/lib/lib.c
index 52c95147..7f4413ad 100644
--- a/lib/lib.c
+++ b/lib/lib.c
@@ -277,42 +277,42 @@ int stridx(char *haystack, char needle)
// 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 the ioctl works for this, return it.
+ if (!fstat(fd, &st) && S_ISREG(st.st_mode)) return st.st_size;
- if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L;
+ // If the ioctl works for this, return it.
+ // 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;
-
- // If we can't, it's smaller.
+ off_t delta = (pos + 1) - base;
+ 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.