Remember to close directories when we are finished with them.
[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);
2f6079dc 37 if (n <0 || n >= 1024)
e86c9dd6
NB
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
549e9569
NB
59int sysfs_open(int devnum, char *devname, char *attr)
60{
61 char fname[50];
62 char sys_name[16];
63 int fd;
64 if (devnum >= 0)
65 sprintf(sys_name, "md%d", devnum);
66 else
67 sprintf(sys_name, "md_d%d",
68 -1-devnum);
69
70 sprintf(fname, "/sys/block/%s/md/", sys_name);
71 if (devname) {
72 strcat(fname, devname);
73 strcat(fname, "/");
74 }
75 strcat(fname, attr);
76 fd = open(fname, O_RDWR);
77 if (fd < 0 && errno == -EACCES)
78 fd = open(fname, O_RDONLY);
79 return fd;
80}
81
7e0f6979 82struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
e86c9dd6
NB
83{
84 /* Longest possible name in sysfs, mounted at /sys, is
85 * /sys/block/md_dXXX/md/dev-XXXXX/block/dev
86 * /sys/block/md_dXXX/md/metadata_version
87 * which is about 41 characters. 50 should do for now
88 */
89 char fname[50];
90 char buf[1024];
91 char *base;
92 char *dbase;
7e0f6979 93 struct mdinfo *sra;
06c7f68e 94 struct mdinfo *dev;
355726fa 95 DIR *dir = NULL;
e86c9dd6
NB
96 struct dirent *de;
97
98 sra = malloc(sizeof(*sra));
99 if (sra == NULL)
100 return sra;
7e0f6979 101 sra->next = NULL;
e86c9dd6
NB
102
103 if (fd >= 0) {
104 struct stat stb;
2faf1f5f
NB
105 mdu_version_t vers;
106 if (fstat(fd, &stb)) return NULL;
107 if (ioctl(fd, RAID_VERSION, &vers) != 0)
108 return NULL;
e86c9dd6 109 if (major(stb.st_rdev)==9)
ea24acd0 110 sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev));
e86c9dd6 111 else
7e0f6979 112 sprintf(sra->sys_name, "md_d%d",
ea24acd0 113 (int)minor(stb.st_rdev)>>MdpMinorShift);
e86c9dd6
NB
114 } else {
115 if (devnum >= 0)
7e0f6979 116 sprintf(sra->sys_name, "md%d", devnum);
e86c9dd6 117 else
7e0f6979 118 sprintf(sra->sys_name, "md_d%d",
e86c9dd6
NB
119 -1-devnum);
120 }
7e0f6979 121 sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
e86c9dd6
NB
122 base = fname + strlen(fname);
123
124 sra->devs = NULL;
8382f19b
NB
125 if (options & GET_VERSION) {
126 strcpy(base, "metadata_version");
127 if (load_sys(fname, buf))
128 goto abort;
294d6f45 129 if (strncmp(buf, "none", 4) == 0) {
7e0f6979
NB
130 sra->array.major_version =
131 sra->array.minor_version = -1;
294d6f45
NB
132 strcpy(sra->text_version, "");
133 } else if (strncmp(buf, "external:", 9) == 0) {
142cb9e1
NB
134 sra->array.major_version = -1;
135 sra->array.minor_version = -2;
136 strcpy(sra->text_version, buf+9);
137 } else
8382f19b 138 sscanf(buf, "%d.%d",
7e0f6979
NB
139 &sra->array.major_version,
140 &sra->array.minor_version);
8382f19b 141 }
e86c9dd6
NB
142 if (options & GET_LEVEL) {
143 strcpy(base, "level");
144 if (load_sys(fname, buf))
145 goto abort;
7e0f6979 146 sra->array.level = map_name(pers, buf);
e86c9dd6
NB
147 }
148 if (options & GET_LAYOUT) {
149 strcpy(base, "layout");
150 if (load_sys(fname, buf))
151 goto abort;
7e0f6979 152 sra->array.layout = strtoul(buf, NULL, 0);
e86c9dd6 153 }
549e9569
NB
154 if (options & GET_DISKS) {
155 strcpy(base, "raid_disks");
156 if (load_sys(fname, buf))
157 goto abort;
158 sra->array.raid_disks = strtoul(buf, NULL, 0);
159 }
e86c9dd6
NB
160 if (options & GET_COMPONENT) {
161 strcpy(base, "component_size");
162 if (load_sys(fname, buf))
163 goto abort;
164 sra->component_size = strtoull(buf, NULL, 0);
353632d9
NB
165 /* sysfs reports "K", but we want sectors */
166 sra->component_size *= 2;
e86c9dd6
NB
167 }
168 if (options & GET_CHUNK) {
169 strcpy(base, "chunk_size");
170 if (load_sys(fname, buf))
171 goto abort;
7e0f6979 172 sra->array.chunk_size = strtoul(buf, NULL, 0);
e86c9dd6 173 }
758d3a8e
NB
174 if (options & GET_CACHE) {
175 strcpy(base, "stripe_cache_size");
176 if (load_sys(fname, buf))
177 goto abort;
178 sra->cache_size = strtoul(buf, NULL, 0);
179 }
37dfc3d6
NB
180 if (options & GET_MISMATCH) {
181 strcpy(base, "mismatch_cnt");
182 if (load_sys(fname, buf))
183 goto abort;
184 sra->mismatch_cnt = strtoul(buf, NULL, 0);
185 }
e86c9dd6
NB
186
187 if (! (options & GET_DEVS))
188 return sra;
189
190 /* Get all the devices as well */
191 *base = 0;
192 dir = opendir(fname);
193 if (!dir)
194 goto abort;
7e0f6979 195 sra->array.spare_disks = 0;
e86c9dd6
NB
196
197 while ((de = readdir(dir)) != NULL) {
198 char *ep;
199 if (de->d_ino == 0 ||
200 strncmp(de->d_name, "dev-", 4) != 0)
201 continue;
202 strcpy(base, de->d_name);
203 dbase = base + strlen(base);
204 *dbase++ = '/';
205
206 dev = malloc(sizeof(*dev));
207 if (!dev)
208 goto abort;
209 dev->next = sra->devs;
210 sra->devs = dev;
06c7f68e 211 strcpy(dev->sys_name, de->d_name);
e86c9dd6
NB
212
213 /* Always get slot, major, minor */
214 strcpy(dbase, "slot");
215 if (load_sys(fname, buf))
216 goto abort;
06c7f68e
NB
217 dev->disk.raid_disk = strtoul(buf, &ep, 10);
218 if (*ep) dev->disk.raid_disk = -1;
e86c9dd6
NB
219
220 strcpy(dbase, "block/dev");
221 if (load_sys(fname, buf))
222 goto abort;
06c7f68e 223 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
e86c9dd6
NB
224
225 if (options & GET_OFFSET) {
226 strcpy(dbase, "offset");
227 if (load_sys(fname, buf))
228 goto abort;
06c7f68e 229 dev->data_offset = strtoull(buf, NULL, 0);
e86c9dd6
NB
230 }
231 if (options & GET_SIZE) {
232 strcpy(dbase, "size");
233 if (load_sys(fname, buf))
234 goto abort;
06c7f68e 235 dev->component_size = strtoull(buf, NULL, 0);
e86c9dd6
NB
236 }
237 if (options & GET_STATE) {
06c7f68e 238 dev->disk.state = 0;
e86c9dd6
NB
239 strcpy(dbase, "state");
240 if (load_sys(fname, buf))
241 goto abort;
242 if (strstr(buf, "in_sync"))
06c7f68e 243 dev->disk.state |= (1<<MD_DISK_SYNC);
e86c9dd6 244 if (strstr(buf, "faulty"))
06c7f68e
NB
245 dev->disk.state |= (1<<MD_DISK_FAULTY);
246 if (dev->disk.state == 0)
7e0f6979 247 sra->array.spare_disks++;
e86c9dd6
NB
248 }
249 if (options & GET_ERROR) {
250 strcpy(buf, "errors");
251 if (load_sys(fname, buf))
252 goto abort;
253 dev->errors = strtoul(buf, NULL, 0);
254 }
255 }
355726fa 256 closedir(dir);
e86c9dd6
NB
257 return sra;
258
259 abort:
355726fa
NB
260 if (dir)
261 closedir(dir);
8382f19b 262 sysfs_free(sra);
e86c9dd6
NB
263 return NULL;
264}
265
266unsigned long long get_component_size(int fd)
267{
268 /* Find out the component size of the array.
269 * We cannot trust GET_ARRAY_INFO ioctl as it's
270 * size field is only 32bits.
271 * So look in /sys/block/mdXXX/md/component_size
353632d9 272 *
8686f3ed 273 * This returns in units of sectors.
e86c9dd6
NB
274 */
275 struct stat stb;
276 char fname[50];
277 int n;
278 if (fstat(fd, &stb)) return 0;
279 if (major(stb.st_rdev) == 9)
280 sprintf(fname, "/sys/block/md%d/md/component_size",
ea24acd0 281 (int)minor(stb.st_rdev));
e86c9dd6
NB
282 else
283 sprintf(fname, "/sys/block/md_d%d/md/component_size",
ea24acd0 284 (int)minor(stb.st_rdev)>>MdpMinorShift);
e86c9dd6
NB
285 fd = open(fname, O_RDONLY);
286 if (fd < 0)
287 return 0;
288 n = read(fd, fname, sizeof(fname));
289 close(fd);
290 if (n == sizeof(fname))
291 return 0;
292 fname[n] = 0;
8686f3ed 293 return strtoull(fname, NULL, 10) * 2;
e86c9dd6
NB
294}
295
7e0f6979 296int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
297 char *name, char *val)
298{
299 char fname[50];
300 int n;
301 int fd;
302 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 303 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
304 fd = open(fname, O_WRONLY);
305 if (fd < 0)
306 return -1;
307 n = write(fd, val, strlen(val));
308 close(fd);
309 if (n != strlen(val))
310 return -1;
311 return 0;
312}
313
7e0f6979 314int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
315 char *name, unsigned long long val)
316{
317 char valstr[50];
318 sprintf(valstr, "%llu", val);
319 return sysfs_set_str(sra, dev, name, valstr);
320}
321
7e0f6979 322int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
323 char *name, unsigned long long *val)
324{
325 char fname[50];
326 char buf[50];
327 int n;
328 int fd;
329 char *ep;
330 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 331 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
332 fd = open(fname, O_RDONLY);
333 if (fd < 0)
334 return -1;
335 n = read(fd, buf, sizeof(buf));
336 close(fd);
337 if (n <= 0)
338 return -1;
339 buf[n] = 0;
340 *val = strtoull(buf, &ep, 0);
341 if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
342 return -1;
343 return 0;
344}
2503d23b
NB
345
346int sysfs_set_array(struct mdinfo *sra,
347 struct mdinfo *info)
348{
349 int rv = 0;
350 sra->array = info->array;
2f6079dc 351
2503d23b
NB
352 if (info->array.level < 0)
353 return 0; /* FIXME */
354 rv |= sysfs_set_str(sra, NULL, "level",
355 map_num(pers, info->array.level));
356 rv |= sysfs_set_num(sra, NULL, "raid_disks", info->array.raid_disks);
357 rv |= sysfs_set_num(sra, NULL, "chunk_size", info->array.chunk_size);
358 rv |= sysfs_set_num(sra, NULL, "layout", info->array.layout);
359 rv |= sysfs_set_num(sra, NULL, "component_size", info->component_size);
0fd5c350 360 rv |= sysfs_set_num(sra, NULL, "resync_start", info->resync_start);
2503d23b
NB
361 sra->array = info->array;
362 return rv;
363}
364
2318b9f0 365int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
2503d23b
NB
366{
367 char dv[100];
368 char nm[100];
369 struct mdinfo *sd2;
370 char *dname;
371 int rv;
372
373 sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
374 rv = sysfs_set_str(sra, NULL, "new_dev", dv);
375 if (rv)
376 return rv;
377
378 memset(nm, 0, sizeof(nm));
379 sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
380 if (readlink(dv, nm, sizeof(nm)) < 0)
381 return -1;
382 dname = strrchr(nm, '/');
383 if (dname) dname++;
384 strcpy(sd->sys_name, "dev-");
385 strcpy(sd->sys_name+4, dname);
386
387 rv |= sysfs_set_num(sra, sd, "offset", sd->data_offset);
388 rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
389 if (sra->array.level != LEVEL_CONTAINER) {
390 rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
391// rv |= sysfs_set_str(sra, sd, "state", "in_sync");
392 }
393 sd2 = malloc(sizeof(*sd2));
394 *sd2 = *sd;
395 sd2->next = sra->devs;
396 sra->devs = sd2;
397
398 return rv;
399}