OSDN Git Service

dp-*: fix some wrong size_t vs ssize_t usage.
[android-x86/external-efivar.git] / src / generics.h
1 /*
2  * Copyright 2012-2016 Red Hat, Inc.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public License as
6  * published by the Free Software Foundation; either version 2.1 of the
7  * License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, see
16  * <http://www.gnu.org/licenses/>.
17  *
18  */
19
20 #ifndef LIBEFIVAR_GENERIC_NEXT_VARIABLE_NAME_H
21 #define LIBEFIVAR_GENERIC_NEXT_VARIABLE_NAME_H 1
22
23 #include <dirent.h>
24 #include <err.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <limits.h>
28 #include <stdlib.h>
29 #include <sys/types.h>
30 #include <unistd.h>
31
32 #include "guid.h"
33
34 static DIR *dir;
35
36 static inline int
37 __attribute__((unused))
38 generic_get_next_variable_name(const char *path, efi_guid_t **guid, char **name)
39 {
40         static char ret_name[NAME_MAX+1];
41         static efi_guid_t ret_guid;
42
43         if (!guid || !name) {
44                 errno = EINVAL;
45                 return -1;
46         }
47
48         /* if only one of guid and name are null, there's no "next" variable,
49          * because the current variable is invalid. */
50         if ((*guid == NULL && *name != NULL) ||
51                         (*guid != NULL && *name == NULL)) {
52                 errno = EINVAL;
53                 return -1;
54         }
55
56         /* if dir is NULL, we're also starting over */
57         if (!dir) {
58                 dir = opendir(path);
59                 if (!dir)
60                         return -1;
61
62                 int fd = dirfd(dir);
63                 if (fd < 0) {
64                         typeof(errno) errno_value = errno;
65                         closedir(dir);
66                         errno = errno_value;
67                         return -1;
68                 }
69                 int flags = fcntl(fd, F_GETFD);
70                 if (flags < 0) {
71                         warn("fcntl(fd, F_GETFD) failed");
72                 } else {
73                         flags |= FD_CLOEXEC;
74                         if (fcntl(fd, F_SETFD, flags) < 0)
75                                 warn("fcntl(fd, F_SETFD, "
76                                         "flags | FD_CLOEXEC) failed");
77                 }
78
79                 *guid = NULL;
80                 *name = NULL;
81         }
82
83         struct dirent *de = NULL;
84         char *guidtext = "8be4df61-93ca-11d2-aa0d-00e098032b8c";
85         size_t guidlen = strlen(guidtext);
86
87         while (1) {
88                 de = readdir(dir);
89                 if (de == NULL) {
90                         closedir(dir);
91                         dir = NULL;
92                         return 0;
93                 }
94                 /* a proper entry must have space for a guid, a dash, and
95                  * the variable name */
96                 size_t namelen = strlen(de->d_name);
97                 if (namelen < guidlen + 2)
98                         continue;
99
100                 int rc = text_to_guid(de->d_name +namelen -guidlen, &ret_guid);
101                 if (rc < 0) {
102                         closedir(dir);
103                         dir = NULL;
104                         errno = EINVAL;
105                         return -1;
106                 }
107
108                 strncpy(ret_name, de->d_name, sizeof(ret_name));
109                 ret_name[namelen - guidlen - 1] = '\0';
110
111                 *guid = &ret_guid;
112                 *name = ret_name;
113                 break;
114         }
115
116         return 1;
117 }
118
119 static void __attribute__((destructor)) close_dir(void);
120 static void
121 close_dir(void)
122 {
123         if (dir != NULL) {
124                 closedir(dir);
125                 dir = NULL;
126         }
127 }
128
129 /* this is a simple read/delete/write implementation of "update".  Good luck.
130  * -- pjones */
131 static int
132 __attribute__((__unused__))
133 __attribute__((__flatten__))
134 generic_append_variable(efi_guid_t guid, const char *name,
135                        uint8_t *new_data, size_t new_data_size,
136                        uint32_t new_attributes)
137 {
138         int rc;
139         uint8_t *data = NULL;
140         size_t data_size = 0;
141         uint32_t attributes = 0;
142
143         rc = efi_get_variable(guid, name, &data, &data_size, &attributes);
144         if (rc >= 0) {
145                 if ((attributes | EFI_VARIABLE_APPEND_WRITE) !=
146                                 (new_attributes | EFI_VARIABLE_APPEND_WRITE)) {
147                         free(data);
148                         errno = EINVAL;
149                         return -1;
150                 }
151                 uint8_t *d = malloc(data_size + new_data_size);
152                 size_t ds = data_size + new_data_size;
153                 memcpy(d, data, data_size);
154                 memcpy(d + data_size, new_data, new_data_size);
155                 attributes &= ~EFI_VARIABLE_APPEND_WRITE;
156                 rc = efi_del_variable(guid, name);
157                 if (rc < 0) {
158                         free(data);
159                         free(d);
160                         return rc;
161                 }
162                 /* if this doesn't work, we accidentally deleted.  There's
163                  * really not much to do about it, so return the error and
164                  * let our caller attempt to clean up :/
165                  */
166                 rc = efi_set_variable(guid, name, d, ds, attributes, 0600);
167                 free(d);
168                 free(data);
169                 return rc;
170         } else if (errno == ENOENT) {
171                 data = new_data;
172                 data_size = new_data_size;
173                 attributes = new_attributes & ~EFI_VARIABLE_APPEND_WRITE;
174                 rc = efi_set_variable(guid, name, data, data_size,
175                                       attributes, 0600);
176                 return rc;
177         }
178         return rc;
179 }
180
181 #endif /* LIBEFIVAR_GENERIC_NEXT_VARIABLE_NAME_H */