]> git.ipfire.org Git - thirdparty/mdadm.git/blame - sysfs.c
ddf: Set container_member from subarray in getinfo_super.
[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);
ea6d09b0 77 if (fd < 0 && errno == EACCES)
549e9569
NB
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);
b8ac1967 137 } else {
8382f19b 138 sscanf(buf, "%d.%d",
7e0f6979
NB
139 &sra->array.major_version,
140 &sra->array.minor_version);
b8ac1967
NB
141 strcpy(sra->text_version, buf);
142 }
8382f19b 143 }
e86c9dd6
NB
144 if (options & GET_LEVEL) {
145 strcpy(base, "level");
146 if (load_sys(fname, buf))
147 goto abort;
7e0f6979 148 sra->array.level = map_name(pers, buf);
e86c9dd6
NB
149 }
150 if (options & GET_LAYOUT) {
151 strcpy(base, "layout");
152 if (load_sys(fname, buf))
153 goto abort;
7e0f6979 154 sra->array.layout = strtoul(buf, NULL, 0);
e86c9dd6 155 }
549e9569
NB
156 if (options & GET_DISKS) {
157 strcpy(base, "raid_disks");
158 if (load_sys(fname, buf))
159 goto abort;
160 sra->array.raid_disks = strtoul(buf, NULL, 0);
161 }
e86c9dd6
NB
162 if (options & GET_COMPONENT) {
163 strcpy(base, "component_size");
164 if (load_sys(fname, buf))
165 goto abort;
166 sra->component_size = strtoull(buf, NULL, 0);
353632d9
NB
167 /* sysfs reports "K", but we want sectors */
168 sra->component_size *= 2;
e86c9dd6
NB
169 }
170 if (options & GET_CHUNK) {
171 strcpy(base, "chunk_size");
172 if (load_sys(fname, buf))
173 goto abort;
7e0f6979 174 sra->array.chunk_size = strtoul(buf, NULL, 0);
e86c9dd6 175 }
758d3a8e
NB
176 if (options & GET_CACHE) {
177 strcpy(base, "stripe_cache_size");
178 if (load_sys(fname, buf))
179 goto abort;
180 sra->cache_size = strtoul(buf, NULL, 0);
181 }
37dfc3d6
NB
182 if (options & GET_MISMATCH) {
183 strcpy(base, "mismatch_cnt");
184 if (load_sys(fname, buf))
185 goto abort;
186 sra->mismatch_cnt = strtoul(buf, NULL, 0);
187 }
e86c9dd6
NB
188
189 if (! (options & GET_DEVS))
190 return sra;
191
192 /* Get all the devices as well */
193 *base = 0;
194 dir = opendir(fname);
195 if (!dir)
196 goto abort;
7e0f6979 197 sra->array.spare_disks = 0;
e86c9dd6
NB
198
199 while ((de = readdir(dir)) != NULL) {
200 char *ep;
201 if (de->d_ino == 0 ||
202 strncmp(de->d_name, "dev-", 4) != 0)
203 continue;
204 strcpy(base, de->d_name);
205 dbase = base + strlen(base);
206 *dbase++ = '/';
207
208 dev = malloc(sizeof(*dev));
209 if (!dev)
210 goto abort;
211 dev->next = sra->devs;
212 sra->devs = dev;
06c7f68e 213 strcpy(dev->sys_name, de->d_name);
e86c9dd6
NB
214
215 /* Always get slot, major, minor */
216 strcpy(dbase, "slot");
217 if (load_sys(fname, buf))
218 goto abort;
06c7f68e
NB
219 dev->disk.raid_disk = strtoul(buf, &ep, 10);
220 if (*ep) dev->disk.raid_disk = -1;
e86c9dd6
NB
221
222 strcpy(dbase, "block/dev");
223 if (load_sys(fname, buf))
224 goto abort;
06c7f68e 225 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
e86c9dd6
NB
226
227 if (options & GET_OFFSET) {
228 strcpy(dbase, "offset");
229 if (load_sys(fname, buf))
230 goto abort;
06c7f68e 231 dev->data_offset = strtoull(buf, NULL, 0);
e86c9dd6
NB
232 }
233 if (options & GET_SIZE) {
234 strcpy(dbase, "size");
235 if (load_sys(fname, buf))
236 goto abort;
06c7f68e 237 dev->component_size = strtoull(buf, NULL, 0);
e86c9dd6
NB
238 }
239 if (options & GET_STATE) {
06c7f68e 240 dev->disk.state = 0;
e86c9dd6
NB
241 strcpy(dbase, "state");
242 if (load_sys(fname, buf))
243 goto abort;
244 if (strstr(buf, "in_sync"))
06c7f68e 245 dev->disk.state |= (1<<MD_DISK_SYNC);
e86c9dd6 246 if (strstr(buf, "faulty"))
06c7f68e
NB
247 dev->disk.state |= (1<<MD_DISK_FAULTY);
248 if (dev->disk.state == 0)
7e0f6979 249 sra->array.spare_disks++;
e86c9dd6
NB
250 }
251 if (options & GET_ERROR) {
252 strcpy(buf, "errors");
253 if (load_sys(fname, buf))
254 goto abort;
255 dev->errors = strtoul(buf, NULL, 0);
256 }
257 }
355726fa 258 closedir(dir);
e86c9dd6
NB
259 return sra;
260
261 abort:
355726fa
NB
262 if (dir)
263 closedir(dir);
8382f19b 264 sysfs_free(sra);
e86c9dd6
NB
265 return NULL;
266}
267
268unsigned long long get_component_size(int fd)
269{
270 /* Find out the component size of the array.
271 * We cannot trust GET_ARRAY_INFO ioctl as it's
272 * size field is only 32bits.
273 * So look in /sys/block/mdXXX/md/component_size
353632d9 274 *
8686f3ed 275 * This returns in units of sectors.
e86c9dd6
NB
276 */
277 struct stat stb;
278 char fname[50];
279 int n;
280 if (fstat(fd, &stb)) return 0;
281 if (major(stb.st_rdev) == 9)
282 sprintf(fname, "/sys/block/md%d/md/component_size",
ea24acd0 283 (int)minor(stb.st_rdev));
e86c9dd6
NB
284 else
285 sprintf(fname, "/sys/block/md_d%d/md/component_size",
ea24acd0 286 (int)minor(stb.st_rdev)>>MdpMinorShift);
e86c9dd6
NB
287 fd = open(fname, O_RDONLY);
288 if (fd < 0)
289 return 0;
290 n = read(fd, fname, sizeof(fname));
291 close(fd);
292 if (n == sizeof(fname))
293 return 0;
294 fname[n] = 0;
8686f3ed 295 return strtoull(fname, NULL, 10) * 2;
e86c9dd6
NB
296}
297
7e0f6979 298int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
299 char *name, char *val)
300{
301 char fname[50];
302 int n;
303 int fd;
7e1432fb 304
e86c9dd6 305 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 306 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
307 fd = open(fname, O_WRONLY);
308 if (fd < 0)
309 return -1;
310 n = write(fd, val, strlen(val));
311 close(fd);
312 if (n != strlen(val))
313 return -1;
314 return 0;
315}
316
7e0f6979 317int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
318 char *name, unsigned long long val)
319{
320 char valstr[50];
321 sprintf(valstr, "%llu", val);
322 return sysfs_set_str(sra, dev, name, valstr);
323}
324
7e0f6979 325int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
e86c9dd6
NB
326 char *name, unsigned long long *val)
327{
328 char fname[50];
329 char buf[50];
330 int n;
331 int fd;
332 char *ep;
333 sprintf(fname, "/sys/block/%s/md/%s/%s",
7e0f6979 334 sra->sys_name, dev?dev->sys_name:"", name);
e86c9dd6
NB
335 fd = open(fname, O_RDONLY);
336 if (fd < 0)
337 return -1;
338 n = read(fd, buf, sizeof(buf));
339 close(fd);
340 if (n <= 0)
341 return -1;
342 buf[n] = 0;
343 *val = strtoull(buf, &ep, 0);
344 if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
345 return -1;
346 return 0;
347}
2503d23b
NB
348
349int sysfs_set_array(struct mdinfo *sra,
350 struct mdinfo *info)
351{
352 int rv = 0;
353 sra->array = info->array;
2f6079dc 354
2503d23b
NB
355 if (info->array.level < 0)
356 return 0; /* FIXME */
357 rv |= sysfs_set_str(sra, NULL, "level",
358 map_num(pers, info->array.level));
359 rv |= sysfs_set_num(sra, NULL, "raid_disks", info->array.raid_disks);
360 rv |= sysfs_set_num(sra, NULL, "chunk_size", info->array.chunk_size);
361 rv |= sysfs_set_num(sra, NULL, "layout", info->array.layout);
362 rv |= sysfs_set_num(sra, NULL, "component_size", info->component_size);
0fd5c350 363 rv |= sysfs_set_num(sra, NULL, "resync_start", info->resync_start);
2503d23b
NB
364 sra->array = info->array;
365 return rv;
366}
367
2318b9f0 368int sysfs_add_disk(struct mdinfo *sra, struct mdinfo *sd)
2503d23b
NB
369{
370 char dv[100];
371 char nm[100];
372 struct mdinfo *sd2;
373 char *dname;
374 int rv;
375
376 sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
377 rv = sysfs_set_str(sra, NULL, "new_dev", dv);
378 if (rv)
379 return rv;
380
381 memset(nm, 0, sizeof(nm));
382 sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
383 if (readlink(dv, nm, sizeof(nm)) < 0)
384 return -1;
385 dname = strrchr(nm, '/');
386 if (dname) dname++;
387 strcpy(sd->sys_name, "dev-");
388 strcpy(sd->sys_name+4, dname);
389
390 rv |= sysfs_set_num(sra, sd, "offset", sd->data_offset);
391 rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
392 if (sra->array.level != LEVEL_CONTAINER) {
393 rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
394// rv |= sysfs_set_str(sra, sd, "state", "in_sync");
395 }
3cb07116
NB
396 if (! rv) {
397 sd2 = malloc(sizeof(*sd2));
398 *sd2 = *sd;
399 sd2->next = sra->devs;
400 sra->devs = sd2;
401 }
2503d23b
NB
402 return rv;
403}
90c8b707
DW
404
405int sysfs_disk_to_sg(int fd)
406{
407 /* from an open block device, try find and open its corresponding
408 * scsi_generic interface
409 */
410 struct stat st;
411 char path[256];
412 char sg_path[256];
413 char sg_major_minor[8];
414 char *c;
415 DIR *dir;
416 struct dirent *de;
417 int major, minor, rv;
418
419 if (fstat(fd, &st))
420 return -1;
421
422 snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
423 major(st.st_rdev), minor(st.st_rdev));
424
425 dir = opendir(path);
426 if (!dir)
427 return -1;
428
429 de = readdir(dir);
430 while (de) {
431 if (strncmp("scsi_generic:", de->d_name,
432 strlen("scsi_generic:")) == 0)
433 break;
434 de = readdir(dir);
435 }
436 closedir(dir);
437
438 if (!de)
439 return -1;
440
441 snprintf(sg_path, sizeof(sg_path), "%s/%s/dev", path, de->d_name);
442 fd = open(sg_path, O_RDONLY);
443 if (fd < 0)
444 return fd;
445
446 rv = read(fd, sg_major_minor, sizeof(sg_major_minor));
447 close(fd);
448 if (rv < 0)
449 return -1;
450 else
451 sg_major_minor[rv - 1] = '\0';
452
453 c = strchr(sg_major_minor, ':');
454 *c = '\0';
455 c++;
456 major = strtol(sg_major_minor, NULL, 10);
457 minor = strtol(c, NULL, 10);
458 snprintf(path, sizeof(path), "/dev/.tmp.md.%d:%d:%d",
459 (int) getpid(), major, minor);
460 if (mknod(path, S_IFCHR|0600, makedev(major, minor))==0) {
461 fd = open(path, O_RDONLY);
462 unlink(path);
463 return fd;
464 }
465
466 return -1;
467}
468
f1665f72
DW
469int sysfs_disk_to_scsi_id(int fd, __u32 *id)
470{
471 /* from an open block device, try to retrieve it scsi_id */
472 struct stat st;
473 char path[256];
474 char *c1, *c2;
475 DIR *dir;
476 struct dirent *de;
477
478 if (fstat(fd, &st))
479 return 1;
480
481 snprintf(path, sizeof(path), "/sys/dev/block/%d:%d/device",
482 major(st.st_rdev), minor(st.st_rdev));
483
484 dir = opendir(path);
485 if (!dir)
486 return 1;
487
488 de = readdir(dir);
489 while (de) {
490 if (strncmp("scsi_disk:", de->d_name,
491 strlen("scsi_disk:")) == 0)
492 break;
493 de = readdir(dir);
494 }
495 closedir(dir);
496
497 if (!de)
498 return 1;
499
500 c1 = strchr(de->d_name, ':');
501 c1++;
502 c2 = strchr(c1, ':');
503 *c2 = '\0';
504 *id = strtol(c1, NULL, 10) << 24; /* host */
505 c1 = c2 + 1;
506 c2 = strchr(c1, ':');
507 *c2 = '\0';
508 *id |= strtol(c1, NULL, 10) << 16; /* channel */
509 c1 = c2 + 1;
510 c2 = strchr(c1, ':');
511 *c2 = '\0';
512 *id |= strtol(c1, NULL, 10) << 8; /* lun */
513 c1 = c2 + 1;
514 *id |= strtol(c1, NULL, 10); /* id */
515
516 return 0;
517}
f94d52f4
NB
518
519
520int sysfs_unique_holder(int devnum, long rdev)
521{
522 /* Check that devnum is a holder of rdev,
523 * and is the only holder.
524 * we should be locked against races by
525 * an O_EXCL on devnum
526 */
527 DIR *dir;
528 struct dirent *de;
529 char dirname[100];
530 char l;
531 int found = 0;
532 sprintf(dirname, "/sys/dev/block/%d:%d/holders",
533 major(rdev), minor(rdev));
534 dir = opendir(dirname);
535 errno = ENOENT;
536 if (!dir)
537 return 0;
538 l = strlen(dirname);
539 while ((de = readdir(dir)) != NULL) {
540 char buf[10];
541 int n;
542 int mj, mn;
543 char c;
544 int fd;
545
546 if (de->d_ino == 0)
547 continue;
548 if (de->d_name[0] == '.')
549 continue;
550 strcpy(dirname+l, "/");
551 strcat(dirname+l, de->d_name);
552 strcat(dirname+l, "/dev");
553 fd = open(dirname, O_RDONLY);
554 if (fd < 0) {
555 errno = ENOENT;
556 break;
557 }
558 n = read(fd, buf, sizeof(buf)-1);
559 close(fd);
560 buf[n] = 0;
561 if (sscanf(buf, "%d:%d%c", &mj, &mn, &c) != 3 ||
562 c != '\n') {
563 errno = ENOENT;
564 break;
565 }
566 if (mj != MD_MAJOR)
567 mn = -1-(mn>>6);
568
569 if (devnum != mn) {
570 errno = EEXIST;
571 break;
572 }
573 found = 1;
574 }
575 closedir(dir);
576 if (de)
577 return 0;
578 else
579 return found;
580}