]> git.ipfire.org Git - thirdparty/mdadm.git/blame - sysfs.c
Fix possible NULL dereference in super_by_fd
[thirdparty/mdadm.git] / sysfs.c
CommitLineData
e86c9dd6
NB
1/*
2 * sysfs - extract md related information from sysfs. Part of:
3 * mdadm - manage Linux "md" devices aka RAID arrays.
4 *
5 * Copyright (C) 2006 Neil Brown <neilb@suse.de>
6 *
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 * Author: Neil Brown
23 * Email: <neilb@suse.de>
24 */
25
26#include "mdadm.h"
27#include <dirent.h>
28
29int load_sys(char *path, char *buf)
30{
31 int fd = open(path, O_RDONLY);
32 int n;
33 if (fd < 0)
34 return -1;
35 n = read(fd, buf, 1024);
36 close(fd);
37 if (n <=0 || n >= 1024)
38 return -1;
39 buf[n] = 0;
40 if (buf[n-1] == '\n')
41 buf[n-1] = 0;
42 return 0;
43}
44
7e0f6979 45void sysfs_free(struct mdinfo *sra)
8382f19b 46{
7e0f6979
NB
47 while (sra) {
48 struct mdinfo *sra2 = sra->next;
49 while (sra->devs) {
50 struct mdinfo *d = sra->devs;
51 sra->devs = d->next;
52 free(d);
53 }
54 free(sra);
55 sra = sra2;
8382f19b 56 }
8382f19b
NB
57}
58
7e0f6979 59struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
e86c9dd6
NB
60{
61 /* Longest possible name in sysfs, mounted at /sys, is
62 * /sys/block/md_dXXX/md/dev-XXXXX/block/dev
63 * /sys/block/md_dXXX/md/metadata_version
64 * which is about 41 characters. 50 should do for now
65 */
66 char fname[50];
67 char buf[1024];
68 char *base;
69 char *dbase;
7e0f6979 70 struct mdinfo *sra;
06c7f68e 71 struct mdinfo *dev;
e86c9dd6
NB
72 DIR *dir;
73 struct dirent *de;
74
75 sra = malloc(sizeof(*sra));
76 if (sra == NULL)
77 return sra;
7e0f6979 78 sra->next = NULL;
e86c9dd6
NB
79
80 if (fd >= 0) {
81 struct stat stb;
2faf1f5f
NB
82 mdu_version_t vers;
83 if (fstat(fd, &stb)) return NULL;
84 if (ioctl(fd, RAID_VERSION, &vers) != 0)
85 return NULL;
e86c9dd6 86 if (major(stb.st_rdev)==9)
7e0f6979 87 sprintf(sra->sys_name, "md%d", minor(stb.st_rdev));
e86c9dd6 88 else
7e0f6979 89 sprintf(sra->sys_name, "md_d%d",
7fa42a0b 90 minor(stb.st_rdev)>>MdpMinorShift);
e86c9dd6
NB
91 } else {
92 if (devnum >= 0)
7e0f6979 93 sprintf(sra->sys_name, "md%d", devnum);
e86c9dd6 94 else
7e0f6979 95 sprintf(sra->sys_name, "md_d%d",
e86c9dd6
NB
96 -1-devnum);
97 }
7e0f6979 98 sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
e86c9dd6
NB
99 base = fname + strlen(fname);
100
101 sra->devs = NULL;
8382f19b
NB
102 if (options & GET_VERSION) {
103 strcpy(base, "metadata_version");
104 if (load_sys(fname, buf))
105 goto abort;
106 if (strncmp(buf, "none", 4) == 0)
7e0f6979
NB
107 sra->array.major_version =
108 sra->array.minor_version = -1;
142cb9e1
NB
109 else if (strncmp(buf, "external:", 9) == 0) {
110 sra->array.major_version = -1;
111 sra->array.minor_version = -2;
112 strcpy(sra->text_version, buf+9);
113 } else
8382f19b 114 sscanf(buf, "%d.%d",
7e0f6979
NB
115 &sra->array.major_version,
116 &sra->array.minor_version);
8382f19b 117 }
e86c9dd6
NB
118 if (options & GET_LEVEL) {
119 strcpy(base, "level");
120 if (load_sys(fname, buf))
121 goto abort;
7e0f6979 122 sra->array.level = map_name(pers, buf);
e86c9dd6
NB
123 }
124 if (options & GET_LAYOUT) {
125 strcpy(base, "layout");
126 if (load_sys(fname, buf))
127 goto abort;
7e0f6979 128 sra->array.layout = strtoul(buf, NULL, 0);
e86c9dd6
NB
129 }
130 if (options & GET_COMPONENT) {
131 strcpy(base, "component_size");
132 if (load_sys(fname, buf))
133 goto abort;
134 sra->component_size = strtoull(buf, NULL, 0);
353632d9
NB
135 /* sysfs reports "K", but we want sectors */
136 sra->component_size *= 2;
e86c9dd6
NB
137 }
138 if (options & GET_CHUNK) {
139 strcpy(base, "chunk_size");
140 if (load_sys(fname, buf))
141 goto abort;
7e0f6979 142 sra->array.chunk_size = strtoul(buf, NULL, 0);
e86c9dd6 143 }
758d3a8e
NB
144 if (options & GET_CACHE) {
145 strcpy(base, "stripe_cache_size");
146 if (load_sys(fname, buf))
147 goto abort;
148 sra->cache_size = strtoul(buf, NULL, 0);
149 }
37dfc3d6
NB
150 if (options & GET_MISMATCH) {
151 strcpy(base, "mismatch_cnt");
152 if (load_sys(fname, buf))
153 goto abort;
154 sra->mismatch_cnt = strtoul(buf, NULL, 0);
155 }
e86c9dd6
NB
156
157 if (! (options & GET_DEVS))
158 return sra;
159
160 /* Get all the devices as well */
161 *base = 0;
162 dir = opendir(fname);
163 if (!dir)
164 goto abort;
7e0f6979 165 sra->array.spare_disks = 0;
e86c9dd6
NB
166
167 while ((de = readdir(dir)) != NULL) {
168 char *ep;
169 if (de->d_ino == 0 ||
170 strncmp(de->d_name, "dev-", 4) != 0)
171 continue;
172 strcpy(base, de->d_name);
173 dbase = base + strlen(base);
174 *dbase++ = '/';
175
176 dev = malloc(sizeof(*dev));
177 if (!dev)
178 goto abort;
179 dev->next = sra->devs;
180 sra->devs = dev;
06c7f68e 181 strcpy(dev->sys_name, de->d_name);
e86c9dd6
NB
182
183 /* Always get slot, major, minor */
184 strcpy(dbase, "slot");
185 if (load_sys(fname, buf))
186 goto abort;
06c7f68e
NB
187 dev->disk.raid_disk = strtoul(buf, &ep, 10);
188 if (*ep) dev->disk.raid_disk = -1;
e86c9dd6
NB
189
190 strcpy(dbase, "block/dev");
191 if (load_sys(fname, buf))
192 goto abort;
06c7f68e 193 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
e86c9dd6
NB
194
195 if (options & GET_OFFSET) {
196 strcpy(dbase, "offset");
197 if (load_sys(fname, buf))
198 goto abort;
06c7f68e 199 dev->data_offset = strtoull(buf, NULL, 0);
e86c9dd6
NB
200 }
201 if (options & GET_SIZE) {
202 strcpy(dbase, "size");
203 if (load_sys(fname, buf))
204 goto abort;
06c7f68e 205 dev->component_size = strtoull(buf, NULL, 0);
e86c9dd6
NB
206 }
207 if (options & GET_STATE) {
06c7f68e 208 dev->disk.state = 0;
e86c9dd6
NB
209 strcpy(dbase, "state");
210 if (load_sys(fname, buf))
211 goto abort;
212 if (strstr(buf, "in_sync"))
06c7f68e 213 dev->disk.state |= (1<<MD_DISK_SYNC);
e86c9dd6 214 if (strstr(buf, "faulty"))
06c7f68e
NB
215 dev->disk.state |= (1<<MD_DISK_FAULTY);
216 if (dev->disk.state == 0)
7e0f6979 217 sra->array.spare_disks++;
e86c9dd6
NB
218 }
219 if (options & GET_ERROR) {
220 strcpy(buf, "errors");
221 if (load_sys(fname, buf))
222 goto abort;
223 dev->errors = strtoul(buf, NULL, 0);
224 }
225 }
226 return sra;
227
228 abort:
8382f19b 229 sysfs_free(sra);
e86c9dd6
NB
230 return NULL;
231}
232
233unsigned long long get_component_size(int fd)
234{
235 /* Find out the component size of the array.
236 * We cannot trust GET_ARRAY_INFO ioctl as it's
237 * size field is only 32bits.
238 * So look in /sys/block/mdXXX/md/component_size
353632d9 239 *
8686f3ed 240 * This returns in units of sectors.
e86c9dd6
NB
241 */
242 struct stat stb;
243 char fname[50];
244 int n;
245 if (fstat(fd, &stb)) return 0;
246 if (major(stb.st_rdev) == 9)
247 sprintf(fname, "/sys/block/md%d/md/component_size",
248 minor(stb.st_rdev));
249 else
250 sprintf(fname, "/sys/block/md_d%d/md/component_size",
7fa42a0b 251 minor(stb.st_rdev)>>MdpMinorShift);
e86c9dd6
NB
252 fd = open(fname, O_RDONLY);
253 if (fd < 0)
254 return 0;
255 n = read(fd, fname, sizeof(fname));
256 close(fd);
257 if (n == sizeof(fname))
258 return 0;
259 fname[n] = 0;
8686f3ed 260 return strtoull(fname, NULL, 10) * 2;
e86c9dd6
NB
261}
262
7e0f6979 263int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
264 char *name, char *val)
265{
266 char fname[50];
267 int n;
268 int fd;
269 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 270 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
271 fd = open(fname, O_WRONLY);
272 if (fd < 0)
273 return -1;
274 n = write(fd, val, strlen(val));
275 close(fd);
276 if (n != strlen(val))
277 return -1;
278 return 0;
279}
280
7e0f6979 281int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
282 char *name, unsigned long long val)
283{
284 char valstr[50];
285 sprintf(valstr, "%llu", val);
286 return sysfs_set_str(sra, dev, name, valstr);
287}
288
7e0f6979 289int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
290 char *name, unsigned long long *val)
291{
292 char fname[50];
293 char buf[50];
294 int n;
295 int fd;
296 char *ep;
297 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 298 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
299 fd = open(fname, O_RDONLY);
300 if (fd < 0)
301 return -1;
302 n = read(fd, buf, sizeof(buf));
303 close(fd);
304 if (n <= 0)
305 return -1;
306 buf[n] = 0;
307 *val = strtoull(buf, &ep, 0);
308 if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
309 return -1;
310 return 0;
311}