Mercurial > hg > toybox
comparison lib/lib.c @ 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 | 62d59b8aea34 |
children | 9686469a857a |
comparison
equal
deleted
inserted
replaced
991:252caf3d2b88 | 992:910f35ff76be |
---|---|
275 } | 275 } |
276 | 276 |
277 // Return how long the file at fd is, if there's any way to determine it. | 277 // Return how long the file at fd is, if there's any way to determine it. |
278 off_t fdlength(int fd) | 278 off_t fdlength(int fd) |
279 { | 279 { |
280 off_t bottom = 0, top = 0, pos, old; | 280 struct stat st; |
281 int size; | 281 off_t base = 0, range = 1, expand = 1, old; |
282 | |
283 if (!fstat(fd, &st) && S_ISREG(st.st_mode)) return st.st_size; | |
282 | 284 |
283 // If the ioctl works for this, return it. | 285 // If the ioctl works for this, return it. |
284 | 286 // TODO: is blocksize still always 512, or do we stat for it? |
285 if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L; | 287 // unsigned int size; |
288 // if (ioctl(fd, BLKGETSIZE, &size) >= 0) return size*512L; | |
286 | 289 |
287 // If not, do a binary search for the last location we can read. (Some | 290 // If not, do a binary search for the last location we can read. (Some |
288 // block devices don't do BLKGETSIZE right.) This should probably have | 291 // block devices don't do BLKGETSIZE right.) This should probably have |
289 // a CONFIG option... | 292 // a CONFIG option... |
290 | 293 |
294 // If not, do a binary search for the last location we can read. | |
295 | |
291 old = lseek(fd, 0, SEEK_CUR); | 296 old = lseek(fd, 0, SEEK_CUR); |
292 do { | 297 do { |
293 char temp; | 298 char temp; |
294 | 299 off_t pos = base + range / 2; |
295 pos = bottom + (top - bottom) / 2; | |
296 | |
297 // If we can read from the current location, it's bigger. | |
298 | 300 |
299 if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) { | 301 if (lseek(fd, pos, 0)>=0 && read(fd, &temp, 1)==1) { |
300 if (bottom == top) bottom = top = (top+1) * 2; | 302 off_t delta = (pos + 1) - base; |
301 else bottom = pos; | 303 |
302 | 304 base += delta; |
303 // If we can't, it's smaller. | 305 if (expand) range = (expand <<= 1) - base; |
304 | 306 else range -= delta; |
305 } else { | 307 } else { |
306 if (bottom == top) { | 308 expand = 0; |
307 if (!top) return 0; | 309 range = pos - base; |
308 bottom = top/2; | 310 } |
309 } else top = pos; | 311 } while (range > 0); |
310 } | |
311 } while (bottom + 1 != top); | |
312 | 312 |
313 lseek(fd, old, SEEK_SET); | 313 lseek(fd, old, SEEK_SET); |
314 | 314 |
315 return pos + 1; | 315 return base; |
316 } | 316 } |
317 | 317 |
318 // Read contents of file as a single freshly allocated nul-terminated string. | 318 // Read contents of file as a single freshly allocated nul-terminated string. |
319 char *readfile(char *name) | 319 char *readfile(char *name) |
320 { | 320 { |