libfstools: fit: improve fit_volume_find string handling
[project/fstools.git] / libfstools / partname.c
1 // SPDX-License-Identifier: GPL-2.0-or-later
2
3 #include "common.h"
4
5 #define BUFLEN 64
6
7 struct devpath {
8 char prefix[5];
9 char device[11];
10 };
11
12 struct partname_volume {
13 struct volume v;
14 union {
15 char devpathstr[16];
16 struct devpath devpath;
17 } dev;
18
19 union {
20 char devpathstr[16];
21 struct devpath devpath;
22 } parent_dev;
23 };
24
25 static struct driver partname_driver;
26
27 static int partname_volume_identify(struct volume *v)
28 {
29 struct partname_volume *p = container_of(v, struct partname_volume, v);
30 int ret = FS_NONE;
31 FILE *f;
32
33 f = fopen(p->dev.devpathstr, "r");
34 if (!f)
35 return ret;
36
37 ret = block_file_identify(f, 0);
38
39 fclose(f);
40
41 return ret;
42 }
43
44 static int partname_volume_init(struct volume *v)
45 {
46 struct partname_volume *p = container_of(v, struct partname_volume, v);
47 char voldir[BUFLEN];
48 unsigned int volsize;
49
50 snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->dev.devpath.device);
51
52 if (read_uint_from_file(voldir, "size", &volsize))
53 return -1;
54
55 v->type = BLOCKDEV;
56 v->size = volsize << 9; /* size is returned in sectors of 512 bytes */
57 v->blk = p->dev.devpathstr;
58
59 return block_volume_format(v, 0, p->parent_dev.devpathstr);
60 }
61
62 /* adapted from procd/utils.c -> should go to libubox */
63 static char* get_var_from_file(const char* filename, const char* name, char* out, int len)
64 {
65 char line[1024], *c, *sptr;
66 int fd = open(filename, O_RDONLY);
67 if (fd == -1)
68 return NULL;
69
70 ssize_t r = read(fd, line, sizeof(line) - 1);
71 close(fd);
72
73 if (r <= 0)
74 return NULL;
75
76 line[r] = 0;
77
78 for (c = strtok_r(line, " \t\n", &sptr); c;
79 c = strtok_r(NULL, " \t\n", &sptr)) {
80 char *sep = strchr(c, '=');
81 if (sep == NULL)
82 continue;
83
84 ssize_t klen = sep - c;
85 if (strncmp(name, c, klen) || name[klen] != 0)
86 continue;
87
88 strncpy(out, &sep[1], len);
89 out[len-1] = '\0';
90 return out;
91 }
92
93 return NULL;
94 }
95
96 static char *rootdevname(char *devpath) {
97 int l;
98
99 l = strlen(devpath) - 1;
100
101 /* strip partition suffix from root=/dev/... string */
102 while (l > 0 && (devpath[l] >= '0' && devpath[l] <= '9'))
103 --l;
104
105 if (devpath[l] != 'p')
106 ++l;
107
108 devpath[l] = '\0';
109
110 return basename(devpath);
111 }
112
113 static struct volume *partname_volume_find(char *name)
114 {
115 struct partname_volume *p;
116 char ueventgstr[BUFLEN];
117 char namebuf[BUFLEN];
118 char rootparam[BUFLEN];
119 char *rootdev = NULL, *devname, *tmp;
120 int j;
121 bool found = false;
122 bool allow_fallback = false;
123 bool has_root = false;
124 glob_t gl;
125
126 if (get_var_from_file("/proc/cmdline", "fstools_ignore_partname", rootparam, sizeof(rootparam))) {
127 if (!strcmp("1", rootparam))
128 return NULL;
129 }
130
131 /*
132 * Some device may contains a GPT partition named rootfs_data that may not be suitable.
133 * To save from regression with old implementation that doesn't use fstools_ignore_partname to
134 * explicitly say that that partname scan should be ignored, make explicit that scanning each
135 * partition should be done by providing fstools_partname_fallback_scan=1 and skip partname scan
136 * in every other case.
137 */
138 if (get_var_from_file("/proc/cmdline", "fstools_partname_fallback_scan", rootparam, sizeof(rootparam))) {
139 if (!strcmp("1", rootparam))
140 allow_fallback = true;
141 }
142
143 if (get_var_from_file("/proc/cmdline", "root", rootparam, sizeof(rootparam)))
144 has_root = true;
145
146 if (has_root && rootparam[0] == '/') {
147 rootdev = rootdevname(rootparam);
148 /* find partition on same device as rootfs */
149 snprintf(ueventgstr, sizeof(ueventgstr), "%s/%s/*/uevent", block_dir_name, rootdev);
150 } else {
151 /* For compatibility, devices with root= params must explicitly opt into this fallback. */
152 if (has_root && !allow_fallback)
153 return NULL;
154
155 /* no useful 'root=' kernel cmdline parameter, find on any block device */
156 snprintf(ueventgstr, sizeof(ueventgstr), "%s/*/uevent", block_dir_name);
157 }
158
159 if (!glob(ueventgstr, GLOB_NOESCAPE, NULL, &gl))
160 for (j = 0; j < gl.gl_pathc; j++) {
161 if (!get_var_from_file(gl.gl_pathv[j], "PARTNAME", namebuf, sizeof(namebuf)))
162 continue;
163 if (!strncmp(namebuf, name, sizeof(namebuf))) {
164 found = 1;
165 break;
166 }
167 }
168
169 if (!found)
170 return NULL;
171
172 devname = gl.gl_pathv[j];
173 tmp = strrchr(devname, '/');
174 if (!tmp)
175 return NULL;
176
177 *tmp = '\0';
178 devname = strrchr(devname, '/') + 1;
179
180 p = calloc(1, sizeof(*p));
181 memcpy(p->dev.devpath.prefix, "/dev/", sizeof(p->dev.devpath.prefix));
182 strncpy(p->dev.devpath.device, devname, sizeof(p->dev.devpath.device) - 1);
183 p->dev.devpath.device[sizeof(p->dev.devpath.device)-1] = '\0';
184
185 memcpy(p->parent_dev.devpath.prefix, "/dev/", sizeof(p->parent_dev.devpath.prefix));
186 if (rootdev)
187 strncpy(p->parent_dev.devpath.device, rootdev, sizeof(p->parent_dev.devpath.device) - 1);
188 else
189 strncpy(p->parent_dev.devpath.device, rootdevname(devname), sizeof(p->parent_dev.devpath.device) - 1);
190
191 p->parent_dev.devpath.device[sizeof(p->parent_dev.devpath.device)-1] = '\0';
192
193 p->v.drv = &partname_driver;
194 p->v.blk = p->dev.devpathstr;
195 p->v.name = name;
196
197 return &p->v;
198 }
199
200 static struct driver partname_driver = {
201 .name = "partname",
202 .priority = 25,
203 .find = partname_volume_find,
204 .init = partname_volume_init,
205 .identify = partname_volume_identify,
206 };
207
208 DRIVER(partname_driver);