#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
+#include <dirent.h>
#include "include/int_types.h"
#ifdef __linux__
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<struct dirent*>(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;
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)
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+#include <iostream>
+#include <string.h>
+#include <errno.h>
+
+#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<struct dirent*>(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<struct dirent*>(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);
+}