OSDN Git Service

Silence some trivial clang-analyzer/coverity complaints.
[android-x86/external-efivar.git] / src / efivar.c
1 /*
2  * libefivar - library for the manipulation of EFI variables
3  * Copyright 2012 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 <ctype.h>
24 #include <err.h>
25 #include <fcntl.h>
26 #include <getopt.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <sys/mman.h>
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <errno.h>
34 #include <string.h>
35 #include <limits.h>
36
37 extern char *optarg;
38 extern int optind, opterr, optopt;
39
40 #include <efivar/efivar.h>
41 #include "guid.h"
42
43 #define ACTION_LIST             0x1
44 #define ACTION_PRINT            0x2
45 #define ACTION_APPEND           0x4
46 #define ACTION_LIST_GUIDS       0x8
47 #define ACTION_WRITE            0x10
48 #define ACTION_PRINT_DEC        0x20
49
50 #define EDIT_APPEND     0
51 #define EDIT_WRITE      1
52
53 #define SHOW_VERBOSE    0
54 #define SHOW_DECIMAL    1
55
56 static const char *attribute_names[] = {
57         "Non-Volatile",
58         "Boot Service Access",
59         "Runtime Service Access",
60         "Hardware Error Record",
61         "Authenticated Write Access",
62         "Time-Based Authenticated Write Access",
63         "Append Write",
64         ""
65 };
66
67 static int verbose_errors = 0;
68
69 static void
70 show_errors(void)
71 {
72         int rc = 1;
73
74         if (!verbose_errors)
75                 return;
76
77         printf("Error trace:\n");
78         for (int i = 0; rc > 0; i++) {
79                 char *filename = NULL;
80                 char *function = NULL;
81                 int line = 0;
82                 char *message = NULL;
83                 int error = 0;
84
85                 rc = efi_error_get(i, &filename, &function, &line, &message,
86                                    &error);
87                 if (rc < 0)
88                         err(1, "error fetching trace value");
89                 if (rc == 0)
90                         break;
91                 printf(" %s:%d %s(): %s: %s", filename, line, function,
92                        strerror(error), message);
93         }
94 }
95
96 static void
97 list_all_variables(void)
98 {
99         efi_guid_t *guid = NULL;
100         char *name = NULL;
101         int rc;
102         while ((rc = efi_get_next_variable_name(&guid, &name)) > 0)
103                  printf(GUID_FORMAT "-%s\n",
104                         guid->a, guid->b, guid->c, bswap_16(guid->d),
105                         guid->e[0], guid->e[1], guid->e[2], guid->e[3],
106                         guid->e[4], guid->e[5], name);
107
108         if (rc < 0) {
109                 fprintf(stderr, "efivar: error listing variables: %m\n");
110                 show_errors();
111                 exit(1);
112         }
113 }
114
115 static void
116 parse_name(const char *guid_name, char **name, efi_guid_t *guid)
117 {
118         unsigned int guid_len = sizeof("84be9c3e-8a32-42c0-891c-4cd3b072becc");
119         char guid_buf[guid_len + 2];
120         int rc;
121         off_t name_pos = 0;
122
123         const char *left, *right;
124
125         left = strchr(guid_name, '{');
126         right = strchr(guid_name, '}');
127         if (left && right) {
128                 if (right[1] != '-' || right[2] == '\0') {
129 bad_name:
130                         errno = -EINVAL;
131                         fprintf(stderr, "efivar: invalid name \"%s\"\n",
132                                 guid_name);
133                         show_errors();
134                         exit(1);
135                 }
136                 name_pos = right + 1 - guid_name;
137
138                 strncpy(guid_buf, guid_name, name_pos);
139                 guid_buf[name_pos++] = '\0';
140
141                 rc = efi_id_guid_to_guid(guid_buf, guid);
142                 if (rc < 0)
143                         goto bad_name;
144         } else {
145                 /* it has to be at least the length of the guid, a dash, and
146                  * one more character */
147                 if (strlen(guid_name) < guid_len + 2)
148                         goto bad_name;
149                 name_pos = guid_len - 1;
150
151                 if (guid_name[name_pos] != '-' || guid_name[name_pos+1] == '\0')
152                         goto bad_name;
153                 name_pos++;
154
155                 memset(guid_buf, 0, sizeof(guid_buf));
156                 strncpy(guid_buf, guid_name, guid_len - 1);
157
158                 rc = text_to_guid(guid_buf, guid);
159                 if (rc < 0)
160                         goto bad_name;
161         }
162
163         char *name_buf = NULL;
164         int name_len;
165         name_len = strlen(guid_name + name_pos) + 1;
166         name_buf = calloc(1, name_len);
167         if (!name_buf) {
168                 fprintf(stderr, "efivar: %m\n");
169                 exit(1);
170         }
171         strcpy(name_buf, guid_name + name_pos);
172         *name = name_buf;
173 }
174
175 static void
176 show_variable(char *guid_name, int display_type)
177 {
178         efi_guid_t guid = efi_guid_empty;
179         char *name = NULL;
180         int rc;
181
182         uint8_t *data = NULL;
183         size_t data_size = 0;
184         uint32_t attributes;
185
186         parse_name(guid_name, &name, &guid);
187         if (!name || efi_guid_is_empty(&guid)) {
188                 fprintf(stderr, "efivar: could not parse variable name.\n");
189                 show_errors();
190                 exit(1);
191         }
192
193         errno = 0;
194         rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
195         if (rc < 0) {
196                 fprintf(stderr, "efivar: show variable: %m\n");
197                 show_errors();
198                 exit(1);
199         }
200
201         if (display_type == SHOW_VERBOSE) {
202                 printf("GUID: "GUID_FORMAT "\n",
203                        guid.a, guid.b, guid.c, bswap_16(guid.d),
204                        guid.e[0], guid.e[1], guid.e[2], guid.e[3],
205                        guid.e[4], guid.e[5]);
206                 printf("Name: \"%s\"\n", name);
207                 printf("Attributes:\n");
208                 for (int i = 0; attribute_names[i][0] != '\0'; i++) {
209                         if(attributes & (1 << i))
210                                 printf("\t%s\n", attribute_names[i]);
211                 }
212                 printf("Value:\n");
213
214                 uint32_t index = 0;
215                 while (index < data_size) {
216                         char charbuf[] = "................";
217                         printf("%08x  ", index);
218                         /* print the hex values, and render the ascii bits into
219                          * charbuf */
220                         while (index < data_size) {
221                                 printf("%02x ", data[index]);
222                                 if (index % 8 == 7)
223                                         printf(" ");
224                                 if (isprint(data[index]))
225                                         charbuf[index % 16] = data[index];
226                                 index++;
227                                 if (index % 16 == 0)
228                                         break;
229                         }
230
231                         /* If we're above data_size, finish out the line with
232                          * space, and also finish out charbuf with space */
233                         while (index >= data_size && index % 16 != 0) {
234                                 if (index % 8 == 7)
235                                         printf(" ");
236                                 printf("   ");
237                                 charbuf[index % 16] = ' ';
238
239                                 index++;
240                                 if (index % 16 == 0)
241                                         break;
242                         }
243                         printf("|%s|\n", charbuf);
244                 }
245         } else if (display_type == SHOW_DECIMAL) {
246                 uint32_t index = 0;
247                 while (index < data_size) {
248                         // print the dec values
249                         while (index < data_size) {
250                                 printf("%d ", data[index]);
251                                 if (index % 8 == 7)
252                                         printf(" ");
253                                 index++;
254                                 if (index % 16 == 0)
255                                         break;
256                         }
257                 }
258                 printf("\n");
259         }
260
261         free(name);
262         if (data)
263                 free(data);
264 }
265
266 static void
267 edit_variable(const char *guid_name, void *data, size_t data_size,
268               uint32_t attrib, int edit_type)
269 {
270         efi_guid_t guid = efi_guid_empty;
271         char *name = NULL;
272         int rc;
273         uint8_t *old_data = NULL;
274         size_t old_data_size = 0;
275         uint32_t old_attributes = 0;
276
277         parse_name(guid_name, &name, &guid);
278         if (!name || efi_guid_is_empty(&guid)) {
279                 fprintf(stderr, "efivar: could not parse variable name.\n");
280                 show_errors();
281                 exit(1);
282         }
283
284         rc = efi_get_variable(guid, name, &old_data, &old_data_size,
285                                 &old_attributes);
286         if (rc < 0 && edit_type != EDIT_WRITE) {
287                 fprintf(stderr, "efivar: %m\n");
288                 show_errors();
289                 exit(1);
290         }
291
292         if (attrib != 0)
293                 old_attributes = attrib;
294
295         switch (edit_type){
296                 case EDIT_APPEND:
297                         rc = efi_append_variable(guid, name, data, data_size,
298                                         old_attributes);
299                         break;
300                 case EDIT_WRITE:
301                         rc = efi_set_variable(guid, name, data, data_size,
302                                         old_attributes, 0644);
303                         break;
304         }
305
306         free(name);
307         if (old_data)
308                 free(old_data);
309
310         if (rc < 0) {
311                 fprintf(stderr, "efivar: %m\n");
312                 show_errors();
313                 exit(1);
314         }
315 }
316
317 static void
318 validate_name(const char *name)
319 {
320         if (name == NULL) {
321                 fprintf(stderr, "Invalid variable name\n");
322                 show_errors();
323                 exit(1);
324         }
325 }
326
327 static void
328 prepare_data(const char *filename, void **data, size_t *data_size)
329 {
330         int fd = -1;
331         void *buf;
332         size_t buflen = 0;
333         struct stat statbuf;
334         int rc;
335
336         if (filename == NULL) {
337                 fprintf(stderr, "Input filename must be provided.\n");
338                 exit(1);
339         }
340
341         fd = open(filename, O_RDONLY);
342         if (fd < 0)
343                 goto err;
344
345         memset(&statbuf, '\0', sizeof (statbuf));
346         rc = fstat(fd, &statbuf);
347         if (rc < 0)
348                 goto err;
349
350         buflen = statbuf.st_size;
351         buf = mmap(NULL, buflen, PROT_READ, MAP_PRIVATE|MAP_POPULATE, fd, 0);
352         if (!buf)
353                 goto err;
354
355         *data = buf;
356         *data_size = buflen;
357
358         close(fd);
359         return;
360 err:
361         if (fd >= 0)
362                 close(fd);
363         fprintf(stderr, "Could not use \"%s\": %m\n", filename);
364         exit(1);
365 }
366
367 static void
368 usage(const char *progname)
369 {
370         printf("Usage: %s [OPTION...]\n", basename(progname));
371         printf("  -l, --list                        list current variables\n");
372         printf("  -p, --print                       print variable specified by --name\n");
373         printf("  -d, --print-decimal               print variable in decimal values specified\n");
374         printf("                                    by --name\n");
375         printf("  -n, --name=<guid-name>            variable to manipulate, in the form\n");
376         printf("                                    8be4df61-93ca-11d2-aa0d-00e098032b8c-Boot0000\n");
377         printf("  -a, --append                      append to variable specified by --name\n");
378         printf("  -f, --fromfile=<file>             use data from <file>\n");
379         printf("  -t, --attributes=<attributes>     attributes to use on append\n");
380         printf("  -L, --list-guids                  show internal guid list\n");
381         printf("  -w, --write                       write to variable specified by --name\n\n");
382         printf("Help options:\n");
383         printf("  -?, --help                        Show this help message\n");
384         printf("      --usage                       Display brief usage message\n");
385         return;
386 }
387
388 int main(int argc, char *argv[])
389 {
390         int c = 0;
391         int i = 0;
392         int action = 0;
393         void *data = NULL;
394         size_t data_size = 0;
395         char *name = NULL;
396         char *file = NULL;
397         uint32_t attributes = 0;
398         char *sopts = "lpdn:af:t:Lw?";
399         struct option lopts[] =
400                 { {"list", no_argument, 0, 'l'},
401                   {"print", no_argument, 0, 'p'},
402                   {"print-decimal", no_argument, 0, 'd'},
403                   {"name", required_argument, 0, 'n'},
404                   {"append", no_argument, 0, 'a'},
405                   {"fromfile", required_argument, 0, 'f'},
406                   {"attributes", required_argument, 0, 't'},
407                   {"list-guids", no_argument, 0, 'L'},
408                   {"write", no_argument, 0, 'w'},
409                   {"help", no_argument, 0, '?'},
410                   {"usage", no_argument, 0, 0},
411                   {0, 0, 0, 0}
412                 };
413
414         while ((c = getopt_long(argc, argv, sopts, lopts, &i)) != -1) {
415                 switch (c) {
416                         case 'l':
417                                 action |= ACTION_LIST;
418                                 break;
419                         case 'p':
420                                 action |= ACTION_PRINT;
421                                 break;
422                         case 'd':
423                                 action |= ACTION_PRINT_DEC;
424                                 break;
425                         case 'n':
426                                 name = optarg;
427                                 break;
428                         case 'a':
429                                 action |= ACTION_APPEND;
430                                 break;
431                         case 'f':
432                                 file = optarg;
433                                 break;
434                         case 't':
435                                 attributes = strtoul(optarg, NULL, 10);
436                                 if (errno == ERANGE || errno == EINVAL) {
437                                         fprintf(stderr, "invalid argument for -t: %s: %s\n", optarg, strerror(errno));
438                                         return EXIT_FAILURE;
439                                 }
440                                 break;
441                         case 'L':
442                                 action |= ACTION_LIST_GUIDS;
443                                 break;
444                         case 'w':
445                                 action |= ACTION_WRITE;
446                                 break;
447                         case '?':
448                                 usage(argv[0]);
449                                 return EXIT_SUCCESS;
450                         case 0:
451                                 if (strcmp(lopts[i].name, "usage")) {
452                                         usage(argv[0]);
453                                         return EXIT_SUCCESS;
454                                 }
455                                 break;
456                 }
457         }
458
459         if (name)
460                 action |= ACTION_PRINT;
461
462         switch (action) {
463                 case ACTION_LIST:
464                         list_all_variables();
465                         break;
466                 case ACTION_PRINT:
467                         validate_name(name);
468                         show_variable(name, SHOW_VERBOSE);
469                         break;
470                 case ACTION_PRINT_DEC | ACTION_PRINT:
471                         validate_name(name);
472                         show_variable(name, SHOW_DECIMAL);
473                         break;
474                 case ACTION_APPEND | ACTION_PRINT:
475                         validate_name(name);
476                         prepare_data(file, &data, &data_size);
477                         edit_variable(name, data, data_size, attributes,
478                                       EDIT_APPEND);
479                         break;
480                 case ACTION_WRITE | ACTION_PRINT:
481                         validate_name(name);
482                         prepare_data(file, &data, &data_size);
483                         edit_variable(name, data, data_size, attributes,
484                                       EDIT_WRITE);
485                         break;
486                 case ACTION_LIST_GUIDS: {
487                         efi_guid_t sentinal = {0xffffffff,0xffff,0xffff,0xffff,
488                                                {0xff,0xff,0xff,0xff,0xff,0xff}};
489                         extern struct guidname efi_well_known_guids[];
490                         extern struct guidname *efi_well_known_guids_end;
491                         intptr_t start = (intptr_t)&efi_well_known_guids;
492                         intptr_t end = (intptr_t)&efi_well_known_guids_end;
493                         unsigned int i;
494
495                         struct guidname *guid = &efi_well_known_guids[0];
496                         for (i = 0; i < (end-start) / sizeof(*guid); i++) {
497                                 if (!efi_guid_cmp(&sentinal, &guid[i].guid))
498                                         break;
499                                 printf("{"GUID_FORMAT"} {%s} %s %s\n",
500                                         guid[i].guid.a, guid[i].guid.b,
501                                         guid[i].guid.c, bswap_16(guid[i].guid.d),
502                                         guid[i].guid.e[0], guid[i].guid.e[1],
503                                         guid[i].guid.e[2], guid[i].guid.e[3],
504                                         guid[i].guid.e[4], guid[i].guid.e[5],
505                                         guid[i].symbol + strlen("efi_guid_"),
506                                         guid[i].symbol, guid[i].name);
507                         }
508                 }
509         };
510
511         return 0;
512 }