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);
90 pb_debug_fn("Failed to set env\n");
97 static int update_env(char *buf, int buflen, const char *name,
100 /* head and tail define the pointers within the existing data that we
101 * can insert our env entry, end is the last byte of valid environment
102 * data. if tail - head != entrylen, when we'll memmove to create (or
104 int i, j, linelen, namelen, valuelen, entrylen, delta, space;
105 char *head, *tail, *end;
108 namelen = strlen(name);
109 valuelen = strlen(value);
110 entrylen = namelen + strlen("=") + valuelen + strlen("\n");
112 head = tail = end = NULL;
115 /* For each line (where linelen includes the trailing \n). Find
116 * head, tail and end.
118 for (i = 0; i < buflen; i += linelen) {
119 char *eol = NULL, *sep = NULL, *line = buf + i;
121 /* find eol and sep */
122 for (j = 0; !eol && i + j < buflen; j++) {
134 /* no eol, put the new value at the start of this line,
135 * no need to shift data. This will also match the
136 * padding '#' chars at the end of the entries. */
140 tail = head + entrylen;
146 linelen = (eol - line) + 1;
147 end = line + linelen;
149 /* invalid line? may as well overwrite it with valid data */
150 if (!sep && !replace) {
152 tail = line + linelen;
155 /* an existing entry for this var? We set replace, as we prefer
156 * to overwrite an existing entry (the first one, in
157 * particular) rather than use an invalid line.
159 if (sep && !replace && sep - line == namelen &&
160 !strncmp(name, line, namelen)) {
162 tail = line + linelen;
167 if (!head || !tail || !end) {
168 pb_log("grub save_env: can't parse buffer space\n");
172 /* how much extra space do we need? */
173 delta = entrylen - (tail - head);
174 /* how much space do we have? */
175 space = (buf + buflen) - end;
177 /* check we have enough space. the tail > buf-end check is required
178 * for the case where there was no eol, and we set
179 * tail = head + entrylen
181 if (delta > space || tail > buf + buflen) {
182 pb_log("grub save_env: not enough buffer space\n");
186 /* create space between head & tail */
188 /* for positive delta, we need to reduce the copied data size */
189 int shiftlen = delta > 0 ? delta : 0;
190 memmove(tail + delta, tail, (buf + buflen) - tail - shiftlen);
193 /* if we've shifted data down towards head, we'll need to append
196 memset(buf + buflen + delta, '#', 0 - delta);
198 /* set the entry data */
199 memcpy(head, name, namelen);
200 memcpy(head + namelen, "=", 1);
201 memcpy(head + namelen + 1, value, valuelen);
202 memcpy(head + namelen + 1 + valuelen, "\n", 1);
207 int builtin_save_env(struct grub2_script *script,
208 void *data __attribute__((unused)),
209 int argc, char *argv[]);
211 int builtin_save_env(struct grub2_script *script,
212 void *data __attribute__((unused)),
213 int argc, char *argv[])
215 struct discover_device *dev = script->ctx->device;
216 int i, rc, len, siglen;
219 bool using_dash_f = false;
221 /* we only support local filesystems */
223 pb_log("save_env: can't save to a non-mounted device (%s)\n",
228 if (argc >= 2 && !strcmp(argv[1], "-f")) {
230 pb_log("save_env: for -f, need argument\n");
237 envfile = default_envfile;
239 envpath = talloc_asprintf(script, "%s/%s",
240 script_env_get(script, "prefix") ? : "",
244 rc = parser_request_file(script->ctx, dev, envpath, &buf, &len);
246 siglen = strlen(signature);
248 /* we require the environment to be pre-allocated, so abort if
249 * the file isn't present and valid */
250 if (rc || len < siglen || memcmp(buf, signature, siglen))
253 /* For "-f", skip the "-f <file>" arguments in picking the
254 * variables to save. */
255 i = (using_dash_f ? 3 : 1);
257 for (; i < argc; i++) {
258 const char *name, *value;
261 value = script_env_get(script, name);
263 pb_log("Saved unset environment variable %s!\n", name);
267 update_env(buf + siglen, len - siglen, name, value);
270 rc = parser_replace_file(script->ctx, dev, envpath, buf, len);
274 talloc_free(envpath);