Skip to content

Commit b666e69

Browse files
committed
Fix creating dm devices when there are mounted partitions on the disk.
The idea is to use LDM protective partition block devices for dm calls instead of the whole disk block device. This allows scenarios like booting Linux from LDM, when a separate protective partition is created exactly over the rootfs LDM partition, which allows mounting it directly, i.e. without ldmtool. Originally, it then prevents using ldmtool after such direct mount. This change fixes that.
1 parent 1eafb65 commit b666e69

1 file changed

Lines changed: 196 additions & 8 deletions

File tree

src/ldm.c

Lines changed: 196 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717

1818
#include <config.h>
1919

20+
#include <dirent.h>
2021
#include <endian.h>
2122
#include <errno.h>
2223
#include <fcntl.h>
24+
#include <glib/gstdio.h>
2325
#include <inttypes.h>
2426
#include <libdevmapper.h>
2527
#include <linux/fs.h>
@@ -39,6 +41,15 @@
3941
#include "gpt.h"
4042
#include "ldm.h"
4143

44+
#ifndef G_DEFINE_AUTOPTR_CLEANUP_FUNC_FILE_CLOSER
45+
#define G_DEFINE_AUTOPTR_CLEANUP_FUNC_FILE_CLOSER
46+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(FILE, fclose)
47+
#endif
48+
#ifndef G_DEFINE_AUTOPTR_CLEANUP_FUNC_DIR_CLOSER
49+
#define G_DEFINE_AUTOPTR_CLEANUP_FUNC_DIR_CLOSER
50+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(DIR, closedir)
51+
#endif
52+
4253
#define DM_UUID_PREFIX "LDM-"
4354

4455
#define UUID_FMT "%02x%02x%02x%02x-%02x%02x-%02x%02x-" \
@@ -2729,6 +2740,187 @@ _dm_remove(const gchar * const name, uint32_t udev_cookie, GError ** const err)
27292740
return r;
27302741
}
27312742

