Go to:
Gentoo Home
Documentation
Forums
Lists
Bugs
Planet
Store
Wiki
Get Gentoo!
Gentoo's Bugzilla – Attachment 74968 Details for
Bug 115875
Use EVMS for cryptsetup-luks DM objects
Home
|
New
–
[Ex]
|
Browse
|
Search
|
Privacy Policy
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
[patch]
portage/sys-fs/evms/files/2.5.4/dev_mapper_input.patch for DM input to EVMS
dev_mapper_input.patch (text/plain), 78.16 KB, created by
Brad Allen
on 2005-12-17 17:08:16 UTC
(
hide
)
Description:
portage/sys-fs/evms/files/2.5.4/dev_mapper_input.patch for DM input to EVMS
Filename:
MIME Type:
Creator:
Brad Allen
Created:
2005-12-17 17:08:16 UTC
Size:
78.16 KB
patch
obsolete
>Comments from original patch for 2.5.3: >from URL http://marc.theaimsgroup.com/?l=evms-devel&m=112887246404441&w=2 > ><head><title>'[Evms-devel] [PATCH] 2.5.3 device-mapper input' - MARC</title></head> ><META NAME="robots" CONTENT="noarchive"> > ><body bgcolor="#000000" text="#ffffff" link="#ffffff" vlink="#ddddff"> ><pre><b>[<a href="?l=evms-devel&m=112853428509330&w=2">prev in list</a>] [<a href="?l=evms-devel&m=112888799008508&w=2">next in list</a>] [<font color="#c0c0c0">prev in thread</font>] [<font color="#c0c0c0">next in thread</font>] </b> ><b><font size=+1> >List: <a href="?l=evms-devel&r=1&w=2">evms-devel</a> >Subject: [Evms-devel] [PATCH] 2.5.3 device-mapper input >From: <a href="?a=108404336000003&r=1&w=2"><garlicbread () ntlworld ! com></a> >Date: <a href="?l=evms-devel&r=1&w=2&b=200510">2005-10-09 15:22:10</a> >Message-ID: <a href="?i=20051009142313.TJFF8556.aamta10-winn.ispmail.ntl.com%20()%20smtp%20!%20ntlworld%20!%20com">20051009142313.TJFF8556.aamta10-winn.ispmail.ntl.com () smtp ! ntlworld ! com</a></font> >[<a href="?l=evms-devel&m=112887246404441&q=raw">Download message RAW</a>]</b> > >Hi, Some of you may find this interesting >a patch against evms 2.5.3 to enable device-mapper devices for input into evms >This is useful for things such as layering evms on top of dmraid and dm-crypt without \ >the need for loop back devices (I've tried this with dmraid and I think this should \ >also work for dm-crypt) I'm posting this from a web-mail system at the moment so I \ >hope the patch gets through okay > >a better approach would probably be to have a seperate plugin to control the aspects \ >of device-mapper raid / crypt within evms perhaps as some form of region layer but at \ >the moment I'm trying to see if I can write a reiser4 plugin > >only device mapper targets stated in the new config section will be picked up (no \ >wildcards) devices will show up with a prefix of dm/<dm target> >it's best to specify the entire disk target and let evms determine the seperate \ >partitions for itself > >it may even be possible to layer an evms volume on top of another evms volume \ >(assuming your crazy enough to try) but I wouldn't advise it :) > > >----------------------------------------- >Email sent from <a href="http://www.ntlworld.com">www.ntlworld.com</a> >Virus-checked using McAfee(R) Software >Visit <a href="http://www.ntlworld.com/security">www.ntlworld.com/security</a> for more information > > ><a href="?l=evms-devel&m=112887246404441&q=p3"><b>["evms-2.5.3-userdm-0.1.patch" (text/x-patch)]</b></a> > >[... then the patch followed ...] > >------------------------------------------------------- >This SF.Net email is sponsored by: >Power Architecture Resource Center: Free content, downloads, discussions, >and more. <a href="http://solutions.newsforge.com/ibmarch.tmpl">http://solutions.newsforge.com/ibmarch.tmpl</a> >_______________________________________________ >Evms-devel mailing list >Evms-devel@lists.sourceforge.net >To subscribe/unsubscribe, please visit: ><a href="https://lists.sourceforge.net/lists/listinfo/evms-devel">https://lists.sourceforge.net/lists/listinfo/evms-devel</a> > ><b>[<a href="?l=evms-devel&m=112853428509330&w=2">prev in list</a>] [<a href="?l=evms-devel&m=112888799008508&w=2">next in list</a>] [<font color="#c0c0c0">prev in thread</font>] [<font color="#c0c0c0">next in thread</font>] </b> ></pre> > </pre><br><center> > <a href="?q=configure">Configure</a> | > > <a href="?q=about">About MARC</a> | > <a href="?q=about#Begware">Support MARC</a> | > <a href="mailto:webguy@theaimsgroup.com?subject=Add a list to MARC">Got a list to add?</a> | > Sponsored by <a href="http://www.10East.com/">10East</a> and <a href="http://www.korelogic.com/">KoreLogic</a> ></center> ></body> ></html> > >Here is the patch: I assigned it USE flag "evmsdminput" in Gentoo -u > >diff -Nrup evms-2.5.4/doc/evms.conf evms-2.5.4-patched_with_dev_mapper_input/doc/evms.conf >--- evms-2.5.4/doc/evms.conf 2005-11-30 06:23:58.000000000 -0800 >+++ evms-2.5.4-patched_with_dev_mapper_input/doc/evms.conf 2005-12-17 15:29:48.000000000 -0800 >@@ -251,3 +251,22 @@ lvm2 { > device_size_prompt = yes > } > >+# User devmapper section >+# This allows us to use a device-mapper device directly as a disk >+# without the need for loopback >+# Be careful not to specify any targets that are created by evms >+# This can be useful for any devices created via dmraid or other means >+ >+# In order for this to work "dm*" has to be in the list of includes >+ >+devmapper { >+ # This needs to be set to yes to enable >+ #dm_user = yes >+ >+ # List of targets to discover as disks >+ # These are the target names as seen with "dmsetup ls" >+ # only specific targets can be used >+ # we need to avoid evms created targets >+ #dm_user_targets = [ raid1_device raid0_device ] >+} >+ >diff -Nrup evms-2.5.4/doc/evms.conf.~1~ evms-2.5.4-patched_with_dev_mapper_input/doc/evms.conf.~1~ >--- evms-2.5.4/doc/evms.conf.~1~ 1969-12-31 16:00:00.000000000 -0800 >+++ evms-2.5.4-patched_with_dev_mapper_input/doc/evms.conf.~1~ 2005-11-30 06:23:58.000000000 -0800 >@@ -0,0 +1,253 @@ >+# EVMS Configuration file >+ >+# This file is a useable sample. >+# Its location should be /etc/evms.conf >+# It contains the default values which will be used in the absence of a >+# configuration file or the absence of a configuration option being set. >+ >+# Global engine section >+engine { >+ mode = readwrite >+ >+ # Possible values for debug_level in order are: critical, serious, >+ # error, warning, default, details, entry_exit, debug, extra, everything >+ # >+ # The default value is "default". Only log entries designated at the >+ # debug_level or at a more severe level than the debug_level will be >+ # printed to the log. Thus, a debug_level of "default" will also log >+ # critical, serious, error, and warning messages. "critical" will >+ # produce the smallest log, and "everything" will produce the largest >+ # log. >+ >+ debug_level = default >+ >+ log_file = /var/log/evms-engine.log >+ >+ # Include microseconds in the log timestamps. Default is "no". >+ >+# log_usec = yes >+ >+ # Include process IDs in the timestamps for log entries. Default is >+ # "no", unless the Engine is running in a clustered environment, in >+ # which case PIDs are always included, since the Engine will have >+ # several threads running. >+ >+# log_pid = yes >+ >+ # Open the log file with O_SYNC so that all writes to the log file >+ # are guaranteed to be on the disk rather than just in cache. >+ # Default is "no". >+ >+# sync_log = no >+ >+ # The directory where EVMS puts its metadata backup files. >+ >+ metadata_backup_dir = /var/evms/metadata_backups >+ >+ # Save a backup of the metadata after each successful save of a >+ # configuration change >+ >+# auto_metadata_backup = yes >+} >+ >+# Settings if the Engine is opened in daemon mode >+daemon { >+ debug_level = default # Same settings as available for >+ # engine.debug_level >+ >+ log_file = /var/log/evms-daemon.log >+ >+ # Include microseconds in the log timestamps. Default is "no". >+ >+# log_usec = yes >+ >+ # Include process IDs in the timestamps for log entries. Default is >+ # "no", unless the Engine is running in a clustered environment, in >+ # which case PIDs are always included, since the Engine will have >+ # several threads running. >+ >+# log_pid = yes >+ >+ # Open the log file with O_SYNC so that all writes to the log file >+ # are guaranteed to be on the disk rather than just in cache. >+ # Default is "no". >+ >+# sync_log = no >+} >+ >+ >+# Clustering section >+ >+clustering { >+ >+ # The number of seconds the engine/daemon should wait for a valid >+ # cluster membership to show up. >+ >+ membership_timeout = 10 >+} >+ >+ >+# Activation section >+# >+# Use this section to tell EVMS which volumes and objects should be activated. >+ >+activate { >+ >+ # Names of volumes and objects that should be activated. >+ # >+ # Names can be specified using "*", "?", and "[...]" notations. >+ >+ include = [ * ] >+ >+ # Names of volumes and objects that should not be activated. >+ # >+ # Names can be specified using "*", "?", and "[...]" notations. >+ >+ exclude = [ ] >+} >+ >+ >+# Local disk manager sections >+ >+# Use this section to tell EVMS where to look for devices and which devices to >+# include or exclude on a system without sysfs (i.e., 2.4 kernels). If you are >+# using a 2.6 kernel, you'll likely want to see the "sysfs_devices" section >+# instead of this one. >+ >+legacy_devices { >+ >+ # "scan" is the location of the dev node tree. >+ >+ scan = /dev >+ >+ # "directories" is any directories under the "scan" directory you want >+ # searched recursively. On systems running devfs without devfsd, the >+ # default settings will find all IDE and SCSI disks. >+ >+ directories = [ ide scsi dasd ] >+ >+ # "include" are the block devices found in the dev tree that you want >+ # EVMS to use as disks. By default, this will search for traditional >+ # style device names (e.g., hda, sdb). If you know the exact disks that >+ # your system uses, you can specify them here to cut down on unnecessary >+ # searching. >+ # >+ # If you are running devfs with devfsd, the default settings will find >+ # the old-style names (eg. hda). If you wish to use the new-style names >+ # (eg. ide/host0/bus0/target0/lun0/disc), simply remove "sd?" and "hd?" >+ # from the list below. If you are running devfs without devfsd, the >+ # new-style names will be used. >+ # >+ # Block device names can be specified using "*", "?", and "[...]" >+ # notations. >+ >+ include = [ hd? sd? dasd? disc ] >+ >+ # "exclude" are the block devices found in the dev tree that you don't >+ # want EVMS to use as disks. Entries here will override any possible >+ # matches from the "include" setting. Thus, if you specify "hd?" in >+ # "include", and "hdc" in "exclude", EVMS will examine all IDE disks >+ # except hdc. >+ # >+ # Block device names can be specified using "*", "?", and "[...]" >+ # notations. >+ >+ exclude = [ ] >+ >+ # "max_open_disks" is the maximum number of disks that EVMS will have >+ # open file-descriptors for while the engine is running. The allowable >+ # range is 1 to 1024, and the default value is 64. >+ >+# max_open_disks = 64 >+} >+ >+# Use this section to tell EVMS where to look for devices and which devices to >+# include or exclude on a system with sysfs (i.e., 2.5 and later kernels). >+ >+sysfs_devices { >+ >+ # "include" are the block devices found in the /sys/block/ >+ # directory that you want EVMS to use as disks. >+ # >+ # Block device names can be specified using "*", "?", and "[...]" >+ # notations. >+ >+ include = [ * ] >+ >+ # "exclude" are the block devices found in the /sys/block/ >+ # directory that you don't want EVMS to use as disks. Entries here >+ # will override any possible matches from the "include" setting. >+ # >+ # Block device names can be specified using "*", "?", and "[...]" >+ # notations. >+ >+ exclude = [ ] >+ >+ # "max_open_disks" is the maximum number of disks that EVMS will have >+ # open file-descriptors for while the engine is running. The allowable >+ # range is 1 to 1024, and the default value is 64. >+ >+# max_open_disks = 64 >+ >+ # "ignore_sysfs" will tell the disk plugin to ignore the sysfs_devices >+ # section, and fall back to the legacy_devices section, even if sysfs >+ # is available. The default is "no", which should be fine for almost >+ # all users. >+ >+# ignore_sysfs = no >+} >+ >+ >+# Cluster Segment Manager (CSM) section >+ >+csm { >+ >+ # Set admin_mode to yes when you wish to force the CSM to discover >+ # objects from all cluster containers, allowing you to perform >+ # configuration and maintenance. Setting admin_mode to yes will cause >+ # the CSM to ignore container ownership which will allow you to >+ # configure storage in a maintenance mode. >+ # >+ # The default is no. >+ >+# admin_mode = yes >+} >+ >+ >+# Multipath section >+# >+# Use this section to tell EVMS which paths in a multipath device should be >+# treated only as "backup" paths. These paths will be activated in the kernel >+# in a separate priority-group, and will only be used when all of the "normal" >+# paths have failed. >+# >+# Each entry in this section should be the name of a multipath device on your >+# system. The value for each entry should be the names of the child objects >+# which should be treated as "backup" paths. Child objects which should be >+# treated as "active" paths should not be listed here. >+# >+# Do not use any wildcard characters in this section. >+ >+multipath { >+# md/md0 = [ hdc ] >+# mp/lvm/vg1-pv1 = [ hdd ] >+} >+ >+ >+# LVM2 plugin section. >+ >+lvm2 { >+ # Should the LVM2 plugin prompt you for confirmation when it finds >+ # LVM2 metadata on an object, but the object does not pass the >+ # necessary size checks? If yes, it will ask you if the object is >+ # really an LVM2 PV. If no, it will assume the object is not a PV. >+ # >+ # If you have used EVMS to create an LVM2 container on top of an >+ # MD Software-RAID region, you'll most likely want to set this >+ # parameter to no. >+ # >+ # The default is yes. >+ >+ device_size_prompt = yes >+} >+ >diff -Nrup evms-2.5.4/engine/dm-targets.c evms-2.5.4-patched_with_dev_mapper_input/engine/dm-targets.c >--- evms-2.5.4/engine/dm-targets.c 2005-11-07 07:46:41.000000000 -0800 >+++ evms-2.5.4-patched_with_dev_mapper_input/engine/dm-targets.c 2005-12-17 15:29:48.000000000 -0800 >@@ -800,28 +800,39 @@ static int mirror_build_params(dm_target > * Fill in a mirror target structure based on an ASCII table string of the form: > * <log_type> <num_log_params> [<log_params>]* <num_mirrors> [<major>:<minor> <start_lba>]{2,} > * >- * Currently, log_type will always be "core", num_log_params is 1, and >+ * Currently, log_type will always be "core", num_log_params is 1 or 2, and > * log_params is chunk-size (in sectors). > **/ > static int mirror_translate_params(dm_target_t *target) > { > dm_target_mirror_t *mirror = target->data.mirror; > char *params = target->params; >- int i, rc; >+ int i, rc, num_opts; > > LOG_PROC_ENTRY(); > >- /* Skip "core 1" at the start of the string. */ >- params = next_token(params); >+ /* Skip "core" at the start of the string. */ > params = next_token(params); > >- rc = sscanf(params, "%u %u", &mirror->chunk_size, &mirror->num_mirrors); >+ /* get the number of options for core and the chunk size */ >+ rc = sscanf(params, "%u %u", &num_opts, &mirror->chunk_size); > if (rc != 2) { > rc = EINVAL; > goto out; > } >- > params = next_token(params); >+ >+ /* skip the options for core based on num_opts */ >+ for (i = 0; i < num_opts; i++) { >+ params = next_token(params); >+ } >+ >+ /* get the number of mirrors */ >+ rc = sscanf(params, "%u", &mirror->num_mirrors); >+ if (rc != 1) { >+ rc = EINVAL; >+ goto out; >+ } > params = next_token(params); > > for (i = 0; i < mirror->num_mirrors; i++) { >@@ -855,13 +866,33 @@ out: > static int mirror_pretranslate_params(char *params, u_int32_t *num_devs, > u_int32_t *num_groups) > { >- int rc; >+ int rc, i, num_opts = 0; >+ char *num_devs_point; > > LOG_PROC_ENTRY(); > >- rc = sscanf(params, "%*s %*d %*u %u", num_devs); >+ /* The mirror target can have more than 1 option >+ this affects the location of the number of member disks */ >+ >+ rc = sscanf(params, "%*s %u", &num_opts); >+ if (rc != 1) { >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* skip "core <num_opts>" */ >+ num_devs_point = next_token(params); >+ num_devs_point = next_token(params); >+ >+ /* skip the options for core based on num_opts */ >+ for (i = 0; i < num_opts; i++) { >+ num_devs_point = next_token(params); >+ } >+ >+ rc = sscanf(num_devs_point, "%u", num_devs); > rc = (rc != 1) ? EINVAL : 0; > >+out: > LOG_PROC_EXIT_INT(rc); > return rc; > } >diff -Nrup evms-2.5.4/plugins/disk/localdskmgr.c evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.c >--- evms-2.5.4/plugins/disk/localdskmgr.c 2005-10-19 08:47:24.000000000 -0700 >+++ evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.c 2005-12-17 15:29:48.000000000 -0800 >@@ -1329,13 +1329,14 @@ static int check_multipath(storage_objec > rc = EngFncs->dm_get_targets(disk, &targets); > if (rc) { > LOG_ERROR("Error getting DM mapping for disk %s.\n", disk->name); >+ rc=0; > goto out; > } > > /* Reject all non-multipath devices. */ > if (targets->type != DM_TARGET_MULTIPATH) { > LOG_DEBUG("Disk %s is not a multipath device.\n", disk->name); >- rc = EINVAL; >+ //rc = EINVAL; > goto out; > } > >@@ -1396,6 +1397,98 @@ static void remove_multipath_children(li > } > > /** >+ * check_user_devicemapper >+ * >+ * Check if this disk is a User created DM device. >+ **/ >+static int check_user_devicemapper(storage_object_t * disk) >+{ >+ dm_device_list_t * dm_list, * dm_entry; >+ dm_target_t * targets = NULL; >+ local_disk_t * ld = disk->private_data; >+ int rc = 0, dm_user_targets_count = 0, i, dmup_len; >+ boolean user_dm_enable; >+ const char * const * dm_user_targets; >+ char * dm_user_prefix = "dm/"; >+ >+ LOG_ENTRY(); >+ >+ /* Get the list of active DM devices. */ >+ dm_list = get_dm_device_list(); >+ if (!dm_list) { >+ LOG_WARNING("Cannot get list of DM devices.\n"); >+ goto out; >+ } >+ >+ /* Search the DM list for an entry that matches this disk. */ >+ dm_entry = find_disk_in_dm_devices(disk, dm_list); >+ if (!dm_entry) { >+ LOG_DEBUG("Disk %s is not a DM device.\n", disk->name); >+ goto out; >+ } >+ >+ user_dm_enable = FALSE; >+ EngFncs->get_config_bool("devmapper.dm_user", &user_dm_enable); >+ >+ EngFncs->get_config_string_array("devmapper.dm_user_targets", >+ &dm_user_targets_count, &dm_user_targets); >+ >+ if (user_dm_enable == FALSE) { >+ LOG_DEBUG("devmapper.dm_user not enabled.\n"); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* search the named targets in the config for the DM name*/ >+ user_dm_enable = FALSE; >+ for (i = 0; i < dm_user_targets_count; i++) { >+ if (strncmp(dm_user_targets[i], dm_entry->name, EVMS_NAME_SIZE) == 0) { >+ user_dm_enable = TRUE; >+ } >+ } >+ >+ if (user_dm_enable == FALSE) { >+ LOG_DEBUG("%s not found in devmapper.dm_user_targets.\n", dm_entry->name); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* Get the DM mapping for this disk. */ >+ /* we can use this in the future if we need to, and it's good to check */ >+ strncpy(disk->name, dm_entry->name, EVMS_NAME_SIZE); >+ rc = EngFncs->dm_get_targets(disk, &targets); >+ if (rc) { >+ LOG_ERROR("Error getting DM mapping for disk %s.\n", disk->name); >+ goto out; >+ } >+ >+ /* Copy the DM name to this disk. */ >+ dmup_len = strlen(dm_user_prefix); >+ LOG_DEBUG("Changing disk name from %s to %s%s.\n", >+ disk->name, dm_user_prefix ,dm_entry->name); >+ >+ strncpy(disk->name, dm_user_prefix, dmup_len); >+ strncpy((disk->name)+dmup_len, dm_entry->name, EVMS_NAME_SIZE-dmup_len); >+ >+ /* Reject all multipath devices that >+ * were created by other EVMS plugins. >+ */ >+ rc = check_multipath_name(disk); >+ if (rc) { >+ LOG_DEBUG("Multipath disk %s belongs to another EVMS plugin.\n", >+ disk->name); >+ goto out; >+ } >+ >+ ld->flags |= LD_FLAG_USERDM; >+ >+out: >+ /*EngFncs->dm_deallocate_targets(targets); */ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** > * get_geometry > * > * Use the HDIO_GETGEO_BIG or HDIO_GETGEO ioctl to get the disk's geometry. >@@ -1478,6 +1571,7 @@ static int get_fake_geometry(storage_obj > if (disk->dev_major == LOOP_MAJOR || > disk->dev_major == NBD_MAJOR || > disk->dev_major == DRBD_MAJOR || >+ ld->flags & LD_FLAG_USERDM || > ld->flags & LD_FLAG_MULTIPATH) { > LOG_DEBUG("Creating fake geometry for disk %s.\n", disk->name); > disk->geometry.heads = 255; >@@ -1745,6 +1839,13 @@ static int LD_discover(list_anchor_t inp > /* Get the disk's hard-sector-size. */ > get_hardsector_size(&working_disk); > >+ /* Check for User created DM devices. */ >+ rc = check_user_devicemapper(&working_disk); >+ if (rc) { >+ close_dev(&working_disk); >+ continue; >+ } >+ > /* Get the disk's geometry. */ > rc = get_geometry(&working_disk); > if (rc) { >diff -Nrup evms-2.5.4/plugins/disk/localdskmgr.c.~1~ evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.c.~1~ >--- evms-2.5.4/plugins/disk/localdskmgr.c.~1~ 1969-12-31 16:00:00.000000000 -0800 >+++ evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.c.~1~ 2005-10-19 08:47:24.000000000 -0700 >@@ -0,0 +1,2398 @@ >+/* >+ * (C) Copyright IBM Corp. 2001, 2003 >+ * >+ * This program is free software; you can redistribute it and/or modify >+ * it under the terms of the GNU General Public License as published by >+ * the Free Software Foundation; either version 2 of the License, or >+ * (at your option) any later version. >+ * >+ * This program is distributed in the hope that it will be useful, >+ * but WITHOUT ANY WARRANTY; without even the implied warranty of >+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See >+ * the GNU General Public License for more details. >+ * >+ * You should have received a copy of the GNU General Public License >+ * along with this program; if not, write to the Free Software >+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA >+ * >+ * Local Disk Manager plugin. >+ */ >+ >+#define _GNU_SOURCE >+ >+#include <stdlib.h> >+#include <stdio.h> >+#include <string.h> >+#include <fcntl.h> >+#include <mntent.h> >+#include <dirent.h> >+#include <unistd.h> >+#include <sys/ioctl.h> >+#include <sys/stat.h> >+#include <sys/types.h> >+#include <errno.h> >+#include <glob.h> >+#include <malloc.h> >+#include <wait.h> >+ >+#include <plugin.h> >+#include <ldm_funcs.h> >+#include "localdskmgr.h" >+#include "cache.h" >+#include "info.h" >+ >+engine_functions_t * EngFncs = NULL; >+ >+char * base_directory = NULL; >+int base_directory_len; >+char * sysfs_mount_point; >+const char * scan; >+const char * const * directories; >+int directories_count; >+const char * const * includes; >+int include_count; >+const char * const default_legacy_includes[] = { >+ "sd?", >+ "hd?", >+ "dasd?" >+}; >+const char * const default_sysfs_includes[] = { >+ "*?", >+}; >+const char * const * excludes; >+int exclude_count; >+glob_t dev_names_glob; >+int glob_flags; >+static char pattern[PATH_MAX]; >+ >+static dm_device_list_t * dm_devices = NULL; >+static list_anchor_t multipath_children = NULL; >+ >+/* Global list for keeping track of open file-handles. On systems with >+ * a large number of disks, we don't want to keep open file-handles to >+ * every disk, or the process may run out of available file-handles. >+ */ >+static list_anchor_t file_handles = NULL; >+u_int32_t num_file_handles = 0; >+#define DEFAULT_FILE_HANDLES 64 >+#define MAX_FILE_HANDLES 1024 >+ >+static void close_dev(storage_object_t * disk); >+ >+/** >+ * round_down >+ * @value: Value (in sectors) to be rounded down. >+ * @boundary: Boundary (in bytes) to round-down to. >+ * >+ * Given a value, round it down to be a multiple of the specified boundary size. >+ **/ >+static inline sector_count_t round_down(sector_count_t value, >+ u_int32_t boundary) >+{ >+ sector_count_t boundary_in_vsectors = ((sector_count_t)boundary) >> >+ EVMS_VSECTOR_SIZE_SHIFT; >+ return (boundary > EVMS_VSECTOR_SIZE) ? >+ (value & ~(boundary_in_vsectors - 1)) : value; >+} >+ >+/** >+ * round_up >+ * @value: Value (in sectors) to be rounded up. >+ * @boundary: Boundary (in bytes) to round-up to. >+ * >+ * Given a value, round it up to be a multiple of the specified boundary size. >+ **/ >+static inline sector_count_t round_up(sector_count_t value, >+ u_int32_t boundary) >+{ >+ sector_count_t boundary_in_vsectors = ((sector_count_t)boundary) >> >+ EVMS_VSECTOR_SIZE_SHIFT; >+ sector_count_t temp_value = value + boundary_in_vsectors - 1; >+ return (boundary > EVMS_VSECTOR_SIZE) ? >+ (temp_value & ~(boundary_in_vsectors - 1)) : value; >+} >+ >+/** >+ * drbd_active >+ * >+ * Is the drbd driver running? If so, the nbd driver cannot also be running, >+ * since they use the same major-number. >+ **/ >+static int drbd_active = -1; >+ >+static boolean is_drbd_active(void) >+{ >+ struct stat st; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ if (drbd_active == -1) { >+ rc = stat("/proc/drbd", &st); >+ if (rc) { >+ drbd_active = FALSE; >+ } else { >+ drbd_active = TRUE; >+ } >+ } >+ >+ LOG_EXIT_BOOL(drbd_active); >+ return drbd_active; >+} >+ >+static boolean search_mount_records(FILE * records, >+ char * fs_name, >+ char ** mount_name) >+{ >+ boolean found = FALSE; >+ struct mntent * mount_entry; >+ >+ LOG_ENTRY(); >+ >+ while (!found && (mount_entry = getmntent(records)) != NULL) { >+ if (strcmp(mount_entry->mnt_type, fs_name) == 0) { >+ found = TRUE; >+ if (mount_name) { >+ *mount_name = strdup(mount_entry->mnt_dir); >+ } >+ } >+ } >+ >+ LOG_EXIT_BOOL(found); >+ return found; >+} >+ >+/** >+ * where_is_sysfs >+ * >+ * Is sysfs mounted. If so, return the mount point. The caller must free the >+ * returned string. >+ **/ >+static boolean where_is_sysfs(char ** mount_name) >+{ >+ boolean found = FALSE; >+ FILE * mount_records; >+ >+ LOG_ENTRY(); >+ >+ if (mount_name) { >+ *mount_name = NULL; >+ } >+ >+ mount_records = setmntent(MOUNTED, "r"); >+ if (mount_records) { >+ LOG_DEBUG("Searching for sysfs in %s.\n", MOUNTED); >+ found = search_mount_records(mount_records, "sysfs", mount_name); >+ endmntent(mount_records); >+ } >+ >+ if (!found) { >+ mount_records = setmntent("/proc/mounts", "r"); >+ if (mount_records) { >+ LOG_DEBUG("Searching for sysfs in /proc/mounts.\n"); >+ found = search_mount_records(mount_records, "sysfs", mount_name); >+ endmntent(mount_records); >+ } >+ } >+ >+ LOG_EXIT_BOOL(found); >+ return found; >+} >+ >+static void get_legacy_config() >+{ >+ LOG_ENTRY(); >+ >+ scan = "/dev/"; >+ EngFncs->get_config_string("legacy_devices.scan", &scan); >+ >+ directories_count = 0; >+ directories = NULL; >+ EngFncs->get_config_string_array("legacy_devices.directories", >+ &directories_count, &directories); >+ include_count = 0; >+ includes = NULL; >+ EngFncs->get_config_string_array("legacy_devices.include", >+ &include_count, &includes); >+ if (includes == NULL) { >+ includes = default_legacy_includes; >+ include_count = sizeof(default_legacy_includes) / >+ sizeof(default_legacy_includes[0]); >+ } >+ >+ exclude_count = 0; >+ excludes = NULL; >+ EngFncs->get_config_string_array("legacy_devices.exclude", >+ &exclude_count, &excludes); >+ >+ EngFncs->get_config_uint32("legacy_devices.max_open_disks", >+ &num_file_handles); >+ >+ LOG_EXIT_VOID(); >+} >+ >+static void get_sysfs_config() >+{ >+ boolean ignore_sysfs = FALSE; >+ >+ LOG_ENTRY(); >+ >+ EngFncs->get_config_bool("sysfs_devices.ignore_sysfs", &ignore_sysfs); >+ if (ignore_sysfs) { >+ /* Fall back to the legacy_devices section >+ * if the config file says to ignore sysfs. >+ */ >+ free(sysfs_mount_point); >+ sysfs_mount_point = NULL; >+ get_legacy_config(); >+ LOG_EXIT_VOID(); >+ return; >+ } >+ >+ include_count = 0; >+ includes = NULL; >+ EngFncs->get_config_string_array("sysfs_devices.include", >+ &include_count, &includes); >+ if (includes == NULL) { >+ includes = default_sysfs_includes; >+ include_count = sizeof(default_sysfs_includes) / >+ sizeof(default_sysfs_includes[0]); >+ } >+ >+ exclude_count = 0; >+ excludes = NULL; >+ EngFncs->get_config_string_array("sysfs_devices.exclude", >+ &exclude_count, &excludes); >+ >+ EngFncs->get_config_uint32("sysfs_devices.max_open_disks", >+ &num_file_handles); >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * file_handle_cleanup >+ * >+ * Delete the list of file-handle tracking structures. >+ **/ >+static void file_handle_cleanup(void) >+{ >+ file_handle_t *handle; >+ list_element_t iter; >+ >+ LOG_ENTRY(); >+ >+ LIST_FOR_EACH(file_handles, iter, handle) { >+ EngFncs->engine_free(handle); >+ } >+ >+ EngFncs->destroy_list(file_handles); >+ file_handles = NULL; >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * file_handle_setup >+ * >+ * Create and initialize MAX_FILE_HANDLES entries on the file_handles list. >+ **/ >+static int file_handle_setup(void) >+{ >+ file_handle_t *handle; >+ u_int32_t i; >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ >+ /* Make sure the number of entries in the >+ * file-handles list will be reasonable. >+ */ >+ if (num_file_handles == 0) { >+ num_file_handles = DEFAULT_FILE_HANDLES; >+ } else if (num_file_handles > MAX_FILE_HANDLES) { >+ num_file_handles = MAX_FILE_HANDLES; >+ } >+ >+ LOG_DEBUG("Allocating %u entries in the file-handles list.\n", >+ num_file_handles); >+ >+ file_handles = EngFncs->allocate_list(); >+ if (!file_handles) { >+ rc = ENOMEM; >+ goto out; >+ } >+ >+ for (i = 0; i < num_file_handles; i++) { >+ handle = EngFncs->engine_alloc(sizeof(*handle)); >+ if (!handle) { >+ file_handle_cleanup(); >+ rc = ENOMEM; >+ break; >+ } >+ handle->elem = EngFncs->insert_thing(file_handles, handle, >+ INSERT_AFTER, NULL); >+ } >+ >+out: >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * file_handle_release >+ * >+ * Release this file-handle back to the pool of available handles. Move it >+ * to the start of the list so it will be quicker to find on the next search. >+ **/ >+static void file_handle_release(file_handle_t *handle) >+{ >+ LOG_ENTRY(); >+ >+ handle->disk = NULL; >+ EngFncs->remove_element(handle->elem); >+ EngFncs->insert_element(file_handles, handle->elem, INSERT_BEFORE, NULL); >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * file_handle_make_last >+ * >+ * Move this file-handle to the end of the list so it will be the least likely >+ * to be "stolen". >+ **/ >+static void file_handle_make_last(file_handle_t *handle) >+{ >+ LOG_ENTRY(); >+ >+ EngFncs->remove_element(handle->elem); >+ EngFncs->insert_element(file_handles, handle->elem, INSERT_AFTER, NULL); >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * file_handle_find_free >+ * >+ * Search the file-handles list for an unused entry and assign it to this disk. >+ **/ >+static file_handle_t *file_handle_find_free(void) >+{ >+ file_handle_t *handle; >+ list_element_t iter; >+ >+ LOG_ENTRY(); >+ >+ LIST_FOR_EACH(file_handles, iter, handle) { >+ if (!handle->disk) { >+ break; >+ } >+ } >+ >+ LOG_EXIT_PTR(handle); >+ return handle; >+} >+ >+/** >+ * file_handle_steal_first >+ * >+ * "Steal" the first file-handle on the list, forceably close the disk that >+ * currently owns it, and assign it to the new disk. >+ **/ >+static file_handle_t *file_handle_steal_first(void) >+{ >+ file_handle_t *handle; >+ >+ LOG_ENTRY(); >+ >+ handle = EngFncs->first_thing(file_handles, NULL); >+ if (handle->disk) { >+ LOG_DEBUG("Stealing file-handle from disk %s.\n", >+ handle->disk->name); >+ close_dev(handle->disk); >+ } >+ >+ LOG_EXIT_PTR(handle); >+ return handle; >+} >+ >+/** >+ * file_handle_get >+ * >+ * Get a file-handle for this disk. First search the list for an used one. If >+ * we can't find one, simply "steal" the first one on the list. >+ **/ >+static file_handle_t *file_handle_get(void) >+{ >+ file_handle_t *handle; >+ >+ LOG_ENTRY(); >+ >+ handle = file_handle_find_free(); >+ if (!handle) { >+ handle = file_handle_steal_first(); >+ } >+ >+ LOG_EXIT_PTR(handle); >+ return handle; >+} >+ >+static int LD_setup(engine_functions_t * engine_function_table) >+{ >+ int rc; >+ >+ /* save info we get from the engine */ >+ EngFncs = engine_function_table; >+ >+ LOG_ENTRY(); >+ >+ if (where_is_sysfs(&sysfs_mount_point)) { >+ get_sysfs_config(); >+ } else { >+ get_legacy_config(); >+ } >+ >+ rc = file_handle_setup(); >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * open_dev >+ * >+ * Open the specified disk. Use O_DIRECT to avoid caching. Use O_SYNC in case >+ * the kernel does not honor O_DIRECT. Use the Engine's service so we >+ * automatically get a dev-node in the /dev/evms/.nodes/ tree. Record the >+ * file handle in the disk's private data. >+ **/ >+static int open_dev(storage_object_t * disk) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ >+ if (ld->fd <= 0) { >+ ld->file_handle = file_handle_get(); >+ ld->file_handle->disk = disk; >+ >+ ld->fd = EngFncs->open_object(disk, O_RDWR | O_DIRECT | O_SYNC); >+ if (ld->fd < 0) { >+ rc = - ld->fd; >+ file_handle_release(ld->file_handle); >+ ld->file_handle = NULL; >+ LOG_DEBUG("Error opening disk %s: %d: %s\n", >+ disk->name, rc, strerror(rc)); >+ } >+ } >+ >+ if (!rc) { >+ file_handle_make_last(ld->file_handle); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * close_dev >+ * >+ * Close the disk and clear the file handle. >+ **/ >+static void close_dev(storage_object_t * disk) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ if (ld->fd >= 0) { >+ rc = EngFncs->close_object(disk, ld->fd); >+ file_handle_release(ld->file_handle); >+ ld->file_handle = NULL; >+ ld->fd = -1; >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * LD_cleanup >+ * >+ * Find any disks and close the device that was opended during discovery. >+ **/ >+static void LD_cleanup(void) >+{ >+ storage_object_t * disk; >+ list_anchor_t disk_list; >+ list_element_t disk_list_itr; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ /* Get a list of disks that are managed by this plug-in. */ >+ rc = EngFncs->get_object_list(DISK, 0, my_plugin_record, >+ NULL, 0, &disk_list); >+ if (!rc) { >+ /* Close any dev handles that might be open. */ >+ LIST_FOR_EACH(disk_list, disk_list_itr, disk) { >+ close_dev(disk); >+ EngFncs->engine_free(disk->private_data); >+ } >+ EngFncs->destroy_list(disk_list); >+ } >+ >+ destroy_cache(); >+ file_handle_cleanup(); >+ >+ if (base_directory) { >+ free(base_directory); >+ base_directory = NULL; >+ } >+ if (sysfs_mount_point) { >+ free(sysfs_mount_point); >+ sysfs_mount_point = NULL; >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static void filter_out_excludes(char * pattern, int path_len, int new_globs_index) >+{ >+ int rc; >+ int i; >+ glob_t exclude_glob = {0}; >+ >+ LOG_ENTRY(); >+ >+ for (i = 0; i < exclude_count; i++) { >+ >+ strcpy(pattern + path_len, excludes[i]); >+ >+ rc = glob(pattern, glob_flags, NULL, &exclude_glob); >+ >+ if (rc == 0) { >+ glob_flags |= GLOB_APPEND; >+ >+ } else { >+ if (rc != GLOB_NOMATCH) { >+ LOG_WARNING("glob() of pattern %s failed with error %s\n", pattern, >+ (rc == GLOB_NOSPACE) ? "GLOB_NOSPACE" : >+ (rc == GLOB_ABEND) ? "GLOB_ABEND" : >+ "(unknown)"); >+ } >+ } >+ } >+ >+ for (i = 0; i < exclude_glob.gl_pathc; i++) { >+ int j; >+ >+ for (j = new_globs_index; j < dev_names_glob.gl_pathc; j++) { >+ if (strcmp(exclude_glob.gl_pathv[i], dev_names_glob.gl_pathv[j]) == 0) { >+ int k; >+ >+ LOG_DEBUG("Removing %s.\n", dev_names_glob.gl_pathv[j]); >+ free(dev_names_glob.gl_pathv[j]); >+ >+ /* Scoot up all following entries. */ >+ for (k = j+1; k < dev_names_glob.gl_pathc; k++) { >+ dev_names_glob.gl_pathv[k-1] = dev_names_glob.gl_pathv[k]; >+ } >+ dev_names_glob.gl_pathc--; >+ >+ break; >+ } >+ } >+ } >+ >+ if (exclude_glob.gl_pathc >= 0) { >+ globfree(&exclude_glob); >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static void filter_out_non_block_devices(int new_globs_index) >+{ >+ int i; >+ struct stat statbuf; >+ int status; >+ >+ LOG_ENTRY(); >+ >+ i = new_globs_index; >+ while (i < dev_names_glob.gl_pathc) { >+ >+ status = stat(dev_names_glob.gl_pathv[i], &statbuf); >+ >+ if (status == 0) { >+ if (!S_ISBLK(statbuf.st_mode)) { >+ int j; >+ >+ LOG_DEBUG("Removing %s.\n", dev_names_glob.gl_pathv[i]); >+ free(dev_names_glob.gl_pathv[i]); >+ >+ /* Scoot up all following entries. */ >+ for (j = i+1; j < dev_names_glob.gl_pathc; j++) { >+ dev_names_glob.gl_pathv[j-1] = dev_names_glob.gl_pathv[j]; >+ } >+ dev_names_glob.gl_pathc--; >+ dev_names_glob.gl_pathv[dev_names_glob.gl_pathc] = NULL; >+ >+ /* Leave "i" as it is so we check the new >+ * entry at the current index. >+ */ >+ continue; >+ } >+ >+ } else { >+ LOG_WARNING("stat(%s) failed with error code %d: %s\n", dev_names_glob.gl_pathv[i], errno, strerror(errno)); >+ } >+ >+ i++; >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static void get_dev_names(const char * dir) >+{ >+ int rc; >+ int i; >+ int path_len; >+ int new_globs_index; >+ >+ LOG_ENTRY(); >+ LOG_DEBUG("Get device names in directory %s\n", dir); >+ >+ strcpy(pattern, dir); >+ path_len = strlen(pattern); >+ if (pattern[path_len-1] != '/') { >+ pattern[path_len] = '/'; >+ pattern[path_len+1] = '\0'; >+ path_len++; >+ } >+ >+ new_globs_index = dev_names_glob.gl_pathc; >+ >+ for (i = 0; i < include_count; i++) { >+ >+ strcpy(pattern + path_len, includes[i]); >+ >+ rc = glob(pattern, glob_flags, NULL, &dev_names_glob); >+ >+ if (rc == 0) { >+ glob_flags |= GLOB_APPEND; >+ >+ } else { >+ if (rc != GLOB_NOMATCH) { >+ LOG_WARNING("glob() of pattern %s failed with error %s\n", pattern, >+ (rc == GLOB_NOSPACE) ? "GLOB_NOSPACE" : >+ (rc == GLOB_ABEND) ? "GLOB_ABEND" : >+ "(unknown)"); >+ } >+ } >+ } >+ >+ filter_out_excludes(pattern, path_len, new_globs_index); >+ >+ if (sysfs_mount_point == NULL) { >+ filter_out_non_block_devices(new_globs_index); >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static char dir_pattern[PATH_MAX]; >+ >+static void process_dir(char * name) >+{ >+ int i; >+ glob_t dirs_glob; >+ >+ LOG_ENTRY(); >+ >+ /* Process entries in this directory. */ >+ get_dev_names(name); >+ >+ /* Get a list of this directory's subdirectories. */ >+ strcpy(dir_pattern, name); >+ strcat(dir_pattern, "*/"); >+ >+ if (glob(dir_pattern, 0, NULL, &dirs_glob) == 0) { >+ >+ /* Process the subdirectories. */ >+ for (i = 0; i < dirs_glob.gl_pathc; i++) { >+ int status; >+ struct stat statbuf; >+ >+ status = stat(dirs_glob.gl_pathv[i], &statbuf); >+ if (status == 0) { >+ if (S_ISDIR(statbuf.st_mode)) { >+ process_dir(dirs_glob.gl_pathv[i]); >+ } >+ } >+ } >+ >+ globfree(&dirs_glob); >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static char dir_path[PATH_MAX]; >+ >+static void get_legacy_devs() >+{ >+ int base_len, i; >+ char * pch; >+ >+ LOG_ENTRY(); >+ >+ memset(&dev_names_glob, 0, sizeof(dev_names_glob)); >+ glob_flags = 0; >+ >+ /* Make sure the user-specified directory ends with a '/'. */ >+ base_len = strlen(scan); >+ if (scan[base_len-1] != '/') { >+ pch = malloc(base_len + 2); >+ if (pch) { >+ strcpy(pch, scan); >+ strcpy(pch + base_len, "/"); >+ scan = pch; >+ base_len += 2; >+ } >+ } >+ >+ base_directory = strdup(scan); >+ base_directory_len = strlen(base_directory); >+ >+ /* Always find devices in the base directory. */ >+ get_dev_names(base_directory); >+ >+ /* Recursively search any subdirectories the user specified. */ >+ strcpy(dir_path, base_directory); >+ for (i = 0; i < directories_count; i++) { >+ int len; >+ >+ strcpy(dir_path + base_directory_len, directories[i]); >+ >+ len = strlen(dir_path); >+ if (dir_path[len-1] != '/') { >+ strcpy(dir_path + len, "/"); >+ } >+ >+ process_dir(dir_path); >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+static void get_sysfs_devs() >+{ >+ LOG_ENTRY(); >+ >+ memset(&dev_names_glob, 0, sizeof(dev_names_glob)); >+ glob_flags = 0; >+ >+ strcpy(dir_path, sysfs_mount_point); >+ strcat(dir_path, "/block/"); >+ >+ base_directory = strdup(dir_path); >+ base_directory_len = strlen(base_directory); >+ >+ LOG_DEBUG("Scanning %s\n", dir_path); >+ get_dev_names(dir_path); >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * get_sysfs_size >+ * @full_name: Full path-name to the disk device-node. >+ * @p_size: Return pointer to the disk's size (in sectors). >+ * >+ * Use sysfs to get the size (in sectors) of the specified disk. >+ **/ >+static int get_sysfs_size(char * full_name, u_int64_t * p_size) >+{ >+ int rc = 0; >+ int fd; >+ char * size_file = malloc(strlen(full_name) + 6); >+ char size_str[24]; >+ >+ LOG_ENTRY(); >+ >+ if (size_file != NULL) { >+ strcpy(size_file, full_name); >+ strcat(size_file, "/size"); >+ fd = open(size_file, O_RDONLY); >+ if (fd > 0) { >+ int bytes_read; >+ >+ bytes_read = read(fd, size_str, 24); >+ >+ if (bytes_read > 0) { >+ /* Size is already in sectors. */ >+ *p_size = strtoull(size_str, NULL, 10); >+ >+ } else { >+ if (bytes_read == 0) { >+ LOG_ERROR("No bytes read from %s.\n", size_file); >+ } >+ >+ rc = errno; >+ LOG_ERROR("read() returned error %d: %s\n", rc, strerror(rc)); >+ } >+ >+ close(fd); >+ >+ } else { >+ rc = errno; >+ LOG_ERROR("open(%s) returned error %d: %s\n", size_file, rc, strerror(rc)); >+ } >+ >+ free(size_file); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_legacy_size >+ * @full_name: Full path-name to the disk device-node. >+ * @p_size: Return pointer to the disk's size (in sectors). >+ * >+ * Use the BLKGETSIZE64 ioctl to get the size (in sectors) of the >+ * specified disk. >+ **/ >+static int get_legacy_size(char * full_name, u_int64_t * p_size) >+{ >+ int rc = 0; >+ int fd; >+ >+ LOG_ENTRY(); >+ >+ fd = open(full_name, O_RDONLY); >+ >+ if (fd > 0) { >+ >+ /* Ioctl to get size. (returns bytes) */ >+ rc = ioctl(fd, BLKGETSIZE64, p_size); >+ if (rc == 0) { >+ *p_size >>= EVMS_VSECTOR_SIZE_SHIFT; >+ *p_size &= ~1; >+ } else { >+ rc = errno; >+ LOG_DETAILS("ioctl to get the size returned error code " >+ "%d: %s.\n", rc, strerror(rc)); >+ } >+ >+ close(fd); >+ >+ } else { >+ rc = errno; >+ LOG_DETAILS("open(%s) returned error %d: %s\n", >+ full_name, rc, strerror(rc)); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_disk_size >+ * @full_name: Full path-name to the device node. >+ * @disk: Pointer to the disk object. >+ * >+ * Get the size of the disk, and check that it is non-zero. >+ **/ >+static int get_disk_size(char * full_name, storage_object_t * disk) >+{ >+ int rc; >+ LOG_ENTRY(); >+ >+ if (sysfs_mount_point) { >+ rc = get_sysfs_size(full_name, &disk->size); >+ } else { >+ rc = get_legacy_size(full_name, &disk->size); >+ } >+ >+ if (disk->size == 0) { >+ LOG_DEBUG("Disk %s has zero-size. Not a valid disk.\n", >+ disk->name); >+ rc = EINVAL; >+ } else if (disk->dev_major == NBD_MAJOR && >+ ! is_drbd_active()) { >+ >+ /* YUCK!!! Uninitialized NBD devices report a size anyway. >+ * DRBD (which shares the same major) behaves correctly. >+ */ >+ if (EngFncs->is_2_4_kernel()) { >+ if (disk->size == NBD_DEF_SIZE_2_4) { >+ LOG_DEBUG("Disk %s appears to be an uninitialized NBD " >+ "device.\n", disk->name); >+ rc = EINVAL; >+ } >+ >+ } else { >+ if (disk->size == NBD_DEF_SIZE_2_6) { >+ LOG_DEBUG("Disk %s appears to be an uninitialized NBD " >+ "device.\n", disk->name); >+ rc = EINVAL; >+ } >+ } >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_sysfs_major_minor >+ * @full_name: Full path-name to the disk device-node. >+ * @p_major: Return pointer to the disk's major-number. >+ * @p_minor: Return pointer to the disk's minor-number. >+ * >+ * Use sysfs to get the device-number for the specified disk. >+ **/ >+static int get_sysfs_major_minor(char * full_name, >+ u_int32_t * p_major, >+ u_int32_t * p_minor) >+{ >+ int rc = 0; >+ int fd; >+ char * dev_file = malloc(strlen(full_name) + 5); >+ char dev_str[16]; >+ dev_t dev; >+ >+ LOG_ENTRY(); >+ >+ if (dev_file != NULL) { >+ strcpy(dev_file, full_name); >+ strcat(dev_file, "/dev"); >+ fd = open(dev_file, O_RDONLY); >+ if (fd > 0) { >+ int bytes_read; >+ >+ bytes_read = read(fd, dev_str, 16); >+ >+ if (bytes_read > 0) { >+ rc = sscanf(dev_str, "%u:%u", p_major, p_minor); >+ if (rc != 2) { >+ dev = strtoul(dev_str, NULL, 16); >+ *p_major = major(dev); >+ *p_minor = minor(dev); >+ } >+ rc = 0; >+ } else { >+ if (bytes_read == 0) { >+ LOG_ERROR("No bytes read from %s.\n", dev_file); >+ } >+ >+ rc = errno; >+ LOG_ERROR("read() returned error %d: %s\n", rc, strerror(rc)); >+ } >+ >+ close(fd); >+ >+ } else { >+ rc = errno; >+ LOG_ERROR("open(%s) returned error %d: %s\n", dev_file, rc, strerror(rc)); >+ } >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_legacy_major_minor >+ * @full_name: Full path-name to the disk device-node. >+ * @p_major: Return pointer to the disk's major-number. >+ * @p_minor: Return pointer to the disk's minor-number. >+ * >+ * Use stat to get the device-number for the specified disk. >+ **/ >+static int get_legacy_major_minor(char * full_name, >+ u_int32_t * p_major, >+ u_int32_t * p_minor) >+{ >+ int rc = 0; >+ struct stat statbuf; >+ >+ LOG_ENTRY(); >+ >+ rc = stat(full_name, &statbuf); >+ if (rc == 0) { >+ *p_major = major(statbuf.st_rdev); >+ *p_minor = minor(statbuf.st_rdev); >+ >+ } else { >+ rc = errno; >+ LOG_ERROR("stat(%s) returned error code %d: %s\n", >+ full_name, rc, strerror(rc)); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * check_for_duplicate_dev >+ * >+ * Search the current output list for the device-number of the specified disk. >+ * Each device-number should only be discovered once. >+ **/ >+static int check_for_duplicate_dev(storage_object_t * new_disk, >+ list_anchor_t output_list) >+{ >+ storage_object_t * disk; >+ list_element_t itr; >+ >+ LOG_ENTRY(); >+ >+ LIST_FOR_EACH(output_list, itr, disk) { >+ if (disk->dev_major == new_disk->dev_major && >+ disk->dev_minor == new_disk->dev_minor) { >+ LOG_WARNING("Current disk %s has device-number %x:%x, which" >+ "is a duplicate of disk %s. Ignoring %s.\n", >+ new_disk->name, new_disk->dev_major, >+ new_disk->dev_minor, disk->name, new_disk->name); >+ LOG_EXIT_INT(EINVAL); >+ return EINVAL; >+ } >+ } >+ >+ LOG_EXIT_INT(0); >+ return 0; >+} >+ >+/** >+ * get_disk_devnum >+ * @full_name: Full path-name to the disk device-node. >+ * @disk: Pointer to the disk object. >+ * @output_list:Current list of discovered disks. >+ * >+ * Get the device-number for the specified disk. Check that the device-number >+ * is allowed, and that it isn't a duplicate of an already-discovered disk. >+ **/ >+static int get_disk_devnum(char * full_name, >+ storage_object_t * disk, >+ list_anchor_t output_list) >+{ >+ int rc; >+ LOG_ENTRY(); >+ >+ if (sysfs_mount_point) { >+ rc = get_sysfs_major_minor(full_name, &disk->dev_major, >+ &disk->dev_minor); >+ } else { >+ rc = get_legacy_major_minor(full_name, &disk->dev_major, >+ &disk->dev_minor); >+ } >+ if (rc) { >+ goto out; >+ } >+ >+ /* Exclude floppy, md, and lvm1 devices. */ >+ if (disk->dev_major == FLOPPY_MAJOR || >+ disk->dev_major == MD_MAJOR || >+ disk->dev_major == LVM_MAJOR) { >+ LOG_DEBUG("Disk %s has a disallowed major number: %d.\n", >+ disk->name, disk->dev_major); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* Only discover a given device-number once. */ >+ rc = check_for_duplicate_dev(disk, output_list); >+ >+out: >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_dm_device_list >+ * >+ * Get the list of current DM devices (if we haven't gotten it previously). >+ **/ >+static dm_device_list_t * get_dm_device_list(void) >+{ >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ if (!dm_devices) { >+ rc = EngFncs->dm_get_devices(&dm_devices); >+ if (rc) { >+ LOG_ERROR("Error calling dm_get_devices.\n"); >+ } >+ } >+ >+ LOG_EXIT_PTR(dm_devices); >+ return dm_devices; >+} >+ >+/** >+ * find_disk_in_dm_devices >+ * >+ * Search the DM devices list for an entry with the same major:minor >+ * as this disk. >+ **/ >+static dm_device_list_t * find_disk_in_dm_devices(storage_object_t * disk, >+ dm_device_list_t * dm_list) >+{ >+ dm_device_list_t * dm_entry; >+ >+ LOG_ENTRY(); >+ >+ for (dm_entry = dm_list; dm_entry; dm_entry = dm_entry->next) { >+ if (dm_entry->dev_major == disk->dev_major && >+ dm_entry->dev_minor == disk->dev_minor) { >+ goto out; >+ } >+ } >+ >+out: >+ LOG_EXIT_PTR(dm_entry); >+ return dm_entry; >+} >+ >+/** >+ * check_multipath_name >+ * >+ * Other EVMS plugins can create multipath devices. We *don't* want to >+ * recognize those devices as disks. So check the name that we got from >+ * DM to see if it uses the naming format of the EVMS multipath plugins. >+ **/ >+static int check_multipath_name(storage_object_t *disk) >+{ >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ /* Multipath-segment-manager devices start with "mp/". */ >+ rc = strncmp(disk->name, "mp/", 3); >+ if (rc) { >+ /* MD-multipath devices start with "md/". */ >+ rc = strncmp(disk->name, "md/", 3); >+ } >+ >+ rc = rc ? 0 : EINVAL; >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * update_multipath_child_list >+ * >+ * Search the multipath target info for all the child devices. Add these >+ * devices to the global list so we can filter these out at the end of >+ * discovery. That way we won't discover both the multipath devices and >+ * their component disks, which would lead to duplicate discoveries in the >+ * higher levels. >+ * >+ * Use a temporary list to build up the list of children for this multipath. >+ * Then append this list to the global one once we've collected all the >+ * children. This will prevent hitting an error part way through processing >+ * the list of child devices, which could leave the global list in an >+ * inconsistent state. >+ **/ >+static int update_multipath_child_list(dm_target_t * targets) >+{ >+ dm_target_multipath_t * mp = targets->data.multipath; >+ dm_priority_group_t * pg; >+ dm_path_t * path; >+ dm_device_t * device; >+ list_anchor_t children = NULL; >+ list_element_t itr1, itr2; >+ int i, j, rc = 0; >+ >+ LOG_ENTRY(); >+ >+ /* Allocate the global child list if it doesn't exist yet. */ >+ if (!multipath_children) { >+ multipath_children = EngFncs->allocate_list(); >+ if (!multipath_children) { >+ LOG_ERROR("Error allocating multipath_children list.\n"); >+ rc = ENOMEM; >+ goto out; >+ } >+ } >+ >+ /* Allocate a temporary list. */ >+ children = EngFncs->allocate_list(); >+ if (!children) { >+ LOG_ERROR("Error allocating temporary child list.\n"); >+ rc = ENOMEM; >+ goto out; >+ } >+ >+ /* For each priority group in the multipath. */ >+ for (i = 0; i < mp->num_groups; i++) { >+ pg = mp->group + i; >+ /* For each path in the priority group. */ >+ for (j = 0; j < pg->num_paths; j++) { >+ path = pg->path + j; >+ device = EngFncs->engine_alloc(sizeof(*device)); >+ if (!device) { >+ LOG_ERROR("Error allocating device structure " >+ "for path %d:%d.\n", >+ path->device.major, path->device.minor); >+ rc = ENOMEM; >+ goto out; >+ } >+ device->major = path->device.major; >+ device->minor = path->device.minor; >+ >+ /* Add this path's device to the temporary list. */ >+ itr1 = EngFncs->insert_thing(children, device, >+ INSERT_AFTER, NULL); >+ if (!itr1) { >+ LOG_ERROR("Error adding device %d:%d to the " >+ "temporary child list.\n", >+ device->major, device->minor); >+ rc = ENOMEM; >+ goto out; >+ } >+ } >+ } >+ >+out: >+ if (!rc) { >+ /* Success. Append the temporary list to the global list. */ >+ rc = EngFncs->merge_lists(multipath_children, children, NULL, NULL); >+ if (rc) { >+ LOG_ERROR("Error merging temporary list with " >+ "multipath_children list.\n"); >+ } >+ } >+ if (rc) { >+ /* Some error occurred. Delete the temporary >+ * list and all of it's devices. >+ */ >+ if (children) { >+ LIST_FOR_EACH_SAFE(children, itr1, itr2, device) { >+ EngFncs->delete_element(itr1); >+ EngFncs->engine_free(device); >+ } >+ } >+ } >+ if (children) { >+ EngFncs->destroy_list(children); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * check_multipath >+ * >+ * Check if this disk is a DM multipath device. >+ **/ >+static int check_multipath(storage_object_t * disk) >+{ >+ dm_device_list_t * dm_list, * dm_entry; >+ dm_target_t * targets = NULL; >+ local_disk_t * ld = disk->private_data; >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ >+ /* Get the list of active DM devices. */ >+ dm_list = get_dm_device_list(); >+ if (!dm_list) { >+ LOG_WARNING("Cannot get list of DM devices.\n"); >+ goto out; >+ } >+ >+ /* Search the DM list for an entry that matches this disk. */ >+ dm_entry = find_disk_in_dm_devices(disk, dm_list); >+ if (!dm_entry) { >+ LOG_DEBUG("Disk %s is not a DM device.\n", disk->name); >+ goto out; >+ } >+ >+ /* Copy the DM name to this disk. */ >+ LOG_DEBUG("Changing disk name from %s to %s.\n", >+ disk->name, dm_entry->name); >+ strncpy(disk->name, dm_entry->name, EVMS_NAME_SIZE); >+ >+ /* Get the DM mapping for this disk. */ >+ rc = EngFncs->dm_get_targets(disk, &targets); >+ if (rc) { >+ LOG_ERROR("Error getting DM mapping for disk %s.\n", disk->name); >+ goto out; >+ } >+ >+ /* Reject all non-multipath devices. */ >+ if (targets->type != DM_TARGET_MULTIPATH) { >+ LOG_DEBUG("Disk %s is not a multipath device.\n", disk->name); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* Reject all multipath devices that >+ * were created by other EVMS plugins. >+ */ >+ rc = check_multipath_name(disk); >+ if (rc) { >+ LOG_DEBUG("Multipath disk %s belongs to another EVMS plugin.\n", >+ disk->name); >+ goto out; >+ } >+ >+ rc = update_multipath_child_list(targets); >+ if (rc) { >+ LOG_DEBUG("Error building list of children of " >+ "multipath disk %s.\n", disk->name); >+ goto out; >+ } >+ >+ ld->flags |= LD_FLAG_MULTIPATH; >+ >+out: >+ EngFncs->dm_deallocate_targets(targets); >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * remove_multipath_children >+ * >+ * Compare the multipath_children list with the discovery output list. Any >+ * disks on the multipath_children list must be removed from the output list. >+ **/ >+static void remove_multipath_children(list_anchor_t multipath_children, >+ list_anchor_t output_list) >+{ >+ list_element_t itr1, itr2, itr3; >+ storage_object_t * disk; >+ dm_device_t * child; >+ >+ LOG_ENTRY(); >+ >+ LIST_FOR_EACH(multipath_children, itr3, child) { >+ LIST_FOR_EACH_SAFE(output_list, itr1, itr2, disk) { >+ if (child->major == disk->dev_major && >+ child->minor == disk->dev_minor) { >+ EngFncs->delete_element(itr1); >+ close_dev(disk); >+ EngFncs->engine_free(disk->private_data); >+ disk->flags &= ~SOFLAG_ACTIVE; >+ EngFncs->free_logical_disk(disk); >+ } >+ } >+ } >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * get_geometry >+ * >+ * Use the HDIO_GETGEO_BIG or HDIO_GETGEO ioctl to get the disk's geometry. >+ * Check that the geometry is valid for a disk. >+ **/ >+static int get_geometry(storage_object_t * disk) >+{ >+ struct hd_big_geometry big_geometry; >+ struct hd_geometry geometry; >+ local_disk_t * ld = disk->private_data; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ rc = ioctl(ld->fd, HDIO_GETGEO, &geometry); >+ if (rc) { >+ rc = ioctl(ld->fd, HDIO_GETGEO_BIG, &big_geometry); >+ if (rc) { >+ rc = errno; >+ LOG_DEBUG("Error getting geometry for disk %s: %d: " >+ "%s.\n", disk->name, rc, strerror(rc)); >+ } else if (big_geometry.start != 0) { >+ /* A disk's geometry must start at offset 0. */ >+ LOG_DEBUG("Geometry for disk %s reports a non-zero starting " >+ "offset. Not a valid disk.\n", disk->name); >+ rc = EINVAL; >+ } else { >+ disk->geometry.cylinders = big_geometry.cylinders; >+ disk->geometry.heads = big_geometry.heads; >+ disk->geometry.sectors_per_track = big_geometry.sectors; >+ } >+ } else { >+ if (geometry.start != 0) { >+ /* A disk's geometry must start at offset 0. */ >+ LOG_DEBUG("Geometry for disk %s reports a non-zero starting " >+ "offset. Not a valid disk.\n", disk->name); >+ rc = EINVAL; >+ } else { >+ /* ala fdisk: never use geometry.cylinders - it is truncated */ >+ // disk->geometry.cylinders = geometry.cylinders; >+ disk->geometry.heads = geometry.heads; >+ disk->geometry.sectors_per_track = geometry.sectors; >+ } >+ } >+ >+ /* >+ * If the driver answered the ioctl but didn't supply a geometry, >+ * fill in a default geometry. This is what fdisk does. >+ */ >+ if (rc == 0) { >+ if (disk->geometry.heads == 0) { >+ disk->geometry.heads = 255; >+ } >+ if (disk->geometry.sectors_per_track == 0) { >+ disk->geometry.sectors_per_track = 63; >+ } >+ if (disk->geometry.cylinders == 0) { >+ disk->geometry.cylinders = disk->size / (255 * 63 * >+ (disk->geometry.bytes_per_sector / 512)); >+ } >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_fake_geometry >+ * >+ * Some drivers (notably loop, nbd, and drbd) don't >+ * support geometry, so we need to fake it. >+ **/ >+static int get_fake_geometry(storage_object_t * disk) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc = EINVAL; >+ >+ LOG_ENTRY(); >+ >+ if (disk->dev_major == LOOP_MAJOR || >+ disk->dev_major == NBD_MAJOR || >+ disk->dev_major == DRBD_MAJOR || >+ ld->flags & LD_FLAG_MULTIPATH) { >+ LOG_DEBUG("Creating fake geometry for disk %s.\n", disk->name); >+ disk->geometry.heads = 255; >+ disk->geometry.sectors_per_track = 63; >+ disk->geometry.cylinders = disk->size / (255 * 63); >+ rc = 0; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_block_size >+ * >+ * Use the BLKBSZGET ioctl to get the block-size for the specified disk. >+ **/ >+static int get_block_size(storage_object_t * disk) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc, block_size; >+ >+ LOG_ENTRY(); >+ >+ rc = ioctl(ld->fd, BLKBSZGET, &block_size); >+ if (rc) { >+ rc = errno; >+ LOG_ERROR("Error getting block size for disk %s: %d: %s.\n", >+ disk->name, rc, strerror(rc)); >+ } else { >+ LOG_DEBUG("Disk %s has block-size %d.\n", >+ disk->name, block_size); >+ disk->geometry.block_size = block_size; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * set_block_size >+ * >+ * Use the BLKBSZSET ioctl to set the disk's block-size. >+ **/ >+static int set_block_size(storage_object_t * disk, int block_size) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ rc = ioctl(ld->fd, BLKBSZSET, &block_size); >+ if (rc) { >+ rc = errno; >+ LOG_ERROR("Error setting block size (%d) for disk %s: %d: " >+ "%s.\n", block_size, disk->name, rc, strerror(rc)); >+ } else { >+ LOG_DEBUG("Setting disk %s block-size to %d.\n", >+ disk->name, block_size); >+ disk->geometry.block_size = block_size; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_hardsector_size >+ * >+ * Use the BLKSSZGET ioctl to get the disk's hard-sector-size. If the ioctl >+ * returns an error (some drivers don't support it yet), fall back on the >+ * default sector-size (which is the same default used in the kernel - see >+ * include/linux/blkdev.h::get_hardsect_size). >+ **/ >+static void get_hardsector_size(storage_object_t * disk) >+{ >+ u_int32_t hardsector_size; >+ local_disk_t * ld = disk->private_data; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ rc = ioctl(ld->fd, BLKSSZGET, &hardsector_size); >+ if (rc) { >+ rc = errno; >+ LOG_DEBUG("Error getting hardsector size for disk %s: %d: " >+ "%s.\n", disk->name, rc, strerror(rc)); >+ hardsector_size = EVMS_VSECTOR_SIZE; >+ } >+ disk->geometry.bytes_per_sector = hardsector_size; >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * find_disk_type >+ * >+ * Determine if this disk is IDE, SCSI, or something else. This is done by >+ * examining the name of the disk. Names starting with "hd" or that contain >+ * "ide" will be marked as IDE. Names starting with "sd" or that contain >+ * "scsi" will be marked as SCSI. >+ **/ >+static void find_disk_type(storage_object_t * disk) >+{ >+ local_disk_t * ld = disk->private_data; >+ >+ LOG_ENTRY(); >+ >+ if (!strncmp(disk->name, "hd", 2) || >+ strstr(disk->name, "ide")) { >+ ld->flags |= LD_FLAG_IDE; >+ } else if (!strncmp(disk->name, "sd", 2) || >+ strstr(disk->name, "scsi")) { >+ ld->flags |= LD_FLAG_SCSI; >+ } >+ >+ LOG_DEBUG("Type of disk %s is %s\n", disk->name, >+ (ld->flags & LD_FLAG_IDE) ? "IDE" : >+ (ld->flags & LD_FLAG_SCSI) ? "SCSI" : "Unknown"); >+ >+ LOG_EXIT_VOID(); >+} >+ >+/** >+ * create_logical_disk >+ * >+ * Allocate a new disk and initialize all fields. >+ **/ >+static storage_object_t * create_logical_disk(storage_object_t * working_disk) >+{ >+ storage_object_t * disk = NULL; >+ local_disk_t * working_ld = working_disk->private_data; >+ local_disk_t * ld; >+ char *name; >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ /* Replace exclaimation marks with slashes in the disk name. */ >+ for (name = working_disk->name; *name; name++) { >+ if (*name == '!') *name = '/'; >+ } >+ >+ rc = EngFncs->allocate_logical_disk(working_disk->name, &disk); >+ if (rc) { >+ LOG_SERIOUS("Error allocating new disk object for disk %s: %d: " >+ "%s.\n", working_disk->name, rc, EngFncs->strerror(rc)); >+ goto out; >+ } >+ >+ disk->private_data = EngFncs->engine_alloc(sizeof(local_disk_t)); >+ if (!disk->private_data) { >+ LOG_SERIOUS("Error allocating private data for disk %s.\n", >+ disk->name); >+ EngFncs->free_logical_disk(disk); >+ disk = NULL; >+ goto out; >+ } >+ ld = disk->private_data; >+ >+ /* Initialize the logical disk structure */ >+ disk->data_type = DATA_TYPE; >+ disk->dev_major = working_disk->dev_major; >+ disk->dev_minor = working_disk->dev_minor; >+ disk->plugin = my_plugin_record; >+ disk->flags = SOFLAG_ACTIVE; >+ disk->size = working_disk->size; >+ disk->geometry.cylinders = working_disk->geometry.cylinders; >+ disk->geometry.heads = working_disk->geometry.heads; >+ disk->geometry.sectors_per_track= working_disk->geometry.sectors_per_track; >+ disk->geometry.bytes_per_sector = working_disk->geometry.bytes_per_sector; >+ disk->geometry.block_size = working_disk->geometry.block_size; >+ >+ /* Fill in the boot cylinder limit (LBA of 1st sector above boot >+ * cylinder) for this drive. If the drive is too small then the limit >+ * is the size of the drive. Otherwise the limit is calculated. >+ */ >+ disk->geometry.boot_cylinder_limit = (disk->geometry.cylinders < 1024) ? >+ disk->size : >+ (disk->geometry.heads * disk->geometry.sectors_per_track * 1023); >+ >+ *ld = *working_ld; >+ ld->file_handle->disk = disk; >+ >+ find_disk_type(disk); >+ >+ LOG_DETAILS("New Logical Disk:\n"); >+ LOG_DETAILS(" name: %s\n", disk->name); >+ LOG_DETAILS(" size: %"PRIu64"\n", disk->size); >+ LOG_DETAILS(" device-number: %x:%x\n", disk->dev_major, disk->dev_minor); >+ LOG_DETAILS(" file-descriptor: %d\n", ld->fd); >+ LOG_DETAILS(" geometry:\n"); >+ LOG_DETAILS(" cylinders: %"PRIu64"\n", disk->geometry.cylinders); >+ LOG_DETAILS(" heads: %d\n", disk->geometry.heads); >+ LOG_DETAILS(" sectors: %d\n", disk->geometry.sectors_per_track); >+ LOG_DETAILS(" sector size: %d (bytes)\n", disk->geometry.bytes_per_sector); >+ LOG_DETAILS(" block size: %"PRIu64" (bytes)\n", disk->geometry.block_size); >+ >+out: >+ LOG_EXIT_PTR(disk); >+ return disk; >+} >+ >+/** >+ * LD_discover >+ **/ >+static int LD_discover(list_anchor_t input_list, >+ list_anchor_t output_list, >+ boolean final_call) >+{ >+ storage_object_t working_disk; >+ storage_object_t * disk; >+ local_disk_t working_ld; >+ list_element_t itr; >+ char * full_node_path; >+ uint count; >+ int rc, i; >+ >+ LOG_ENTRY(); >+ >+ /* Get the list of devices to examine. */ >+ if (sysfs_mount_point) { >+ get_sysfs_devs(); >+ } else { >+ get_legacy_devs(); >+ } >+ >+ for (i = 0; i < dev_names_glob.gl_pathc; i++) { >+ full_node_path = dev_names_glob.gl_pathv[i]; >+ LOG_DEBUG("Examining disk %s\n", full_node_path); >+ >+ /* Initialize the working disk object. */ >+ memset(&working_disk, 0, sizeof(working_disk)); >+ memset(&working_ld, 0, sizeof(working_ld)); >+ working_disk.private_data = &working_ld; >+ working_ld.fd = -1; >+ strncpy(working_disk.name, full_node_path + base_directory_len, >+ EVMS_NAME_SIZE); >+ >+ /* Get the device-number of the disk. */ >+ rc = get_disk_devnum(full_node_path, &working_disk, output_list); >+ if (rc) { >+ continue; >+ } >+ >+ /* Get the size of the disk. */ >+ rc = get_disk_size(full_node_path, &working_disk); >+ if (rc) { >+ continue; >+ } >+ >+ /* Open the disk. */ >+ rc = open_dev(&working_disk); >+ if (rc) { >+ continue; >+ } >+ >+ /* Check for DM-multipath devices. */ >+ rc = check_multipath(&working_disk); >+ if (rc) { >+ close_dev(&working_disk); >+ continue; >+ } >+ >+ /* Get the disk's hard-sector-size. */ >+ get_hardsector_size(&working_disk); >+ >+ /* Get the disk's geometry. */ >+ rc = get_geometry(&working_disk); >+ if (rc) { >+ rc = get_fake_geometry(&working_disk); >+ if (rc) { >+ close_dev(&working_disk); >+ continue; >+ } >+ } >+ >+ /* Get the disk's block-size. */ >+ rc = get_block_size(&working_disk); >+ if (rc) { >+ close_dev(&working_disk); >+ continue; >+ } >+ >+ /* Passed all checks. Create a new disk. */ >+ disk = create_logical_disk(&working_disk); >+ if (!disk) { >+ close_dev(&working_disk); >+ continue; >+ } >+ >+ /* Insert the new disk into ouput list. */ >+ itr = EngFncs->insert_thing(output_list, disk, >+ INSERT_AFTER, NULL); >+ if (!itr) { >+ LOG_SERIOUS("Error adding new disk %s to output list. " >+ "Deleting the disk.\n", disk->name); >+ EngFncs->engine_free(disk->private_data); >+ EngFncs->free_logical_disk(disk); >+ close_dev(&working_disk); >+ continue; >+ } >+ } >+ >+ remove_multipath_children(multipath_children, output_list); >+ >+ EngFncs->dm_deallocate_device_list(dm_devices); >+ EngFncs->destroy_list(multipath_children); >+ >+ count = EngFncs->list_count(output_list); >+ LOG_DEBUG("Discovered %d disks.\n", count); >+ LOG_EXIT_INT(0); >+ return 0; >+} >+ >+/** >+ * get_alignment_size >+ * >+ * Return the size (in bytes) of the alignment restrictions for O_DIRECT. On >+ * 2.5 kernels, this will be the disk's hard-sector-size. On 2.4 kernels, this >+ * will be the disk's block-size. Since block-size can change at run-time, >+ * always check the current block-size. Also, since we want access to as much >+ * of the disk as possible, try to set the block-size to 1k if it isn't >+ * already. >+ **/ >+static int get_alignment_size(storage_object_t * disk) >+{ >+ int size; >+ int min_block_size = max(disk->geometry.bytes_per_sector, 1024); >+ >+ LOG_ENTRY(); >+ >+ if (EngFncs->is_2_4_kernel()) { >+ get_block_size(disk); >+ size = disk->geometry.block_size; >+ if (size > min_block_size) { >+ set_block_size(disk, min_block_size); >+ size = disk->geometry.block_size; >+ } >+ } else { >+ size = disk->geometry.bytes_per_sector; >+ } >+ >+ LOG_EXIT_INT(size); >+ return size; >+} >+ >+/** >+ * check_alignment >+ * @align_size: >+ * @offset: >+ * @count: >+ * @buffer: >+ * >+ * Check whether the specified offset, count, and buffer are valid for the >+ * specified alignment restriction. >+ **/ >+static int check_alignment(int align_size, >+ lsn_t offset, >+ sector_count_t count, >+ void * buffer) >+{ >+ int align_sectors = align_size >> EVMS_VSECTOR_SIZE_SHIFT; >+ int align_mask = align_size - 1; >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ LOG_EXTRA("Checking alignment.\n"); >+ LOG_EXTRA("\tAlignment Size: %d bytes\n", align_size); >+ LOG_EXTRA("\tBuffer: 0x%p\n", buffer); >+ LOG_EXTRA("\tSector Offset: %"PRIu64"\n", offset); >+ LOG_EXTRA("\tSector Count: %"PRIu64"\n", count); >+ >+ if ((unsigned long)buffer & align_mask) { >+ rc = EINVAL; >+ } else if (offset % align_sectors) { >+ rc = EINVAL; >+ } else if (count % align_sectors) { >+ rc = EINVAL; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * get_aligned_buffer >+ * @offset: Starting offset (in sectors) of engine I/O request. >+ * @count: Size (in sectors) of engine I/O request. >+ * @align_size: Size (in bytes) that the I/O must be aligned on. >+ * @local_offset: Aligned starting offset (in sectors). >+ * @local_count: Aligned I/O size (in sectors). >+ * @buffer: Aligned data buffer. >+ * >+ * To use O_DIRECT, the buffer passed to read() or write() must be aligned on >+ * the device's block/sector size. The size and starting offset of the I/O must >+ * also be a multiple of the block/sector size. >+ **/ >+static int get_aligned_buffer(lsn_t offset, >+ sector_count_t count, >+ int align_size, >+ lsn_t * local_offset, >+ sector_count_t * local_count, >+ void ** buffer) >+{ >+ u_int32_t offset_diff; >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ >+ /* Round down starting offset to the alignment size. */ >+ *local_offset = round_down(offset, align_size); >+ >+ /* Difference between real offset and local offset. */ >+ offset_diff = offset - *local_offset; >+ >+ /* Round up total count of sectors to alignment size. */ >+ *local_count = round_up(count + offset_diff, align_size); >+ >+ /* Allocate the buffer that will actually perform the I/O. The >+ * memalign call guarantees that the allocated buffer is >+ * aligned on the desired alignment-size. >+ */ >+ *buffer = memalign(align_size, *local_count << EVMS_VSECTOR_SIZE_SHIFT); >+ if (!*buffer) { >+ rc = ENOMEM; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * LD_read >+ **/ >+static int LD_read(storage_object_t * disk, >+ lsn_t offset, >+ sector_count_t count, >+ void * buffer) >+{ >+ void * local_buffer = NULL; >+ lsn_t local_offset; >+ sector_count_t local_count; >+ local_disk_t * ld = disk->private_data; >+ int rc, align_size, aligned = FALSE; >+ >+ LOG_ENTRY(); >+ LOG_DEBUG("Read disk:%s offset:%"PRIu64" count:%"PRIu64"\n", >+ disk->name, offset, count); >+ >+ if (offset + count > disk->size) { >+ LOG_ERROR("Read request past end of disk.\n"); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ rc = read_from_cache(disk, offset, count, buffer); >+ if (!rc) { >+ /* Found in the cache. */ >+ goto out; >+ } >+ >+ /* Make sure the disk is open. */ >+ rc = open_dev(disk); >+ if (rc) { >+ goto out; >+ } >+ >+ /* Get the alignment restriction for O_DIRECT. */ >+ align_size = get_alignment_size(disk); >+ >+ /* Check if the supplied buffer, offset, and count >+ * are valid for the alignment restrictions. >+ */ >+ rc = check_alignment(align_size, offset, count, buffer); >+ if (rc) { >+ /* Get a data buffer aligned with this restriction. */ >+ rc = get_aligned_buffer(offset, count, align_size, &local_offset, >+ &local_count, &local_buffer); >+ if (rc) { >+ goto out; >+ } >+ } else { >+ aligned = TRUE; >+ local_offset = offset; >+ local_count = count; >+ local_buffer = buffer; >+ } >+ >+ /* Send the read to the engine. */ >+ rc = EngFncs->read_object(disk, ld->fd, local_buffer, >+ local_count << EVMS_VSECTOR_SIZE_SHIFT, >+ local_offset << EVMS_VSECTOR_SIZE_SHIFT); >+ if (rc < 0) { >+ rc = -rc; >+ goto out; >+ } >+ >+ /* Copy the data back to the caller's buffer. */ >+ if (!aligned) { >+ memcpy(buffer, local_buffer + >+ ((offset - local_offset) << EVMS_VSECTOR_SIZE_SHIFT), >+ count << EVMS_VSECTOR_SIZE_SHIFT); >+ } >+ >+ /* Record this I/O in the cache. */ >+ write_to_cache(disk, offset, count, buffer); >+ rc = 0; >+ >+out: >+ if (!aligned) { >+ free(local_buffer); >+ } >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * LD_write >+ **/ >+static int LD_write(storage_object_t * disk, >+ lsn_t offset, >+ sector_count_t count, >+ void * buffer) >+{ >+ void * local_buffer = NULL; >+ lsn_t local_offset; >+ sector_count_t local_count; >+ local_disk_t * ld = disk->private_data; >+ int rc, align_size, aligned = FALSE; >+ >+ LOG_ENTRY(); >+ LOG_DEBUG("Write disk:%s offset:%"PRIu64" count:%"PRIu64"\n", >+ disk->name, offset, count); >+ >+ if (offset + count > disk->size) { >+ LOG_ERROR("Write request past end of disk.\n"); >+ rc = EINVAL; >+ goto out; >+ } >+ >+ /* Make sure the disk is open. */ >+ rc = open_dev(disk); >+ if (rc) { >+ goto out; >+ } >+ >+ /* Get the alignment restriction for O_DIRECT. */ >+ align_size = get_alignment_size(disk); >+ >+ /* Check if the supplied buffer, offset, and count >+ * are valid for the alignment restrictions. >+ */ >+ rc = check_alignment(align_size, offset, count, buffer); >+ if (rc) { >+ /* Get a data buffer aligned with this restriction. */ >+ rc = get_aligned_buffer(offset, count, align_size, &local_offset, >+ &local_count, &local_buffer); >+ if (rc) { >+ goto out; >+ } >+ } else { >+ aligned = TRUE; >+ local_offset = offset; >+ local_count = count; >+ local_buffer = buffer; >+ } >+ >+ if (local_count != count) { >+ rc = EngFncs->read_object(disk, ld->fd, local_buffer, >+ local_count << EVMS_VSECTOR_SIZE_SHIFT, >+ local_offset << EVMS_VSECTOR_SIZE_SHIFT); >+ if (rc < 0) { >+ rc = -rc; >+ goto out; >+ } >+ } >+ >+ /* Put user data at the right place in the buffer */ >+ if (!aligned) { >+ memcpy(local_buffer + >+ ((offset - local_offset) << EVMS_VSECTOR_SIZE_SHIFT), >+ buffer, count << EVMS_VSECTOR_SIZE_SHIFT); >+ } >+ >+ /* Send the write to the engine. */ >+ rc = EngFncs->write_object(disk, ld->fd, local_buffer, >+ local_count << EVMS_VSECTOR_SIZE_SHIFT, >+ local_offset << EVMS_VSECTOR_SIZE_SHIFT); >+ if (rc < 0) { >+ rc = -rc; >+ goto out; >+ } >+ >+ /* The cache is too simple to do real caching. It's really only a read >+ * cache. A write, which should not happen during discovery, means the >+ * contents of the cache may not be up to date. Purge the cache and >+ * start caching all over again. >+ */ >+ purge_cache(); >+ rc = 0; >+ >+out: >+ if (!aligned) { >+ free(local_buffer); >+ } >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * LD_discard >+ * >+ * We don't expect to get called on this API. Just like commit. >+ **/ >+static int LD_discard(list_anchor_t disks) >+{ >+ LOG_ENTRY(); >+ LOG_EXIT_INT(0); >+ return 0; >+} >+ >+/** >+ * LD_add_sectors_to_kill_list >+ **/ >+static int LD_add_sectors_to_kill_list(storage_object_t * disk, >+ lsn_t lsn, >+ sector_count_t count) >+{ >+ int rc; >+ LOG_ENTRY(); >+ >+ if (lsn + count > disk->size) { >+ LOG_ERROR("Kill-sectors request past end of disk %s.\n", >+ disk->name); >+ rc = EINVAL; >+ } else { >+ rc = EngFncs->add_sectors_to_kill_list(disk, lsn, count); >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * LD_commit_changes >+ * >+ * Disk manager doesn't do anything during commit. Just return success. >+ **/ >+static int LD_commit_changes(storage_object_t * disk, commit_phase_t phase) >+{ >+ LOG_ENTRY(); >+ LOG_EXIT_INT(0); >+ return 0; >+} >+ >+/** >+ * LD_get_info >+ * >+ * Return information about this disk to display to the user. >+ **/ >+static int LD_get_info(storage_object_t * disk, >+ char * name, >+ extended_info_array_t ** info) >+{ >+ local_disk_t * ld = disk->private_data; >+ int rc = EINVAL; >+ >+ LOG_ENTRY(); >+ >+ *info = NULL; >+ >+ if (!name) { >+ rc = get_basic_info(disk, info); >+ } else if (!strncasecmp(name, "Type", 4)) { >+ if (ld->flags & LD_FLAG_IDE) { >+ rc = get_ide_info(disk, info); >+ } else if (ld->flags & LD_FLAG_SCSI) { >+ rc = get_scsi_info(disk, info); >+ } >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+/** >+ * LD_get_plugin_info >+ * >+ * Returns plug-in specific information >+ **/ >+static int LD_get_plugin_info(char * descriptor_name, >+ extended_info_array_t ** info) >+{ >+ int rc = EINVAL; >+ extended_info_array_t * Info; >+ char version_string[64]; >+ char required_engine_api_version_string[64]; >+ char required_plugin_api_version_string[64]; >+ >+ LOG_ENTRY(); >+ >+ if (!info) { >+ goto out; >+ } >+ *info = NULL; >+ >+ if (descriptor_name) { >+ goto out; >+ } >+ >+ Info = EngFncs->engine_alloc(sizeof(extended_info_array_t) + >+ 6 * sizeof(extended_info_t)); >+ if (!Info) { >+ rc = ENOMEM; >+ goto out; >+ } >+ >+ Info->count = 6; >+ >+ sprintf(version_string, "%d.%d.%d", >+ MAJOR_VERSION, MINOR_VERSION, PATCH_LEVEL); >+ >+ sprintf(required_engine_api_version_string, "%d.%d.%d", >+ my_plugin_record->required_engine_api_version.major, >+ my_plugin_record->required_engine_api_version.minor, >+ my_plugin_record->required_engine_api_version.patchlevel); >+ >+ sprintf(required_plugin_api_version_string, "%d.%d.%d", >+ my_plugin_record->required_plugin_api_version.plugin.major, >+ my_plugin_record->required_plugin_api_version.plugin.minor, >+ my_plugin_record->required_plugin_api_version.plugin.patchlevel); >+ >+ Info->info[0].name = EngFncs->engine_strdup("Short Name"); >+ Info->info[0].title = EngFncs->engine_strdup(_("Short Name")); >+ Info->info[0].desc = EngFncs->engine_strdup(_("A short name given to this plug-in")); >+ Info->info[0].type = EVMS_Type_String; >+ Info->info[0].value.s = EngFncs->engine_strdup(my_plugin_record->short_name); >+ >+ Info->info[1].name = EngFncs->engine_strdup("Long Name"); >+ Info->info[1].title = EngFncs->engine_strdup(_("Long Name")); >+ Info->info[1].desc = EngFncs->engine_strdup(_("A longer, more descriptive name for this plug-in")); >+ Info->info[1].type = EVMS_Type_String; >+ Info->info[1].value.s = EngFncs->engine_strdup(my_plugin_record->long_name); >+ >+ Info->info[2].name = EngFncs->engine_strdup("Type"); >+ Info->info[2].title = EngFncs->engine_strdup(_("Plug-in Type")); >+ Info->info[2].desc = EngFncs->engine_strdup(_("There are various types of plug-ins, each responsible for some kind of storage object or logical volume.")); >+ Info->info[2].type = EVMS_Type_String; >+ Info->info[2].value.s = EngFncs->engine_strdup(_("Device Manager")); >+ >+ Info->info[3].name = EngFncs->engine_strdup("Version"); >+ Info->info[3].title = EngFncs->engine_strdup(_("Plug-in Version")); >+ Info->info[3].desc = EngFncs->engine_strdup(_("Version number of this plug-in")); >+ Info->info[3].type = EVMS_Type_String; >+ Info->info[3].value.s = EngFncs->engine_strdup(version_string); >+ >+ Info->info[4].name = EngFncs->engine_strdup("Required Engine Services Version"); >+ Info->info[4].title = EngFncs->engine_strdup(_("Required Engine Services Version")); >+ Info->info[4].desc = EngFncs->engine_strdup(_("Version of the Engine services that this plug-in requires. " >+ "It will not run on older versions of the Engine services.")); >+ Info->info[4].type = EVMS_Type_String; >+ Info->info[4].value.s = EngFncs->engine_strdup(required_engine_api_version_string); >+ >+ Info->info[5].name = EngFncs->engine_strdup("Required Plug-in API Version"); >+ Info->info[5].title = EngFncs->engine_strdup(_("Required Plug-in API Version")); >+ Info->info[5].desc = EngFncs->engine_strdup(_("Version of the Engine plug-in API that this plug-in requires. " >+ "It will not run on older versions of the Engine plug-in API.")); >+ Info->info[5].type = EVMS_Type_String; >+ Info->info[5].value.s = EngFncs->engine_strdup(required_plugin_api_version_string); >+ >+ *info = Info; >+ rc = 0; >+ >+out: >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+ >+static int LD_get_plugin_functions(storage_object_t * object, >+ function_info_array_t * * actions) >+{ >+ LOG_ENTRY(); >+ >+ /* >+ * The Local Disk Manager has plug-in functions, but they are not >+ * available for the asking. Those who want to use them must know >+ * what they are and call the plug-in function directly which will >+ * be processed by LD_plugin_function() below. >+ */ >+ >+ LOG_EXIT_INT(ENOSYS); >+ return ENOSYS; >+} >+ >+static int LD_plugin_function(storage_object_t * object, >+ task_action_t action, >+ list_anchor_t objects, >+ option_array_t * options) >+{ >+ int rc = 0; >+ >+ LOG_ENTRY(); >+ >+ switch (action) { >+ case LDM_Start_Caching: >+ LOG_DEBUG("Start caching\n"); >+ initialize_cache(); >+ break; >+ >+ case LDM_Stop_Caching: >+ LOG_DEBUG("Stop caching\n"); >+ destroy_cache(); >+ break; >+ >+ case LDM_Open_Disk: >+ if (object->plugin == my_plugin_record) { >+ LOG_DEBUG("Open disk %s\n", object->name); >+ rc = open_dev(object); >+ >+ } else { >+ LOG_ERROR("%s is not managed by %s.\n", >+ object->name, my_plugin_record->short_name); >+ rc = EINVAL; >+ } >+ break; >+ >+ case LDM_Close_Disk: >+ if (object->plugin == my_plugin_record) { >+ LOG_DEBUG("Close disk %s\n", object->name); >+ close_dev(object); >+ >+ } else { >+ LOG_ERROR("%s is not managed by %s.\n", >+ object->name, my_plugin_record->short_name); >+ rc = EINVAL; >+ } >+ break; >+ >+ default: >+ LOG_ERROR("%d is not a valid function code.\n", action); >+ rc = EINVAL; >+ } >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+ >+static int LD_backup_metadata(storage_object_t * disk) { >+ >+ int rc; >+ >+ LOG_ENTRY(); >+ >+ if (disk->plugin->id != EVMS_DISK_PLUGIN_ID) { >+ LOG_ERROR("I don't own object %s.\n", disk->name); >+ LOG_EXIT_INT(EINVAL); >+ return EINVAL; >+ } >+ >+ rc = EngFncs->save_metadata(disk->name, NULL, 0, 0, NULL); >+ >+ LOG_EXIT_INT(rc); >+ return rc; >+} >+ >+ >+static plugin_functions_t ft_sysfs = { >+ .setup_evms_plugin = LD_setup, >+ .cleanup_evms_plugin = LD_cleanup, >+ .discover = LD_discover, >+ .discard = LD_discard, >+ .add_sectors_to_kill_list = LD_add_sectors_to_kill_list, >+ .commit_changes = LD_commit_changes, >+ .get_info = LD_get_info, >+ .get_plugin_info = LD_get_plugin_info, >+ .read = LD_read, >+ .write = LD_write, >+ .get_plugin_functions = LD_get_plugin_functions, >+ .plugin_function = LD_plugin_function, >+ .backup_metadata = LD_backup_metadata >+}; >+ >+plugin_record_t LD_Plugin = { >+ .id = EVMS_DISK_PLUGIN_ID, >+ .version = { >+ .major = MAJOR_VERSION, >+ .minor = MINOR_VERSION, >+ .patchlevel = PATCH_LEVEL >+ }, >+ .required_engine_api_version = { >+ .major = 15, >+ .minor = 0, >+ .patchlevel = 0 >+ }, >+ .required_plugin_api_version = { >+ .plugin = { >+ .major = 13, >+ .minor = 1, >+ .patchlevel = 0 >+ } >+ }, >+ .short_name = EVMS_DISK_PLUGIN_SHORT_NAME, >+ .long_name = EVMS_DISK_PLUGIN_LONG_NAME, >+ .oem_name = EVMS_IBM_OEM_NAME, >+ .functions = { >+ .plugin = &ft_sysfs >+ }, >+ .container_functions = NULL >+}; >+ >+plugin_record_t * my_plugin_record = &LD_Plugin; >+ >+plugin_record_t * evms_plugin_records[] = { &LD_Plugin, >+ NULL }; >+ >diff -Nrup evms-2.5.4/plugins/disk/localdskmgr.h evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.h >--- evms-2.5.4/plugins/disk/localdskmgr.h 2005-05-17 13:38:55.000000000 -0700 >+++ evms-2.5.4-patched_with_dev_mapper_input/plugins/disk/localdskmgr.h 2005-12-17 15:29:48.000000000 -0800 >@@ -80,6 +80,7 @@ typedef struct local_disk { > #define LD_FLAG_MULTIPATH (1 << 0) > #define LD_FLAG_IDE (1 << 1) > #define LD_FLAG_SCSI (1 << 2) >+#define LD_FLAG_USERDM (1 << 3) > > extern engine_functions_t *EngFncs; > extern plugin_record_t *my_plugin_record;
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Diff
Attachments on
bug 115875
:
74958
|
74963
|
74965
|
74966
|
74968
|
74969
|
74970
|
74971
|
74973
|
81447