OSDN Git Service

linux-pci-root: remove an unused assignment
[android-x86/external-efivar.git] / src / creator.c
1 /*
2  * libefiboot - library for the manipulation of EFI boot variables
3  * Copyright 2012-2015 Red Hat, Inc.
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public License as
7  * published by the Free Software Foundation; either version 2.1 of the
8  * License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, see
17  * <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "fix_coverity.h"
22
23 #include <fcntl.h>
24 #include <inttypes.h>
25 #include <limits.h>
26 #include <mntent.h>
27 #include <netinet/in.h>
28 #include <netinet/ip.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/socket.h>
34
35 #include "efiboot.h"
36
37 static int NONNULL(1, 2, 3)
38 find_file(const char * const filepath, char **devicep, char **relpathp)
39 {
40         struct stat fsb = { 0, };
41         int rc;
42         int ret = -1;
43         FILE *mounts = NULL;
44         char linkbuf[PATH_MAX+1] = "";
45         ssize_t linklen = 0;
46
47         linklen = strlen(filepath);
48         if (linklen > PATH_MAX) {
49                 errno = ENAMETOOLONG;
50                 efi_error("filepath length exceeds PATH_MAX");
51                 return -1;
52         }
53         strcpy(linkbuf, filepath);
54
55         do {
56                 rc = stat(linkbuf, &fsb);
57                 if (rc < 0)
58                         return rc;
59
60                 if (S_ISLNK(fsb.st_mode)) {
61                         char tmp[PATH_MAX+1] = "";
62                         ssize_t l;
63
64                         l = readlink(linkbuf, tmp, PATH_MAX);
65                         if (l < 0) {
66                                 efi_error("readlink failed");
67                                 return -1;
68                         }
69                         tmp[l] = '\0';
70                         linklen = l;
71                         strcpy(linkbuf, tmp);
72                 } else {
73                         break;
74                 }
75         } while (1);
76
77         mounts = fopen("/proc/self/mounts", "r");
78         if (mounts == NULL) {
79                 efi_error("couldn not open /proc/self/mounts");
80                 return -1;
81         }
82
83         struct mntent *me;
84         while (1) {
85                 struct stat dsb = { 0, };
86
87                 errno = 0;
88                 me = getmntent(mounts);
89                 if (!me) {
90                         if (feof(mounts)) {
91                                 errno = ENOENT;
92                                 efi_error("could not find mountpoint");
93                         }
94                         goto err;
95                 }
96
97                 if (me->mnt_fsname[0] != '/')
98                         continue;
99
100                 rc = stat(me->mnt_fsname, &dsb);
101                 if (rc < 0) {
102                         if (errno == ENOENT)
103                                 continue;
104                         efi_error("could not stat mountpoint");
105                         goto err;
106                 }
107
108                 if (!S_ISBLK(dsb.st_mode))
109                         continue;
110
111                 if (dsb.st_rdev == fsb.st_dev) {
112                         ssize_t mntlen = strlen(me->mnt_dir);
113                         if (mntlen >= linklen)
114                                 continue;
115                         if (strncmp(linkbuf, me->mnt_dir, mntlen))
116                                 continue;
117                         *devicep = strdup(me->mnt_fsname);
118                         if (!*devicep) {
119                                 errno = ENOMEM;
120                                 efi_error("strdup failed");
121                                 goto err;
122                         }
123                         *relpathp = strdup(linkbuf + mntlen);
124                         if (!*relpathp) {
125                                 free(*devicep);
126                                 *devicep = NULL;
127                                 errno = ENOMEM;
128                                 efi_error("strdup failed");
129                                 goto err;
130                         }
131                         ret = 0;
132                         break;
133                 }
134         }
135 err:
136         if (mounts)
137                 endmntent(mounts);
138         return ret;
139 }
140
141 static int
142 open_disk(struct device *dev, int flags)
143 {
144         char *diskpath = NULL;
145         int rc;
146
147         rc = asprintfa(&diskpath, "/dev/%s", dev->disk_name);
148         if (rc < 0) {
149                 efi_error("could not allocate buffer");
150                 return -1;
151         }
152
153         rc = open(diskpath, flags);
154         if (rc < 0)
155                 efi_error("could not open disk");
156
157         return rc;
158 }
159
160 static char *
161 tilt_slashes(char *s)
162 {
163         char *p;
164         for (p = s; *p; p++)
165                 if (*p == '/')
166                         *p = '\\';
167         return s;
168 }
169
170 ssize_t
171 efi_va_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
172                                        const char *devpath, int partition,
173                                        const char *relpath,
174                                        uint32_t options, va_list ap)
175 {
176         ssize_t ret = -1, off = 0, sz;
177         struct device *dev = NULL;
178         int fd = -1;
179         int saved_errno;
180
181         debug("partition:%d", partition);
182
183         if (buf && size)
184                 memset(buf, '\0', size);
185
186         fd = open(devpath, O_RDONLY);
187         if (fd < 0) {
188                 efi_error("could not open device for ESP");
189                 goto err;
190         }
191
192         dev = device_get(fd, partition);
193         if (dev == NULL) {
194                 efi_error("could not get ESP disk info");
195                 goto err;
196         }
197
198         if (partition < 0) {
199                 int disk_fd;
200
201                 debug("partition: %d", partition);
202                 disk_fd = open_disk(dev,
203                                     (options & EFIBOOT_OPTIONS_WRITE_SIGNATURE)
204                                      ? O_RDWR : O_RDONLY);
205                 if (disk_fd < 0) {
206                         efi_error("could not open disk");
207                         goto err;
208                 }
209
210                 if (is_partitioned(disk_fd))
211                         partition = 1;
212                 else
213                         partition = 0;
214                 debug("is_partitioned(): partition -> %d", partition);
215
216                 close(disk_fd);
217         }
218
219         set_part(dev, partition);
220
221         if (partition == 0) {
222                 options |= EFIBOOT_ABBREV_NONE;
223                 options &= ~(EFIBOOT_ABBREV_HD|
224                              EFIBOOT_ABBREV_FILE|
225                              EFIBOOT_ABBREV_EDD10);
226         }
227
228         if (options & EFIBOOT_ABBREV_NONE)
229                 debug("EFIBOOT_ABBREV_NONE");
230         if (options & EFIBOOT_ABBREV_HD)
231                 debug("EFIBOOT_ABBREV_HD");
232         if (options & EFIBOOT_ABBREV_FILE)
233                 debug("EFIBOOT_ABBREV_FILE");
234         if (options & EFIBOOT_ABBREV_EDD10)
235                 debug("EFIBOOT_ABBREV_EDD10");
236
237         if (options & EFIBOOT_ABBREV_EDD10) {
238                 va_list aq;
239                 va_copy(aq, ap);
240
241                 dev->edd10_devicenum = va_arg(aq, uint32_t);
242
243                 va_end(aq);
244         }
245
246         if (!(options & (EFIBOOT_ABBREV_FILE|EFIBOOT_ABBREV_HD)) &&
247             (dev->flags & DEV_ABBREV_ONLY)) {
248                 efi_error_clear();
249                 errno = EINVAL;
250                 efi_error("Device must use File() or HD() device path");
251                 goto err;
252         }
253
254         if ((options & EFIBOOT_ABBREV_EDD10)
255                         && (!(options & EFIBOOT_ABBREV_FILE)
256                             && !(options & EFIBOOT_ABBREV_HD))) {
257                 sz = efidp_make_edd10(buf, size, dev->edd10_devicenum);
258                 if (sz < 0) {
259                         efi_error("could not make EDD 1.0 device path");
260                         goto err;
261                 }
262                 off = sz;
263         } else if (!(options & EFIBOOT_ABBREV_FILE)
264                    && !(options & EFIBOOT_ABBREV_HD)) {
265
266                 /*
267                  * We're probably on a modern kernel, so just parse the
268                  * symlink from /sys/dev/block/$major:$minor and get it
269                  * from there.
270                  */
271                 sz = make_blockdev_path(buf, size, dev);
272                 if (sz < 0) {
273                         efi_error("could not create device path");
274                         goto err;
275                 }
276                 off += sz;
277         }
278
279         if ((!(options & EFIBOOT_ABBREV_FILE) && dev->part_name) ||
280             ((options & EFIBOOT_ABBREV_HD) && ! dev->part_name)) {
281                 int disk_fd;
282                 int saved_errno;
283
284                 disk_fd = open_disk(dev,
285                                     (options & EFIBOOT_OPTIONS_WRITE_SIGNATURE)
286                                      ? O_RDWR : O_RDONLY);
287                 if (disk_fd < 0) {
288                         efi_error("could not open disk");
289                         goto err;
290                 }
291
292                 sz = make_hd_dn(buf+off, size?size-off:0,
293                                 disk_fd, dev->part, options);
294                 saved_errno = errno;
295                 close(disk_fd);
296                 errno = saved_errno;
297                 if (sz < 0) {
298                         efi_error("could not make HD() DP node");
299                         goto err;
300                 }
301                 off += sz;
302         }
303
304         char *filepath = strdupa(relpath);
305         tilt_slashes(filepath);
306         sz = efidp_make_file(buf+off, size?size-off:0, filepath);
307         if (sz < 0) {
308                 efi_error("could not make File() DP node");
309                 goto err;
310         }
311         off += sz;
312
313         sz = efidp_make_end_entire(buf+off, size?size-off:0);
314         if (sz < 0) {
315                 efi_error("could not make EndEntire DP node");
316                 goto err;
317         }
318         off += sz;
319         ret = off;
320 err:
321         saved_errno = errno;
322         if (dev)
323                 device_free(dev);
324         if (fd >= 0)
325                 close(fd);
326         errno = saved_errno;
327         debug("= %zd", ret);
328         return ret;
329 }
330
331 ssize_t NONNULL(3, 5) PUBLIC
332 efi_generate_file_device_path_from_esp(uint8_t *buf, ssize_t size,
333                                        const char *devpath, int partition,
334                                        const char *relpath,
335                                        uint32_t options, ...)
336 {
337         ssize_t ret;
338         int saved_errno;
339         va_list ap;
340
341         va_start(ap, options);
342         ret = efi_va_generate_file_device_path_from_esp(buf, size, devpath,
343                                                         partition, relpath,
344                                                         options, ap);
345         saved_errno = errno;
346         va_end(ap);
347         errno = saved_errno;
348         if (ret < 0)
349                 efi_error("could not generate File DP from ESP");
350         return ret;
351 }
352
353 static int
354 get_part(char *devpath)
355 {
356         int fd;
357         int partition = -1;
358         struct device *dev = NULL;
359
360         fd = open(devpath, O_RDONLY);
361         if (fd < 0) {
362                 efi_error("could not open device for ESP");
363                 goto err;
364         }
365
366         dev = device_get(fd, -1);
367         if (dev == NULL) {
368                 efi_error("could not get ESP disk info");
369                 goto err;
370         }
371
372         partition = dev->part;
373         if (partition < 0)
374                 partition = 0;
375 err:
376         if (dev)
377                 device_free(dev);
378         if (fd >= 0)
379                 close(fd);
380         return partition;
381 }
382
383 ssize_t NONNULL(3) PUBLIC
384 efi_generate_file_device_path(uint8_t *buf, ssize_t size,
385                               const char * const filepath,
386                               uint32_t options, ...)
387 {
388         int rc;
389         ssize_t ret = -1;
390         char *child_devpath = NULL;
391         char *parent_devpath = NULL;
392         char *relpath = NULL;
393         va_list ap;
394         int saved_errno;
395
396         rc = find_file(filepath, &child_devpath, &relpath);
397         if (rc < 0) {
398                 efi_error("could not canonicalize fs path");
399                 goto err;
400         }
401
402         rc = find_parent_devpath(child_devpath, &parent_devpath);
403         if (rc < 0) {
404                 efi_error("could not find parent device for file");
405                 goto err;
406         }
407         debug("child_devpath:%s", child_devpath);
408
409         debug("parent_devpath:%s", parent_devpath);
410         debug("child_devpath:%s", child_devpath);
411         debug("rc:%d", rc);
412
413         rc = get_part(child_devpath);
414         if (rc < 0) {
415                 efi_error("Couldn't get partition number for %s",
416                           child_devpath);
417                 goto err;
418         }
419         debug("detected partition:%d", rc);
420
421         va_start(ap, options);
422
423         if (!strcmp(parent_devpath, "/dev/block"))
424                 ret = efi_va_generate_file_device_path_from_esp(buf, size,
425                                                         child_devpath, rc,
426                                                         relpath, options, ap);
427         else
428                 ret = efi_va_generate_file_device_path_from_esp(buf, size,
429                                                         parent_devpath, rc,
430                                                         relpath, options, ap);
431         saved_errno = errno;
432         va_end(ap);
433         errno = saved_errno;
434         if (ret < 0)
435                 efi_error("could not generate File DP from ESP");
436 err:
437         saved_errno = errno;
438         if (child_devpath)
439                 free(child_devpath);
440         if (parent_devpath)
441                 free(parent_devpath);
442         if (relpath)
443                 free(relpath);
444         errno = saved_errno;
445         return ret;
446 }
447
448 static ssize_t NONNULL(3, 4, 5, 6)
449 make_ipv4_path(uint8_t *buf, ssize_t size,
450                const char * const local_addr UNUSED,
451                const char * const remote_addr UNUSED,
452                const char * const gateway_addr UNUSED,
453                const char * const netmask UNUSED,
454                uint16_t local_port UNUSED,
455                uint16_t remote_port UNUSED,
456                uint16_t protocol UNUSED,
457                uint8_t addr_origin UNUSED)
458 {
459         ssize_t ret;
460
461 #if 0
462         if (local_addr == NULL || remote_addr == NULL ||
463             gateway_addr == NULL || netmask == NULL) {
464                 errno = EINVAL;
465                 return -1;
466         }
467 #endif
468         ret = efidp_make_ipv4(buf, size, 0, 0, 0, 0, 0, 0, 0, 0);
469         if (ret < 0)
470                 efi_error("could not make ipv4 DP node");
471         return ret;
472 }
473
474 ssize_t NONNULL(3, 4, 5, 6, 7) PUBLIC
475 efi_generate_ipv4_device_path(uint8_t *buf, ssize_t size,
476                               const char * const ifname,
477                               const char * const local_addr,
478                               const char * const remote_addr,
479                               const char * const gateway_addr,
480                               const char * const netmask,
481                               uint16_t local_port,
482                               uint16_t remote_port,
483                               uint16_t protocol,
484                               uint8_t addr_origin)
485 {
486         ssize_t off = 0;
487         ssize_t sz;
488
489         sz = make_mac_path(buf, size, ifname);
490         if (sz < 0) {
491                 efi_error("could not make MAC DP node");
492                 return -1;
493         }
494         off += sz;
495
496         sz = make_ipv4_path(buf+off, size?size-off:0, local_addr, remote_addr,
497                             gateway_addr, netmask, local_port, remote_port,
498                             protocol, addr_origin);
499         if (sz < 0) {
500                 efi_error("could not make IPV4 DP node");
501                 return -1;
502         }
503         off += sz;
504
505         sz = efidp_make_end_entire(buf+off, size?size-off:0);
506         if (sz < 0) {
507                 efi_error("could not make EndEntire DP node");
508                 return -1;
509         }
510         off += sz;
511
512         return off;
513 }