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>
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+
27322924static 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