g13: First chunk of code to support dm-crypt.
[gnupg.git] / g13 / sh-blockdev.c
1 /* sh-blockdev.c - Block device functions for g13-syshelp
2  * Copyright (C) 2015 Werner Koch
3  *
4  * This file is part of GnuPG.
5  *
6  * GnuPG is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GnuPG is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <stdarg.h>
25 #include <errno.h>
26 #include <assert.h>
27 #include <limits.h>
28
29 #include "g13-syshelp.h"
30 #include <assuan.h>
31 #include "i18n.h"
32 #include "keyblob.h"
33
34 #ifndef HAVE_STRTOULL
35 # error building this tool requires strtoull(3)
36 #endif
37 #ifndef ULLONG_MAX
38 # error ULLONG_MAX missing
39 #endif
40
41
42 /* Return the size measured in the number of 512 byte sectors for the
43    block device NAME.  */
44 gpg_error_t
45 sh_blockdev_getsz (const char *name, unsigned long long *r_nblocks)
46 {
47   gpg_error_t err;
48   const char *argv[3];
49   char *result;
50
51   *r_nblocks = 0;
52   argv[0] = "--getsz";
53   argv[1] = name;
54   argv[2] = NULL;
55   err = sh_exec_tool ("/sbin/blockdev", argv, NULL, &result, NULL);
56   if (!err)
57     {
58       gpg_err_set_errno (0);
59       *r_nblocks = strtoull (result, NULL, 10);
60       if (*r_nblocks == ULLONG_MAX && errno)
61         {
62           err = gpg_error_from_syserror ();
63           *r_nblocks = 0;
64         }
65       xfree (result);
66     }
67   return err;
68 }
69
70
71 /* Return 0 if the device NAME looks like an empty partition. */
72 gpg_error_t
73 sh_is_empty_partition (const char *name)
74 {
75   gpg_error_t err;
76   const char *argv[6];
77   char *buffer;
78   estream_t fp;
79   char *p;
80   size_t nread;
81
82   argv[0] = "-o";
83   argv[1] = "value";
84   argv[2] = "-s";
85   argv[3] = "UUID";
86   argv[4] = name;
87   argv[5] = NULL;
88   err = sh_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
89   if (err)
90     return gpg_error (GPG_ERR_FALSE);
91   if (*buffer)
92     {
93       /* There seems to be an UUID - thus we have a file system.  */
94       xfree (buffer);
95       return gpg_error (GPG_ERR_FALSE);
96     }
97   xfree (buffer);
98
99   argv[0] = "-o";
100   argv[1] = "value";
101   argv[2] = "-s";
102   argv[3] = "PARTUUID";
103   argv[4] = name;
104   argv[5] = NULL;
105   err = sh_exec_tool ("/sbin/blkid", argv, NULL, &buffer, NULL);
106   if (err)
107     return gpg_error (GPG_ERR_FALSE);
108   if (!*buffer)
109     {
110       /* If there is no PARTUUID we assume that name has already a
111          mapped partition.  */
112       xfree (buffer);
113       return gpg_error (GPG_ERR_FALSE);
114     }
115   xfree (buffer);
116
117   /* As a safeguard we require that the first 32k of a partition are
118      all zero before we assume the partition is empty.  */
119   buffer = xtrymalloc (32 * 1024);
120   if (!buffer)
121     return gpg_error_from_syserror ();
122   fp = es_fopen (name, "rb,samethread");
123   if (!fp)
124     {
125       err = gpg_error_from_syserror ();
126       log_error ("error opening '%s': %s\n", name, gpg_strerror (err));
127       xfree (buffer);
128       return gpg_error (GPG_ERR_FALSE);
129     }
130   if (es_read (fp, buffer, 32 * 1024, &nread))
131     err = gpg_error_from_syserror ();
132   else if (nread != 32 *1024)
133     err = gpg_error (GPG_ERR_TOO_SHORT);
134   else
135     err = 0;
136   es_fclose (fp);
137   if (err)
138     {
139       log_error ("error reading the first 32 KiB from '%s': %s\n",
140                  name, gpg_strerror (err));
141       xfree (buffer);
142       return err;
143     }
144   for (p=buffer; nread && !*p; nread--, p++)
145     ;
146   xfree (buffer);
147   if (nread)
148     return gpg_error (GPG_ERR_FALSE);  /* No all zeroes.  */
149
150   return 0;
151 }