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