int get_var (FILE *fp, char **name, char **value) { static char *buffer; static size_t bufsize = OFF_T_STRSIZE_BOUND; char *p, *q; buffer = emalloc (bufsize); do { size_t len, s; if (!fgets (buffer, bufsize, fp)) return 0; len = strlen (buffer); if (len == 0) return 0; s = string_to_size (buffer, &p); if (*p != ' ') die (1, "malformed header: expected space but found %s", p); if (buffer[len-1] != '\n') { if (bufsize < s + 1) { bufsize = s + 1; buffer = realloc (buffer, bufsize); if (!buffer) die (1, "not enough memory"); } if (!fgets (buffer + len, s - len + 1, fp)) die (1, "unexpected end of file or read error"); } p++; } while (memcmp (p, "GNU.sparse.", 11)); p += 11; q = strchr (p, '='); if (!q) die (1, "malformed header: expected '=' not found"); *q++ = 0; q[strlen (q) - 1] = 0; *name = p; *value = q; return 1; }
void read_map (FILE *ifp) { size_t i; char nbuf[OFF_T_STRSIZE_BOUND]; if (verbose) printf ("Reading v.1.0 sparse map\n"); get_line (nbuf, sizeof nbuf, ifp); sparse_map_size = string_to_size (nbuf, NULL); sparse_map = emalloc (sparse_map_size * sizeof *sparse_map); for (i = 0; i < sparse_map_size; i++) { get_line (nbuf, sizeof nbuf, ifp); sparse_map[i].offset = string_to_off (nbuf, NULL); get_line (nbuf, sizeof nbuf, ifp); sparse_map[i].numbytes = string_to_off (nbuf, NULL); } fseeko (ifp, ((ftell (ifp) + BLOCKSIZE - 1) / BLOCKSIZE) * BLOCKSIZE, SEEK_SET); }
/// /// It all starts here. /// int32_t main(int32_t ac, char *av[]) { for (int32_t i = 1; i < ac; ++i) { if (av[i][0] == '-') { switch (av[i][1]) { case 'r': rand_flag = true; break; default: fprintf(stderr, "invalid option: %s\n", av[i]); usage(); return 1; } } else if (device == 0) { device = av[i]; } else if (block_size == 0) { if (!string_to_size(av[i], &block_size)) { fprintf(stderr, "invalid block size: %s\n", av[i]); usage(); return 1; } } else if (limit == 0) { if (!string_to_size(av[i], &limit)) { fprintf(stderr, "invalid limit: %s\n", av[i]); usage(); return 1; } } else if (size == 0) { if (!string_to_size(av[i], &size)) { fprintf(stderr, "invalid size: %s\n", av[i]); usage(); return 1; } } } bool err = false; if (device == 0) { fprintf(stderr, "missing device\n"); err = true; } if (block_size == 0) { fprintf(stderr, "missing block size\n"); err = true; } if (limit == 0) { fprintf(stderr, "missing limit\n"); err = true; } if (size == 0) { fprintf(stderr, "missing size\n"); err = true; } if (block_size != 0 && limit % block_size != 0) { fprintf(stderr, "limit must be a multiple of block size\n"); err = true; } if (block_size != 0 && size % block_size != 0) { fprintf(stderr, "size must be a multiple of block size\n"); err = true; } if (block_size != 0 && limit / block_size % 3 == 0) { fprintf(stderr, "limit must be relatively prime with random " "multiplier %lu\n", rand_mult); err = true; } if (err) { usage(); return 1; } int32_t ret_val = 1; printf("reading %lu byte(s) from %s, block size = %lu, limit = %lu (%s)\n", size, device, block_size, limit, rand_flag ? "random" : "linear"); uint8_t *buffer = malloc(block_size + 16384); if (buffer == 0) { fprintf(stderr, "cannot allocate block buffer\n"); goto cleanup0; } uint64_t buff_off = (((uint64_t)buffer + 16383) & (uint64_t)~16383) - (uint64_t)buffer; printf("(buffer at %p, offset is %lu, effective buffer at %p)\n", buffer, buff_off, buffer + buff_off); int32_t fd = open(device, O_RDONLY | O_DIRECT); if (fd < 0) { fprintf(stderr, "cannot open device %s: %d, %s\n", device, errno, strerror(errno)); goto cleanup1; } const uint64_t block_limit = limit / block_size; uint64_t count = 0; uint64_t start; if (!get_time(&start)) { fprintf(stderr, "cannot get start time\n"); goto cleanup2; } uint64_t last_now = 0; while (count < size) { uint64_t block_count = count / block_size; if (rand_flag) { block_count = block_count * rand_mult; } block_count = block_count % block_limit; uint64_t seek = block_count * block_size; if (lseek(fd, (off_t)seek, SEEK_SET) < 0) { fprintf(stderr, "error while seeking to %lu: %d, %s\n", seek, errno, strerror(errno)); goto cleanup2; } ssize_t res = read(fd, buffer + buff_off, block_size); if (res != (ssize_t)block_size) { fprintf(stderr, "error while reading at %lu: %ld byte(s) read\n", seek, res); if (res < 0) { fprintf(stderr, "error %d, %s\n", errno, strerror(errno)); } goto cleanup2; } count += block_size; uint64_t now; if (!get_time(&now)) { fprintf(stderr, "cannot get current time\n"); goto cleanup2; } if (now - last_now >= 5000 || count == size) { uint32_t perc = (uint32_t)(count * 100 / size); printf("%d%% complete\n", perc); last_now = now; } } uint64_t end; if (!get_time(&end)) { fprintf(stderr, "cannot get end time\n"); goto cleanup2; } float sec = (float)(end - start) / (float)1000.0; float speed = (float)size / sec / (float)1024.0 / (float)1024.0; float iops = (float)(size / block_size) / sec; printf("%.1f s, %.1f MiB/s, %.1f IOPS\n", sec, speed, iops); ret_val = 0; cleanup2: close(fd); cleanup1: free(buffer); cleanup0: return ret_val; }
void read_xheader (char *name) { char *kw, *val; FILE *fp = fopen (name, "r"); char *expect = NULL; size_t i = 0; if (verbose) printf ("Reading extended header file\n"); while (get_var (fp, &kw, &val)) { if (verbose) printf ("Found variable GNU.sparse.%s = %s\n", kw, val); if (expect && strcmp (kw, expect)) die (1, "bad keyword sequence: expected '%s' but found '%s'", expect, kw); expect = NULL; if (strcmp (kw, "name") == 0) { outname = emalloc (strlen (val) + 1); strcpy (outname, val); } else if (strcmp (kw, "major") == 0) { version_major = string_to_size (val, NULL); } else if (strcmp (kw, "minor") == 0) { version_minor = string_to_size (val, NULL); } else if (strcmp (kw, "realsize") == 0 || strcmp (kw, "size") == 0) { outsize = string_to_off (val, NULL); } else if (strcmp (kw, "numblocks") == 0) { sparse_map_size = string_to_size (val, NULL); sparse_map = emalloc (sparse_map_size * sizeof *sparse_map); } else if (strcmp (kw, "offset") == 0) { sparse_map[i].offset = string_to_off (val, NULL); expect = "numbytes"; } else if (strcmp (kw, "numbytes") == 0) { sparse_map[i++].numbytes = string_to_off (val, NULL); } else if (strcmp (kw, "map") == 0) { for (i = 0; i < sparse_map_size; i++) { sparse_map[i].offset = string_to_off (val, &val); if (*val != ',') die (1, "bad GNU.sparse.map: expected ',' but found '%c'", *val); sparse_map[i].numbytes = string_to_off (val+1, &val); if (*val != ',') { if (!(*val == 0 && i == sparse_map_size-1)) die (1, "bad GNU.sparse.map: expected ',' but found '%c'", *val); } else val++; } if (*val) die (1, "bad GNU.sparse.map: garbage at the end"); } } if (expect) die (1, "bad keyword sequence: expected '%s' not found", expect); if (version_major == 0 && sparse_map_size == 0) die (1, "size of the sparse map unknown"); if (i != sparse_map_size) die (1, "not all sparse entries supplied"); fclose (fp); }