1 /* SPDX-License-Identifier: GPL-2.0-or-later */
2 /*
3  * probe disks for filesystems and partitions
4  *
5  * Copyright © 2011 Karel Zak <kzak@redhat.com>
6  */
7 
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <getopt.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <sys/stat.h>
14 
15 #include "sd-id128.h"
16 
17 #include "alloc-util.h"
18 #include "blkid-util.h"
19 #include "device-util.h"
20 #include "efi-loader.h"
21 #include "errno-util.h"
22 #include "fd-util.h"
23 #include "gpt.h"
24 #include "parse-util.h"
25 #include "string-util.h"
26 #include "strxcpyx.h"
27 #include "udev-builtin.h"
28 
print_property(sd_device * dev,bool test,const char * name,const char * value)29 static void print_property(sd_device *dev, bool test, const char *name, const char *value) {
30         char s[256];
31 
32         s[0] = '\0';
33 
34         if (streq(name, "TYPE")) {
35                 udev_builtin_add_property(dev, test, "ID_FS_TYPE", value);
36 
37         } else if (streq(name, "USAGE")) {
38                 udev_builtin_add_property(dev, test, "ID_FS_USAGE", value);
39 
40         } else if (streq(name, "VERSION")) {
41                 udev_builtin_add_property(dev, test, "ID_FS_VERSION", value);
42 
43         } else if (streq(name, "UUID")) {
44                 blkid_safe_string(value, s, sizeof(s));
45                 udev_builtin_add_property(dev, test, "ID_FS_UUID", s);
46                 blkid_encode_string(value, s, sizeof(s));
47                 udev_builtin_add_property(dev, test, "ID_FS_UUID_ENC", s);
48 
49         } else if (streq(name, "UUID_SUB")) {
50                 blkid_safe_string(value, s, sizeof(s));
51                 udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB", s);
52                 blkid_encode_string(value, s, sizeof(s));
53                 udev_builtin_add_property(dev, test, "ID_FS_UUID_SUB_ENC", s);
54 
55         } else if (streq(name, "LABEL")) {
56                 blkid_safe_string(value, s, sizeof(s));
57                 udev_builtin_add_property(dev, test, "ID_FS_LABEL", s);
58                 blkid_encode_string(value, s, sizeof(s));
59                 udev_builtin_add_property(dev, test, "ID_FS_LABEL_ENC", s);
60 
61         } else if (streq(name, "PTTYPE")) {
62                 udev_builtin_add_property(dev, test, "ID_PART_TABLE_TYPE", value);
63 
64         } else if (streq(name, "PTUUID")) {
65                 udev_builtin_add_property(dev, test, "ID_PART_TABLE_UUID", value);
66 
67         } else if (streq(name, "PART_ENTRY_NAME")) {
68                 blkid_encode_string(value, s, sizeof(s));
69                 udev_builtin_add_property(dev, test, "ID_PART_ENTRY_NAME", s);
70 
71         } else if (streq(name, "PART_ENTRY_TYPE")) {
72                 blkid_encode_string(value, s, sizeof(s));
73                 udev_builtin_add_property(dev, test, "ID_PART_ENTRY_TYPE", s);
74 
75         } else if (startswith(name, "PART_ENTRY_")) {
76                 strscpyl(s, sizeof(s), "ID_", name, NULL);
77                 udev_builtin_add_property(dev, test, s, value);
78 
79         } else if (streq(name, "SYSTEM_ID")) {
80                 blkid_encode_string(value, s, sizeof(s));
81                 udev_builtin_add_property(dev, test, "ID_FS_SYSTEM_ID", s);
82 
83         } else if (streq(name, "PUBLISHER_ID")) {
84                 blkid_encode_string(value, s, sizeof(s));
85                 udev_builtin_add_property(dev, test, "ID_FS_PUBLISHER_ID", s);
86 
87         } else if (streq(name, "APPLICATION_ID")) {
88                 blkid_encode_string(value, s, sizeof(s));
89                 udev_builtin_add_property(dev, test, "ID_FS_APPLICATION_ID", s);
90 
91         } else if (streq(name, "BOOT_SYSTEM_ID")) {
92                 blkid_encode_string(value, s, sizeof(s));
93                 udev_builtin_add_property(dev, test, "ID_FS_BOOT_SYSTEM_ID", s);
94 
95         } else if (streq(name, "VOLUME_ID")) {
96                 blkid_encode_string(value, s, sizeof(s));
97                 udev_builtin_add_property(dev, test, "ID_FS_VOLUME_ID", s);
98 
99         } else if (streq(name, "LOGICAL_VOLUME_ID")) {
100                 blkid_encode_string(value, s, sizeof(s));
101                 udev_builtin_add_property(dev, test, "ID_FS_LOGICAL_VOLUME_ID", s);
102 
103         } else if (streq(name, "VOLUME_SET_ID")) {
104                 blkid_encode_string(value, s, sizeof(s));
105                 udev_builtin_add_property(dev, test, "ID_FS_VOLUME_SET_ID", s);
106 
107         } else if (streq(name, "DATA_PREPARER_ID")) {
108                 blkid_encode_string(value, s, sizeof(s));
109                 udev_builtin_add_property(dev, test, "ID_FS_DATA_PREPARER_ID", s);
110         }
111 }
112 
find_gpt_root(sd_device * dev,blkid_probe pr,bool test)113 static int find_gpt_root(sd_device *dev, blkid_probe pr, bool test) {
114 
115 #if defined(GPT_ROOT_NATIVE) && ENABLE_EFI
116 
117         _cleanup_free_ char *root_id = NULL, *root_label = NULL;
118         bool found_esp = false;
119         int r;
120 
121         assert(pr);
122 
123         /* Iterate through the partitions on this disk, and see if the
124          * EFI ESP we booted from is on it. If so, find the first root
125          * disk, and add a property indicating its partition UUID. */
126 
127         errno = 0;
128         blkid_partlist pl = blkid_probe_get_partitions(pr);
129         if (!pl)
130                 return errno_or_else(ENOMEM);
131 
132         int nvals = blkid_partlist_numof_partitions(pl);
133         for (int i = 0; i < nvals; i++) {
134                 blkid_partition pp;
135                 const char *stype, *sid, *label;
136                 sd_id128_t type;
137 
138                 pp = blkid_partlist_get_partition(pl, i);
139                 if (!pp)
140                         continue;
141 
142                 sid = blkid_partition_get_uuid(pp);
143                 if (!sid)
144                         continue;
145 
146                 label = blkid_partition_get_name(pp); /* returns NULL if empty */
147 
148                 stype = blkid_partition_get_type_string(pp);
149                 if (!stype)
150                         continue;
151 
152                 if (sd_id128_from_string(stype, &type) < 0)
153                         continue;
154 
155                 if (sd_id128_equal(type, GPT_ESP)) {
156                         sd_id128_t id, esp;
157 
158                         /* We found an ESP, let's see if it matches
159                          * the ESP we booted from. */
160 
161                         if (sd_id128_from_string(sid, &id) < 0)
162                                 continue;
163 
164                         r = efi_loader_get_device_part_uuid(&esp);
165                         if (r < 0)
166                                 return r;
167 
168                         if (sd_id128_equal(id, esp))
169                                 found_esp = true;
170 
171                 } else if (sd_id128_equal(type, GPT_ROOT_NATIVE)) {
172                         unsigned long long flags;
173 
174                         flags = blkid_partition_get_flags(pp);
175                         if (flags & GPT_FLAG_NO_AUTO)
176                                 continue;
177 
178                         /* We found a suitable root partition, let's remember the first one, or the one with
179                          * the newest version, as determined by comparing the partition labels. */
180 
181                         if (!root_id || strverscmp_improved(label, root_label) > 0) {
182                                 r = free_and_strdup(&root_id, sid);
183                                 if (r < 0)
184                                         return r;
185 
186                                 r = free_and_strdup(&root_label, label);
187                                 if (r < 0)
188                                         return r;
189                         }
190                 }
191         }
192 
193         /* We found the ESP on this disk, and also found a root
194          * partition, nice! Let's export its UUID */
195         if (found_esp && root_id)
196                 udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT_UUID", root_id);
197 #endif
198 
199         return 0;
200 }
201 
probe_superblocks(blkid_probe pr)202 static int probe_superblocks(blkid_probe pr) {
203         struct stat st;
204         int rc;
205 
206         /* TODO: Return negative errno. */
207 
208         if (fstat(blkid_probe_get_fd(pr), &st))
209                 return -errno;
210 
211         blkid_probe_enable_partitions(pr, 1);
212 
213         if (!S_ISCHR(st.st_mode) &&
214             blkid_probe_get_size(pr) <= 1024 * 1440 &&
215             blkid_probe_is_wholedisk(pr)) {
216                 /*
217                  * check if the small disk is partitioned, if yes then
218                  * don't probe for filesystems.
219                  */
220                 blkid_probe_enable_superblocks(pr, 0);
221 
222                 rc = blkid_do_fullprobe(pr);
223                 if (rc < 0)
224                         return rc;        /* -1 = error, 1 = nothing, 0 = success */
225 
226                 if (blkid_probe_lookup_value(pr, "PTTYPE", NULL, NULL) == 0)
227                         return 0;        /* partition table detected */
228         }
229 
230         blkid_probe_set_partitions_flags(pr, BLKID_PARTS_ENTRY_DETAILS);
231         blkid_probe_enable_superblocks(pr, 1);
232 
233         return blkid_do_safeprobe(pr);
234 }
235 
builtin_blkid(sd_device * dev,sd_netlink ** rtnl,int argc,char * argv[],bool test)236 static int builtin_blkid(sd_device *dev, sd_netlink **rtnl, int argc, char *argv[], bool test) {
237         const char *devnode, *root_partition = NULL, *data, *name;
238         _cleanup_(blkid_free_probep) blkid_probe pr = NULL;
239         bool noraid = false, is_gpt = false;
240         _cleanup_close_ int fd = -1;
241         int64_t offset = 0;
242         int r;
243 
244         static const struct option options[] = {
245                 { "offset", required_argument, NULL, 'o' },
246                 { "hint",   required_argument, NULL, 'H' },
247                 { "noraid", no_argument,       NULL, 'R' },
248                 {}
249         };
250 
251         errno = 0;
252         pr = blkid_new_probe();
253         if (!pr)
254                 return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to create blkid prober: %m");
255 
256         for (;;) {
257                 int option;
258 
259                 option = getopt_long(argc, argv, "o:H:R", options, NULL);
260                 if (option == -1)
261                         break;
262 
263                 switch (option) {
264                 case 'H':
265 #if HAVE_BLKID_PROBE_SET_HINT
266                         errno = 0;
267                         r = blkid_probe_set_hint(pr, optarg, 0);
268                         if (r < 0)
269                                 return log_device_error_errno(dev, errno_or_else(ENOMEM), "Failed to use '%s' probing hint: %m", optarg);
270                         break;
271 #else
272                         /* Use the hint <name>=<offset> as probing offset for old versions */
273                         optarg = strchr(optarg, '=');
274                         if (!optarg)
275                                 /* no value means 0, do nothing for old versions */
276                                 break;
277                         ++optarg;
278                         _fallthrough_;
279 #endif
280                 case 'o':
281                         r = safe_atoi64(optarg, &offset);
282                         if (r < 0)
283                                 return log_device_error_errno(dev, r, "Failed to parse '%s' as an integer: %m", optarg);
284                         if (offset < 0)
285                                 return log_device_error_errno(dev, SYNTHETIC_ERRNO(EINVAL), "Invalid offset %"PRIi64": %m", offset);
286                         break;
287                 case 'R':
288                         noraid = true;
289                         break;
290                 }
291         }
292 
293         blkid_probe_set_superblocks_flags(pr,
294                 BLKID_SUBLKS_LABEL | BLKID_SUBLKS_UUID |
295                 BLKID_SUBLKS_TYPE | BLKID_SUBLKS_SECTYPE |
296                 BLKID_SUBLKS_USAGE | BLKID_SUBLKS_VERSION);
297 
298         if (noraid)
299                 blkid_probe_filter_superblocks_usage(pr, BLKID_FLTR_NOTIN, BLKID_USAGE_RAID);
300 
301         r = sd_device_get_devname(dev, &devnode);
302         if (r < 0)
303                 return log_device_debug_errno(dev, r, "Failed to get device name: %m");
304 
305         fd = sd_device_open(dev, O_RDONLY|O_CLOEXEC|O_NONBLOCK);
306         if (fd < 0) {
307                 bool ignore = ERRNO_IS_DEVICE_ABSENT(fd);
308                 log_device_debug_errno(dev, fd, "Failed to open block device %s%s: %m",
309                                        devnode, ignore ? ", ignoring" : "");
310                 return ignore ? 0 : fd;
311         }
312 
313         errno = 0;
314         r = blkid_probe_set_device(pr, fd, offset, 0);
315         if (r < 0)
316                 return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to set device to blkid prober: %m");
317 
318         log_device_debug(dev, "Probe %s with %sraid and offset=%"PRIi64, devnode, noraid ? "no" : "", offset);
319 
320         r = probe_superblocks(pr);
321         if (r < 0)
322                 return log_device_debug_errno(dev, r, "Failed to probe superblocks: %m");
323 
324         /* If the device is a partition then its parent passed the root partition UUID to the device */
325         (void) sd_device_get_property_value(dev, "ID_PART_GPT_AUTO_ROOT_UUID", &root_partition);
326 
327         errno = 0;
328         int nvals = blkid_probe_numof_values(pr);
329         if (nvals < 0)
330                 return log_device_debug_errno(dev, errno_or_else(ENOMEM), "Failed to get number of probed values: %m");
331 
332         for (int i = 0; i < nvals; i++) {
333                 if (blkid_probe_get_value(pr, i, &name, &data, NULL) < 0)
334                         continue;
335 
336                 print_property(dev, test, name, data);
337 
338                 /* Is this a disk with GPT partition table? */
339                 if (streq(name, "PTTYPE") && streq(data, "gpt"))
340                         is_gpt = true;
341 
342                 /* Is this a partition that matches the root partition
343                  * property inherited from the parent? */
344                 if (root_partition && streq(name, "PART_ENTRY_UUID") && streq(data, root_partition))
345                         udev_builtin_add_property(dev, test, "ID_PART_GPT_AUTO_ROOT", "1");
346         }
347 
348         if (is_gpt)
349                 find_gpt_root(dev, pr, test);
350 
351         return 0;
352 }
353 
354 const UdevBuiltin udev_builtin_blkid = {
355         .name = "blkid",
356         .cmd = builtin_blkid,
357         .help = "Filesystem and partition probing",
358         .run_once = true,
359 };
360