1 /* -*- mode: C; c-file-style: "gnu"; indent-tabs-mode: nil; -*-
2  *
3  * Copyright (C) 2013 Marius Vollmer <marius.vollmer@redhat.com>
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 2 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  */
20 
21 #include "config.h"
22 #include <glib/gi18n-lib.h>
23 #include <glib/gstdio.h>
24 #include <gio/gunixfdlist.h>
25 
26 #include <stdio.h>
27 #include <ctype.h>
28 
29 #include <sys/types.h>
30 #include <sys/stat.h>
31 #include <fcntl.h>
32 #include <errno.h>
33 #include <pwd.h>
34 #include <fcntl.h>
35 #include <sys/ioctl.h>
36 #include <linux/fs.h>
37 
38 #include <blockdev/lvm.h>
39 
40 #include <limits.h>
41 #include <stdlib.h>
42 
43 #include <src/udisksdaemon.h>
44 #include <src/udisksdaemonutil.h>
45 #include <src/udisksstate.h>
46 #include <src/udiskslogging.h>
47 #include <src/udiskslinuxblockobject.h>
48 #include <src/udiskslinuxdriveobject.h>
49 #include <src/udisksmodulemanager.h>
50 
51 #include "udiskslvm2daemonutil.h"
52 
53 /**
54  * SECTION:udiskslvm2daemonutil
55  * @title: Utilities
56  * @short_description: Various utility routines
57  *
58  * Various utility routines.
59  */
60 
61 gboolean
udisks_daemon_util_lvm2_block_is_unused(UDisksBlock * block,GError ** error)62 udisks_daemon_util_lvm2_block_is_unused (UDisksBlock  *block,
63                                          GError      **error)
64 {
65   const gchar *device_file;
66   int fd;
67 
68   device_file = udisks_block_get_device (block);
69   fd = open (device_file, O_RDONLY | O_EXCL);
70   if (fd < 0)
71     {
72       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
73                    "Error opening device %s for unused block device detection: %m",
74                    device_file);
75       return FALSE;
76     }
77   close (fd);
78 
79   return TRUE;
80 }
81 
82 static gboolean
run_sync(const gchar * prog,...)83 run_sync (const gchar *prog, ...)
84 {
85   va_list ap;
86   GError **error;
87   enum { max_argc = 20 };
88   const gchar *argv[max_argc+1];
89   int argc = 0;
90   const gchar *arg;
91   gchar *standard_output;
92   gchar *standard_error;
93   gint exit_status;
94 
95   argv[argc++] = prog;
96   va_start (ap, prog);
97   while ((arg = va_arg (ap, const gchar *)))
98     {
99       if (argc < max_argc)
100         argv[argc] = arg;
101       argc++;
102     }
103   error = va_arg (ap, GError **);
104   va_end (ap);
105 
106   if (argc > max_argc)
107     {
108       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
109                    "Too many arguments.");
110       return FALSE;
111     }
112 
113   argv[argc] = NULL;
114   if (!g_spawn_sync (NULL,
115                      (gchar **)argv,
116                      NULL,
117                      G_SPAWN_SEARCH_PATH,
118                      NULL,
119                      NULL,
120                      &standard_output,
121                      &standard_error,
122                      &exit_status,
123                      error))
124     return FALSE;
125 
126   if (!g_spawn_check_exit_status (exit_status, error))
127     {
128       g_prefix_error (error, "stdout: '%s', stderr: '%s', ", standard_output, standard_error);
129       g_free (standard_output);
130       g_free (standard_error);
131       return FALSE;
132     }
133 
134   g_free (standard_output);
135   g_free (standard_error);
136   return TRUE;
137 }
138 
139 gboolean
udisks_daemon_util_lvm2_wipe_block(UDisksDaemon * daemon,UDisksBlock * block,GError ** error)140 udisks_daemon_util_lvm2_wipe_block (UDisksDaemon  *daemon,
141                                     UDisksBlock   *block,
142                                     GError       **error)
143 {
144   UDisksObject *block_object;
145   UDisksPhysicalVolume *physical_volume;
146   const gchar *volume_group_objpath;
147   UDisksObject *volume_group_object = NULL;
148   UDisksVolumeGroup *volume_group;
149   gchar *volume_group_name = NULL;
150   gboolean was_partitioned;
151 
152   const gchar *device_file;
153   int fd = -1;
154   gchar zeroes[512];
155   gboolean ret = TRUE;
156   GError *local_error = NULL;
157 
158   /* Find the name of the volume group that this device is a physical
159    * member of, if any.  Easy.
160    */
161 
162   block_object = UDISKS_OBJECT (g_dbus_interface_get_object (G_DBUS_INTERFACE (block)));
163   physical_volume = udisks_object_peek_physical_volume (block_object);
164   if (physical_volume)
165     {
166       volume_group_objpath = udisks_physical_volume_get_volume_group (physical_volume);
167       volume_group_object = udisks_daemon_find_object (daemon, volume_group_objpath);
168       if (volume_group_object)
169         {
170           volume_group = udisks_object_peek_volume_group (volume_group_object);
171           if (volume_group)
172             volume_group_name = g_strdup (udisks_volume_group_get_name (volume_group));
173         }
174     }
175 
176   was_partitioned = (udisks_object_peek_partition_table (block_object) != NULL);
177 
178   device_file = udisks_block_get_device (block);
179 
180   /* Remove partition table */
181   memset (zeroes, 0, 512);
182   fd = open (device_file, O_RDWR | O_EXCL);
183   if (fd < 0)
184     {
185       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
186                    "Error opening device %s for wiping: %m",
187                    device_file);
188       ret = FALSE;
189       goto out;
190     }
191 
192   if (write (fd, zeroes, 512) != 512)
193     {
194       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
195                    "Error erasing device %s: %m",
196                    device_file);
197       ret = FALSE;
198       goto out;
199     }
200 
201   if (was_partitioned && ioctl (fd, BLKRRPART, NULL) < 0)
202     {
203       g_set_error (error, UDISKS_ERROR, UDISKS_ERROR_FAILED,
204                    "Error removing partition devices of %s: %m",
205                    device_file);
206       ret = FALSE;
207       goto out;
208     }
209   close (fd);
210   fd = -1;
211 
212   /* wipe other labels */
213   if (!run_sync ("wipefs", "-a", device_file, NULL, error))
214     {
215       ret = FALSE;
216       goto out;
217     }
218 
219   /* Try to bring affected volume group back into consistency. */
220   if (volume_group_name != NULL)
221     if (!bd_lvm_vgreduce (volume_group_name, NULL /* device */, NULL /* extra */, &local_error)) {
222       udisks_warning ("%s", local_error->message);
223       g_clear_error (&local_error);
224     }
225 
226   /* Make sure lvmetad knows about all this.
227    *
228    * XXX - We need to do this because of a bug in the LVM udev rules
229    * which often fail to run pvscan on "change" events.
230    *
231    * https://bugzilla.redhat.com/show_bug.cgi?id=1063813
232    */
233   if (!run_sync ("pvscan", "--cache", device_file, NULL, &local_error))
234     {
235       udisks_warning ("%s", local_error->message);
236       g_clear_error (&local_error);
237     }
238 
239  out:
240   if (fd >= 0)
241     close (fd);
242   g_clear_object (&volume_group_object);
243   g_free (volume_group_name);
244   return ret;
245 }
246 
247 /* -------------------------------------------------------------------------------- */
248 
249 gboolean
udisks_daemon_util_lvm2_name_is_reserved(const gchar * name)250 udisks_daemon_util_lvm2_name_is_reserved (const gchar *name)
251 {
252  return (strchr (name, '[')
253          || strchr (name, ']')
254          || strstr (name, "_mlog")
255          || strstr (name, "_mimage")
256          || strstr (name, "_rimage")
257          || strstr (name, "_rmeta")
258          || strstr (name, "_tdata")
259          || strstr (name, "_tmeta")
260          || strstr (name, "_pmspare")
261          || g_str_has_prefix (name, "pvmove")
262          || g_str_has_prefix (name, "snapshot"));
263 }
264 
265 /* -------------------------------------------------------------------------------- */
266 
267 void
udisks_daemon_util_lvm2_trigger_udev(const gchar * device_file)268 udisks_daemon_util_lvm2_trigger_udev (const gchar *device_file)
269 {
270   int fd = open (device_file, O_RDWR);
271   if (fd >= 0)
272     close (fd);
273 }
274