lib/efi: Add new struct efi_mount
[petitboot] / lib / efi / efivar.c
1 /*
2  *  This program is free software; you can redistribute it and/or modify
3  *  it under the terms of the GNU General Public License as published by
4  *  the Free Software Foundation; version 2 of the License.
5  *
6  *  This program is distributed in the hope that it will be useful,
7  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
8  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
9  *  GNU General Public License for more details.
10  *
11  *  You should have received a copy of the GNU General Public License
12  *  along with this program; if not, write to the Free Software
13  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
14  *
15  *  Copyright (C) 2018 Huaxintong Semiconductor Technology Co.,Ltd. All rights
16  *  reserved.
17  *  Author: Ge Song <ge.song@hxt-semitech.com>
18  */
19
20 #include <assert.h>
21 #include <errno.h>
22 #include <fcntl.h>
23 #include <stdlib.h>
24 #include <string.h>
25
26 #include <linux/fs.h>
27 #include <sys/ioctl.h>
28 #include <sys/stat.h>
29 #include <sys/statfs.h>
30
31 #include "efivar.h"
32 #include "log/log.h"
33 #include "talloc/talloc.h"
34
35 void efi_init_mount(struct efi_mount *efi_mount, const char *path,
36         const char *guid)
37 {
38         assert(efi_mount);
39
40         efi_mount->path = path;
41         efi_mount->guid = guid;
42
43         pb_debug_fn("%s--%s", efi_mount->path, efi_mount->guid);
44 }
45
46 bool efi_check_mount_magic(const struct efi_mount *efi_mount, bool check_magic)
47 {
48         struct statfs s;
49
50         assert(efi_mount);
51
52         if (!efi_mount->guid) {
53                 pb_debug_fn("guid not set\n");
54                 return false;
55         }
56
57         if (access(efi_mount->path, R_OK | W_OK)) {
58                 pb_debug_fn("Can't access %s\n", efi_mount->path);
59                 return false;
60         }
61
62         memset(&s, '\0', sizeof(s));
63         if (statfs(efi_mount->path, &s)) {
64                 pb_debug_fn("statfs failed: %s: (%d) %s\n", efi_mount->path,
65                         errno, strerror(errno));
66                 return false;
67         }
68
69         if (check_magic && s.f_type != EFIVARFS_MAGIC) {
70                 pb_debug_fn("Bad magic = 0x%lx\n", (unsigned long)s.f_type);
71                 return false;
72         }
73
74         return true;
75 }
76
77 static int efi_open(const struct efi_mount *efi_mount, const char *name,
78         int flags, mode_t mode, char **path)
79 {
80         int fd;
81
82         assert(efi_mount);
83
84         *path = NULL;
85
86         if (!efi_mount->path || !efi_mount->guid)
87                 return -1;
88
89         *path = talloc_asprintf(NULL, "%s/%s-%s", efi_mount->path, name,
90                 efi_mount->guid);
91         if (!*path)
92                 return -1;
93
94         flags = flags ? flags : O_RDONLY | O_NONBLOCK;
95
96         fd = open(*path, flags, mode);
97
98         if (fd < 0) {
99                 pb_log("%s: open failed '%s': (%d) %s\n", __func__, *path,
100                         errno, strerror(errno));
101                 talloc_free(*path);
102                 *path = NULL;
103                 return -1;
104         }
105
106         return fd;
107 }
108
109 int efi_del_variable(const struct efi_mount *efi_mount, const char *name)
110 {
111         int fd, flag;
112         int rc = -1;
113         char *path;
114
115         assert(efi_mount);
116
117         fd = efi_open(efi_mount, name, 0, 0, &path);
118         if (fd < 0)
119                 return -1;
120
121         rc = ioctl(fd, FS_IOC_GETFLAGS, &flag);
122         if (rc == -1 && errno == ENOTTY) {
123                 pb_debug_fn("'%s' does not support ioctl_iflags.\n",
124                         efi_mount->path);
125                 goto delete;
126         } else if (rc == -1) {
127                 pb_log_fn("FS_IOC_GETFLAGS failed: (%d) %s\n", errno,
128                         strerror(errno));
129                 goto exit;
130         }
131
132         flag &= ~FS_IMMUTABLE_FL;
133         rc = ioctl(fd, FS_IOC_SETFLAGS, &flag);
134         if (rc == -1) {
135                 pb_log_fn("FS_IOC_SETFLAGS failed: (%d) %s\n", errno,
136                         strerror(errno));
137                 goto exit;
138         }
139
140 delete:
141         close(fd);
142         fd = 0;
143         rc = unlink(path);
144         if (rc == -1) {
145                 pb_log_fn("unlink failed: (%d) %s\n", errno, strerror(errno));
146                 goto exit;
147         }
148         pb_debug_fn("Deleted: '%s'\n", name);
149 exit:
150         talloc_free(path);
151         close(fd);
152         return rc;
153 }
154
155 int efi_get_variable(void *ctx, const struct efi_mount *efi_mount,
156         const char *name, struct efi_data **efi_data)
157 {
158         int fd;
159         int rc = -1;
160         char *p;
161         char buf[4096];
162         ssize_t total;
163         ssize_t count;
164         char *path;
165
166         assert(efi_mount);
167
168         *efi_data = NULL;
169
170         fd = efi_open(efi_mount, name, 0, 0, &path);
171         if (fd < 0)
172                 return -1;
173
174         for (p = buf, total = 0; ; p = buf + count) {
175                 count = read(fd, p, sizeof(buf) - total);
176                 if (count < 0) {
177                         if (errno == EAGAIN || errno == EWOULDBLOCK)
178                                 continue;
179
180                         pb_log("%s: read failed %s: (%ld) (%d) %s\n", __func__, path,
181                                 count, errno, strerror(errno));
182                         goto exit;
183                 }
184                 if (p >= (buf + sizeof(buf))) {
185                         pb_log("%s: buffer full %s: (%ld)\n", __func__, path,
186                                 sizeof(buf));
187                         goto exit;
188                 }
189                 if (count == 0)
190                         break;
191                 total += count;
192         };
193
194         *efi_data = (void*)talloc_zero_array(ctx, char,
195                 sizeof (struct efi_data) + total);
196
197         (*efi_data)->attributes = *(uint32_t *)buf;
198         (*efi_data)->data_size = total;
199         (*efi_data)->data = (*efi_data)->fill;
200         memcpy((*efi_data)->data, buf + sizeof (uint32_t), total);
201         pb_debug_fn("Found: '%s'='%s'\n", name, (const char *)(*efi_data)->data);
202
203         rc = 0;
204 exit:
205         talloc_free(path);
206         close(fd);
207         return rc;
208 }
209
210 int efi_set_variable(const struct efi_mount *efi_mount, const char *name,
211                 const struct efi_data *efi_data)
212 {
213         int rc = -1;
214         int fd;
215         ssize_t count;
216         void *buf;
217         size_t bufsize;
218         char *path;
219
220         assert(efi_mount);
221
222         efi_del_variable(efi_mount, name);
223
224         fd = efi_open(efi_mount, name, O_CREAT | O_WRONLY,
225                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &path);
226         if (fd < 0)
227                 return -1;
228
229         bufsize = sizeof(uint32_t) + efi_data->data_size;
230         buf = talloc_size(path, bufsize);
231         if (!buf)
232                 goto exit;
233
234         *(uint32_t *)buf = efi_data->attributes;
235         memcpy(buf + sizeof(uint32_t), efi_data->data, efi_data->data_size);
236
237         count = write(fd, buf, bufsize);
238         if ((size_t)count != bufsize) {
239                 pb_log("%s: write failed %s: (%ld) (%d) %s\n", __func__, name,
240                         count, errno, strerror(errno));
241                 goto exit;
242         }
243         rc = 0;
244         pb_debug_fn("Set: '%s'='%s'\n", name,  (const char *)efi_data->data);
245
246 exit:
247         talloc_free(path);
248         close(fd);
249         return rc;
250 }