From: Sage Weil Date: Thu, 11 Dec 2014 17:50:01 +0000 (-0800) Subject: common/blkdev: get_block_device_base X-Git-Tag: v0.90~2^2~3 X-Git-Url: http://git.apps.os.sepia.ceph.com/?a=commitdiff_plain;h=25e37837c363619bc3f39b55fb2dc270b231fea6;p=ceph.git common/blkdev: get_block_device_base And a test. Ideally the test would get run on a machine with weirdo devices like /dev/cciss/c0d1. This mirrors the logic in ceph-disk, which has been tested. Signed-off-by: Sage Weil --- diff --git a/src/common/blkdev.cc b/src/common/blkdev.cc index 4846463962e77..bff23cee804fb 100644 --- a/src/common/blkdev.cc +++ b/src/common/blkdev.cc @@ -1,9 +1,11 @@ #include #include #include +#include #include #include #include +#include #include "include/int_types.h" #ifdef __linux__ @@ -25,6 +27,73 @@ int get_block_device_size(int fd, int64_t *psize) return ret; } +/** + * get the base device (strip off partition suffix and /dev/ prefix) + * e.g., + * /dev/sda3 -> sda + * /dev/cciss/c0d1p2 -> cciss/c0d1 + */ +int get_block_device_base(const char *dev, char *out, size_t out_len) +{ + struct stat st; + int r = 0; + char buf[PATH_MAX*2]; + struct dirent *de; + DIR *dir; + char devname[PATH_MAX], fn[PATH_MAX]; + char *p; + + if (strncmp(dev, "/dev/", 5) != 0) + return -EINVAL; + + strcpy(devname, dev + 5); + for (p = devname; *p; ++p) + if (*p == '/') + *p = '!'; + + snprintf(fn, sizeof(fn), "/sys/block/%s", devname); + if (stat(fn, &st) == 0) { + if (strlen(devname) + 1 > out_len) { + return -ERANGE; + } + strncpy(out, devname, out_len); + return 0; + } + + dir = opendir("/sys/block"); + if (!dir) + return -errno; + + while (!::readdir_r(dir, reinterpret_cast(buf), &de)) { + if (!de) { + if (errno) { + r = -errno; + goto out; + } + break; + } + if (de->d_name[0] == '.') + continue; + snprintf(fn, sizeof(fn), "/sys/block/%s/%s", de->d_name, devname); + + if (stat(fn, &st) == 0) { + // match! + if (strlen(de->d_name) + 1 > out_len) { + r = -ERANGE; + goto out; + } + strncpy(out, de->d_name, out_len); + r = 0; + goto out; + } + } + r = -ENOENT; + + out: + closedir(dir); + return r; +} + bool block_device_support_discard(const char *devname) { bool can_trim = false; diff --git a/src/common/blkdev.h b/src/common/blkdev.h index 5606205cc3277..2f76d40d53154 100644 --- a/src/common/blkdev.h +++ b/src/common/blkdev.h @@ -1,6 +1,7 @@ #ifndef __CEPH_COMMON_BLKDEV_H #define __CEPH_COMMON_BLKDEV_H +extern int get_block_device_base(const char *dev, char *out, size_t out_len); extern int get_block_device_size(int fd, int64_t *psize); extern bool block_device_support_discard(const char *devname); extern int block_device_discard(int fd, int64_t offset, int64_t len); diff --git a/src/test/Makefile.am b/src/test/Makefile.am index 9ade407730234..738dc8e58a336 100644 --- a/src/test/Makefile.am +++ b/src/test/Makefile.am @@ -286,6 +286,11 @@ unittest_addrs_CXXFLAGS = $(UNITTEST_CXXFLAGS) unittest_addrs_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) check_PROGRAMS += unittest_addrs +unittest_blkdev_SOURCES = test/common/test_blkdev.cc +unittest_blkdev_CXXFLAGS = $(UNITTEST_CXXFLAGS) +unittest_blkdev_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) +check_PROGRAMS += unittest_blkdev + unittest_bloom_filter_SOURCES = test/common/test_bloom_filter.cc unittest_bloom_filter_CXXFLAGS = $(UNITTEST_CXXFLAGS) unittest_bloom_filter_LDADD = $(UNITTEST_LDADD) $(CEPH_GLOBAL) diff --git a/src/test/common/test_blkdev.cc b/src/test/common/test_blkdev.cc new file mode 100644 index 0000000000000..2eb8750d72dbc --- /dev/null +++ b/src/test/common/test_blkdev.cc @@ -0,0 +1,70 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include +#include +#include + +#include "include/types.h" +#include "common/blkdev.h" + +#include "gtest/gtest.h" + +TEST(blkdev, get_block_device_base) { + char buf[PATH_MAX*2]; + char buf2[PATH_MAX*2]; + char buf3[PATH_MAX*2]; + struct dirent *de, *de2; + + ASSERT_EQ(-ERANGE, get_block_device_base("/dev/sda1", buf, 1)); + + // work backwards + DIR *dir = opendir("/sys/block"); + ASSERT_TRUE(dir); + while (!::readdir_r(dir, reinterpret_cast(buf), &de)) { + if (!de) + break; + if (de->d_name[0] == '.') + continue; + + char base[PATH_MAX]; + sprintf(base, "/dev/%s", de->d_name); + printf("base %s (%s)\n", base, de->d_name); + for (char *p = base; *p; ++p) + if (*p == '!') + *p = '/'; + + ASSERT_EQ(0, get_block_device_base(base, buf3, sizeof(buf3))); + printf(" got '%s' expected '%s'\n", buf3, de->d_name); + ASSERT_EQ(0, strcmp(de->d_name, buf3)); + + char subdirfn[PATH_MAX]; + sprintf(subdirfn, "/sys/block/%s", de->d_name); + DIR *subdir = opendir(subdirfn); + ASSERT_TRUE(subdir); + while (!::readdir_r(subdir, reinterpret_cast(buf2), &de2)) { + if (!de2) + break; + if (de2->d_name[0] == '.') + continue; + // partiions will be prefixed with the base name + if (strncmp(de2->d_name, de->d_name, strlen(de->d_name))) { + //printf("skipping %s\n", de2->d_name); + continue; + } + char part[PATH_MAX]; + sprintf(part, "/dev/%s", de2->d_name); + for (char *p = part; *p; ++p) + if (*p == '!') + *p = '/'; + printf(" part %s (%s %s)\n", part, de->d_name, de2->d_name); + + ASSERT_EQ(0, get_block_device_base(part, buf3, sizeof(buf3))); + printf(" got '%s' expected '%s'\n", buf3, de->d_name); + ASSERT_EQ(0, strcmp(buf3, de->d_name)); + } + + closedir(subdir); + } + closedir(dir); +}