]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
common/blkdev: get_block_device_base
authorSage Weil <sage@redhat.com>
Thu, 11 Dec 2014 17:50:01 +0000 (09:50 -0800)
committerSage Weil <sage@redhat.com>
Thu, 11 Dec 2014 18:18:56 +0000 (10:18 -0800)
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 <sage@redhat.com>
src/common/blkdev.cc
src/common/blkdev.h
src/test/Makefile.am
src/test/common/test_blkdev.cc [new file with mode: 0644]

index 4846463962e777d4e15a7df7a899c24adb14f458..bff23cee804fb76479a33c5070607bb2c4bde061 100644 (file)
@@ -1,9 +1,11 @@
 #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__
@@ -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<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;
index 5606205cc32778e43224d6e590357fbda3ce1e01..2f76d40d53154fbd2cdf45ee1ddcef921a122fe9 100644 (file)
@@ -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);
index 9ade407730234a5c57dbf440c9aac1965087b7ba..738dc8e58a33687574bda1fc00a04bf92020c800 100644 (file)
@@ -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 (file)
index 0000000..2eb8750
--- /dev/null
@@ -0,0 +1,70 @@
+// -*- 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);
+}