lib/efi: Cleanup read/write routines
[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 <errno.h>
21 #include <fcntl.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include <linux/fs.h>
26 #include <sys/ioctl.h>
27 #include <sys/stat.h>
28
29 #include "efivar.h"
30 #include "log/log.h"
31 #include "talloc/talloc.h"
32
33 static const char *efivarfs_path;
34
35 inline void set_efivarfs_path(const char *path)
36 {
37         efivarfs_path = path;
38 }
39
40 inline const char *get_efivarfs_path(void)
41 {
42
43         return efivarfs_path;
44 }
45
46 static int efi_open(const char *name, const char *guidstr, int flags,
47         mode_t mode, char **path)
48 {
49         int fd;
50
51         *path = NULL;
52
53         if (!get_efivarfs_path())
54                 return -1;
55
56         *path = talloc_asprintf(NULL, "%s%s-%s", get_efivarfs_path(), name, guidstr);
57         if (!*path)
58                 return -1;
59
60         flags = flags ? flags : O_RDONLY | O_NONBLOCK;
61
62         fd = open(*path, flags, mode);
63
64         if (fd < 0) {
65                 pb_log("%s: open failed %s: %s\n", __func__, *path,
66                         strerror(errno));
67                 talloc_free(*path);
68                 *path = NULL;
69                 return -1;
70         }
71
72         return fd;
73 }
74
75 int efi_del_variable(const char *guidstr, const char *name)
76 {
77         int fd, flag;
78         int rc = -1;
79         char *path;
80
81         fd = efi_open(name, guidstr, 0, 0, &path);
82         if (fd < 0)
83                 return -1;
84
85         rc = ioctl(fd, FS_IOC_GETFLAGS, &flag);
86         if (rc == -1)
87                 goto exit;
88
89         flag &= ~FS_IMMUTABLE_FL;
90         rc = ioctl(fd, FS_IOC_SETFLAGS, &flag);
91         if (rc == -1)
92                 goto exit;
93
94         close(fd);
95         fd = 0;
96         rc = unlink(path);
97 exit:
98         talloc_free(path);
99         close(fd);
100         return rc;
101 }
102
103 int efi_get_variable(void *ctx, const char *guidstr, const char *name,
104                 struct efi_data **efi_data)
105 {
106         int fd;
107         int rc = -1;
108         char *p;
109         char buf[4096];
110         ssize_t total;
111         ssize_t count;
112         char *path;
113
114         *efi_data = NULL;
115
116         fd = efi_open(name, guidstr, 0, 0, &path);
117         if (fd < 0)
118                 return -1;
119
120         for (p = buf, total = 0; ; p = buf + count) {
121                 count = read(fd, p, sizeof(buf) - total);
122                 if (count < 0) {
123                         if (errno == EAGAIN || errno == EWOULDBLOCK)
124                                 continue;
125
126                         pb_log("%s: read failed %s: (%ld) %s\n", __func__, path,
127                                 count, strerror(errno));
128                         goto exit;
129                 }
130                 if (p >= (buf + sizeof(buf))) {
131                         pb_log("%s: buffer full %s: (%ld)\n", __func__, path,
132                                 sizeof(buf));
133                         goto exit;
134                 }
135                 if (count == 0)
136                         break;
137                 total += count;
138         };
139
140         *efi_data = (void*)talloc_zero_array(ctx, char,
141                 sizeof (struct efi_data) + total);
142
143         (*efi_data)->attributes = *(uint32_t *)buf;
144         (*efi_data)->data_size = total;
145         (*efi_data)->data = (*efi_data)->fill;
146         memcpy((*efi_data)->data, buf + sizeof (uint32_t), total);
147
148         rc = 0;
149 exit:
150         talloc_free(path);
151         close(fd);
152         return rc;
153 }
154
155 int efi_set_variable(const char *guidstr, const char *name,
156                 const struct efi_data *efi_data)
157 {
158         int rc = -1;
159         int fd;
160         ssize_t count;
161         void *buf;
162         size_t bufsize;
163         char *path;
164
165         efi_del_variable(guidstr, name);
166
167         fd = efi_open(name, guidstr, O_CREAT | O_WRONLY,
168                 S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH, &path);
169         if (fd < 0)
170                 return -1;
171
172         bufsize = sizeof(uint32_t) + efi_data->data_size;
173         buf = talloc_size(path, bufsize);
174         if (!buf)
175                 goto exit;
176
177         *(uint32_t *)buf = efi_data->attributes;
178         memcpy(buf + sizeof(uint32_t), efi_data->data, efi_data->data_size);
179
180         count = write(fd, buf, bufsize);
181         if ((size_t)count != bufsize) {
182                 pb_log("%s: write failed %s: (%ld) %s\n", __func__, name,
183                         count, strerror(errno));
184                 goto exit;
185         }
186         rc = 0;
187
188 exit:
189         talloc_free(path);
190         close(fd);
191         return rc;
192 }