Home > OS >  how to get partition number and block device name seperating from a path in c program?
how to get partition number and block device name seperating from a path in c program?

Time:06-30

I have path name like this str[20]="/dev/sda1" and needs to get partion number i.e '1' and device name "sda1" seperatly stored in variables.

I tried something like this to get string but I want both partion number and string seperatly.

                 sscanf(str,"%[^0123456789]", temp);

but temp will give me string only not number?

CodePudding user response:

In this case you can reuse the str buffer, but I will not use sscanf, try with strcspn, it works even if the path doesn't have a number:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[] = "/dev/sda1";
    char num[sizeof str] = "";
    size_t pos = strcspn(str, "0123456789");

    strcpy(num, str   pos);
    str[pos] = '\0';
    puts(str);
    puts(num);
    return 0;
}

Output:

/dev/sda
1

A better version scanning from the last slash:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char str[] = "/dev1/sda1";
    char num[sizeof str] = "";
    // Searches for the last occurrence of '/'
    char *ptr = strrchr(str, '/');

    if (ptr != NULL)
    {
        size_t pos = strcspn(ptr, "0123456789");

        strcpy(num, ptr   pos);
        ptr[pos] = '\0';
    }
    puts(str);
    puts(num);
    return 0;
}

Output:

/dev1/sda
1

EDIT:

any specific reason ,why can't we use sscanf here

You can, but notice that an extra buffer is needed:

#include <stdio.h>

int main(void)
{
    char str[] = "/dev/sda1";
    char dev[sizeof str] = "";
    char num[sizeof str] = ""; 

    sscanf(str, "%[^0-9]%s", dev, num); 
    puts(dev);
    puts(num);
    return 0;
}

CodePudding user response:

I would recommend:

  • Use strrchr(path, '/') to find the last slash in the path, and if non-NULL, use the rest of the string (starting at the character following the last slash) as the device name. If there is no slash in the path, then it consists of the device name only.

  • Use sscanf() to extract the components of the device name, with a terminating %c (taking a pointer to a char) to detect if the name or path contains extra characters. The return value is the number of successful conversions, and we require that terminating conversion to fail. (If you use %n, it is not counted as a conversion.)

  • Support at least /dev/hdXP (ATA/ATAPI devices), /dev/sdXP (SCSI and SATA devices), nvmeCnDpP (NVMe devices), and /dev/mmcblkDpP (eMMC devices) partition types.

Here is a working example program. Supply one or more partition names or paths, and it describes each name.

#define  _POSIX_C_SOURCE  200809L
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>

enum {
    BLOCKDEV_NONE = -1,
    BLOCKDEV_HD,        /* device hd<letter>, partition hd<letter><number> */
    BLOCKDEV_SD,        /* device sd<letter>, partition sd<letter><number> */
    BLOCKDEV_NVME,      /* device nvme<number>n<number>, partition nvme<number>n<number>p<number> */
    BLOCKDEV_MMC,       /* device mmcblk<number>, partition mmcblk<number>p<number> */
};

struct partition_info {
    char *blockdev_path;    /* Full path to the block device */
    char *blockdev_name;    /* Block device name, basename(blockdev_path) */
    char *partition_path;   /* Full path to the partition device */
    char *partition_name;   /* Partition device name, basename(partition_path) */
    int   type;             /* One of the BLOCKDEV_ constants */
    int   controller;       /* Controller number for NVMe, 0 for others */
    int   device;           /* Device number; 0..25 for letters a..z */
    int   partition;        /* Partition number */
};

const char *blockdev_type_desc(const int type)
{
    switch (type) {
    case BLOCKDEV_NONE: return "None";
    case BLOCKDEV_HD:   return "ATA block device";
    case BLOCKDEV_SD:   return "SCSI or SATA block device";
    case BLOCKDEV_NVME: return "NVMe block device";
    case BLOCKDEV_MMC:  return "eMMC block device";
    default:            return "(Unknown block device)";
    }
}

void free_partition_info(struct partition_info *info)
{
    /* Since free(NULL) is safe, make free_partition_info(NULL) safe also. */
    if (!info)
        return;

    info->type = BLOCKDEV_NONE;
    info->controller = -1;
    info->device = -1;
    info->partition = -1;

    free(info->blockdev_path);
    info->blockdev_path = NULL;
    info->blockdev_name = NULL;

    free(info->partition_path);
    info->partition_path = NULL;
    info->partition_name = NULL;
}

static inline char *name_part(char *path)
{
    if (!path)
        return NULL;

    char *last_slash = strrchr(path, '/');
    if (last_slash)
        return last_slash   1;
    else
        return path;
}

