7 #include <types/types.h>
8 #include <talloc/talloc.h>
11 #include <discover/parser.h>
15 static const char *default_envfile = "grubenv";
16 static const char *signature = "# GRUB Environment Block\n";
18 static int parse_buf_to_env(struct grub2_script *script, void *buf, int len)
20 char *tmp = NULL, *line, *sep;
23 siglen = strlen(signature);
26 pb_log("grub environment block too small\n");
30 if (memcmp(buf, signature, siglen)) {
31 pb_log("grub environment block has invalid signature\n");
37 for (line = strtok_r(buf, "\n", &tmp); line;
38 line = strtok_r(NULL, "\n", &tmp)) {
43 sep = strchr(line, '=');
50 script_env_set(script, line, sep + 1);
56 int builtin_load_env(struct grub2_script *script,
57 void *data __attribute__((unused)),
58 int argc, char *argv[]);
60 int builtin_load_env(struct grub2_script *script,
61 void *data __attribute__((unused)),
62 int argc, char *argv[])
64 struct discover_device *dev = script->ctx->device;
69 /* we only support local filesystems */
71 pb_log("load_env: can't load from a non-mounted device (%s)\n",
76 if (argc == 3 && !strcmp(argv[1], "-f"))
79 envfile = default_envfile;
81 envpath = talloc_asprintf(script, "%s/%s",
82 script_env_get(script, "prefix") ? : "",
85 rc = parser_request_file(script->ctx, dev, envpath, &buf, &len);
88 rc = parse_buf_to_env(script, buf, len);
95 static int update_env(char *buf, int buflen, const char *name,
98 /* head and tail define the pointers within the existing data that we
99 * can insert our env entry, end is the last byte of valid environment
100 * data. if tail - head != entrylen, when we'll memmove to create (or
102 int i, j, linelen, namelen, valuelen, entrylen, delta, space;
103 char *head, *tail, *end;
106 namelen = strlen(name);
107 valuelen = strlen(value);
108 entrylen = namelen + strlen("=") + valuelen + strlen("\n");
110 head = tail = end = NULL;
113 /* For each line (where linelen includes the trailing \n). Find
114 * head, tail and end.
116 for (i = 0; i < buflen; i += linelen) {
117 char *eol = NULL, *sep = NULL, *line = buf + i;
119 /* find eol and sep */
120 for (j = 0; !eol && i + j < buflen; j++) {
132 /* no eol, put the new value at the start of this line,
133 * no need to shift data. This will also match the
134 * padding '#' chars at the end of the entries. */
138 tail = head + entrylen;
144 linelen = (eol - line) + 1;
145 end = line + linelen;
147 /* invalid line? may as well overwrite it with valid data */
148 if (!sep && !replace) {
150 tail = line + linelen;
153 /* an existing entry for this var? We set replace, as we prefer
154 * to overwrite an existing entry (the first one, in
155 * particular) rather than use an invalid line.
157 if (sep && !replace && sep - line == namelen &&
158 !strncmp(name, line, namelen)) {
160 tail = line + linelen;
165 if (!head || !tail || !end) {
166 pb_log("grub save_env: can't parse buffer space\n");
170 /* how much extra space do we need? */
171 delta = entrylen - (tail - head);
172 /* how much space do we have? */
173 space = (buf + buflen) - end;
175 /* check we have enough space. the tail > buf-end check is required
176 * for the case where there was no eol, and we set
177 * tail = head + entrylen
179 if (delta > space || tail > buf + buflen) {
180 pb_log("grub save_env: not enough buffer space\n");
184 /* create space between head & tail */
186 /* for positive delta, we need to reduce the copied data size */
187 int shiftlen = delta > 0 ? delta : 0;
188 memmove(tail + delta, tail, (buf + buflen) - tail - shiftlen);
191 /* if we've shifted data down towards head, we'll need to append
194 memset(buf + buflen + delta, '#', 0 - delta);
196 /* set the entry data */
197 memcpy(head, name, namelen);
198 memcpy(head + namelen, "=", 1);
199 memcpy(head + namelen + 1, value, valuelen);
200 memcpy(head + namelen + 1 + valuelen, "\n", 1);
205 int builtin_save_env(struct grub2_script *script,
206 void *data __attribute__((unused)),
207 int argc, char *argv[]);
209 int builtin_save_env(struct grub2_script *script,
210 void *data __attribute__((unused)),
211 int argc, char *argv[])
213 struct discover_device *dev = script->ctx->device;
214 int i, rc, len, siglen;
217 bool using_dash_f = false;
219 /* we only support local filesystems */
221 pb_log("save_env: can't save to a non-mounted device (%s)\n",
226 if (argc >= 2 && !strcmp(argv[1], "-f")) {
228 pb_log("save_env: for -f, need argument\n");
235 envfile = default_envfile;
237 envpath = talloc_asprintf(script, "%s/%s",
238 script_env_get(script, "prefix") ? : "",
242 rc = parser_request_file(script->ctx, dev, envpath, &buf, &len);
244 siglen = strlen(signature);
246 /* we require the environment to be pre-allocated, so abort if
247 * the file isn't present and valid */
248 if (rc || len < siglen || memcmp(buf, signature, siglen))
251 /* For "-f", skip the "-f <file>" arguments in picking the
252 * variables to save. */
253 i = (using_dash_f ? 3 : 1);
255 for (; i < argc; i++) {
256 const char *name, *value;
259 value = script_env_get(script, name);
261 pb_log("Saved unset environment variable %s!\n", name);
265 update_env(buf + siglen, len - siglen, name, value);
268 rc = parser_replace_file(script->ctx, dev, envpath, buf, len);
272 talloc_free(envpath);