Skip to content

Commit f8733f2

Browse files
committed
utils: Check if resolved path in bd_utils_resolve_device is device
No point in trying to return something that isn't actually a block device, like for example directories in /dev.
1 parent 1fbf7a8 commit f8733f2

File tree

3 files changed

+88
-52
lines changed

3 files changed

+88
-52
lines changed

src/utils/dev_utils.c

Lines changed: 34 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@
1919

2020
#include <glib.h>
2121
#include <libudev.h>
22+
#include <sys/stat.h>
23+
#include <errno.h>
2224

2325
#include "dev_utils.h"
2426

@@ -30,6 +32,24 @@ GQuark bd_utils_dev_utils_error_quark (void)
3032
return g_quark_from_static_string ("g-bd-utils-dev_utils-error-quark");
3133
}
3234

35+
static gboolean _is_block_device (const gchar *path, GError **error) {
36+
struct stat sb;
37+
38+
if (stat (path, &sb) != 0) {
39+
g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
40+
"Failed to stat '%s': %s", path, strerror_l (errno, _C_LOCALE));
41+
return FALSE;
42+
}
43+
44+
if (!S_ISBLK (sb.st_mode)) {
45+
g_set_error (error, BD_UTILS_DEV_UTILS_ERROR, BD_UTILS_DEV_UTILS_ERROR_FAILED,
46+
"'%s' is not a block device", path);
47+
return FALSE;
48+
}
49+
50+
return TRUE;
51+
}
52+
3353
/**
3454
* bd_utils_resolve_device:
3555
* @dev_spec: specification of the device (e.g. "/dev/sda", any symlink, or the name of a file
@@ -40,12 +60,10 @@ GQuark bd_utils_dev_utils_error_quark (void)
4060
* for "/dev/md/my_raid") or %NULL in case of error
4161
*/
4262
gchar* bd_utils_resolve_device (const gchar *dev_spec, GError **error) {
43-
gchar *path = NULL;
44-
gchar *symlink = NULL;
63+
g_autofree gchar *path = NULL;
64+
g_autofree gchar *symlink = NULL;
4565
GError *l_error = NULL;
4666

47-
/* TODO: check that the resulting path is a block device? */
48-
4967
if (!g_str_has_prefix (dev_spec, "/dev/"))
5068
path = g_strdup_printf ("/dev/%s", dev_spec);
5169
else
@@ -56,11 +74,15 @@ gchar* bd_utils_resolve_device (const gchar *dev_spec, GError **error) {
5674
if (g_error_matches (l_error, G_FILE_ERROR, G_FILE_ERROR_INVAL)) {
5775
/* invalid argument -> not a symlink -> nothing to resolve */
5876
g_clear_error (&l_error);
59-
return path;
77+
if (_is_block_device (path, error))
78+
return g_steal_pointer (&path);
79+
else {
80+
g_prefix_error (error, "Failed to resolve '%s': ", dev_spec);
81+
return NULL;
82+
}
6083
} else {
6184
/* some other error, just report it */
6285
g_propagate_error (error, l_error);
63-
g_free (path);
6486
return NULL;
6587
}
6688
}
@@ -70,9 +92,13 @@ gchar* bd_utils_resolve_device (const gchar *dev_spec, GError **error) {
7092
path = g_strdup_printf ("/dev/%s", symlink + 3);
7193
else
7294
path = g_strdup_printf ("/dev/%s", symlink);
73-
g_free (symlink);
7495

75-
return path;
96+
if (_is_block_device (path, error))
97+
return g_steal_pointer (&path);
98+
else {
99+
g_prefix_error (error, "Failed to resolve '%s': ", dev_spec);
100+
return NULL;
101+
}
76102
}
77103

78104
/**

src/utils/dev_utils.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@
2525
GQuark bd_utils_dev_utils_error_quark (void);
2626
#define BD_UTILS_DEV_UTILS_ERROR bd_utils_dev_utils_error_quark ()
2727

28+
/* "C" locale to get the locale-agnostic error messages */
29+
#define _C_LOCALE (locale_t) 0
30+
2831
typedef enum {
2932
BD_UTILS_DEV_UTILS_ERROR_FAILED,
3033
} BDUtilsDevUtilsError;

tests/utils_test.py

Lines changed: 51 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -406,57 +406,74 @@ def test_exec_large_input(self):
406406
self.assertTrue(status)
407407

408408

409-
class UtilsDevUtilsTestCase(UtilsTestCase):
410-
@tag_test(TestTags.NOSTORAGE, TestTags.CORE)
409+
class UtilsStorageTestCase(UtilsTestCase):
410+
def setUp(self):
411+
self.addCleanup(self._clean_up)
412+
self.dev_file = create_sparse_tempfile("utils_test", 1024**3)
413+
try:
414+
self.loop_dev = create_lio_device(self.dev_file)
415+
except RuntimeError as e:
416+
raise RuntimeError("Failed to setup loop device for testing: %s" % e)
417+
418+
def _clean_up(self):
419+
try:
420+
delete_lio_device(self.loop_dev)
421+
except RuntimeError:
422+
# just move on, we can do no better here
423+
pass
424+
os.unlink(self.dev_file)
425+
426+
def _setup_lvm(self):
427+
ret, _out, _err = run_command ("pvcreate %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
428+
self.assertEqual(ret, 0)
429+
self.addCleanup(run_command, "pvremove %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
430+
431+
ret, _out, _err = run_command ("vgcreate utilsTestVG %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
432+
self.assertEqual(ret, 0)
433+
self.addCleanup(run_command, "vgremove -y utilsTestVG --config \"devices {use_devicesfile = 0}\"")
434+
435+
ret, _out, _err = run_command ("lvcreate -n utilsTestLV -L 12M utilsTestVG --config \"devices {use_devicesfile = 0}\"")
436+
self.assertEqual(ret, 0)
437+
self.addCleanup(run_command, "lvremove -y utilsTestVG/utilsTestLV --config \"devices {use_devicesfile = 0}\"")
438+
439+
440+
class UtilsDevUtilsTestCase(UtilsStorageTestCase):
441+
@tag_test(TestTags.CORE)
411442
def test_resolve_device(self):
412443
"""Verify that resolving device spec works as expected"""
413444

414445
with self.assertRaises(GLib.GError):
415446
BlockDev.utils_resolve_device("no_such_device")
416447

417-
dev = "/dev/libblockdev-test-dev"
418-
with open(dev, "w"):
419-
pass
420-
self.addCleanup(os.unlink, dev)
448+
self._setup_lvm()
449+
dev_link = "/dev/mapper/utilsTestVG-utilsTestLV"
450+
dev_link2 = "/dev/utilsTestVG/utilsTestLV"
451+
dev_node = os.path.realpath(dev_link)
421452

422453
# full path, no symlink, should just return the same
423-
self.assertEqual(BlockDev.utils_resolve_device(dev), dev)
454+
self.assertEqual(BlockDev.utils_resolve_device(dev_node), dev_node)
424455

425456
# just the name of the device, should return the full path
426-
self.assertEqual(BlockDev.utils_resolve_device(dev[5:]), dev)
427-
428-
dev_dir = "/dev/libblockdev-test-dir"
429-
os.mkdir(dev_dir)
430-
self.addCleanup(os.rmdir, dev_dir)
431-
432-
dev_link = dev_dir + "/test-dev-link"
433-
os.symlink("../" + dev[5:], dev_link)
434-
self.addCleanup(os.unlink, dev_link)
457+
self.assertEqual(BlockDev.utils_resolve_device(dev_node[5:]), dev_node)
435458

436459
# should resolve the symlink
437-
self.assertEqual(BlockDev.utils_resolve_device(dev_link), dev)
460+
self.assertEqual(BlockDev.utils_resolve_device(dev_link), dev_node)
438461

439462
# should resolve the symlink even without the "/dev" prefix
440-
self.assertEqual(BlockDev.utils_resolve_device(dev_link[5:]), dev)
463+
self.assertEqual(BlockDev.utils_resolve_device(dev_link[5:]), dev_node)
441464

465+
# should resolve the other symlink too
466+
self.assertEqual(BlockDev.utils_resolve_device(dev_link2), dev_node)
442467

443-
class UtilsDevUtilsSymlinksTestCase(UtilsTestCase):
444-
def setUp(self):
445-
self.addCleanup(self._clean_up)
446-
self.dev_file = create_sparse_tempfile("lvm_test", 1024**3)
447-
try:
448-
self.loop_dev = create_lio_device(self.dev_file)
449-
except RuntimeError as e:
450-
raise RuntimeError("Failed to setup loop device for testing: %s" % e)
468+
# should resolve the other symlink even without the "/dev" prefix
469+
self.assertEqual(BlockDev.utils_resolve_device(dev_link2[5:]), dev_node)
451470

452-
def _clean_up(self):
453-
try:
454-
delete_lio_device(self.loop_dev)
455-
except RuntimeError:
456-
# just move on, we can do no better here
457-
pass
458-
os.unlink(self.dev_file)
471+
# vg is not a block device, shouldn't be resolved
472+
with self.assertRaisesRegex(GLib.GError, "not a block device"):
473+
BlockDev.utils_resolve_device("/dev/utilsTestVG")
459474

475+
476+
class UtilsDevUtilsSymlinksTestCase(UtilsStorageTestCase):
460477
@tag_test(TestTags.CORE)
461478
def test_get_device_symlinks(self):
462479
"""Verify that getting device symlinks works as expected"""
@@ -472,17 +489,7 @@ def test_get_device_symlinks(self):
472489
self.assertGreaterEqual(len(symlinks), 2)
473490

474491
# create an LV to get a device with more symlinks
475-
ret, _out, _err = run_command ("pvcreate %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
476-
self.assertEqual(ret, 0)
477-
self.addCleanup(run_command, "pvremove %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
478-
479-
ret, _out, _err = run_command ("vgcreate utilsTestVG %s --config \"devices {use_devicesfile = 0}\"" % self.loop_dev)
480-
self.assertEqual(ret, 0)
481-
self.addCleanup(run_command, "vgremove -y utilsTestVG --config \"devices {use_devicesfile = 0}\"")
482-
483-
ret, _out, _err = run_command ("lvcreate -n utilsTestLV -L 12M utilsTestVG --config \"devices {use_devicesfile = 0}\"")
484-
self.assertEqual(ret, 0)
485-
self.addCleanup(run_command, "lvremove -y utilsTestVG/utilsTestLV --config \"devices {use_devicesfile = 0}\"")
492+
self._setup_lvm()
486493

487494
symlinks = BlockDev.utils_get_device_symlinks("utilsTestVG/utilsTestLV")
488495
# there should be at least 4 symlinks for an LV

0 commit comments

Comments
 (0)