Skip to content

Commit c2536a2

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 c2536a2

1 file changed

Lines changed: 119 additions & 8 deletions

File tree

src/ldm.c

Lines changed: 119 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2729,6 +2729,121 @@ _dm_remove(const gchar * const name, uint32_t udev_cookie, GError ** const err)
27292729
return r;
27302730
}
27312731

2732+
static int _find_partition(GString* target_params,
2733+
const gchar* dev_name, guint64 abs_part_start, guint64 part_size)
2734+
{
2735+
char *dev_name_copy = strdup(dev_name); // basename might modify its arg
2736+
if (!dev_name_copy) {
2737+
return -1;
2738+
}
2739+
char *base_name = basename(dev_name_copy);
2740+
if (!base_name || strlen(base_name) == 0) {
2741+
free(dev_name_copy);
2742+
return -1;
2743+
}
2744+
2745+
char sysfs_path[PATH_MAX];
2746+
snprintf(sysfs_path, sizeof(sysfs_path), "/sys/block/%s", base_name);
2747+
2748+
DIR *dir = opendir(sysfs_path);
2749+
if (!dir) {
2750+
free(dev_name_copy);
2751+
return -1;
2752+
}
2753+
2754+
struct dirent *entry;
2755+
int found = 0;
2756+
while ((entry = readdir(dir)) != NULL && !found) {
2757+
if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) {
2758+
continue;
2759+
}
2760+
2761+
char partition_file_path[PATH_MAX];
2762+
if (snprintf(partition_file_path, sizeof(partition_file_path),
2763+
"%s/%s/partition", sysfs_path, entry->d_name) < 0)
2764+
continue;
2765+
2766+
char start_file_path[PATH_MAX];
2767+
if (snprintf(start_file_path, sizeof(start_file_path), "%s/%s/start",
2768+
sysfs_path, entry->d_name) < 0)
2769+
continue;
2770+
2771+
char size_file_path[PATH_MAX];
2772+
if (snprintf(size_file_path, sizeof(size_file_path), "%s/%s/size",
2773+
sysfs_path, entry->d_name) < 0)
2774+
continue;
2775+
2776+
// Check if the 'partition' file exists within this subdirectory
2777+
// This is a filter against other subdirectories that do not
2778+
// refer to partitions
2779+
if (access(partition_file_path, F_OK) == 0 &&
2780+
access(start_file_path, F_OK) == 0 &&
2781+
access(size_file_path, F_OK) == 0) {
2782+
2783+
FILE *fstart = fopen(start_file_path, "r");
2784+
if (fstart) {
2785+
guint64 curr_part_start = 0;
2786+
if (fscanf(fstart, "%" PRIu64, &curr_part_start) == 1) {
2787+
FILE *fsize = fopen(size_file_path, "r");
2788+
if (fsize) {
2789+
guint64 curr_part_size = 0;
2790+
if (fscanf(fsize, "%" PRIu64, &curr_part_size) == 1) {
2791+
if (abs_part_start >= curr_part_start &&
2792+
abs_part_start + part_size <=
2793+
curr_part_start + curr_part_size)
2794+
{
2795+
char *dir_name = strdup(dev_name);
2796+
int last_slash = strlen(dev_name) - strlen(base_name);
2797+
dir_name[last_slash] = '\0';
2798+
2799+
g_string_printf(target_params, "%s%s %" PRIu64,
2800+
dir_name, entry->d_name,
2801+
abs_part_start - curr_part_start);
2802+
found = 1;
2803+
free(dir_name);
2804+
}
2805+
}
2806+
fclose(fsize);
2807+
}
2808+
}
2809+
fclose(fstart);
2810+
}
2811+
}
2812+
}
2813+
2814+
closedir(dir);
2815+
free(dev_name_copy);
2816+
2817+
return found ? 0 : -1;
2818+
}
2819+
2820+
static void _refine_partition(GString* target_params,
2821+
const LDMDiskPrivate * const disk, guint64 part_start, guint64 part_size)
2822+
{
2823+
// The goal here is to try to use partition's block device instead
2824+
// of whole disk's device. The problem of using the whole disk is this:
2825+
// If LDM disk has at least one partition mounted directly, then it won't
2826+
// be possible to create *any* LDM devmapper device using that disk.
2827+
//
2828+
// Mounting LDM partition directly may make sense for cases like Linux
2829+
// system booting from LDM. The approach is the following:
2830+
// 1. First, you need to split GPT/MBR's LDM protective partition into a few
2831+
// separate protective partitions, so that your target LDM partition is
2832+
// exactly matched by one protective partition.
2833+
// 2. Then you'll be able to mount it as usual, boot from it and so on.
2834+
// Windows actually does this for its C:\ partition is the respective
2835+
// disk is converted to LDM.
2836+
2837+
if (_find_partition(target_params, disk->device,
2838+
disk->data_start + part_start, part_size) == 0) {
2839+
return;
2840+
}
2841+
2842+
// fallback
2843+
g_string_printf(target_params, "%s %" PRIu64,
2844+
disk->device, disk->data_start + part_start);
2845+
}
2846+
27322847
static GString *
27332848
_dm_create_part(const LDMPartitionPrivate * const part, uint32_t cookie,
27342849
GError ** const err)
@@ -2747,8 +2862,7 @@ _dm_create_part(const LDMPartitionPrivate * const part, uint32_t cookie,
27472862
target.size = part->size;
27482863
target.type = "linear";
27492864
target.params = g_string_new("");
2750-
g_string_printf(target.params, "%s %" PRIu64,
2751-
disk->device, disk->data_start + part->start);
2865+
_refine_partition(target.params, disk, part->start, part->size);
27522866

27532867
GString *name = _dm_part_name(part);
27542868
GString *uuid = _dm_part_uuid(part);
@@ -2817,9 +2931,8 @@ _dm_create_spanned(const LDMVolumePrivate * const vol, GError ** const err)
28172931
target->size = part->size;
28182932
target->type = "linear";
28192933
target->params = g_string_new("");
2820-
g_string_append_printf(target->params, "%s %" PRIu64,
2821-
disk->device,
2822-
disk->data_start + part->start);
2934+
_refine_partition(target->params, disk, part->start, part->size);
2935+
28232936
pos += part->size;
28242937
}
28252938

@@ -2878,9 +2991,7 @@ _dm_create_striped(const LDMVolumePrivate * const vol, GError ** const err)
28782991
goto out;
28792992
}
28802993

2881-
g_string_append_printf(target.params, " %s %" PRIu64,
2882-
disk->device,
2883-
disk->data_start + part->start);
2994+
_refine_partition(target.params, disk, part->start, part->size);
28842995
}
28852996

28862997
uint32_t cookie;

0 commit comments

Comments
 (0)