#include #include #include #include #include #include #include #include "name_to_dev_t.h" static dev_t try_name(char *name, int part) { char path[64]; char buf[32]; int range; dev_t res; char *s; int len; int fd; unsigned int maj, min; struct stat sb; /* read device number from .../dev */ sprintf(path, "/sys/block/%s/dev", name); fd = open(path, O_RDONLY); if (fd < 0) goto fail; len = read(fd, buf, 32); close(fd); if (len <= 0 || len == 32 || buf[len - 1] != '\n') goto fail; buf[len - 1] = '\0'; if (sscanf(buf, "%u:%u", &maj, &min) != 2) goto fail; res = makedev(maj, min); if (maj != major(res) || min != minor(res)) goto fail; /* if it's there and we are not looking for a partition - that's it */ if (!part) return res; /* otherwise read range from .../range */ snprintf(path, 64, "/sys/block/%s/range", name); fd = open(path, O_RDONLY); if (fd < 0) goto fail; len = read(fd, buf, 32); close(fd); if (len <= 0 || len == 32 || buf[len - 1] != '\n') goto fail; buf[len - 1] = '\0'; range = strtoul(buf, &s, 10); if (*s) goto fail; /* if partition is within range - we got it */ if (part < range) return res + part; fail: sprintf(path, "/dev/%s", name); if (!stat(path, &sb)) { if (S_ISBLK(sb.st_mode)) return sb.st_rdev; } return 0; } /* * Convert a name into device number. We accept the following variants: * * 1) device number in hexadecimal represents itself * 2) /dev/nfs represents Root_NFS (0xff) * 3) /dev/ represents the device number of disk * 4) /dev/ represents the device number * of partition - device number of disk plus the partition number * 5) /dev/p - same as the above, that form is * used when disk name of partitioned disk ends on a digit. * * If name doesn't have fall into the categories above, we return 0. * sysfs is used to check if something is a disk name - it has * all known disks under bus/block/devices. If the disk name * contains slashes, name of driverfs node has them replaced with * bangs. try_name() does the actual checks, assuming that sysfs * is mounted on /sys. * * Note that cases (1) and (2) are already handled by the kernel, * so we can ifdef them out, provided that we check real-root-dev * first. */ dev_t name_to_dev_t(char *name) { char s[32]; char *p; dev_t res = 0; int part; if (strncmp(name, "/dev/", 5) != 0) { #if 1 /* kernel used to do this */ unsigned maj, min; if (sscanf(name, "%u:%u", &maj, &min) == 2) { res = makedev(maj, min); if (maj != major(res) || min != minor(res)) return 0; } else { res = strtoul(name, &p, 16); if (*p) return 0; } #endif return res; } name += 5; #if 1 /* kernel used to do this */ if (strcmp(name, "nfs") == 0) return makedev(0, 255); #endif if (strchr(name, '/')) { res = try_name(name, 1); if (res) return res; } if (strlen(name) > 31) return 0; strcpy(s, name); for (p = s; *p; p++) if (*p == '/') *p = '!'; res = try_name(s, 0); if (res) return res; while (p > s && isdigit(p[-1])) p--; if (p == s || !*p || *p == '0') return 0; part = strtoul(p, NULL, 10); *p = '\0'; res = try_name(s, part); if (res) return res; if (p < s + 2 || !isdigit(p[-2]) || p[-1] != 'p') return 0; p[-1] = '\0'; return try_name(s, part); }