static int _md_sysfs_attribute_snprintf(char *path, size_t size, struct dev_types *dt, struct device *blkdev, const char *attribute) { const char *sysfs_dir = dm_sysfs_dir(); struct stat info; dev_t dev = blkdev->dev; int ret = -1; if (!sysfs_dir || !*sysfs_dir) return ret; if (MAJOR(dev) == dt->blkext_major) { /* lookup parent MD device from blkext partition */ if (!dev_get_primary_dev(dt, blkdev, &dev)) return ret; } if (MAJOR(dev) != dt->md_major) return ret; ret = dm_snprintf(path, size, "%s/dev/block/%d:%d/md/%s", sysfs_dir, (int)MAJOR(dev), (int)MINOR(dev), attribute); if (ret < 0) { log_error("dm_snprintf md %s failed", attribute); return ret; } if (stat(path, &info) == -1) { if (errno != ENOENT) { log_sys_error("stat", path); return ret; } /* old sysfs structure */ ret = dm_snprintf(path, size, "%s/block/md%d/md/%s", sysfs_dir, (int)MINOR(dev), attribute); if (ret < 0) { log_error("dm_snprintf old md %s failed", attribute); return ret; } } return ret; }
static int _sysfs_get_kvdo_value(const char *dm_name, const char *vdo_param, uint64_t *value) { char path[PATH_MAX]; char temp[64]; int fd, size, r = 0; if (dm_snprintf(path, sizeof(path), "%skvdo/%s/%s", dm_sysfs_dir(), dm_name, vdo_param) < 0) { log_error("Failed to build kmod path."); return 0; } if ((fd = open(path, O_RDONLY)) < 0) { if (errno != ENOENT) log_sys_error("open", path); else log_sys_debug("open", path); goto bad; } if ((size = read(fd, temp, sizeof(temp) - 1)) < 0) { log_sys_error("read", path); goto bad; } temp[size] = 0; errno = 0; *value = strtoll(temp, NULL, 0); if (errno) { log_sys_error("strtool", path); goto bad; } r = 1; bad: if (fd >= 0 && close(fd)) log_sys_error("close", path); return r; }
static unsigned long _dev_topology_attribute(struct dev_types *dt, const char *attribute, struct device *dev, unsigned long default_value) { const char *sysfs_dir = dm_sysfs_dir(); char path[PATH_MAX], buffer[64]; FILE *fp; struct stat info; dev_t uninitialized_var(primary); unsigned long result = default_value; unsigned long value = 0UL; if (!attribute || !*attribute) goto_out; if (!sysfs_dir || !*sysfs_dir) goto_out; if (!_snprintf_attr(path, sizeof(path), sysfs_dir, attribute, dev->dev)) goto_out; /* * check if the desired sysfs attribute exists * - if not: either the kernel doesn't have topology support * or the device could be a partition */ if (stat(path, &info) == -1) { if (errno != ENOENT) { log_sys_debug("stat", path); goto out; } if (!dev_get_primary_dev(dt, dev, &primary)) goto out; /* get attribute from partition's primary device */ if (!_snprintf_attr(path, sizeof(path), sysfs_dir, attribute, primary)) goto_out; if (stat(path, &info) == -1) { if (errno != ENOENT) log_sys_debug("stat", path); goto out; } } if (!(fp = fopen(path, "r"))) { log_sys_debug("fopen", path); goto out; } if (!fgets(buffer, sizeof(buffer), fp)) { log_sys_debug("fgets", path); goto out_close; } if (sscanf(buffer, "%lu", &value) != 1) { log_warn("sysfs file %s not in expected format: %s", path, buffer); goto out_close; } log_very_verbose("Device %s: %s is %lu%s.", dev_name(dev), attribute, result, default_value ? "" : " bytes"); result = value >> SECTOR_SHIFT; out_close: if (fclose(fp)) log_sys_debug("fclose", path); out: return result; }
/* * Get primary dev for the dev supplied. * * We can get a primary device for a partition either by: * A: knowing the number of partitions allowed for the dev and also * which major:minor number represents the primary and partition device * (by using the dev_types->dev_type_array) * B: by the existence of the 'partition' sysfs attribute * (/dev/block/<major>:<minor>/partition) * * Method A is tried first, then method B as a fallback if A fails. * * N.B. Method B can only do the decision based on the pure existence of * the 'partition' sysfs item. There's no direct scan for partition * tables whatsoever! * * Returns: * 0 on error * 1 if the dev is already a primary dev, primary dev in 'result' * 2 if the dev is a partition, primary dev in 'result' */ int dev_get_primary_dev(struct dev_types *dt, struct device *dev, dev_t *result) { const char *sysfs_dir = dm_sysfs_dir(); int major = (int) MAJOR(dev->dev); int minor = (int) MINOR(dev->dev); char path[PATH_MAX]; char temp_path[PATH_MAX]; char buffer[64]; struct stat info; FILE *fp = NULL; int parts, residue, size, ret = 0; /* * Try to get the primary dev out of the * list of known device types first. */ if ((parts = dt->dev_type_array[major].max_partitions) > 1) { if ((residue = minor % parts)) { *result = MKDEV((dev_t)major, (minor - residue)); ret = 2; } else { *result = dev->dev; ret = 1; /* dev is not a partition! */ } goto out; } /* * If we can't get the primary dev out of the list of known device * types, try to look at sysfs directly then. This is more complex * way and it also requires certain sysfs layout to be present * which might not be there in old kernels! */ /* check if dev is a partition */ if (dm_snprintf(path, sizeof(path), "%s/dev/block/%d:%d/partition", sysfs_dir, major, minor) < 0) { log_error("dm_snprintf partition failed"); goto out; } if (stat(path, &info) == -1) { if (errno != ENOENT) log_sys_error("stat", path); *result = dev->dev; ret = 1; goto out; /* dev is not a partition! */ } /* * extract parent's path from the partition's symlink, e.g.: * - readlink /sys/dev/block/259:0 = ../../block/md0/md0p1 * - dirname ../../block/md0/md0p1 = ../../block/md0 * - basename ../../block/md0/md0 = md0 * Parent's 'dev' sysfs attribute = /sys/block/md0/dev */ if ((size = readlink(dirname(path), temp_path, sizeof(temp_path) - 1)) < 0) { log_sys_error("readlink", path); goto out; } temp_path[size] = '\0'; if (dm_snprintf(path, sizeof(path), "%s/block/%s/dev", sysfs_dir, basename(dirname(temp_path))) < 0) { log_error("dm_snprintf dev failed"); goto out; } /* finally, parse 'dev' attribute and create corresponding dev_t */ if (stat(path, &info) == -1) { if (errno == ENOENT) log_error("sysfs file %s does not exist", path); else log_sys_error("stat", path); goto out; } fp = fopen(path, "r"); if (!fp) { log_sys_error("fopen", path); goto out; } if (!fgets(buffer, sizeof(buffer), fp)) { log_sys_error("fgets", path); goto out; } if (sscanf(buffer, "%d:%d", &major, &minor) != 2) { log_error("sysfs file %s not in expected MAJ:MIN format: %s", path, buffer); goto out; } *result = MKDEV((dev_t)major, minor); ret = 2; out: if (fp && fclose(fp)) log_sys_error("fclose", path); return ret; }