/* Sometimes libparted operates on device mapper files, with a path of /dev/mapper/foo. With newer lvm versions /dev/mapper/foo is a symlink to /dev/dm-#. However some storage administration programs (anaconda for example) may do the following: 1) Create a ped_device for /dev/mapper/foo 2) ped_get_device resolves the symlink to /dev/dm-#, and the path in the PedDevice struct points to /dev/dm-# 3) The program does some things to lvm, causing the symlink to point to a different /dev/dm-# node 4) The program does something with the PedDevice, which results in an operation on the wrong device Newer libparted versions do not suffer from this problem, as they do not canonicalize device names under /dev/mapper. This test checks for this bug. */ #include #include #include #include #include "common.h" #include "progname.h" static char *temporary_disk; static void create_disk (void) { temporary_disk = _create_disk (4096 * 1024); ck_assert_msg(temporary_disk != NULL, "Failed to create temporary disk"); } static void destroy_disk (void) { unlink (temporary_disk); free (temporary_disk); } START_TEST (test_symlink) { char cwd[256], ln[256] = "/dev/mapper/parted-test-XXXXXX"; if (!getcwd (cwd, sizeof cwd)) { ck_abort_msg("Could not get cwd"); return; } /* Create a symlink under /dev/mapper to our temporary disk */ int tmp_fd = mkstemp (ln); if (tmp_fd == -1) { ck_abort_msg("Could not create tempfile"); return; } /* There is a temp file vulnerability symlink attack possibility here, but as /dev/mapper is root owned this is a non issue */ close (tmp_fd); unlink (ln); char temp_disk_path[256]; int r = snprintf(temp_disk_path, sizeof temp_disk_path, "%s/%s", cwd, temporary_disk); if (r < 0 || r >= sizeof temp_disk_path) { ck_abort_msg("symlink truncated"); return; } int res = symlink (temp_disk_path, ln); if (res) { ck_abort_msg("could not create symlink"); return; } PedDevice *dev = ped_device_get (ln); if (dev == NULL) goto exit_unlink_ln; /* Create a second temporary_disk */ char *temporary_disk2 = _create_disk (4096 * 1024); if (temporary_disk2 == NULL) { ck_abort_msg("Failed to create 2nd temporary disk"); goto exit_destroy_dev; } /* Remove first temporary disk, and make temporary_disk point to temporary_disk2 (for destroy_disk()). */ unlink (temporary_disk); free (temporary_disk); temporary_disk = temporary_disk2; /* Update symlink to point to our new / second temporary disk */ unlink (ln); r = snprintf (temp_disk_path, sizeof temp_disk_path, "%s/%s", cwd, temporary_disk); if (r < 0 || r >= sizeof temp_disk_path) { ck_abort_msg("2nd symlink truncated"); goto exit_destroy_dev; } res = symlink (temp_disk_path, ln); if (res) { ck_abort_msg("could not create 2nd symlink"); goto exit_destroy_dev; } /* Do something to our PedDevice, if the symlink was resolved, instead of remembering the /dev/mapper/foo name, this will fail */ ped_disk_clobber (dev); exit_destroy_dev: ped_device_destroy (dev); exit_unlink_ln: unlink (ln); } END_TEST int main (int argc, char **argv) { set_program_name (argv[0]); int number_failed; Suite* suite = suite_create ("Symlink"); TCase* tcase_symlink = tcase_create ("/dev/mapper symlink"); /* Fail when an exception is raised */ ped_exception_set_handler (_test_exception_handler); tcase_add_checked_fixture (tcase_symlink, create_disk, destroy_disk); tcase_add_test (tcase_symlink, test_symlink); /* Disable timeout for this test */ tcase_set_timeout (tcase_symlink, 0); suite_add_tcase (suite, tcase_symlink); SRunner* srunner = srunner_create (suite); srunner_run_all (srunner, CK_VERBOSE); number_failed = srunner_ntests_failed (srunner); srunner_free (srunner); return (number_failed == 0) ? EXIT_SUCCESS : EXIT_FAILURE; }