| |
| #include "fdiskP.h" |
| #include "pathnames.h" |
| #include "canonicalize.h" |
| |
| #include <ctype.h> |
| |
| /** |
| * SECTION: utils |
| * @title: Utils |
| * @short_description: misc fdisk functions |
| */ |
| |
| static int read_from_device(struct fdisk_context *cxt, |
| unsigned char *buf, |
| uintmax_t start, size_t size) |
| { |
| ssize_t r; |
| |
| assert(cxt); |
| |
| DBG(CXT, ul_debugobj(cxt, "reading: offset=%ju, size=%zu", |
| start, size)); |
| |
| r = lseek(cxt->dev_fd, start, SEEK_SET); |
| if (r == -1) |
| { |
| DBG(CXT, ul_debugobj(cxt, "failed to seek to offset %ju: %m", start)); |
| return -errno; |
| } |
| |
| r = read(cxt->dev_fd, buf, size); |
| if (r < 0 || (size_t)r != size) { |
| if (!errno) |
| errno = EINVAL; /* probably too small file/device */ |
| DBG(CXT, ul_debugobj(cxt, "failed to read %zu from offset %ju: %m", |
| size, start)); |
| return -errno; |
| } |
| |
| return 0; |
| } |
| |
| |
| /* |
| * Zeros in-memory first sector buffer |
| */ |
| int fdisk_init_firstsector_buffer(struct fdisk_context *cxt, |
| unsigned int protect_off, |
| unsigned int protect_size) |
| { |
| if (!cxt) |
| return -EINVAL; |
| |
| assert(protect_off + protect_size <= cxt->sector_size); |
| |
| if (!cxt->firstsector || cxt->firstsector_bufsz != cxt->sector_size) { |
| /* Let's allocate a new buffer if no allocated yet, or the |
| * current buffer has incorrect size */ |
| if (!cxt->parent || cxt->parent->firstsector != cxt->firstsector) |
| free(cxt->firstsector); |
| |
| DBG(CXT, ul_debugobj(cxt, "initialize in-memory first sector " |
| "buffer [sector_size=%lu]", cxt->sector_size)); |
| cxt->firstsector = calloc(1, cxt->sector_size); |
| if (!cxt->firstsector) |
| return -ENOMEM; |
| |
| cxt->firstsector_bufsz = cxt->sector_size; |
| return 0; |
| } |
| |
| DBG(CXT, ul_debugobj(cxt, "zeroize in-memory first sector buffer")); |
| memset(cxt->firstsector, 0, cxt->firstsector_bufsz); |
| |
| if (protect_size) { |
| /* |
| * It would be possible to reuse data from cxt->firstsector |
| * (call memset() for non-protected area only) and avoid one |
| * read() from the device, but it seems like a too fragile |
| * solution as we have no clue about stuff in the buffer -- |
| * maybe it was already modified. Let's re-read from the device |
| * to be sure. -- kzak 13-Apr-2015 |
| */ |
| DBG(CXT, ul_debugobj(cxt, "first sector protection enabled -- re-reading")); |
| read_from_device(cxt, cxt->firstsector, protect_off, protect_size); |
| } |
| return 0; |
| } |
| |
| int fdisk_read_firstsector(struct fdisk_context *cxt) |
| { |
| int rc; |
| |
| assert(cxt); |
| assert(cxt->sector_size); |
| |
| rc = fdisk_init_firstsector_buffer(cxt, 0, 0); |
| if (rc) |
| return rc; |
| |
| assert(cxt->sector_size == cxt->firstsector_bufsz); |
| |
| |
| return read_from_device(cxt, cxt->firstsector, 0, cxt->sector_size); |
| } |
| |
| /** |
| * fdisk_partname: |
| * @dev: device name |
| * @partno: partition name |
| * |
| * Return: allocated buffer with partition name, use free() to deallocate. |
| */ |
| char *fdisk_partname(const char *dev, size_t partno) |
| { |
| char *res = NULL; |
| const char *p = ""; |
| char *dev_mapped = NULL; |
| int w = 0; |
| |
| if (!dev || !*dev) { |
| if (asprintf(&res, "%zd", partno) > 0) |
| return res; |
| return NULL; |
| } |
| |
| /* It is impossible to predict /dev/dm-N partition names. */ |
| if (strncmp(dev, "/dev/dm-", sizeof("/dev/dm-") - 1) == 0) { |
| dev_mapped = canonicalize_dm_name (dev + 5); |
| if (dev_mapped) |
| dev = dev_mapped; |
| } |
| |
| w = strlen(dev); |
| if (isdigit(dev[w - 1])) |
| #ifdef __GNU__ |
| p = "s"; |
| #else |
| p = "p"; |
| #endif |
| |
| /* devfs kludge - note: fdisk partition names are not supposed |
| to equal kernel names, so there is no reason to do this */ |
| if (strcmp(dev + w - 4, "disc") == 0) { |
| w -= 4; |
| p = "part"; |
| } |
| |
| /* udev names partitions by appending -partN |
| e.g. ata-SAMSUNG_SV8004H_0357J1FT712448-part1 |
| multipath-tools kpartx.rules also append -partN */ |
| if ((strncmp(dev, _PATH_DEV_BYID, sizeof(_PATH_DEV_BYID) - 1) == 0) || |
| strncmp(dev, _PATH_DEV_BYPATH, sizeof(_PATH_DEV_BYPATH) - 1) == 0 || |
| strncmp(dev, "/dev/mapper", sizeof("/dev/mapper") - 1) == 0) { |
| |
| /* check for <name><partno>, e.g. mpatha1 */ |
| if (asprintf(&res, "%.*s%zu", w, dev, partno) <= 0) |
| res = NULL; |
| if (res && access(res, F_OK) == 0) |
| goto done; |
| |
| free(res); |
| |
| /* check for partition seperator "p" */ |
| if (asprintf(&res, "%.*sp%zu", w, dev, partno) <= 0) |
| res = NULL; |
| if (res && access(res, F_OK) == 0) |
| goto done; |
| |
| free(res); |
| |
| /* otherwise, default to "-path" */ |
| p = "-part"; |
| } |
| |
| if (asprintf(&res, "%.*s%s%zu", w, dev, p, partno) <= 0) |
| res = NULL; |
| done: |
| free(dev_mapped); |
| return res; |
| } |
| |
| #ifdef TEST_PROGRAM |
| struct fdisk_label *fdisk_new_dos_label(struct fdisk_context *cxt) { return NULL; } |
| struct fdisk_label *fdisk_new_bsd_label(struct fdisk_context *cxt) { return NULL; } |
| |
| static int test_partnames(struct fdisk_test *ts, int argc, char *argv[]) |
| { |
| size_t i; |
| const char *disk = argv[1]; |
| |
| for (i = 0; i < 5; i++) { |
| char *p = fdisk_partname(disk, i + 1); |
| if (p) |
| printf("%zu: '%s'\n", i + 1, p); |
| free(p); |
| } |
| |
| return 0; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| struct fdisk_test tss[] = { |
| { "--partnames", test_partnames, "<diskname>" }, |
| { NULL } |
| }; |
| |
| return fdisk_run_test(tss, argc, argv); |
| } |
| |
| #endif |