Skip to content

Commit

Permalink
common/blkdev: get_block_device_base
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
liewegas committed Dec 11, 2014
1 parent 8b195ec commit 25e3783
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 0 deletions.
69 changes: 69 additions & 0 deletions src/common/blkdev.cc
@@ -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__
Expand All @@ -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;
Expand Down
1 change: 1 addition & 0 deletions 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);
Expand Down
5 changes: 5 additions & 0 deletions src/test/Makefile.am
Expand Up @@ -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)
Expand Down
70 changes: 70 additions & 0 deletions 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 <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);
}

0 comments on commit 25e3783

Please sign in to comment.