libfstools: use uevent instead of relying on custom kernel patch
[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 const char *const block_dir_name = "/sys/class/block";
8
9 struct devpath {
10 char prefix[5];
11 char device[11];
12 };
13
14 struct partname_volume {
15 struct volume v;
16 union {
17 char devpathstr[16];
18 struct devpath devpath;
19 } dev;
20
21 union {
22 char devpathstr[16];
23 struct devpath devpath;
24 } parent_dev;
25 };
26
27 static struct driver partname_driver;
28
29 static int partname_volume_identify(struct volume *v)
30 {
31 struct partname_volume *p = container_of(v, struct partname_volume, v);
32 int ret = FS_NONE;
33 FILE *f;
34
35 f = fopen(p->dev.devpathstr, "r");
36 if (!f)
37 return ret;
38
39 ret = block_file_identify(f, 0);
40
41 fclose(f);
42
43 return ret;
44 }
45
46 static int partname_volume_init(struct volume *v)
47 {
48 struct partname_volume *p = container_of(v, struct partname_volume, v);
49 char voldir[BUFLEN];
50 unsigned int volsize;
51
52 snprintf(voldir, sizeof(voldir), "%s/%s", block_dir_name, p->dev.devpath.device);
53
54 if (read_uint_from_file(voldir, "size", &volsize))
55 return -1;
56
57 v->type = BLOCKDEV;
58 v->size = volsize << 9; /* size is returned in sectors of 512 bytes */
59 v->blk = p->dev.devpathstr;
60
61 return block_volume_format(v, 0, p->parent_dev.devpathstr);
62 }
63
64 /* adapted from procd/utils.c -> should go to libubox */
65 static char* get_var_from_file(const char* filename, const char* name, char* out, int len)
66 {
67 char line[1024], *c, *sptr;
68 int fd = open(filename, O_RDONLY);
69 if (fd == -1)
70 return NULL;
71
72 ssize_t r = read(fd, line, sizeof(line) - 1);
73 close(fd);
74
75 if (r <= 0)
76 return NULL;
77
78 line[r] = 0;
79
80 for (c = strtok_r(line, " \t\n", &sptr); c;
81 c = strtok_r(NULL, " \t\n", &sptr)) {
82 char *sep = strchr(c, '=');
83 if (sep == NULL)
84 continue;
85
86 ssize_t klen = sep - c;
87 if (strncmp(name, c, klen) || name[klen] != 0)
88 continue;
89
90 strncpy(out, &sep[1], len);
91 out[len-1] = '\0';
92 return out;
93 }
94
95 return NULL;
96 }
97
98 static char *rootdevname(char *devpath) {
99 int l;
100
101 l = strlen(devpath) - 1;
102
103 /* strip partition suffix from root=/dev/... string */
104 while (l > 0 && (devpath[l] >= '0' && devpath[l] <= '9'))
105 --l;
106
107 if (devpath[l] != 'p')
108 ++l;
109
110 devpath[l] = '\0';
111
112 return basename(devpath);
113 }
114
115 static struct volume *partname_volume_find(char *name)
116 {
117 struct partname_volume *p;
118 char ueventgstr[BUFLEN];
119 char namebuf[BUFLEN];
120 char rootparam[BUFLEN];
121 char *rootdev = NULL, *devname, *tmp;
122 int j;
123 bool found = 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 if (get_var_from_file("/proc/cmdline", "root", rootparam, sizeof(rootparam))) {
132 rootdev = rootdevname(rootparam);
133 /* find partition on same device as rootfs */
134 snprintf(ueventgstr, sizeof(ueventgstr), "%s/%s/*/uevent", block_dir_name, rootdev);
135 } else {
136 /* no 'root=' kernel cmdline parameter, find on any block device */
137 snprintf(ueventgstr, sizeof(ueventgstr), "%s/*/uevent", block_dir_name);
138 }
139
140 if (!glob(ueventgstr, GLOB_NOESCAPE, NULL, &gl))
141 for (j = 0; j < gl.gl_pathc; j++) {
142 if (!get_var_from_file(gl.gl_pathv[j], "PARTNAME", namebuf, sizeof(namebuf)))
143 continue;
144 if (!strncmp(namebuf, name, sizeof(namebuf))) {
145 found = 1;
146 break;
147 }
148 }
149
150 if (!found)
151 return NULL;
152
153 devname = gl.gl_pathv[j];
154 tmp = strrchr(devname, '/');
155 *tmp = '\0';
156 devname = strrchr(devname, '/') + 1;
157
158 p = calloc(1, sizeof(*p));
159 memcpy(p->dev.devpath.prefix, "/dev/", sizeof(p->dev.devpath.prefix));
160 strncpy(p->dev.devpath.device, devname, sizeof(p->dev.devpath.device) - 1);
161 p->dev.devpath.device[sizeof(p->dev.devpath.device)-1] = '\0';
162
163 memcpy(p->parent_dev.devpath.prefix, "/dev/", sizeof(p->parent_dev.devpath.prefix));
164 if (rootdev)
165 strncpy(p->parent_dev.devpath.device, rootdev, sizeof(p->parent_dev.devpath.device) - 1);
166 else
167 strncpy(p->parent_dev.devpath.device, rootdevname(devname), sizeof(p->parent_dev.devpath.device) - 1);
168
169 p->parent_dev.devpath.device[sizeof(p->parent_dev.devpath.device)-1] = '\0';
170
171 p->v.drv = &partname_driver;
172 p->v.blk = p->dev.devpathstr;
173 p->v.name = name;
174
175 return &p->v;
176 }
177
178 static struct driver partname_driver = {
179 .name = "partname",
180 .find = partname_volume_find,
181 .init = partname_volume_init,
182 .identify = partname_volume_identify,
183 };
184
185 DRIVER(partname_driver);