Can now mostly assemble DDF arrays
[thirdparty/mdadm.git] / sysfs.c
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
29 int 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
45 void sysfs_free(struct mdinfo *sra)
46 {
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;
56         }
57 }
58
59 struct mdinfo *sysfs_read(int fd, int devnum, unsigned long options)
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;
70         struct mdinfo *sra;
71         struct mdinfo *dev;
72         DIR *dir;
73         struct dirent *de;
74
75         sra = malloc(sizeof(*sra));
76         if (sra == NULL)
77                 return sra;
78         sra->next = NULL;
79
80         if (fd >= 0) {
81                 struct stat stb;
82                 mdu_version_t vers;
83                 if (fstat(fd, &stb)) return NULL;
84                 if (ioctl(fd, RAID_VERSION, &vers) != 0)
85                         return NULL;
86                 if (major(stb.st_rdev)==9)
87                         sprintf(sra->sys_name, "md%d", (int)minor(stb.st_rdev));
88                 else
89                         sprintf(sra->sys_name, "md_d%d",
90                                 (int)minor(stb.st_rdev)>>MdpMinorShift);
91         } else {
92                 if (devnum >= 0)
93                         sprintf(sra->sys_name, "md%d", devnum);
94                 else
95                         sprintf(sra->sys_name, "md_d%d",
96                                 -1-devnum);
97         }
98         sprintf(fname, "/sys/block/%s/md/", sra->sys_name);
99         base = fname + strlen(fname);
100
101         sra->devs = NULL;
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) {
107                         sra->array.major_version =
108                                 sra->array.minor_version = -1;
109                         strcpy(sra->text_version, "");
110                 } else if (strncmp(buf, "external:", 9) == 0) {
111                         sra->array.major_version = -1;
112                         sra->array.minor_version = -2;
113                         strcpy(sra->text_version, buf+9);
114                 } else
115                         sscanf(buf, "%d.%d",
116                                &sra->array.major_version,
117                                &sra->array.minor_version);
118         }
119         if (options & GET_LEVEL) {
120                 strcpy(base, "level");
121                 if (load_sys(fname, buf))
122                         goto abort;
123                 sra->array.level = map_name(pers, buf);
124         }
125         if (options & GET_LAYOUT) {
126                 strcpy(base, "layout");
127                 if (load_sys(fname, buf))
128                         goto abort;
129                 sra->array.layout = strtoul(buf, NULL, 0);
130         }
131         if (options & GET_COMPONENT) {
132                 strcpy(base, "component_size");
133                 if (load_sys(fname, buf))
134                         goto abort;
135                 sra->component_size = strtoull(buf, NULL, 0);
136                 /* sysfs reports "K", but we want sectors */
137                 sra->component_size *= 2;
138         }
139         if (options & GET_CHUNK) {
140                 strcpy(base, "chunk_size");
141                 if (load_sys(fname, buf))
142                         goto abort;
143                 sra->array.chunk_size = strtoul(buf, NULL, 0);
144         }
145         if (options & GET_CACHE) {
146                 strcpy(base, "stripe_cache_size");
147                 if (load_sys(fname, buf))
148                         goto abort;
149                 sra->cache_size = strtoul(buf, NULL, 0);
150         }
151         if (options & GET_MISMATCH) {
152                 strcpy(base, "mismatch_cnt");
153                 if (load_sys(fname, buf))
154                         goto abort;
155                 sra->mismatch_cnt = strtoul(buf, NULL, 0);
156         }
157
158         if (! (options & GET_DEVS))
159                 return sra;
160
161         /* Get all the devices as well */
162         *base = 0;
163         dir = opendir(fname);
164         if (!dir)
165                 goto abort;
166         sra->array.spare_disks = 0;
167
168         while ((de = readdir(dir)) != NULL) {
169                 char *ep;
170                 if (de->d_ino == 0 ||
171                     strncmp(de->d_name, "dev-", 4) != 0)
172                         continue;
173                 strcpy(base, de->d_name);
174                 dbase = base + strlen(base);
175                 *dbase++ = '/';
176
177                 dev = malloc(sizeof(*dev));
178                 if (!dev)
179                         goto abort;
180                 dev->next = sra->devs;
181                 sra->devs = dev;
182                 strcpy(dev->sys_name, de->d_name);
183
184                 /* Always get slot, major, minor */
185                 strcpy(dbase, "slot");
186                 if (load_sys(fname, buf))
187                         goto abort;
188                 dev->disk.raid_disk = strtoul(buf, &ep, 10);
189                 if (*ep) dev->disk.raid_disk = -1;
190
191                 strcpy(dbase, "block/dev");
192                 if (load_sys(fname, buf))
193                         goto abort;
194                 sscanf(buf, "%d:%d", &dev->disk.major, &dev->disk.minor);
195
196                 if (options & GET_OFFSET) {
197                         strcpy(dbase, "offset");
198                         if (load_sys(fname, buf))
199                                 goto abort;
200                         dev->data_offset = strtoull(buf, NULL, 0);
201                 }
202                 if (options & GET_SIZE) {
203                         strcpy(dbase, "size");
204                         if (load_sys(fname, buf))
205                                 goto abort;
206                         dev->component_size = strtoull(buf, NULL, 0);
207                 }
208                 if (options & GET_STATE) {
209                         dev->disk.state = 0;
210                         strcpy(dbase, "state");
211                         if (load_sys(fname, buf))
212                                 goto abort;
213                         if (strstr(buf, "in_sync"))
214                                 dev->disk.state |= (1<<MD_DISK_SYNC);
215                         if (strstr(buf, "faulty"))
216                                 dev->disk.state |= (1<<MD_DISK_FAULTY);
217                         if (dev->disk.state == 0)
218                                 sra->array.spare_disks++;
219                 }
220                 if (options & GET_ERROR) {
221                         strcpy(buf, "errors");
222                         if (load_sys(fname, buf))
223                                 goto abort;
224                         dev->errors = strtoul(buf, NULL, 0);
225                 }
226         }
227         return sra;
228
229  abort:
230         sysfs_free(sra);
231         return NULL;
232 }
233
234 unsigned long long get_component_size(int fd)
235 {
236         /* Find out the component size of the array.
237          * We cannot trust GET_ARRAY_INFO ioctl as it's
238          * size field is only 32bits.
239          * So look in /sys/block/mdXXX/md/component_size
240          *
241          * This returns in units of sectors.
242          */
243         struct stat stb;
244         char fname[50];
245         int n;
246         if (fstat(fd, &stb)) return 0;
247         if (major(stb.st_rdev) == 9)
248                 sprintf(fname, "/sys/block/md%d/md/component_size",
249                         (int)minor(stb.st_rdev));
250         else
251                 sprintf(fname, "/sys/block/md_d%d/md/component_size",
252                         (int)minor(stb.st_rdev)>>MdpMinorShift);
253         fd = open(fname, O_RDONLY);
254         if (fd < 0)
255                 return 0;
256         n = read(fd, fname, sizeof(fname));
257         close(fd);
258         if (n == sizeof(fname))
259                 return 0;
260         fname[n] = 0;
261         return strtoull(fname, NULL, 10) * 2;
262 }
263
264 int sysfs_set_str(struct mdinfo *sra, struct mdinfo *dev,
265                   char *name, char *val)
266 {
267         char fname[50];
268         int n;
269         int fd;
270         sprintf(fname, "/sys/block/%s/md/%s/%s",
271                 sra->sys_name, dev?dev->sys_name:"", name);
272         fd = open(fname, O_WRONLY);
273         if (fd < 0)
274                 return -1;
275         n = write(fd, val, strlen(val));
276         close(fd);
277         if (n != strlen(val))
278                 return -1;
279         return 0;
280 }
281
282 int sysfs_set_num(struct mdinfo *sra, struct mdinfo *dev,
283                   char *name, unsigned long long val)
284 {
285         char valstr[50];
286         sprintf(valstr, "%llu", val);
287         return sysfs_set_str(sra, dev, name, valstr);
288 }
289
290 int sysfs_get_ll(struct mdinfo *sra, struct mdinfo *dev,
291                        char *name, unsigned long long *val)
292 {
293         char fname[50];
294         char buf[50];
295         int n;
296         int fd;
297         char *ep;
298         sprintf(fname, "/sys/block/%s/md/%s/%s",
299                 sra->sys_name, dev?dev->sys_name:"", name);
300         fd = open(fname, O_RDONLY);
301         if (fd < 0)
302                 return -1;
303         n = read(fd, buf, sizeof(buf));
304         close(fd);
305         if (n <= 0)
306                 return -1;
307         buf[n] = 0;
308         *val = strtoull(buf, &ep, 0);
309         if (ep == buf || (*ep != 0 && *ep != '\n' && *ep != ' '))
310                 return -1;
311         return 0;
312 }
313
314 int sysfs_set_array(struct mdinfo *sra,
315                     struct mdinfo *info)
316 {
317         int rv = 0;
318         sra->array = info->array;
319         if (info->array.level < 0)
320                 return 0; /* FIXME */
321         rv |= sysfs_set_str(sra, NULL, "level",
322                             map_num(pers, info->array.level));
323         rv |= sysfs_set_num(sra, NULL, "raid_disks", info->array.raid_disks);
324         rv |= sysfs_set_num(sra, NULL, "chunk_size", info->array.chunk_size);
325         rv |= sysfs_set_num(sra, NULL, "layout", info->array.layout);
326         rv |= sysfs_set_num(sra, NULL, "component_size", info->component_size);
327         sra->array = info->array;
328         return rv;
329 }
330
331 int sysfs_add_disk(struct mdinfo *sra, int fd, struct mdinfo *sd)
332 {
333         char dv[100];
334         char nm[100];
335         struct mdinfo *sd2;
336         char *dname;
337         int rv;
338
339         sprintf(dv, "%d:%d", sd->disk.major, sd->disk.minor);
340         rv = sysfs_set_str(sra, NULL, "new_dev", dv);
341         if (rv)
342                 return rv;
343
344         memset(nm, 0, sizeof(nm));
345         sprintf(dv, "/sys/dev/block/%d:%d", sd->disk.major, sd->disk.minor);
346         if (readlink(dv, nm, sizeof(nm)) < 0)
347                 return -1;
348         dname = strrchr(nm, '/');
349         if (dname) dname++;
350         strcpy(sd->sys_name, "dev-");
351         strcpy(sd->sys_name+4, dname);
352
353         rv |= sysfs_set_num(sra, sd, "offset", sd->data_offset);
354         rv |= sysfs_set_num(sra, sd, "size", (sd->component_size+1) / 2);
355         if (sra->array.level != LEVEL_CONTAINER) {
356                 rv |= sysfs_set_num(sra, sd, "slot", sd->disk.raid_disk);
357 //              rv |= sysfs_set_str(sra, sd, "state", "in_sync");
358         }
359         sd2 = malloc(sizeof(*sd2));
360         *sd2 = *sd;
361         sd2->next = sra->devs;
362         sra->devs = sd2;
363
364         return rv;
365 }