volume_id: improve handling of too-small (usually zero-byte or erroring) blockdevs
No binary (build failed)
gcc -Wp,-MD,applets/.usage.d -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer -Iinclude -Iinclude -o applets/usage applets/usage.c
(log yok)
| @@ -176,6 +176,12 @@ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) | |
| unsigned small_off; | |
| ssize_t read_len; | |
| + | if (id->known_size != UNKNOWN_SIZE |
| + | && id->known_size < off + len |
| + | ) { |
| + | return NULL; |
| + | } |
| + | |
| dbg("get buffer off 0x%llx(%llu), len 0x%zx", | |
| (unsigned long long) off, (unsigned long long) off, len); | |
| @@ -224,20 +230,29 @@ void *volume_id_get_buffer(struct volume_id *id, uint64_t off, size_t len) | |
| do_read: | |
| if (lseek(id->fd, off, SEEK_SET) != off) { | |
| dbg("seek(0x%llx) failed", (unsigned long long) off); | |
| + | off = lseek(id->fd, 0, SEEK_END); |
| + | if (off < 0) |
| + | off = 0; |
| goto err; | |
| } | |
| read_len = full_read(id->fd, dst, len); | |
| if (read_len != len) { | |
| dbg("requested 0x%x bytes, got 0x%x bytes", | |
| (unsigned) len, (unsigned) read_len); | |
| + | if (read_len > 0) |
| + | off += read_len; |
| err: | |
| - | /* No filesystem can be this tiny. It's most likely |
| - | * non-associated loop device, empty drive and so on. |
| - | * Flag it, avoiding future accesses. Rationale: |
| - | * users complained of slow blkid due to empty floppy drives. |
| - | */ |
| - | if (off <= 1024) |
| - | id->error = 1; |
| + | /* The image is definitely only OFF bytes large */ |
| + | if (off < UNKNOWN_SIZE) { |
| + | /* ...and OFF is small, we can use |
| + | * that knowledge to skip future probes: |
| + | * cases such as non-associated loop devices, |
| + | * empty (floppy) drives and so on. |
| + | * Record it, avoiding future accesses. Users |
| + | * complained of slow blkid due to empty floppys. |
| + | */ |
| + | id->known_size = off; |
| + | } |
| /* id->seekbuf_len or id->sbbuf_len is wrong now! Fixing. */ | |
| volume_id_free_buffer(id); | |
| return NULL; | |
| @@ -180,6 +180,7 @@ static const probe_fptr fs2[] ALIGN_PTR = { | |
| int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size) | |
| { | |
| + | int retval = 0; |
| unsigned i; | |
| /* probe for raid first, cause fs probes may be successful on raid members */ | |
| @@ -187,24 +188,26 @@ int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64 | |
| for (i = 0; i < ARRAY_SIZE(raid1); i++) { | |
| if (raid1[i](id, /*off,*/ size) == 0) | |
| goto ret; | |
| - | if (id->error) |
| - | goto ret; |
| + | //if (id->known_size < MIN_VALID_FS_SIZE) |
| + | // goto ret_bad; |
| + | //Redundant? none of the subsequent probers will succeed |
| + | //(or even attempt reads) if the above is true. |
| } | |
| } | |
| for (i = 0; i < ARRAY_SIZE(raid2); i++) { | |
| if (raid2[i](id /*,off*/) == 0) | |
| goto ret; | |
| - | if (id->error) |
| - | goto ret; |
| + | //if (id->known_size < MIN_VALID_FS_SIZE) |
| + | // goto ret_bad; |
| } | |
| /* signature in the first block, only small buffer needed */ | |
| for (i = 0; i < ARRAY_SIZE(fs1); i++) { | |
| if (fs1[i](id /*,off*/) == 0) | |
| goto ret; | |
| - | if (id->error) |
| - | goto ret; |
| + | //if (id->known_size < MIN_VALID_FS_SIZE) |
| + | // goto ret_bad; |
| } | |
| /* fill buffer with maximum */ | |
| @@ -213,13 +216,14 @@ int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64 | |
| for (i = 0; i < ARRAY_SIZE(fs2); i++) { | |
| if (fs2[i](id /*,off*/) == 0) | |
| goto ret; | |
| - | if (id->error) |
| - | goto ret; |
| + | //if (id->known_size < MIN_VALID_FS_SIZE) |
| + | // goto ret_bad; |
| } | |
| - | |
| + | //ret_bad: |
| + | retval = -1; /* "not found" */ |
| ret: | |
| volume_id_free_buffer(id); | |
| - | return (- id->error); /* 0 or -1 */ |
| + | return retval; |
| } | |
| /* open volume by device node */ | |
| @@ -229,6 +233,7 @@ struct volume_id* FAST_FUNC volume_id_open_node(int fd) | |
| id = xzalloc(sizeof(struct volume_id)); | |
| id->fd = fd; | |
| + | id->known_size = UNKNOWN_SIZE; |
| ///* close fd on device close */ | |
| //id->fd_close = 1; | |
| return id; | |
| @@ -60,7 +60,9 @@ struct volume_id_partition { | |
| struct volume_id { | |
| int fd; | |
| // int fd_close:1; | |
| - | int error; |
| + | /* UNKNOWN_SIZE: unknown, N: seek+read stopped at N prematurely */ |
| + | unsigned known_size; |
| + | #define UNKNOWN_SIZE UINT_MAX |
| size_t sbbuf_len; | |
| size_t seekbuf_len; | |
| uint8_t *sbbuf; | |
| @@ -85,6 +87,18 @@ struct volume_id { | |
| // const char *usage; | |
| }; | |
| + | // Technically, the tiniest possible linux FS image (romfs) with only "." and ".." is: |
| + | //00000000 2D 72 6F 6D 31 66 73 2D 00 00 00 60 A1 27 1D 06 -rom1fs-...`.'.. |
| + | //00000010 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ |
| + | //00000020 00 00 00 49 00 00 00 20 00 00 00 00 D1 FF FF 97 ...I... ........ |
| + | //00000030 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ |
| + | //00000040 00 00 00 00 00 00 00 20 00 00 00 00 D1 D1 FF E0 ....... ........ |
| + | //00000050 2E 2E 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................ |
| + | // but kernel won't mount it unless it's padded to 1K. |
| + | // Stop trying new FS types in volume_id_probe_all() outright |
| + | // if a previous probe had a read which stopped before 1K: |
| + | #define MIN_VALID_FS_SIZE 1024 |
| + | |
| struct volume_id* FAST_FUNC volume_id_open_node(int fd); | |
| int FAST_FUNC volume_id_probe_all(struct volume_id *id, /*uint64_t off,*/ uint64_t size); | |
| void FAST_FUNC free_volume_id(struct volume_id *id); | |