protocol: Separate device add from boot-option add messages
[petitboot] / discover / paths.c
1 #define _GNU_SOURCE
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include <talloc/talloc.h>
8 #include <system/system.h>
9 #include <url/url.h>
10 #include <log/log.h>
11
12 #include "paths.h"
13
14 #define DEVICE_MOUNT_BASE (LOCAL_STATE_DIR "/petitboot/mnt")
15
16 struct mount_map {
17         char *dev, *mnt;
18 };
19
20 static struct mount_map *mount_map;
21 static int mount_map_size;
22
23 static int is_prefix(const char *str, const char *prefix)
24 {
25         return !strncmp(str, prefix, strlen(prefix));
26 }
27
28 static int is_prefix_ignorecase(const char *str, const char *prefix)
29 {
30         return !strncasecmp(str, prefix, strlen(prefix));
31 }
32
33 const char *mount_base(void)
34 {
35         return DEVICE_MOUNT_BASE;
36 }
37
38 char *encode_label(void *alloc_ctx, const char *label)
39 {
40         char *str, *c;
41         unsigned int i;
42
43         /* the label can be expanded by up to four times */
44         str = talloc_size(alloc_ctx, strlen(label) * 4 + 1);
45         c = str;
46
47         for (i = 0; i < strlen(label); i++) {
48
49                 if (label[i] == '/' || label[i] == '\\') {
50                         sprintf(c, "\\x%02x", label[i]);
51                         c += 4;
52                         continue;
53                 }
54
55                 *(c++) = label[i];
56         }
57
58         *c = '\0';
59
60         return str;
61 }
62
63 char *parse_device_path(void *alloc_ctx,
64                 const char *dev_str, const char __attribute__((unused)) *cur_dev)
65 {
66         char *dev, *enc;
67
68         if (is_prefix_ignorecase(dev_str, "uuid=")) {
69                 dev = talloc_asprintf(alloc_ctx, "/dev/disk/by-uuid/%s",
70                                 dev_str + strlen("uuid="));
71                 return dev;
72         }
73
74         if (is_prefix_ignorecase(dev_str, "label=")) {
75                 enc = encode_label(NULL, dev_str + strlen("label="));
76                 dev = talloc_asprintf(alloc_ctx, "/dev/disk/by-label/%s", enc);
77                 talloc_free(enc);
78                 return dev;
79         }
80
81         /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand
82          * back before returning.
83          */
84         if (is_prefix(dev_str, "/dev/"))
85                 dev_str += strlen("/dev/");
86
87         return join_paths(alloc_ctx, "/dev", dev_str);
88 }
89
90 const char *mountpoint_for_device(const char *dev)
91 {
92         int i;
93
94         if (is_prefix(dev, "/dev/"))
95                 dev += strlen("/dev/");
96
97         /* check existing entries in the map */
98         for (i = 0; i < mount_map_size; i++)
99                 if (!strcmp(mount_map[i].dev, dev))
100                         return mount_map[i].mnt;
101
102         /* no existing entry, create a new one */
103         i = mount_map_size++;
104         mount_map = talloc_realloc(NULL, mount_map,
105                         struct mount_map, mount_map_size);
106
107         mount_map[i].dev = talloc_strdup(mount_map, dev);
108         mount_map[i].mnt = join_paths(mount_map, DEVICE_MOUNT_BASE, dev);
109         return mount_map[i].mnt;
110 }
111
112 char *resolve_path(void *alloc_ctx, const char *path, const char *current_dev)
113 {
114         static const char s_file[] = "file://";
115         char *ret;
116         const char *devpath, *sep;
117
118         /* test for urls */
119
120         if (!strncasecmp(path, s_file, sizeof(s_file) - 1))
121                 path += sizeof(s_file) - 1;
122         else if (strstr(path, "://"))
123                 return talloc_strdup(alloc_ctx, path);
124
125         sep = strchr(path, ':');
126         if (!sep) {
127                 devpath = mountpoint_for_device(current_dev);
128                 ret = join_paths(alloc_ctx, devpath, path);
129         } else {
130                 /* parse just the device name into dev */
131                 char *tmp, *dev;
132                 tmp = talloc_strndup(NULL, path, sep - path);
133                 dev = parse_device_path(NULL, tmp, current_dev);
134
135                 devpath = mountpoint_for_device(dev);
136                 ret = join_paths(alloc_ctx, devpath, sep + 1);
137
138                 talloc_free(dev);
139                 talloc_free(tmp);
140         }
141
142         return ret;
143 }
144
145 char *join_paths(void *alloc_ctx, const char *a, const char *b)
146 {
147         char *full_path;
148
149         full_path = talloc_array(alloc_ctx, char, strlen(a) + strlen(b) + 2);
150
151         strcpy(full_path, a);
152         if (b[0] != '/' && a[strlen(a) - 1] != '/')
153                 strcat(full_path, "/");
154         strcat(full_path, b);
155
156         return full_path;
157 }
158
159
160 static char *local_name(void *ctx)
161 {
162         char *tmp, *ret;
163
164         tmp = tempnam(NULL, "pb-");
165
166         if (!tmp)
167                 return NULL;
168
169         ret = talloc_strdup(ctx, tmp);
170         free(tmp);
171
172         return ret;
173 }
174
175 /**
176  * pb_load_nfs - Mounts the NFS export and returns the local file path.
177  *
178  * Returns the local file path in a talloc'ed character string on success,
179  * or NULL on error.
180  */
181 static char *load_nfs(void *ctx, struct pb_url *url)
182 {
183         int result;
184         const char *argv[8];
185         const char **p;
186         char *local;
187         char *opts;
188
189         local = local_name(ctx);
190
191         if (!local)
192                 return NULL;
193
194         result = pb_mkdir_recursive(local);
195
196         if (result)
197                 goto fail;
198
199         opts = talloc_strdup(NULL, "ro,nolock,nodiratime");
200
201         if (url->port)
202                 opts = talloc_asprintf_append(opts, ",port=%s", url->port);
203
204         p = argv;
205         *p++ = pb_system_apps.mount;    /* 1 */
206         *p++ = "-t";                    /* 2 */
207         *p++ = "nfs";                   /* 3 */
208         *p++ = opts;                    /* 4 */
209         *p++ = url->host;               /* 5 */
210         *p++ = url->dir;                /* 6 */
211         *p++ = local;                   /* 7 */
212         *p++ = NULL;                    /* 8 */
213
214         result = pb_run_cmd(argv, 1, 0);
215
216         talloc_free(opts);
217
218         if (result)
219                 goto fail;
220
221         local = talloc_asprintf_append(local,  "/%s", url->path);
222         pb_log("%s: local '%s'\n", __func__, local);
223
224         return local;
225
226 fail:
227         pb_rmdir_recursive("/", local);
228         talloc_free(local);
229         return NULL;
230 }
231
232 /**
233  * pb_load_sftp - Loads a remote file via sftp and returns the local file path.
234  *
235  * Returns the local file path in a talloc'ed character string on success,
236  * or NULL on error.
237  */
238 static char *load_sftp(void *ctx, struct pb_url *url)
239 {
240         int result;
241         const char *argv[4];
242         const char **p;
243         char *local;
244
245         local = local_name(ctx);
246
247         if (!local)
248                 return NULL;
249
250         p = argv;
251         *p++ = pb_system_apps.sftp;                                     /* 1 */
252         *p++ = talloc_asprintf(local, "%s:%s", url->host, url->path);   /* 2 */
253         *p++ = local;                                                   /* 3 */
254         *p++ = NULL;                                                    /* 4 */
255
256         result = pb_run_cmd(argv, 1, 0);
257
258         if (result)
259                 goto fail;
260
261         return local;
262
263 fail:
264         talloc_free(local);
265         return NULL;
266 }
267
268 /**
269  * pb_load_tftp - Loads a remote file via tftp and returns the local file path.
270  *
271  * Returns the local file path in a talloc'ed character string on success,
272  * or NULL on error.
273  */
274
275 static char *load_tftp(void *ctx, struct pb_url *url)
276 {
277         int result;
278         const char *argv[10];
279         const char **p;
280         char *local;
281
282         local = local_name(ctx);
283
284         if (!local)
285                 return NULL;
286
287         /* first try busybox tftp args */
288
289         p = argv;
290         *p++ = pb_system_apps.tftp;     /* 1 */
291         *p++ = "-g";                    /* 2 */
292         *p++ = "-l";                    /* 3 */
293         *p++ = local;                   /* 4 */
294         *p++ = "-r";                    /* 5 */
295         *p++ = url->path;               /* 6 */
296         *p++ = url->host;               /* 7 */
297         if (url->port)
298                 *p++ = url->port;       /* 8 */
299         *p++ = NULL;                    /* 9 */
300
301         result = pb_run_cmd(argv, 1, 0);
302
303         if (!result)
304                 return local;
305
306         /* next try tftp-hpa args */
307
308         p = argv;
309         *p++ = pb_system_apps.tftp;     /* 1 */
310         *p++ = "-m";                    /* 2 */
311         *p++ = "binary";                /* 3 */
312         *p++ = url->host;               /* 4 */
313         if (url->port)
314                 *p++ = url->port;       /* 5 */
315         *p++ = "-c";                    /* 6 */
316         *p++ = "get";                   /* 7 */
317         *p++ = url->path;               /* 8 */
318         *p++ = local;                   /* 9 */
319         *p++ = NULL;                    /* 10 */
320
321         result = pb_run_cmd(argv, 1, 0);
322
323         if (!result)
324                 return local;
325
326         talloc_free(local);
327         return NULL;
328 }
329
330 enum wget_flags {
331         wget_empty = 0,
332         wget_no_check_certificate = 1,
333 };
334
335 /**
336  * pb_load_wget - Loads a remote file via wget and returns the local file path.
337  *
338  * Returns the local file path in a talloc'ed character string on success,
339  * or NULL on error.
340  */
341
342 static char *load_wget(void *ctx, struct pb_url *url, enum wget_flags flags)
343 {
344         int result;
345         const char *argv[7];
346         const char **p;
347         char *local;
348
349         local = local_name(ctx);
350
351         if (!local)
352                 return NULL;
353
354         p = argv;
355         *p++ = pb_system_apps.wget;                     /* 1 */
356 #if !defined(DEBUG)
357         *p++ = "--quiet";                               /* 2 */
358 #endif
359         *p++ = "-O";                                    /* 3 */
360         *p++ = local;                                   /* 4 */
361         *p++ = url->full;                               /* 5 */
362         if (flags & wget_no_check_certificate)
363                 *p++ = "--no-check-certificate";        /* 6 */
364         *p++ = NULL;                                    /* 7 */
365
366         result = pb_run_cmd(argv, 1, 0);
367
368         if (result)
369                 goto fail;
370
371         return local;
372
373 fail:
374         talloc_free(local);
375         return NULL;
376 }
377
378 /**
379  * pb_load_file - Loads a (possibly) remote file and returns the local file
380  * path.
381  * @ctx: The talloc context to associate with the returned string.
382  * @remote: The remote file URL.
383  * @tempfile: An optional variable pointer to be set when a temporary local
384  *  file is created.
385  *
386  * Returns the local file path in a talloc'ed character string on success,
387  * or NULL on error.
388  */
389
390 char *load_file(void *ctx, const char *remote, unsigned int *tempfile)
391 {
392         struct pb_url *url = pb_url_parse(ctx, remote);
393         char *local;
394         int tmp = 0;
395
396         if (!url)
397                 return NULL;
398
399         switch (url->scheme) {
400         case pb_url_ftp:
401         case pb_url_http:
402                 local = load_wget(ctx, url, 0);
403                 tmp = !!local;
404                 break;
405         case pb_url_https:
406                 local = load_wget(ctx, url, wget_no_check_certificate);
407                 tmp = !!local;
408                 break;
409         case pb_url_nfs:
410                 local = load_nfs(ctx, url);
411                 tmp = !!local;
412                 break;
413         case pb_url_sftp:
414                 local = load_sftp(ctx, url);
415                 tmp = !!local;
416                 break;
417         case pb_url_tftp:
418                 local = load_tftp(ctx, url);
419                 tmp = !!local;
420                 break;
421         default:
422                 local = talloc_strdup(ctx, url->full);
423                 tmp = 0;
424                 break;
425         }
426
427         if (tempfile)
428                 *tempfile = tmp;
429
430         talloc_free(url);
431         return local;
432 }