2743+
static int _find_partition(GString * target_params, const gchar * dev_name,
2744+
guint64 abs_part_start, guint64 part_size,
2745+
GError ** const err)
2746+
{
2747+
g_autofree gchar * base_name = g_path_get_basename(dev_name);
2748+
if (!base_name || strlen(base_name) == 0) {
2749+
g_set_error(err, LDM_ERROR, LDM_ERROR_INTERNAL,
2750+
"Unable to obtain base name for %s", dev_name);
2751+
return -1;
2752+
}
2753+
2754+
g_autofree gchar * sysfs_path = g_strdup_printf("/sys/block/%s",
2755+
base_name);
2756+
if (!sysfs_path) {
2757+
g_set_error(err, LDM_ERROR, LDM_ERROR_INTERNAL,
2758+
"Unable to create sysfs path for %s", base_name);
2759+
return -1;
2760+
}
2761+
2762+
g_autoptr(DIR) dir = opendir(sysfs_path);
2763+
if (!dir) {
2764+
g_set_error(err, LDM_ERROR, LDM_ERROR_IO,
2765+
"Unable to open dir %s", sysfs_path);
2766+
return -1;
2767+
}
2768+
2769+
struct dirent * entry;
2770+
int found = 0;
2771+
while (errno = 0, (entry = readdir(dir)) != NULL && !found) {
2772+
if (strcmp(entry->d_name, ".") == 0 ||
2773+
strcmp(entry->d_name, "..") == 0) {
2774+
continue;
2775+
}
2776+
2777+
g_autofree gchar * partition_dir_path = g_build_filename(sysfs_path,
2778+
entry->d_name,
2779+
NULL);
2780+
if (!partition_dir_path)
2781+
{
2782+
g_warning("Unable to build partition dir for %s/%s", sysfs_path,
2783+
entry->d_name);
2784+
continue;
2785+
}
2786+
2787+
g_autofree gchar * partition_file_path = g_build_filename(
2788+
partition_dir_path, "partition", NULL);
2789+
g_autofree gchar * start_file_path = g_build_filename(
2790+
partition_dir_path, "start", NULL);
2791+
g_autofree gchar * size_file_path = g_build_filename(
2792+
partition_dir_path, "size", NULL);
2793+
2794+
if (!partition_file_path)
2795+
{
2796+
g_warning("Unable to build partition file path for %s",
2797+
partition_dir_path);
2798+
continue;
2799+
}
2800+
if (!start_file_path)
2801+
{
2802+
g_warning("Unable to build start file path for %s",
2803+
partition_dir_path);
2804+
continue;
2805+
}
2806+
if (!size_file_path)
2807+
{
2808+
g_warning("Unable to build size file path for %s",
2809+
partition_dir_path);
2810+
continue;
2811+
}
2812+
2813+
if (g_access(partition_file_path, F_OK) != 0) {
2814+
/* no warning - this is a check that filters out non-partition
2815+
subdirs */
2816+
continue;
2817+
}
2818+
if (g_access(start_file_path, F_OK) != 0) {
2819+
g_warning("Unable to access %s", start_file_path);
2820+
continue;
2821+
}
2822+
if (g_access(size_file_path, F_OK) != 0) {
2823+
g_warning("Unable to access %s", size_file_path);
2824+
continue;
2825+
}
2826+
2827+
/* Check if the 'partition' file exists within this subdirectory
2828+
This is a filter against other subdirectories that do not
2829+
refer to partitions */
2830+
2831+
g_autoptr(FILE) fstart = fopen(start_file_path, "r");
2832+
if (!fstart)
2833+
{
2834+
g_warning("Unable to open %s", start_file_path);
2835+
continue;
2836+
}
2837+
2838+
guint64 curr_part_start = 0;
2839+
if (fscanf(fstart, "%" PRIu64, &curr_part_start) != 1) {
2840+
g_warning("Unable to parse %s", start_file_path);
2841+
continue;
2842+
}
2843+
2844+
g_autoptr(FILE) fsize = fopen(size_file_path, "r");
2845+
if (!fsize)
2846+
{
2847+
g_warning("Unable to open %s", size_file_path);
2848+
continue;
2849+
}
2850+
2851+
guint64 curr_part_size = 0;
2852+
if (fscanf(fsize, "%" PRIu64, &curr_part_size) != 1) {
2853+
g_warning("Unable to parse %s", size_file_path);
2854+
continue;
2855+
}
2856+
2857+
guint64 curr_part_end = curr_part_start + curr_part_size;
2858+
guint64 target_part_end = abs_part_start + part_size;
2859+
2860+
if (abs_part_start >= curr_part_start &&
2861+
target_part_end <= curr_part_end)
2862+
{
2863+
g_autofree gchar * dev_dir = g_path_get_dirname(dev_name);
2864+
if (!dev_dir)
2865+
{
2866+
g_set_error(err, LDM_ERROR, LDM_ERROR_INTERNAL,
2867+
"Unable to extract dir from %s", dev_name);
2868+
return -1;
2869+
}
2870+
2871+
g_autofree gchar * full_part_path = g_build_filename(dev_dir, entry->d_name, NULL);
2872+
if (!full_part_path)
2873+
{
2874+
g_set_error(err, LDM_ERROR, LDM_ERROR_INTERNAL,
2875+
"Unable to build file path for %s/%s", dev_dir,
2876+
entry->d_name);
2877+
return -1;
2878+
}
2879+
2880+
g_string_printf(target_params, "%s %" PRIu64,
2881+
full_part_path,
2882+
abs_part_start - curr_part_start);
2883+
found = 1;
2884+
}
2885+
}
2886+
2887+
if (errno != 0) {
2888+
g_set_error(err, LDM_ERROR, LDM_ERROR_IO,
2889+
"Unable to enumerate drive dir %s", sysfs_path);
2890+
}
2891+
2892+
return found ? 0 : -1;
2893+
}
2894+
2895+
static void _refine_partition(GString* target_params,
2896+
const LDMDiskPrivate * const disk,
2897+
guint64 part_start, guint64 part_size,
2898+
GError ** const err)
2899+
{
2900+
/* The goal here is to try to use partition's block device instead
2901+
of whole disk's device. The problem of using the whole disk is this:
2902+
If LDM disk has at least one partition mounted directly, then it won't
2903+
be possible to create *any* LDM devmapper device using that disk.
2904+
2905+
Mounting LDM partition directly may make sense for cases like Linux
2906+
system booting from LDM. The approach is the following:
2907+
1. First, you need to split GPT/MBR's LDM protective partition into a few
2908+
separate protective partitions, so that your target LDM partition is
2909+
exactly matched by one protective partition.
2910+
2. Then you'll be able to mount it as usual, boot from it and so on.
2911+
Windows actually does this for its C:\ partition is the respective
2912+
disk is converted to LDM. */
2913+
2914+
if (_find_partition(target_params, disk->device,
2915+
disk->data_start + part_start, part_size, err) == 0) {
2916+
return;
2917+
}
2918+
2919+
/* fallback */
2920+
g_string_printf(target_params, "%s %" PRIu64,
2921+
disk->device, disk->data_start + part_start);
2922+
}
2923+
27322924
static GString *
27332925
_dm_create_part(const LDMPartitionPrivate * const part, uint32_t cookie,
27342926
GError ** const err)
@@ -2747,8 +2939,7 @@ _dm_create_part(const LDMPartitionPrivate * const part, uint32_t cookie,
27472939
target.size = part->size;
27482940
target.type = "linear";
27492941
target.params = g_string_new("");
2750-
g_string_printf(target.params, "%s %" PRIu64,
2751-
disk->device, disk->data_start + part->start);
2942+
_refine_partition(target.params, disk, part->start, part->size, err);
27522943

27532944
GString *name = _dm_part_name(part);
27542945
GString *uuid = _dm_part_uuid(part);
@@ -2817,9 +3008,8 @@ _dm_create_spanned(const LDMVolumePrivate * const vol, GError ** const err)
28173008
target->size = part->size;
28183009
target->type = "linear";
28193010
target->params = g_string_new("");
2820-
g_string_append_printf(target->params, "%s %" PRIu64,
2821-
disk->device,
2822-
disk->data_start + part->start);
3011+
_refine_partition(target->params, disk, part->start, part->size, err);
3012+
28233013
pos += part->size;
28243014
}
28253015

@@ -2878,9 +3068,7 @@ _dm_create_striped(const LDMVolumePrivate * const vol, GError ** const err)
28783068
goto out;
28793069
}
28803070

2881-
g_string_append_printf(target.params, " %s %" PRIu64,
2882-
disk->device,
2883-
disk->data_start + part->start);
3071+
_refine_partition(target.params, disk, part->start, part->size, err);
28843072
}
28853073

28863074
uint32_t cookie;

0 commit comments

Comments
 (0)