Skip to content

Commit eb00889

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 eb00889

1 file changed

Lines changed: 122 additions & 8 deletions

File tree

src/ldm.c

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

27532870
GString *name = _dm_part_name(part);
27542871
GString *uuid = _dm_part_uuid(part);
@@ -2817,9 +2934,8 @@ _dm_create_spanned(const LDMVolumePrivate * const vol, GError ** const err)
28172934
target->size = part->size;
28182935
target->type = "linear";
28192936
target->params = g_string_new("");
2820-
g_string_append_printf(target->params, "%s %" PRIu64,
2821-
disk->device,
2822-
disk->data_start + part->start);
2937+
_refine_partition(target->params, disk, part->start, part->size);
2938+
28232939
pos += part->size;
28242940
}
28252941

@@ -2878,9 +2994,7 @@ _dm_create_striped(const LDMVolumePrivate * const vol, GError ** const err)
28782994
goto out;
28792995
}
28802996

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

28863000
uint32_t cookie;

0 commit comments

Comments
 (0)