int set_partition_info(struct partition_info *info, const char *path)
{
    int   ctrlnum, devnum, partnum, n;
    char  letter[2], dummy;

    /* A pointer to an uninitialized partition_info structure is required. */
    if (!info) {
        errno = EINVAL;
        return -1;
    }

    info->type = BLOCKDEV_NONE;
    info->controller = -1;
    info->device = -1;
    info->partition = -1;
    info->blockdev_path = NULL;
    info->blockdev_name = NULL;
    info->partition_path = NULL;
    info->partition_name = NULL;

    /* Partition path cannot be NULL or empty. */
    if (!path || !*path) {
        errno = ENOENT;
        return -1;
    }

    /* Partition name part follows the last slash, or is the full string. */
    const char   *last_slash = strrchr(path, '/');
    const char   *name = (last_slash) ? last_slash   1 : path;
    /* Prefixlen is the length of the string up to and including the last slash. */
    const size_t  prefixlen = (last_slash) ? (size_t)(last_slash - path   1) : 0;

    /* NVMe partition? */
    if (sscanf(name, "nvme%dn%dp%d%c", &ctrlnum, &devnum, &partnum, &dummy) == 3) {
        info->type = BLOCKDEV_NVME;
        info->controller = ctrlnum;
        info->device = devnum;
        info->partition = partnum;

        info->blockdev_path = strdup(path);
        info->partition_path = strdup(path);
        if (!info->blockdev_path || !info->partition_path) {
            free(info->partition_path);
            free(info->blockdev_path);
            info->blockdev_path = NULL;
            info->partition_path = NULL;
            errno = ENOMEM;
            return -1;
        }

        *(strrchr(info->blockdev_path, 'p')) = '\0';
        info->blockdev_name = name_part(info->blockdev_path);
        info->partition_name = name_part(info->partition_path);
        return 0;
    }

    /* MMC partition? */
    if (sscanf(name, "mmcblk%dp%d%c", &devnum, &partnum, &dummy) == 2) {
        info->type = BLOCKDEV_MMC;
        info->controller = 0;
        info->device = devnum;
        info->partition = partnum;

        info->blockdev_path = strdup(path);
        info->partition_path = strdup(path);
        if (!info->blockdev_path || !info->partition_path) {
            free(info->partition_path);
            free(info->blockdev_path);
            info->blockdev_path = NULL;
            info->partition_path = NULL;
            errno = ENOMEM;
            return -1;
        }

        *(strrchr(info->blockdev_path, 'p')) = '\0';
        info->blockdev_name = name_part(info->blockdev_path);
        info->partition_name = name_part(info->partition_path);
        return 0;
    }

    /* HD partition? */
    if (sscanf(name, "hd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
        info->type = BLOCKDEV_HD;
        info->controller = 0;
        info->device = letter[0] - 'a';
        info->partition = partnum;

        info->blockdev_path = strndup(path, prefixlen   3);
        info->partition_path = strdup(path);
        if (!info->blockdev_path || !info->partition_path) {
            free(info->partition_path);
            free(info->blockdev_path);
            info->blockdev_path = NULL;
            info->partition_path = NULL;
            errno = ENOMEM;
            return -1;
        }

        info->blockdev_name = name_part(info->blockdev_path);
        info->partition_name = name_part(info->partition_path);
        return 0;
    }

    /* SD partition? */
    if (sscanf(name, "sd%1[a-z]%n%d%c", letter, &n, &partnum, &dummy) == 2 && n == 3) {
        info->type = BLOCKDEV_SD;
        info->controller = 0;
        info->device = letter[0] - 'a';
        info->partition = partnum;

        info->blockdev_path = strndup(path, prefixlen   3);
        info->partition_path = strdup(path);
        if (!info->blockdev_path || !info->partition_path) {
            free(info->partition_path);
            free(info->blockdev_path);
            info->blockdev_path = NULL;
            info->partition_path = NULL;
            errno = ENOMEM;
            return -1;
        }

        info->blockdev_name = name_part(info->blockdev_path);
        info->partition_name = name_part(info->partition_path);
        return 0;
    }

    /* Unknown device; return "no such device" error. */
    errno = ENODEV;
    return -1;
}

int main(int argc, char *argv[])
{
    for (int arg = 1; arg < argc; arg  ) {
        struct partition_info  p;

        if (set_partition_info(&p, argv[arg])) {
            fprintf(stderr, "%s: %s.\n", argv[arg], strerror(errno));
            return EXIT_FAILURE;
        }

        printf("Specified path: \"%s\" (%p)\n", argv[arg], argv[arg]);
        printf("          Type: %s\n", blockdev_type_desc(p.type));

        if (p.type == BLOCKDEV_NVME)
            printf("    Controller: %d\n", p.controller);

        if (p.type == BLOCKDEV_HD || p.type == BLOCKDEV_SD)
            printf("        Device: '%c' (%d)\n", p.device   'a', p.device);
        else
            printf("        Device: %d\n", p.device);

        printf("     Partition: %d\n", p.partition);
        printf("    Block device path: \"%s\" (%p)\n", p.blockdev_path, p.blockdev_path);
        printf("    Block device name: \"%s\" (%p)\n", p.blockdev_name, p.blockdev_name);
        printf("Partition device path: \"%s\" (%p)\n", p.partition_path, p.partition_path);
        printf("Partition device name: \"%s\" (%p)\n", p.partition_name, p.partition_name);

        printf("\n");
        fflush(stdout);

        free_partition_info(&p);
    }

    return EXIT_SUCCESS;
}

Note that the struct partition_info contains dynamically allocated copies. The name parts are pointers to the corresponding path member, just skipping any directory components in the path.

For illustration, the example program shows the string pointer values (target address in hexadecimal) in parentheses after each string output in double quotes. This is to show how the various strings relate (or not) to each other.

  • Related