+ efi_tilt_backslashes(path);
+
+ printf(" Title: %s\n", strna(title));
+ printf(" ID: 0x%04X\n", id);
+ printf(" Status: %sactive%s\n", active ? "" : "in", in_order ? ", boot-order" : "");
+ printf(" Partition: /dev/disk/by-partuuid/%02x%02x%02x%02x-%02x%02x-%02x%02x-%02x%02x-%02x%02x%02x%02x%02x%02x\n", SD_ID128_FORMAT_VAL(partition));
+ printf(" File: └─%s\n", path);
+ printf("\n");
+
+finish:
+ free(title);
+ free(path);
+ return r;
+}
+
+static int status_variables(void) {
+ int n_options, n_order;
+ uint16_t *options = NULL, *order = NULL;
+ int r, i;
+
+ if (!is_efi_boot()) {
+ fprintf(stderr, "Not booted with EFI, not showing EFI variables.\n");
+ return 0;
+ }
+
+ n_options = efi_get_boot_options(&options);
+ if (n_options < 0) {
+ if (n_options == -ENOENT)
+ fprintf(stderr, "Failed to access EFI variables, "
+ "efivarfs needs to be available at /sys/firmware/efi/efivars/.\n");
+ else
+ fprintf(stderr, "Failed to read EFI boot entries: %s\n", strerror(-n_options));
+ r = n_options;
+ goto finish;
+ }
+
+ printf("Boot Loader Entries in EFI Variables:\n");
+ n_order = efi_get_boot_order(&order);
+ if (n_order == -ENOENT) {
+ n_order = 0;
+ } else if (n_order < 0) {
+ fprintf(stderr, "Failed to read EFI boot order.\n");
+ r = n_order;
+ goto finish;
+ }
+
+ /* print entries in BootOrder first */
+ for (i = 0; i < n_order; i++)
+ print_efi_option(order[i], true);
+
+ /* print remaining entries */
+ for (i = 0; i < n_options; i++) {
+ int j;
+ bool found = false;
+
+ for (j = 0; j < n_order; j++)
+ if (options[i] == order[j]) {
+ found = true;
+ break;
+ }
+
+ if (found)
+ continue;
+
+ print_efi_option(options[i], false);
+ }
+
+ r = 0;
+finish:
+ free(options);
+ free(order);
+
+ return r;
+}
+
+static int compare_product(const char *a, const char *b) {
+ size_t x, y;
+
+ assert(a);
+ assert(b);
+
+ x = strcspn(a, " ");
+ y = strcspn(b, " ");
+ if (x != y)
+ return x < y ? -1 : x > y ? 1 : 0;
+
+ return strncmp(a, b, x);
+}
+
+static int compare_version(const char *a, const char *b) {
+ assert(a);
+ assert(b);
+
+ a += strcspn(a, " ");
+ a += strspn(a, " ");
+ b += strcspn(b, " ");
+ b += strspn(b, " ");
+
+ return strverscmp(a, b);
+}
+
+static int version_check(FILE *f, const char *from, const char *to) {
+ FILE *g = NULL;
+ char *a = NULL, *b = NULL;
+ int r;
+
+ assert(f);
+ assert(from);
+ assert(to);
+
+ r = get_file_version(f, &a);
+ if (r < 0)
+ goto finish;
+ if (r == 0) {
+ r = -EINVAL;
+ fprintf(stderr, "Source file %s does not carry version information!\n", from);
+ goto finish;
+ }
+
+ g = fopen(to, "re");
+ if (!g) {
+ if (errno == ENOENT) {
+ r = 0;
+ goto finish;
+ }
+
+ r = -errno;
+ fprintf(stderr, "Failed to open %s for reading: %m\n", to);
+ goto finish;
+ }
+
+ r = get_file_version(g, &b);
+ if (r < 0)
+ goto finish;
+ if (r == 0 || compare_product(a, b) != 0) {
+ r = -EEXIST;
+ fprintf(stderr, "Skipping %s, since it's owned by another boot loader.\n", to);
+ goto finish;
+ }
+
+ if (compare_version(a, b) < 0) {
+ r = -EEXIST;
+ fprintf(stderr, "Skipping %s, since it's a newer boot loader version already.\n", to);
+ goto finish;
+ }
+
+ r = 0;
+
+finish:
+ free(a);
+ free(b);
+ if (g)
+ fclose(g);
+ return r;
+}
+
+static int copy_file(const char *from, const char *to, bool force) {
+ FILE *f = NULL, *g = NULL;
+ char *p = NULL;
+ int r;
+ struct timespec t[2];
+ struct stat st;
+
+ assert(from);
+ assert(to);
+
+ f = fopen(from, "re");
+ if (!f) {
+ fprintf(stderr, "Failed to open %s for reading: %m\n", from);
+ return -errno;
+ }
+
+ if (!force) {
+ /* If this is an update, then let's compare versions first */
+ r = version_check(f, from, to);
+ if (r < 0)
+ goto finish;
+ }
+
+ if (asprintf(&p, "%s~", to) < 0) {
+ fprintf(stderr, "Out of memory.\n");
+ r = -ENOMEM;
+ goto finish;
+ }
+
+ g = fopen(p, "wxe");
+ if (!g) {
+ /* Directory doesn't exist yet? Then let's skip this... */
+ if (!force && errno == ENOENT) {
+ r = 0;
+ goto finish;
+ }
+
+ fprintf(stderr, "Failed to open %s for writing: %m\n", to);
+ r = -errno;
+ goto finish;
+ }
+
+ rewind(f);
+ do {
+ size_t k;
+ uint8_t buf[32*1024];
+
+ k = fread(buf, 1, sizeof(buf), f);
+ if (ferror(f)) {
+ fprintf(stderr, "Failed to read %s: %m\n", from);
+ r = -errno;
+ goto finish;
+ }
+ if (k == 0)
+ break;
+
+ fwrite(buf, 1, k, g);
+ if (ferror(g)) {
+ fprintf(stderr, "Failed to write %s: %m\n", to);
+ r = -errno;
+ goto finish;
+ }
+ } while (!feof(f));
+
+ fflush(g);
+ if (ferror(g)) {
+ fprintf(stderr, "Failed to write %s: %m\n", to);
+ r = -errno;
+ goto finish;
+ }
+
+ r = fstat(fileno(f), &st);
+ if (r < 0) {
+ fprintf(stderr, "Failed to get file timestamps of %s: %m", from);
+ r = -errno;
+ goto finish;
+ }
+
+ t[0] = st.st_atim;
+ t[1] = st.st_mtim;
+
+ r = futimens(fileno(g), t);
+ if (r < 0) {
+ fprintf(stderr, "Failed to change file timestamps for %s: %m", p);
+ r = -errno;
+ goto finish;
+ }
+
+ if (rename(p, to) < 0) {
+ fprintf(stderr, "Failed to rename %s to %s: %m\n", p, to);
+ r = -errno;
+ goto finish;
+ }
+
+ fprintf(stderr, "Copied %s to %s.\n", from, to);
+
+ free(p);
+ p = NULL;
+ r = 0;
+
+finish:
+ if (f)
+ fclose(f);
+ if (g)
+ fclose(g);
+ if (p) {
+ unlink(p);
+ free(p);
+ }
+ return r;
+}
+
+static char* strupper(char *s) {
+ char *p;
+
+ for (p = s; *p; p++)
+ *p = toupper(*p);
+
+ return s;
+}
+
+static int mkdir_one(const char *prefix, const char *suffix) {
+ char *p;
+
+ if (asprintf(&p, "%s/%s", prefix, suffix) < 0) {
+ fprintf(stderr, "Out of memory.\n");