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 {