static int get_partition_start(int fd, int partno, uint64_t *start) { struct stat st; struct sysfs_cxt disk = UL_SYSFSCXT_EMPTY, part = UL_SYSFSCXT_EMPTY; dev_t devno = 0; int rc = -1; /* * wholedisk */ if (fstat(fd, &st) || !S_ISBLK(st.st_mode)) goto done; devno = st.st_rdev; if (sysfs_init(&disk, devno, NULL)) goto done; /* * partition */ devno = sysfs_partno_to_devno(&disk, partno); if (!devno) goto done; if (sysfs_init(&part, devno, &disk)) goto done; if (sysfs_read_u64(&part, "start", start)) goto done; rc = 0; done: sysfs_deinit(&part); sysfs_deinit(&disk); return rc; }
static int has_discard(const char *devname, struct sysfs_cxt *wholedisk) { struct sysfs_cxt cxt, *parent = NULL; uint64_t dg = 0; dev_t disk = 0, dev; int rc; dev = sysfs_devname_to_devno(devname, NULL); if (!dev) return 1; /* * This is tricky to read the info from sys/, because the queue * atrributes are provided for whole devices (disk) only. We're trying * to reuse the whole-disk sysfs context to optimize this stuff (as * system usually have just one disk only). */ if (sysfs_devno_to_wholedisk(dev, NULL, 0, &disk) || !disk) return 1; if (dev != disk) { if (wholedisk->devno != disk) { sysfs_deinit(wholedisk); if (sysfs_init(wholedisk, disk, NULL)) return 1; } parent = wholedisk; } rc = sysfs_init(&cxt, dev, parent); if (!rc) rc = sysfs_read_u64(&cxt, "queue/discard_granularity", &dg); sysfs_deinit(&cxt); return rc == 0 && dg > 0; }
/* * Returns 1 if the device is private LVM device. */ int sysfs_devno_is_lvm_private(dev_t devno) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char *uuid = NULL; int rc = 0; if (sysfs_init(&cxt, devno, NULL) != 0) return 0; uuid = sysfs_strdup(&cxt, "dm/uuid"); /* Private LVM devices use "LVM-<uuid>-<name>" uuid format (important * is the "LVM" prefix and "-<name>" postfix). */ if (uuid && strncmp(uuid, "LVM-", 4) == 0) { char *p = strrchr(uuid + 4, '-'); if (p && *(p + 1)) rc = 1; } sysfs_deinit(&cxt); free(uuid); return rc; }
int sysfs_init(struct sysfs_cxt *cxt, dev_t devno, struct sysfs_cxt *parent) { char path[PATH_MAX]; int fd, rc; memset(cxt, 0, sizeof(*cxt)); cxt->dir_fd = -1; if (!sysfs_devno_path(devno, path, sizeof(path))) goto err; fd = open(path, O_RDONLY|O_CLOEXEC); if (fd < 0) goto err; cxt->dir_fd = fd; cxt->dir_path = strdup(path); if (!cxt->dir_path) goto err; cxt->devno = devno; cxt->parent = parent; return 0; err: rc = errno > 0 ? -errno : -1; sysfs_deinit(cxt); return rc; }
/* * Returns devname (e.g. "/dev/sda1") for the given devno. * * Note that the @buf has to be large enough to store /sys/dev/block/<maj:min> * symlinks. * * Please, use more robust blkid_devno_to_devname() in your applications. */ char *sysfs_devno_to_devpath(dev_t devno, char *buf, size_t bufsiz) { struct sysfs_cxt cxt; char *name; size_t sz; struct stat st; if (sysfs_init(&cxt, devno, NULL)) return NULL; name = sysfs_get_devname(&cxt, buf, bufsiz); sysfs_deinit(&cxt); if (!name) return NULL; sz = strlen(name); if (sz + sizeof("/dev/") > bufsiz) return NULL; /* create the final "/dev/<name>" string */ memmove(buf + 5, name, sz + 1); memcpy(buf, "/dev/", 5); if (!stat(buf, &st) && S_ISBLK(st.st_mode) && st.st_rdev == devno) return buf; return NULL; }
int main(int argc, char *argv[]) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char *devname; dev_t devno; char path[PATH_MAX]; int i; uint64_t u64; ssize_t len; if (argc != 2) errx(EXIT_FAILURE, "usage: %s <devname>", argv[0]); devname = argv[1]; devno = sysfs_devname_to_devno(devname, NULL); if (!devno) err(EXIT_FAILURE, "failed to read devno"); printf("NAME: %s\n", devname); printf("DEVNO: %u\n", (unsigned int) devno); printf("DEVNOPATH: %s\n", sysfs_devno_path(devno, path, sizeof(path))); printf("DEVPATH: %s\n", sysfs_devno_to_devpath(devno, path, sizeof(path))); printf("PARTITION: %s\n", sysfs_devno_has_attribute(devno, "partition") ? "YES" : "NOT"); if (sysfs_init(&cxt, devno, NULL)) return EXIT_FAILURE; len = sysfs_readlink(&cxt, NULL, path, sizeof(path) - 1); if (len > 0) { path[len] = '\0'; printf("DEVNOLINK: %s\n", path); } printf("SLAVES: %d\n", sysfs_count_dirents(&cxt, "slaves")); if (sysfs_read_u64(&cxt, "size", &u64)) printf("read SIZE failed\n"); else printf("SIZE: %jd\n", u64); if (sysfs_read_int(&cxt, "queue/hw_sector_size", &i)) printf("read SECTOR failed\n"); else printf("SECTOR: %d\n", i); printf("DEVNAME: %s\n", sysfs_get_devname(&cxt, path, sizeof(path))); sysfs_deinit(&cxt); return EXIT_SUCCESS; }
static int is_hotpluggable(const struct eject_control *ctl) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; dev_t devno; int rc = 0; devno = sysfs_devname_to_devno(ctl->device, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; rc = sysfs_is_hotpluggable(&cxt); sysfs_deinit(&cxt); return rc; }
static int is_hotpluggable(const char* device) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; char devchain[PATH_MAX]; char subbuf[PATH_MAX]; dev_t devno; int rc = 0; ssize_t sz; char *sub; devno = sysfs_devname_to_devno(device, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; /* check /sys/dev/block/<maj>:<min>/removable attribute */ if (sysfs_read_int(&cxt, "removable", &rc) == 0 && rc == 1) { verbose(_("%s: is removable device"), device); goto done; } /* read /sys/dev/block/<maj>:<min> symlink */ sz = sysfs_readlink(&cxt, NULL, devchain, sizeof(devchain)); if (sz <= 0 || sz + sizeof(_PATH_SYS_DEVBLOCK "/") > sizeof(devchain)) goto done; devchain[sz++] = '\0'; /* create absolute patch from the link */ memmove(devchain + sizeof(_PATH_SYS_DEVBLOCK "/") - 1, devchain, sz); memcpy(devchain, _PATH_SYS_DEVBLOCK "/", sizeof(_PATH_SYS_DEVBLOCK "/") - 1); while ((sub = get_subsystem(devchain, subbuf, sizeof(subbuf)))) { rc = is_hotpluggable_subsystem(sub); if (rc) { verbose(_("%s: connected by hotplug subsystem: %s"), device, sub); break; } } done: sysfs_deinit(&cxt); return rc; }
static int umount_partitions(const char *disk, int checkonly) { struct sysfs_cxt cxt = UL_SYSFSCXT_EMPTY; dev_t devno; DIR *dir = NULL; struct dirent *d; int count = 0; devno = sysfs_devname_to_devno(disk, NULL); if (sysfs_init(&cxt, devno, NULL) != 0) return 0; /* open /sys/block/<wholedisk> */ if (!(dir = sysfs_opendir(&cxt, NULL))) goto done; /* scan for partition subdirs */ while ((d = readdir(dir))) { if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) continue; if (sysfs_is_partition_dirent(dir, d, disk)) { char *mnt = NULL; char *dev = find_device(d->d_name); if (dev && device_get_mountpoint(&dev, &mnt) == 0) { verbose(_("%s: mounted on %s"), dev, mnt); if (!checkonly) umount_one(mnt); count++; } free(dev); free(mnt); } } done: if (dir) closedir(dir); sysfs_deinit(&cxt); return count; }
/* * @lc: context * @device: device name, absolute device path or NULL to reset the current setting * * Sets device, absolute paths (e.g. "/dev/loop<N>") are unchanged, device * names ("loop<N>") are converted to the path (/dev/loop<N> or to * /dev/loop/<N>) * * Returns: <0 on error, 0 on success */ int loopcxt_set_device(struct loopdev_cxt *lc, const char *device) { if (!lc) return -EINVAL; if (lc->fd >= 0) close(lc->fd); lc->fd = -1; lc->mode = 0; lc->has_info = 0; lc->info_failed = 0; *lc->device = '\0'; memset(&lc->info, 0, sizeof(lc->info)); /* set new */ if (device) { if (*device != '/') { const char *dir = _PATH_DEV; /* compose device name for /dev/loop<n> or /dev/loop/<n> */ if (lc->flags & LOOPDEV_FL_DEVSUBDIR) { if (strlen(device) < 5) return -1; device += 4; dir = _PATH_DEV_LOOP "/"; /* _PATH_DEV uses tailing slash */ } snprintf(lc->device, sizeof(lc->device), "%s%s", dir, device); } else { strncpy(lc->device, device, sizeof(lc->device)); lc->device[sizeof(lc->device) - 1] = '\0'; } DBG(lc, loopdev_debug("%s successfully assigned", device)); } sysfs_deinit(&lc->sysfs); return 0; }
/* * Returns by @diskdevno whole disk device devno and (optionaly) by * @diskname the whole disk device name. */ int sysfs_devno_to_wholedisk(dev_t dev, char *diskname, size_t len, dev_t *diskdevno) { struct sysfs_cxt cxt; int is_part = 0; if (!dev || sysfs_init(&cxt, dev, NULL) != 0) return -1; is_part = sysfs_has_attribute(&cxt, "partition"); if (!is_part) { /* * Extra case for partitions mapped by device-mapper. * * All regualar partitions (added by BLKPG ioctl or kernel PT * parser) have the /sys/.../partition file. The partitions * mapped by DM don't have such file, but they have "part" * prefix in DM UUID. */ char *uuid = sysfs_strdup(&cxt, "dm/uuid"); char *tmp = uuid; char *prefix = uuid ? strsep(&tmp, "-") : NULL; if (prefix && strncasecmp(prefix, "part", 4) == 0) is_part = 1; free(uuid); if (is_part && get_dm_wholedisk(&cxt, diskname, len, diskdevno) == 0) /* * partitioned device, mapped by DM */ goto done; is_part = 0; } if (!is_part) { /* * unpartitioned device */ if (diskname && len) { if (!sysfs_get_devname(&cxt, diskname, len)) goto err; } if (diskdevno) *diskdevno = dev; } else { /* * partitioned device * - readlink /sys/dev/block/8:1 = ../../block/sda/sda1 * - dirname ../../block/sda/sda1 = ../../block/sda * - basename ../../block/sda = sda */ char linkpath[PATH_MAX]; char *name; int linklen; linklen = sysfs_readlink(&cxt, NULL, linkpath, sizeof(linkpath) - 1); if (linklen < 0) goto err; linkpath[linklen] = '\0'; stripoff_last_component(linkpath); /* dirname */ name = stripoff_last_component(linkpath); /* basename */ if (!name) goto err; if (diskname && len) { strncpy(diskname, name, len); diskname[len - 1] = '\0'; } if (diskdevno) { *diskdevno = sysfs_devname_to_devno(name, NULL); if (!*diskdevno) goto err; } } done: sysfs_deinit(&cxt); return 0; err: sysfs_deinit(&cxt); return -1; }