setup: remove TODO
[platform/upstream/gummiboot.git] / src / setup / setup.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Lennart Poettering
7   Copyright 2013 Kay Sievers
8
9   systemd is free software; you can redistribute it and/or modify it
10   under the terms of the GNU Lesser General Public License as published by
11   the Free Software Foundation; either version 2.1 of the License, or
12   (at your option) any later version.
13
14   systemd is distributed in the hope that it will be useful, but
15   WITHOUT ANY WARRANTY; without even the implied warranty of
16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17   Lesser General Public License for more details.
18
19   You should have received a copy of the GNU Lesser General Public License
20   along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 ***/
22
23 #include <stdio.h>
24 #include <getopt.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <assert.h>
28 #include <sys/statfs.h>
29 #include <sys/stat.h>
30 #include <errno.h>
31 #include <string.h>
32 #include <unistd.h>
33 #include <sys/mman.h>
34 #include <dirent.h>
35 #include <ctype.h>
36 #include <limits.h>
37 #include <ftw.h>
38 #include <stdbool.h>
39 #include <blkid.h>
40
41 #include "efivars.h"
42
43 #define ELEMENTSOF(x) (sizeof(x)/sizeof((x)[0]))
44 #define streq(a,b) (strcmp((a),(b)) == 0)
45
46 static inline bool isempty(const char *p) {
47         return !p || !p[0];
48 }
49
50 static inline const char *strna(const char *s) {
51         return isempty(s) ? "n/a" : s;
52 }
53
54 static int help(void) {
55         printf("%s [COMMAND] [OPTIONS...]\n"
56                "\n"
57                "Install, update or remove the Gummiboot EFI boot loader.\n\n"
58                "  -h --help          Show this help\n"
59                "     --path=PATH     Path to the EFI System Partition (ESP)\n"
60                "     --no-variables  Don't touch EFI variables\n"
61                "\n"
62                "Comands:\n"
63                "     install         Install Gummiboot to the ESP and EFI variables\n"
64                "     update          Update Gummiboot in the ESP and EFI variables\n"
65                "     remove          Remove Gummiboot from the ESP and EFI variables\n",
66                program_invocation_short_name);
67
68         return 0;
69 }
70
71 static int uuid_parse(const char *s, uint8_t uuid[16]) {
72         int u[16];
73         int i;
74
75         if (sscanf(s, "%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x",
76             &u[0], &u[1], &u[2], &u[3], &u[4], &u[5], &u[6], &u[7],
77             &u[8], &u[9], &u[10], &u[11], &u[12], &u[13], &u[14], &u[15]) != 16)
78                 return -EINVAL;
79
80         for (i = 0; i < 16; i++)
81                 uuid[i] = u[i];
82
83         return 0;
84 }
85
86 static int verify_esp(const char *p, uint32_t *part, uint64_t *pstart, uint64_t *psize, uint8_t uuid[16]) {
87         struct statfs sfs;
88         struct stat st, st2;
89         char *t;
90         blkid_probe b = NULL;
91         int r;
92         const char *v;
93
94         if (statfs(p, &sfs) < 0) {
95                 fprintf(stderr, "Failed to check file system type of %s: %m\n", p);
96                 return -errno;
97         }
98
99         if (sfs.f_type != 0x4d44) {
100                 fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system.\n", p);
101                 return -ENODEV;
102         }
103
104         if (stat(p, &st) < 0) {
105                 fprintf(stderr, "Failed to determine block device node of %s: %m\n", p);
106                 return -errno;
107         }
108
109         if (major(st.st_dev) == 0) {
110                 fprintf(stderr, "Block device node of %p is invalid.\n", p);
111                 return -ENODEV;
112         }
113
114         r = asprintf(&t, "%s/..", p);
115         if (r < 0) {
116                 fprintf(stderr, "Out of memory.\n");
117                 return -ENOMEM;
118         }
119
120         r = stat(t, &st2);
121         free(t);
122         if (r < 0) {
123                 fprintf(stderr, "Failed to determine block device node of parent of %s: %m\n", p);
124                 return -errno;
125         }
126
127         if (st.st_dev == st2.st_dev) {
128                 fprintf(stderr, "Directory %s is not the root of the EFI System Partition (ESP) file system.\n", p);
129                 return -ENODEV;
130         }
131
132         r = asprintf(&t, "/dev/block/%u:%u", major(st.st_dev), minor(st.st_dev));
133         if (r < 0) {
134                 fprintf(stderr, "Out of memory.\n");
135                 return -ENOMEM;
136         }
137
138         errno = 0;
139         b = blkid_new_probe_from_filename(t);
140         free(t);
141         if (!b) {
142                 if (errno != 0) {
143                         fprintf(stderr, "Failed to open file system %s: %m\n", p);
144                         return -errno;
145                 }
146
147                 fprintf(stderr, "Out of memory.\n");
148                 return -ENOMEM;
149         }
150
151         blkid_probe_enable_superblocks(b, 1);
152         blkid_probe_set_superblocks_flags(b, BLKID_SUBLKS_TYPE);
153         blkid_probe_enable_partitions(b, 1);
154         blkid_probe_set_partitions_flags(b, BLKID_PARTS_ENTRY_DETAILS);
155
156         errno = 0;
157         r = blkid_do_safeprobe(b);
158         if (r == -2) {
159                 fprintf(stderr, "File system %s is ambigious.\n", p);
160                 r = -ENODEV;
161                 goto fail;
162         } else if (r == 1) {
163                 fprintf(stderr, "File system %s does not contain a label.\n", p);
164                 r = -ENODEV;
165                 goto fail;
166         } else if (r != 0) {
167                 r = errno ? -errno : -EIO;
168                 fprintf(stderr, "Failed to probe file system %s: %s\n", p, strerror(-r));
169                 goto fail;
170         }
171
172         errno = 0;
173         r = blkid_probe_lookup_value(b, "TYPE", &v, NULL);
174         if (r != 0) {
175                 r = errno ? -errno : -EIO;
176                 fprintf(stderr, "Failed to probe file system type %s: %s\n", p, strerror(-r));
177                 goto fail;
178         }
179
180         if (strcmp(v, "vfat") != 0) {
181                 fprintf(stderr, "File system %s is not a FAT EFI System Partition (ESP) file system after all.\n", p);
182                 r = -ENODEV;
183                 goto fail;
184         }
185
186         errno = 0;
187         r = blkid_probe_lookup_value(b, "PART_ENTRY_SCHEME", &v, NULL);
188         if (r != 0) {
189                 r = errno ? -errno : -EIO;
190                 fprintf(stderr, "Failed to probe partition scheme %s: %s\n", p, strerror(-r));
191                 goto fail;
192         }
193
194         if (strcmp(v, "gpt") != 0) {
195                 fprintf(stderr, "File system %s is not on a GPT partition table.\n", p);
196                 r = -ENODEV;
197                 goto fail;
198         }
199
200         errno = 0;
201         r = blkid_probe_lookup_value(b, "PART_ENTRY_TYPE", &v, NULL);
202         if (r != 0) {
203                 r = errno ? -errno : -EIO;
204                 fprintf(stderr, "Failed to probe partition type UUID %s: %s\n", p, strerror(-r));
205                 goto fail;
206         }
207
208         if (strcmp(v, "c12a7328-f81f-11d2-ba4b-00a0c93ec93b") != 0) {
209                 r = -ENODEV;
210                 fprintf(stderr, "File system %s is not an EFI System Partition (ESP).\n", p);
211                 goto fail;
212         }
213
214         errno = 0;
215         r = blkid_probe_lookup_value(b, "PART_ENTRY_UUID", &v, NULL);
216         if (r != 0) {
217                 r = errno ? -errno : -EIO;
218                 fprintf(stderr, "Failed to probe partition entry UUID %s: %s\n", p, strerror(-r));
219                 goto fail;
220         }
221         uuid_parse(v, uuid);
222
223         errno = 0;
224         r = blkid_probe_lookup_value(b, "PART_ENTRY_NUMBER", &v, NULL);
225         if (r != 0) {
226                 r = errno ? -errno : -EIO;
227                 fprintf(stderr, "Failed to probe partition number %s: %s\n", p, strerror(-r));
228                 goto fail;
229         }
230         *part = strtoul(v, NULL, 10);
231
232         errno = 0;
233         r = blkid_probe_lookup_value(b, "PART_ENTRY_OFFSET", &v, NULL);
234         if (r != 0) {
235                 r = errno ? -errno : -EIO;
236                 fprintf(stderr, "Failed to probe partition offset %s: %s\n", p, strerror(-r));
237                 goto fail;
238         }
239         *pstart = strtoul(v, NULL, 10);
240
241         errno = 0;
242         r = blkid_probe_lookup_value(b, "PART_ENTRY_SIZE", &v, NULL);
243         if (r != 0) {
244                 r = errno ? -errno : -EIO;
245                 fprintf(stderr, "Failed to probe partition size %s: %s\n", p, strerror(-r));
246                 goto fail;
247         }
248         *psize = strtoul(v, NULL, 10);
249
250         blkid_free_probe(b);
251         return 0;
252 fail:
253         if (b)
254                 blkid_free_probe(b);
255         return r;
256 }
257
258 /* search for "#### LoaderInfo: gummiboot 31 ####" string inside the binary */
259 static int get_file_version(FILE *f, char **v) {
260         struct stat st;
261         char *buf;
262         const char *s, *e;
263         char *x = NULL;
264         int r = 0;
265
266         assert(f);
267         assert(v);
268
269         if (fstat(fileno(f), &st) < 0)
270                 return -errno;
271
272         if (st.st_size < 27)
273                 return 0;
274
275         buf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fileno(f), 0);
276         if (buf == MAP_FAILED)
277                 return -errno;
278
279         s = memmem(buf, st.st_size - 8, "#### LoaderInfo: ", 17);
280         if (!s)
281                 goto finish;
282         s += 17;
283
284         e = memmem(s, st.st_size - (s - buf), " ####", 5);
285         if (!e || e - s < 3) {
286                 fprintf(stderr, "Malformed version string.\n");
287                 r = -EINVAL;
288                 goto finish;
289         }
290
291         x = strndup(s, e - s);
292         if (!x) {
293                 fprintf(stderr, "Out of memory.\n");
294                 r = -ENOMEM;
295                 goto finish;
296         }
297         r = 1;
298
299 finish:
300         munmap(buf, st.st_size);
301         *v = x;
302         return r;
303 }
304
305 static int enumerate_binaries(const char *esp_path, const char *path, const char *prefix) {
306         struct dirent *de;
307         char *p = NULL, *q = NULL;
308         DIR *d = NULL;
309         int r = 0, c = 0;
310
311         if (asprintf(&p, "%s/%s", esp_path, path) < 0) {
312                 fprintf(stderr, "Out of memory.\n");
313                 r = -ENOMEM;
314                 goto finish;
315         }
316
317         d = opendir(p);
318         if (!d) {
319                 if (errno == ENOENT) {
320                         r = 0;
321                         goto finish;
322                 }
323
324                 fprintf(stderr, "Failed to read %s: %m\n", p);
325                 r = -errno;
326                 goto finish;
327         }
328
329         while ((de = readdir(d))) {
330                 char *v;
331                 size_t n;
332                 FILE *f;
333
334                 if (de->d_name[0] == '.')
335                         continue;
336
337                 n = strlen(de->d_name);
338                 if (n < 4 || strcasecmp(de->d_name + n - 4, ".efi") != 0)
339                         continue;
340
341                 if (prefix && strncasecmp(de->d_name, prefix, strlen(prefix)) != 0)
342                         continue;
343
344                 free(q);
345                 q = NULL;
346                 if (asprintf(&q, "%s/%s/%s", esp_path, path, de->d_name) < 0) {
347                         fprintf(stderr, "Out of memory.\n");
348                         r = -ENOMEM;
349                         goto finish;
350                 }
351
352                 f = fopen(q, "re");
353                 if (!f) {
354                         fprintf(stderr, "Failed to open %s for reading: %m\n", q);
355                         r = -errno;
356                         goto finish;
357                 }
358
359                 r = get_file_version(f, &v);
360                 fclose(f);
361
362                 if (r < 0)
363                         goto finish;
364
365                 if (r == 0)
366                         printf("\t%s (Unknown product and version)\n", q);
367                 else
368                         printf("\t%s (%s)\n", q, v);
369
370                 c++;
371
372                 free(v);
373         }
374
375         r = c;
376
377 finish:
378         if (d)
379                 closedir(d);
380
381         free(p);
382         free(q);
383
384         return r;
385 }
386
387 static int status_binaries(const char *esp_path) {
388         int r;
389
390         printf("Boot Loader Binaries found in ESP:\n");
391
392         r = enumerate_binaries(esp_path, "EFI/gummiboot", NULL);
393         if (r == 0)
394                 fprintf(stderr, "\tGummiboot not installed in ESP.\n");
395         else if (r < 0)
396                 return r;
397
398         r = enumerate_binaries(esp_path, "EFI/BOOT", "BOOT");
399         if (r == 0)
400                 fprintf(stderr, "\tNo fallback for removable devices installed in ESP.\n");
401         else if (r < 0)
402                 return r;
403
404         return 0;
405 }
406
407 static int print_efi_option(uint16_t id, bool in_order) {
408         char *title = NULL;
409         char *path = NULL;
410         uint8_t partition[16];
411         int r = 0;
412
413         r = efi_get_boot_option(id, &title, partition, &path);
414         if (r < 0) {
415                 fprintf(stderr, "Failed to read EFI boot entry %i.\n", id);
416                 goto finish;
417         }
418
419         printf("\t%s%s\n", strna(title), in_order ? " [ENABLED]" : "");
420         if (path) {
421                  printf("\t\t%s\n", path);
422                  printf("\t\t/dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n",
423                         partition[0], partition[1], partition[2], partition[3], partition[4], partition[5], partition[6], partition[7],
424                         partition[8], partition[9], partition[10], partition[11], partition[12], partition[13], partition[14], partition[15]);
425         }
426
427 finish:
428         free(title);
429         free(path);
430         return r;
431 }
432
433 static int status_variables(void) {
434         int n_options, n_order;
435         uint16_t *options = NULL, *order = NULL;
436         int r, i;
437
438         if (!is_efi_boot()) {
439                 fprintf(stderr, "Not booted with EFI, not showing EFI variables.\n");
440                 return 0;
441         }
442
443         printf("\nBoot Entries found in EFI variables:\n");
444
445         n_options = efi_get_boot_options(&options);
446         if (n_options < 0) {
447                 if (n_options == -ENOENT)
448                         fprintf(stderr, "\tFailed to access EFI variables. Is the \"efivarfs\" filesystem mounted?\n");
449                 else
450                         fprintf(stderr, "\tFailed to read EFI boot entries.\n");
451                 r = n_options;
452                 goto finish;
453         }
454
455         n_order = efi_get_boot_order(&order);
456         if (n_order == -ENOENT) {
457                 fprintf(stderr, "\tNo boot entries registered in EFI variables.\n");
458                 r = 0;
459                 goto finish;
460         } else if (n_order < 0) {
461                 fprintf(stderr, "\tFailed to read EFI boot order.\n");
462                 r = n_order;
463                 goto finish;
464         }
465
466         for (i = 0; i < n_order; i++) {
467                 r = print_efi_option(order[i], true);
468                 if (r < 0)
469                         goto finish;
470         }
471
472         for (i = 0; i < n_options; i++) {
473                 int j;
474                 bool found = false;
475
476                 for (j = 0; j < n_order; j++)
477                         if (options[i] == order[j]) {
478                                 found = true;
479                                 break;
480                         }
481
482                 if (found)
483                         continue;
484
485                 r = print_efi_option(options[i], false);
486                 if (r < 0)
487                         goto finish;
488         }
489
490         r = 0;
491 finish:
492         free(options);
493         free(order);
494
495         return r;
496 }
497
498 static int compare_product(const char *a, const char *b) {
499         size_t x, y;
500
501         assert(a);
502         assert(b);
503
504         x = strcspn(a, " ");
505         y = strcspn(b, " ");
506         if (x != y)
507                 return x < y ? -1 : x > y ? 1 : 0;
508
509         return strncmp(a, b, x);
510 }
511
512 static int compare_version(const char *a, const char *b) {
513         assert(a);
514         assert(b);
515
516         a += strcspn(a, " ");
517         a += strspn(a, " ");
518         b += strcspn(b, " ");
519         b += strspn(b, " ");
520
521         return strverscmp(a, b);
522 }
523
524 static int version_check(FILE *f, const char *from, const char *to) {
525         FILE *g = NULL;
526         char *a = NULL, *b = NULL;
527         int r;
528
529         assert(f);
530         assert(from);
531         assert(to);
532
533         r = get_file_version(f, &a);
534         if (r < 0)
535                 goto finish;
536         if (r == 0) {
537                 r = -EINVAL;
538                 fprintf(stderr, "Source file %s does not carry version information!\n", from);
539                 goto finish;
540         }
541
542         g = fopen(to, "re");
543         if (!g) {
544                 if (errno == ENOENT) {
545                         r = 0;
546                         goto finish;
547                 }
548
549                 r = -errno;
550                 fprintf(stderr, "Failed to open %s for reading: %m\n", to);
551                 goto finish;
552         }
553
554         r = get_file_version(g, &b);
555         if (r < 0)
556                 goto finish;
557         if (r == 0 || compare_product(a, b) != 0) {
558                 r = -EEXIST;
559                 fprintf(stderr, "Skipping %s, since it's owned by another boot loader.\n", to);
560                 goto finish;
561         }
562
563         if (compare_version(a, b) < 0) {
564                 r = -EEXIST;
565                 fprintf(stderr, "Skipping %s, since it's a newer boot loader version already.\n", to);
566                 goto finish;
567         }
568
569         r = 0;
570
571 finish:
572         free(a);
573         free(b);
574         if (g)
575                 fclose(g);
576         return r;
577 }
578
579 static int copy_file(const char *from, const char *to, bool force) {
580         FILE *f = NULL, *g = NULL;
581         char *p = NULL;
582         int r;
583         struct timespec t[2];
584         struct stat st;
585
586         assert(from);
587         assert(to);
588
589         f = fopen(from, "re");
590         if (!f) {
591                 fprintf(stderr, "Failed to open %s for reading: %m\n", from);
592                 return -errno;
593         }
594
595         if (!force) {
596                 /* If this is an update, then let's compare versions first */
597                 r = version_check(f, from, to);
598                 if (r < 0)
599                         goto finish;
600         }
601
602         if (asprintf(&p, "%s~", to) < 0) {
603                 fprintf(stderr, "Out of memory.\n");
604                 r = -ENOMEM;
605                 goto finish;
606         }
607
608         g = fopen(p, "wxe");
609         if (!g) {
610                 /* Directory doesn't exist yet? Then let's skip this... */
611                 if (!force && errno == ENOENT) {
612                         r = 0;
613                         goto finish;
614                 }
615
616                 fprintf(stderr, "Failed to open %s for writing: %m\n", to);
617                 r = -errno;
618                 goto finish;
619         }
620
621         rewind(f);
622         do {
623                 size_t k;
624                 uint8_t buf[32*1024];
625
626                 k = fread(buf, 1, sizeof(buf), f);
627                 if (ferror(f)) {
628                         fprintf(stderr, "Failed to read %s: %m\n", from);
629                         r = -errno;
630                         goto finish;
631                 }
632                 if (k == 0)
633                         break;
634
635                 fwrite(buf, 1, k, g);
636                 if (ferror(g)) {
637                         fprintf(stderr, "Failed to write %s: %m\n", to);
638                         r = -errno;
639                         goto finish;
640                 }
641         } while (!feof(f));
642
643         fflush(g);
644         if (ferror(g)) {
645                 fprintf(stderr, "Failed to write %s: %m\n", to);
646                 r = -errno;
647                 goto finish;
648         }
649
650         r = fstat(fileno(f), &st);
651         if (r < 0) {
652                 fprintf(stderr, "Failed to get file timestamps of %s: %m", from);
653                 r = -errno;
654                 goto finish;
655         }
656
657         t[0] = st.st_atim;
658         t[1] = st.st_mtim;
659
660         r = futimens(fileno(g), t);
661         if (r < 0) {
662                 fprintf(stderr, "Failed to change file timestamps for %s: %m", p);
663                 r = -errno;
664                 goto finish;
665         }
666
667         if (rename(p, to) < 0) {
668                 fprintf(stderr, "Failed to rename %s to %s: %m\n", p, to);
669                 r = -errno;
670                 goto finish;
671         }
672
673         fprintf(stderr, "Copied %s to %s.\n", from, to);
674
675         free(p);
676         p = NULL;
677         r = 0;
678
679 finish:
680         if (f)
681                 fclose(f);
682         if (g)
683                 fclose(g);
684         if (p) {
685                 unlink(p);
686                 free(p);
687         }
688         return r;
689 }
690
691 static char* strupper(char *s) {
692         char *p;
693
694         for (p = s; *p; p++)
695                 *p = toupper(*p);
696
697         return s;
698 }
699
700 static int mkdir_one(const char *prefix, const char *suffix) {
701         char *p;
702
703         if (asprintf(&p, "%s/%s", prefix, suffix) < 0) {
704                 fprintf(stderr, "Out of memory.\n");
705                 return -ENOMEM;
706         }
707
708         if (mkdir(p, 0700) < 0) {
709                 if (errno != EEXIST) {
710                         fprintf(stderr, "Failed to create %s: %m\n", p);
711                         free(p);
712                         return -errno;
713                 }
714         } else
715                 fprintf(stderr, "Created %s.\n", p);
716
717         free(p);
718         return 0;
719 }
720
721 static int create_dirs(const char *esp_path) {
722         int r;
723
724         r = mkdir_one(esp_path, "EFI");
725         if (r < 0)
726                 return r;
727
728         r = mkdir_one(esp_path, "EFI/gummiboot");
729         if (r < 0)
730                 return r;
731
732         r = mkdir_one(esp_path, "EFI/BOOT");
733         if (r < 0)
734                 return r;
735
736         r = mkdir_one(esp_path, "loader");
737         if (r < 0)
738                 return r;
739
740         r = mkdir_one(esp_path, "loader/entries");
741         if (r < 0)
742                 return r;
743
744         return 0;
745 }
746
747 static int copy_one_file(const char *esp_path, const char *name, bool force) {
748         char *p = NULL, *q = NULL, *v = NULL;
749         int r;
750
751         if (asprintf(&p, "/usr/lib/gummiboot/%s", name) < 0) {
752                 fprintf(stderr, "Out of memory.\n");
753                 r = -ENOMEM;
754                 goto finish;
755         }
756
757         if (asprintf(&q, "%s/EFI/gummiboot/%s", esp_path, name) < 0) {
758                 fprintf(stderr, "Out of memory.\n");
759                 r = -ENOMEM;
760                 goto finish;
761         }
762
763         r = copy_file(p, q, force);
764
765         if (strncmp(name, "gummiboot", 9) == 0) {
766                 int k;
767
768                 /* Create the fallback names for removable devices */
769                 if (asprintf(&v, "%s/EFI/BOOT/%s", esp_path, name + 5) < 0) {
770                         fprintf(stderr, "Out of memory.\n");
771                         r = -ENOMEM;
772                         goto finish;
773                 }
774                 strupper(strrchr(v, '/') + 1);
775
776                 k = copy_file(p, v, force);
777                 if (k < 0 && r == 0) {
778                         r = k;
779                         goto finish;
780                 }
781         }
782
783 finish:
784         free(p);
785         free(q);
786         free(v);
787         return r;
788 }
789
790 static int install_binaries(const char *esp_path, bool force) {
791         struct dirent *de;
792         DIR *d;
793         int r = 0;
794
795         if (force) {
796                 /* Don't create any of these directories when we are
797                  * just updating. When we update we'll drop-in our
798                  * files (unless there are newer ones already), but we
799                  * won't create the directories for them in the first
800                  * place. */
801                 r = create_dirs(esp_path);
802                 if (r < 0)
803                         return r;
804         }
805
806         d = opendir("/usr/lib/gummiboot");
807         if (!d) {
808                 fprintf(stderr, "Failed to open /usr/lib/gummiboot: %m\n");
809                 return -errno;
810         }
811
812         while ((de = readdir(d))) {
813                 size_t n;
814                 int k;
815
816                 if (de->d_name[0] == '.')
817                         continue;
818
819                 n = strlen(de->d_name);
820                 if (n < 4 || strcmp(de->d_name + n - 4, ".efi") != 0)
821                         continue;
822
823                 k = copy_one_file(esp_path, de->d_name, force);
824                 if (k < 0 && r == 0)
825                         r = k;
826         }
827
828         closedir(d);
829         return r;
830 }
831
832 static bool same_entry(uint16_t id, const uint8_t uuid[16], const char *path) {
833         char *opath = NULL;
834         uint8_t ouuid[16];
835         int err;
836         bool same = false;
837
838         err = efi_get_boot_option(id, NULL, ouuid, &opath);
839         if (err < 0)
840                 return false;
841         if (memcmp(uuid, ouuid, 16) != 0)
842                 goto finish;
843
844         if (!streq(path, opath))
845                 goto finish;
846
847         same = true;
848
849 finish:
850         free(opath);
851         return same;
852 }
853
854 static int find_slot(const uint8_t uuid[16], const char *path, uint16_t *id) {
855         uint16_t *options = NULL;
856         int n_options;
857         int i;
858         uint16_t new_id = 0;
859         bool existing = false;
860
861         n_options = efi_get_boot_options(&options);
862         if (n_options < 0)
863                 return n_options;
864
865         /* find already existing gummiboot entry */
866         for (i = 0; i < n_options; i++)
867                 if (same_entry(options[i], uuid, path)) {
868                         new_id = i;
869                         existing = true;
870                         goto finish;
871                 }
872
873         /* find free slot in the sorted BootXXXX variable list */
874         for (i = 0; i < n_options; i++)
875                 if (i != options[i]) {
876                         new_id = i;
877                         goto finish;
878                 }
879
880 finish:
881         *id = new_id;
882         free(options);
883         return existing;
884 }
885
886 static int insert_into_order(uint16_t slot, bool first) {
887         uint16_t *order = NULL;
888         uint16_t *new_order;
889         int n_order;
890         int i;
891         int err = 0;
892
893         n_order = efi_get_boot_order(&order);
894         if (n_order <= 0) {
895                 err = efi_set_boot_order(&slot, 1);
896                 goto finish;
897         }
898
899         /* are we already in the boot order? */
900         for (i = 0; i < n_order; i++)
901                 if (order[i] == slot)
902                         goto finish;
903
904         /* extend array */
905         new_order = realloc(order, (n_order+1) * sizeof(uint16_t));
906         if (!new_order) {
907                 err = -ENOMEM;
908                 goto finish;
909         }
910         order = new_order;
911
912         /* add to the top or end of the list */
913         if (first) {
914                 memmove(&order[1], order, n_order * sizeof(uint16_t));
915                 order[0] = slot;
916         } else
917                 order[n_order] = slot;
918
919         efi_set_boot_order(order, n_order+1);
920
921 finish:
922         free(order);
923         return err;
924 }
925
926 static int remove_from_order(uint16_t slot) {
927         uint16_t *order = NULL;
928         int n_order;
929         int i;
930         int err = 0;
931
932         n_order = efi_get_boot_order(&order);
933         if (n_order < 0)
934                 return n_order;
935         if (n_order == 0)
936                 return 0;
937
938         for (i = 0; i < n_order; i++) {
939                 if (order[i] != slot)
940                         continue;
941
942                 if (i+1 < n_order)
943                         memmove(&order[i], &order[i+1], (n_order - i) * sizeof(uint16_t));
944                 efi_set_boot_order(order, n_order-1);
945                 break;
946         }
947
948         free(order);
949         return err;
950 }
951
952 static int install_variables(const char *esp_path,
953                              uint32_t part, uint64_t pstart, uint64_t psize,
954                              const uint8_t uuid[16], const char *path,
955                              bool force) {
956         char *p = NULL;
957         uint16_t *options = NULL;
958         uint16_t slot;
959         int r;
960
961         if (!is_efi_boot()) {
962                 fprintf(stderr, "Not booted with EFI, skipping EFI variable checks.\n");
963                 return 0;
964         }
965
966         if (asprintf(&p, "%s%s", esp_path, path) < 0) {
967                 fprintf(stderr, "Out of memory.\n");
968                 return -ENOMEM;
969         }
970
971         if (access(p, F_OK) < 0) {
972                 if (errno == ENOENT)
973                         r = 0;
974                 else
975                         r = -errno;
976                 goto finish;
977         }
978
979         r = find_slot(uuid, path, &slot);
980         if (r < 0) {
981                 if (r == -ENOENT)
982                         fprintf(stderr, "Failed to access EFI variables. Is the \"efivarfs\" filesystem mounted?\n");
983                 else
984                         fprintf(stderr, "Failed to determine current boot order: %s\n", strerror(-r));
985                 goto finish;
986         }
987
988         if (force || r == false) {
989                 r = efi_add_boot_option(slot,
990                                           "Linux Boot Manager",
991                                           part, pstart, psize,
992                                           uuid, path);
993                 if (r < 0) {
994                         fprintf(stderr, "Failed to create EFI Boot variable entry: %s\n", strerror(-r));
995                         goto finish;
996                 }
997                 fprintf(stderr, "Created EFI Boot entry \"Linux Boot Manager\".\n");
998         }
999         insert_into_order(slot, force);
1000
1001 finish:
1002         free(p);
1003         free(options);
1004         return r;
1005 }
1006
1007 static int delete_nftw(const char *path, const struct stat *sb, int typeflag, struct FTW *ftw) {
1008         int r;
1009
1010         if (typeflag == FTW_D || typeflag == FTW_DNR || typeflag == FTW_DP)
1011                 r = rmdir(path);
1012         else
1013                 r = unlink(path);
1014
1015         if (r < 0)
1016                 fprintf(stderr, "Failed to remove %s: %m\n", path);
1017         else
1018                 fprintf(stderr, "Removed %s.\n", path);
1019
1020         return 0;
1021 }
1022
1023 static int rm_rf(const char *p) {
1024         nftw(p, delete_nftw, 20, FTW_DEPTH|FTW_MOUNT|FTW_PHYS);
1025         return 0;
1026 }
1027
1028 static int remove_boot_efi(const char *esp_path) {
1029         struct dirent *de;
1030         char *p = NULL, *q = NULL;
1031         DIR *d = NULL;
1032         int r = 0, c = 0;
1033
1034         if (asprintf(&p, "%s/EFI/BOOT", esp_path) < 0) {
1035                 fprintf(stderr, "Out of memory.\n");
1036                 return -ENOMEM;
1037         }
1038
1039         d = opendir(p);
1040         if (!d) {
1041                 if (errno == ENOENT) {
1042                         r = 0;
1043                         goto finish;
1044                 }
1045
1046                 fprintf(stderr, "Failed to read %s: %m\n", p);
1047                 r = -errno;
1048                 goto finish;
1049         }
1050
1051         while ((de = readdir(d))) {
1052                 char *v;
1053                 size_t n;
1054                 FILE *f;
1055
1056                 if (de->d_name[0] == '.')
1057                         continue;
1058
1059                 n = strlen(de->d_name);
1060                 if (n < 4 || strcasecmp(de->d_name + n - 4, ".EFI") != 0)
1061                         continue;
1062
1063                 if (strncasecmp(de->d_name, "BOOT", 4) != 0)
1064                         continue;
1065
1066                 free(q);
1067                 q = NULL;
1068                 if (asprintf(&q, "%s/%s", p, de->d_name) < 0) {
1069                         fprintf(stderr, "Out of memory.\n");
1070                         r = -ENOMEM;
1071                         goto finish;
1072                 }
1073
1074                 f = fopen(q, "re");
1075                 if (!f) {
1076                         fprintf(stderr, "Failed to open %s for reading: %m\n", q);
1077                         r = -errno;
1078                         goto finish;
1079                 }
1080
1081                 r = get_file_version(f, &v);
1082                 fclose(f);
1083
1084                 if (r < 0)
1085                         goto finish;
1086
1087                 if (r > 0 && strncmp(v, "gummiboot ", 10) == 0) {
1088
1089                         r = unlink(q);
1090                         if (r < 0) {
1091                                 fprintf(stderr, "Failed to remove %s: %m\n", q);
1092                                 r = -errno;
1093                                 free(v);
1094                                 goto finish;
1095                         } else
1096                                 fprintf(stderr, "Removed %s.\n", q);
1097                 }
1098
1099                 c++;
1100                 free(v);
1101         }
1102
1103         r = c;
1104
1105 finish:
1106         if (d)
1107                 closedir(d);
1108         free(p);
1109         free(q);
1110
1111         return r;
1112 }
1113
1114 static int rmdir_one(const char *prefix, const char *suffix) {
1115         char *p;
1116
1117         if (asprintf(&p, "%s/%s", prefix, suffix) < 0) {
1118                 fprintf(stderr, "Out of memory.\n");
1119                 return -ENOMEM;
1120         }
1121
1122         if (rmdir(p) < 0) {
1123                 if (errno != ENOENT && errno != ENOTEMPTY) {
1124                         fprintf(stderr, "Failed to remove %s: %m\n", p);
1125                         free(p);
1126                         return -errno;
1127                 }
1128         } else
1129                 fprintf(stderr, "Removed %s.\n", p);
1130
1131         free(p);
1132         return 0;
1133 }
1134
1135
1136 static int remove_binaries(const char *esp_path) {
1137         char *p;
1138         int r, q;
1139
1140         if (asprintf(&p, "%s/EFI/gummiboot", esp_path) < 0) {
1141                 fprintf(stderr, "Out of memory.\n");
1142                 return -ENOMEM;
1143         }
1144
1145         r = rm_rf(p);
1146         free(p);
1147
1148         q = remove_boot_efi(esp_path);
1149         if (q < 0 && r == 0)
1150                 r = q;
1151
1152         q = rmdir_one(esp_path, "loader/entries");
1153         if (q < 0 && r == 0)
1154                 r = q;
1155
1156         q = rmdir_one(esp_path, "loader");
1157         if (q < 0 && r == 0)
1158                 r = q;
1159
1160         q = rmdir_one(esp_path, "EFI/BOOT");
1161         if (q < 0 && r == 0)
1162                 r = q;
1163
1164         q = rmdir_one(esp_path, "EFI/gummiboot");
1165         if (q < 0 && r == 0)
1166                 r = q;
1167
1168         q = rmdir_one(esp_path, "EFI");
1169         if (q < 0 && r == 0)
1170                 r = q;
1171
1172         return r;
1173 }
1174
1175 static int remove_variables(const uint8_t uuid[16], const char *path, bool in_order) {
1176         uint16_t slot;
1177         int r;
1178
1179         if (!is_efi_boot())
1180                 return 0;
1181
1182         r = find_slot(uuid, path, &slot);
1183         if (r != 1)
1184                 return 0;
1185
1186         r = efi_remove_boot_option(slot);
1187         if (r < 0)
1188                 return r;
1189
1190         if (in_order)
1191                 remove_from_order(slot);
1192
1193         return 0;
1194 }
1195
1196 static int install_loader_config(const char *esp_path) {
1197         char *p = NULL;
1198         char line[LINE_MAX];
1199         char *vendor = NULL;
1200         FILE *f;
1201
1202         f = fopen("/etc/os-release", "re");
1203         if (!f)
1204                 return -errno;
1205
1206         while (fgets(line, sizeof(line), f) != NULL) {
1207                 char *s;
1208
1209                 if (strncmp(line, "ID=", 3) != 0)
1210                         continue;
1211                 vendor = line + 3;
1212                 s = strchr(vendor, '\n');
1213                 if (s)
1214                         s[0] = '\0';
1215                 break;
1216         }
1217
1218         fclose(f);
1219
1220         if (!vendor)
1221                 return -ESRCH;
1222
1223         if (asprintf(&p, "%s/%s", esp_path, "loader/loader.conf") < 0) {
1224                 fprintf(stderr, "Out of memory.\n");
1225                 return -ENOMEM;
1226         }
1227
1228         f = fopen(p, "wxe");
1229         if (f) {
1230                 fprintf(f, "#timeout 3\n");
1231                 fprintf(f, "default %s-*\n", vendor);
1232                 fclose(f);
1233         }
1234
1235         free(p);
1236         return 0;
1237 }
1238
1239 static const char *arg_path = NULL;
1240 static bool arg_touch_variables = true;
1241
1242 static int parse_argv(int argc, char *argv[]) {
1243         enum {
1244                 ARG_PATH = 0x100,
1245                 ARG_NO_VARIABLES
1246         };
1247
1248         static const struct option options[] = {
1249                 { "help",         no_argument,       NULL, 'h'              },
1250                 { "path",         required_argument, NULL, ARG_PATH         },
1251                 { "no-variables", no_argument,       NULL, ARG_NO_VARIABLES },
1252                 { NULL,           0,                 NULL, 0                }
1253         };
1254
1255         int c;
1256
1257         assert(argc >= 0);
1258         assert(argv);
1259
1260         while ((c = getopt_long(argc, argv, "h", options, NULL)) >= 0) {
1261                 switch (c) {
1262
1263                 case 'h':
1264                         help();
1265                         return 0;
1266
1267                 case ARG_PATH:
1268                         arg_path = optarg;
1269                         break;
1270
1271                 case ARG_NO_VARIABLES:
1272                         arg_touch_variables = false;
1273                         break;
1274
1275                 case '?':
1276                         return -EINVAL;
1277
1278                 default:
1279                         fprintf(stderr, "Unknown option code '%c'.\n", c);
1280                         return -EINVAL;
1281                 }
1282         }
1283
1284         return 1;
1285 }
1286
1287 int main(int argc, char*argv[]) {
1288         enum action {
1289                 ACTION_STATUS,
1290                 ACTION_INSTALL,
1291                 ACTION_UPDATE,
1292                 ACTION_REMOVE
1293         } arg_action = ACTION_STATUS;
1294
1295         static const struct {
1296                 const char* verb;
1297                 enum action action;
1298         } verbs[] = {
1299                 { "status",  ACTION_STATUS },
1300                 { "install", ACTION_INSTALL },
1301                 { "update",  ACTION_UPDATE },
1302                 { "remove",  ACTION_REMOVE },
1303         };
1304
1305         uint8_t uuid[16] = "";
1306         uint32_t part = 0;
1307         uint64_t pstart = 0;
1308         uint64_t psize = 0;
1309         unsigned int i;
1310         int r, q;
1311
1312         r = parse_argv(argc, argv);
1313         if (r <= 0)
1314                 goto finish;
1315
1316         if (argv[optind]) {
1317                 for (i = 0; i < ELEMENTSOF(verbs); i++) {
1318                         if (!streq(argv[optind], verbs[i].verb))
1319                                 continue;
1320                         arg_action = verbs[i].action;
1321                         break;
1322                 }
1323                 if (i >= ELEMENTSOF(verbs)) {
1324                         fprintf(stderr, "Unknown operation %s\n", argv[optind]);
1325                         r = -EINVAL;
1326                         goto finish;
1327                 }
1328         }
1329
1330         if (!arg_path)
1331                 arg_path = "/boot";
1332
1333         if (geteuid() != 0) {
1334                 fprintf(stderr, "Need to be root.\n");
1335                 r = -EPERM;
1336                 goto finish;
1337         }
1338
1339         r = verify_esp(arg_path, &part, &pstart, &psize, uuid);
1340         if (r == -ENODEV && !arg_path)
1341                 fprintf(stderr, "You might want to use --path= to indicate the path to your ESP, in case it is not mounted to /boot.\n");
1342         if (r < 0)
1343                 goto finish;
1344
1345         switch (arg_action) {
1346         case ACTION_STATUS:
1347                 r = status_binaries(arg_path);
1348                 if (r < 0)
1349                         goto finish;
1350
1351                 if (arg_touch_variables)
1352                         r = status_variables();
1353                 break;
1354
1355         case ACTION_INSTALL:
1356         case ACTION_UPDATE:
1357                 umask(0002);
1358
1359                 r = install_binaries(arg_path, arg_action == ACTION_INSTALL);
1360                 if (r < 0)
1361                         goto finish;
1362
1363                 if (arg_action == ACTION_INSTALL)
1364                         install_loader_config(arg_path);
1365
1366                 if (arg_touch_variables)
1367                         r = install_variables(arg_path,
1368                                               part, pstart, psize, uuid,
1369                                               "/EFI/gummiboot/gummiboot" MACHINE_TYPE_NAME ".efi",
1370                                               arg_action == ACTION_INSTALL);
1371                 break;
1372
1373         case ACTION_REMOVE:
1374                 r = remove_binaries(arg_path);
1375
1376                 if (arg_touch_variables) {
1377                         q = remove_variables(uuid, "/EFI/gummiboot/gummiboot" MACHINE_TYPE_NAME ".efi", true);
1378                         if (q < 0 && r == 0)
1379                                 r = q;
1380                 }
1381                 break;
1382         }
1383
1384 finish:
1385         return r < 0 ? EXIT_FAILURE : EXIT_SUCCESS;
1386 }