Initial support for multiple UIs
[petitboot] / discover / paths.c
1 #define _GNU_SOURCE
2
3 #include <string.h>
4 #include <stdio.h>
5 #include <stdlib.h>
6
7 #include "paths.h"
8
9 static char *mount_base;
10
11 struct device_map {
12         char *dev, *mnt;
13 };
14
15 #define DEVICE_MAP_SIZE 32
16 static struct device_map device_map[DEVICE_MAP_SIZE];
17
18 char *encode_label(const char *label)
19 {
20         char *str, *c;
21         int i;
22
23         /* the label can be expanded by up to four times */
24         str = malloc(strlen(label) * 4 + 1);
25         c = str;
26
27         for (i = 0; i < strlen(label); i++) {
28
29                 if (label[i] == '/' || label[i] == '\\') {
30                         sprintf(c, "\\x%02x", label[i]);
31                         c += 4;
32                         continue;
33                 }
34
35                 *(c++) = label[i];
36         }
37
38         *c = '\0';
39
40         return str;
41 }
42
43 char *parse_device_path(const char *dev_str, const char *cur_dev)
44 {
45         char *dev, tmp[256], *enc;
46
47         if (!strncasecmp(dev_str, "uuid=", 5)) {
48                 asprintf(&dev, "/dev/disk/by-uuid/%s", dev_str + 5);
49                 return dev;
50         }
51
52         if (!strncasecmp(dev_str, "label=", 6)) {
53                 enc = encode_label(dev_str + 6);
54                 asprintf(&dev, "/dev/disk/by-label/%s", enc);
55                 free(enc);
56                 return dev;
57         }
58
59         /* normalise '/dev/foo' to 'foo' for easy comparisons, we'll expand
60          * back before returning.
61          */
62         if (!strncmp(dev_str, "/dev/", 5))
63                 dev_str += 5;
64
65         /* PS3 hack: if we're reading from a ps3dx device, and we refer to
66          * a sdx device, remap to ps3dx */
67         if (cur_dev && !strncmp(cur_dev, "/dev/ps3d", 9)
68                         && !strncmp(dev_str, "sd", 2)) {
69                 snprintf(tmp, 255, "ps3d%s", dev_str + 2);
70                 dev_str = tmp;
71         }
72
73         return join_paths("/dev", dev_str);
74 }
75
76 const char *mountpoint_for_device(const char *dev)
77 {
78         int i;
79
80         if (!strncmp(dev, "/dev/", 5))
81                 dev += 5;
82
83         /* check existing entries in the map */
84         for (i = 0; (i < DEVICE_MAP_SIZE) && device_map[i].dev; i++)
85                 if (!strcmp(device_map[i].dev, dev))
86                         return device_map[i].mnt;
87
88         if (i == DEVICE_MAP_SIZE)
89                 return NULL;
90
91         device_map[i].dev = strdup(dev);
92         device_map[i].mnt = join_paths(mount_base, dev);
93         return device_map[i].mnt;
94 }
95
96 char *resolve_path(const char *path, const char *current_dev)
97 {
98         char *ret;
99         const char *devpath, *sep;
100
101         sep = strchr(path, ':');
102         if (!sep) {
103                 devpath = mountpoint_for_device(current_dev);
104                 ret = join_paths(devpath, path);
105         } else {
106                 /* parse just the device name into dev */
107                 char *tmp, *dev;
108                 tmp = strndup(path, sep - path);
109                 dev = parse_device_path(tmp, current_dev);
110
111                 devpath = mountpoint_for_device(dev);
112                 ret = join_paths(devpath, sep + 1);
113
114                 free(dev);
115                 free(tmp);
116         }
117
118         return ret;
119 }
120
121 void set_mount_base(const char *path)
122 {
123         if (mount_base)
124                 free(mount_base);
125         mount_base = strdup(path);
126 }
127
128 char *join_paths(const char *a, const char *b)
129 {
130         char *full_path;
131
132         full_path = malloc(strlen(a) + strlen(b) + 2);
133
134         strcpy(full_path, a);
135         if (b[0] != '/' && a[strlen(a) - 1] != '/')
136                 strcat(full_path, "/");
137         strcat(full_path, b);
138
139         return full_path;
140 }
141