]> git.neil.brown.name Git - mdadm.git/blob - mapfile.c
Reset bad flag on map update
[mdadm.git] / mapfile.c
1 /*
2  * mapfile - keep track of uuid <-> array mapping. Part of:
3  * mdadm - manage Linux "md" devices aka RAID arrays.
4  *
5  * Copyright (C) 2006-2010 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  *    Paper: Neil Brown
25  *           Novell Inc
26  *           GPO Box Q1283
27  *           QVB Post Office, NSW 1230
28  *           Australia
29  */
30
31 /* The mapfile is used to track arrays being created in --incremental
32  * mode.  It particularly allows lookup from UUID to array device, but
33  * also allows the array device name to be easily found.
34  *
35  * The map file is line based with space separated fields.  The fields are:
36  *  Device id  -  mdX or mdpX  where X is a number.
37  *  metadata   -  0.90 1.0 1.1 1.2 ddf ...
38  *  UUID       -  uuid of the array
39  *  path       -  path where device created: /dev/md/home
40  *
41  * The best place for the mapfile wold be /var/run/mdadm/map.  However
42  * it is needed during initramfs early-boot, and /var/run doesn't exist there
43  * and certainly doesn't persist through to normal boot.
44  * So we store it in /dev/.mdadm/map but allow this to be changed at
45  * compile time. via MAP_DIR and MAP_FILE
46  *
47  */
48 #include        "mdadm.h"
49 #include        <sys/file.h>
50 #include        <ctype.h>
51
52 #ifndef MAP_DIR
53 #define MAP_DIR "/dev/.mdadm"
54 #define MAP_FILE "map"
55 #endif
56
57 #define MAP_READ 0
58 #define MAP_NEW 1
59 #define MAP_LOCK 2
60 #define MAP_DIRNAME 3
61 #define mapnames(dir, base) { \
62
63 char *mapname[4] = {
64         MAP_DIR "/" MAP_FILE,
65         MAP_DIR "/" MAP_FILE ".new",
66         MAP_DIR "/" MAP_FILE ".lock",
67         MAP_DIR
68 };
69
70 int mapmode[3] = { O_RDONLY, O_RDWR|O_CREAT, O_RDWR|O_CREAT|O_TRUNC };
71 char *mapsmode[3] = { "r", "w", "w"};
72
73 FILE *open_map(int modenum)
74 {
75         int fd;
76         if ((mapmode[modenum] & O_CREAT))
77                 /* Attempt to create directory, don't worry about
78                  * failure.
79                  */
80                 (void)mkdir(mapname[MAP_DIRNAME], 0755);
81         fd = open(mapname[modenum], mapmode[modenum], 0600);
82         if (fd >= 0)
83                 return fdopen(fd, mapsmode[modenum]);
84         return NULL;
85 }
86
87 int map_write(struct map_ent *mel)
88 {
89         FILE *f;
90         int err;
91
92         f = open_map(MAP_NEW);
93
94         if (!f)
95                 return 0;
96         for (; mel; mel = mel->next) {
97                 if (mel->bad)
98                         continue;
99                 if (mel->devnum < 0)
100                         fprintf(f, "mdp%d ", -1-mel->devnum);
101                 else
102                         fprintf(f, "md%d ", mel->devnum);
103                 fprintf(f, "%s ", mel->metadata);
104                 fprintf(f, "%08x:%08x:%08x:%08x ", mel->uuid[0],
105                         mel->uuid[1], mel->uuid[2], mel->uuid[3]);
106                 fprintf(f, "%s\n", mel->path?:"");
107         }
108         fflush(f);
109         err = ferror(f);
110         fclose(f);
111         if (err) {
112                 unlink(mapname[1]);
113                 return 0;
114         }
115         return rename(mapname[1],
116                       mapname[0]) == 0;
117 }
118
119
120 static FILE *lf = NULL;
121 int map_lock(struct map_ent **melp)
122 {
123         while (lf == NULL) {
124                 struct stat buf;
125                 lf = open_map(MAP_LOCK);
126                 if (lf == NULL)
127                         return -1;
128                 if (flock(fileno(lf), LOCK_EX) != 0) {
129                         fclose(lf);
130                         lf = NULL;
131                         return -1;
132                 }
133                 if (fstat(fileno(lf), &buf) != 0 ||
134                     buf.st_nlink == 0) {
135                         /* The owner of the lock unlinked it,
136                          * so we have a lock on a stale file,
137                          * try again
138                          */
139                         fclose(lf);
140                         lf = NULL;
141                 }
142         }
143         if (*melp)
144                 map_free(*melp);
145         map_read(melp);
146         return 0;
147 }
148
149 void map_unlock(struct map_ent **melp)
150 {
151         if (lf) {
152                 /* must unlink before closing the file,
153                  * as only the owner of the lock may
154                  * unlink the file
155                  */
156                 unlink(mapname[2]);
157                 fclose(lf);
158         }
159         lf = NULL;
160 }
161
162 void map_fork(void)
163 {
164         /* We are forking, so must close the lock file.
165          * Don't risk flushing anything though.
166          */
167         if (lf) {
168                 close(fileno(lf));
169                 fclose(lf);
170                 lf = NULL;
171         }
172 }
173
174 void map_add(struct map_ent **melp,
175             int devnum, char *metadata, int uuid[4], char *path)
176 {
177         struct map_ent *me = malloc(sizeof(*me));
178
179         me->devnum = devnum;
180         strcpy(me->metadata, metadata);
181         memcpy(me->uuid, uuid, 16);
182         me->path = path ? strdup(path) : NULL;
183         me->next = *melp;
184         me->bad = 0;
185         *melp = me;
186 }
187
188 void map_read(struct map_ent **melp)
189 {
190         FILE *f;
191         char buf[8192];
192         char path[200];
193         int devnum, uuid[4];
194         char metadata[30];
195         char nam[4];
196
197         *melp = NULL;
198
199         f = open_map(MAP_READ);
200         if (!f) {
201                 RebuildMap();
202                 f = open_map(MAP_READ);
203         }
204         if (!f)
205                 return;
206
207         while (fgets(buf, sizeof(buf), f)) {
208                 path[0] = 0;
209                 if (sscanf(buf, " %3[mdp]%d %s %x:%x:%x:%x %200s",
210                            nam, &devnum, metadata, uuid, uuid+1,
211                            uuid+2, uuid+3, path) >= 7) {
212                         if (strncmp(nam, "md", 2) != 0)
213                                 continue;
214                         if (nam[2] == 'p')
215                                 devnum = -1 - devnum;
216                         map_add(melp, devnum, metadata, uuid, path);
217                 }
218         }
219         fclose(f);
220 }
221
222 void map_free(struct map_ent *map)
223 {
224         while (map) {
225                 struct map_ent *mp = map;
226                 map = mp->next;
227                 free(mp->path);
228                 free(mp);
229         }
230 }
231
232 int map_update(struct map_ent **mpp, int devnum, char *metadata,
233                int *uuid, char *path)
234 {
235         struct map_ent *map, *mp;
236         int rv;
237
238         if (mpp && *mpp)
239                 map = *mpp;
240         else
241                 map_read(&map);
242
243         for (mp = map ; mp ; mp=mp->next)
244                 if (mp->devnum == devnum) {
245                         strcpy(mp->metadata, metadata);
246                         memcpy(mp->uuid, uuid, 16);
247                         free(mp->path);
248                         mp->path = path ? strdup(path) : NULL;
249                         mp->bad = 0;
250                         break;
251                 }
252         if (!mp)
253                 map_add(&map, devnum, metadata, uuid, path);
254         if (mpp)
255                 *mpp = NULL;
256         rv = map_write(map);
257         map_free(map);
258         return rv;
259 }
260
261 void map_delete(struct map_ent **mapp, int devnum)
262 {
263         struct map_ent *mp;
264
265         if (*mapp == NULL)
266                 map_read(mapp);
267
268         for (mp = *mapp; mp; mp = *mapp) {
269                 if (mp->devnum == devnum) {
270                         *mapp = mp->next;
271                         free(mp->path);
272                         free(mp);
273                 } else
274                         mapp = & mp->next;
275         }
276 }
277
278 void map_remove(struct map_ent **mapp, int devnum)
279 {
280         if (devnum == NoMdDev)
281                 return;
282
283         map_delete(mapp, devnum);
284         map_write(*mapp);
285         map_free(*mapp);
286 }
287
288 struct map_ent *map_by_uuid(struct map_ent **map, int uuid[4])
289 {
290         struct map_ent *mp;
291         if (!*map)
292                 map_read(map);
293
294         for (mp = *map ; mp ; mp = mp->next) {
295                 if (memcmp(uuid, mp->uuid, 16) != 0)
296                         continue;
297                 if (!mddev_busy(mp->devnum)) {
298                         mp->bad = 1;
299                         continue;
300                 }
301                 return mp;
302         }
303         return NULL;
304 }
305
306 struct map_ent *map_by_devnum(struct map_ent **map, int devnum)
307 {
308         struct map_ent *mp;
309         if (!*map)
310                 map_read(map);
311
312         for (mp = *map ; mp ; mp = mp->next) {
313                 if (mp->devnum != devnum)
314                         continue;
315                 if (!mddev_busy(mp->devnum)) {
316                         mp->bad = 1;
317                         continue;
318                 }
319                 return mp;
320         }
321         return NULL;
322 }
323
324 struct map_ent *map_by_name(struct map_ent **map, char *name)
325 {
326         struct map_ent *mp;
327         if (!*map)
328                 map_read(map);
329
330         for (mp = *map ; mp ; mp = mp->next) {
331                 if (!mp->path)
332                         continue;
333                 if (strncmp(mp->path, "/dev/md/", 8) != 0)
334                         continue;
335                 if (strcmp(mp->path+8, name) != 0)
336                         continue;
337                 if (!mddev_busy(mp->devnum)) {
338                         mp->bad = 1;
339                         continue;
340                 }
341                 return mp;
342         }
343         return NULL;
344 }
345
346 /* sets the proper subarray and container_dev according to the metadata
347  * version super_by_fd does this automatically, this routine is meant as
348  * a supplement for guess_super()
349  */
350 static char *get_member_info(struct mdstat_ent *ent)
351 {
352
353         if (ent->metadata_version == NULL ||
354             strncmp(ent->metadata_version, "external:", 9) != 0)
355                 return NULL;
356
357         if (is_subarray(&ent->metadata_version[9])) {
358                 char *subarray;
359
360                 subarray = strrchr(ent->metadata_version, '/');
361                 return subarray + 1;
362         }
363         return NULL;
364 }
365
366 void RebuildMap(void)
367 {
368         struct mdstat_ent *mdstat = mdstat_read(0, 0);
369         struct mdstat_ent *md;
370         struct map_ent *map = NULL;
371         int mdp = get_mdp_major();
372         int require_homehost;
373         char sys_hostname[256];
374         char *homehost = conf_get_homehost(&require_homehost);
375
376         if (homehost == NULL || strcmp(homehost, "<system>")==0) {
377                 if (gethostname(sys_hostname, sizeof(sys_hostname)) == 0) {
378                         sys_hostname[sizeof(sys_hostname)-1] = 0;
379                         homehost = sys_hostname;
380                 }
381         }
382
383         for (md = mdstat ; md ; md = md->next) {
384                 struct mdinfo *sra = sysfs_read(-1, md->devnum, GET_DEVS);
385                 struct mdinfo *sd;
386
387                 if (!sra)
388                         continue;
389
390                 for (sd = sra->devs ; sd ; sd = sd->next) {
391                         char namebuf[100];
392                         char dn[30];
393                         int dfd;
394                         int ok;
395                         struct supertype *st;
396                         char *subarray = NULL;
397                         char *path;
398                         struct mdinfo *info;
399
400                         sprintf(dn, "%d:%d", sd->disk.major, sd->disk.minor);
401                         dfd = dev_open(dn, O_RDONLY);
402                         if (dfd < 0)
403                                 continue;
404                         st = guess_super(dfd);
405                         if ( st == NULL)
406                                 ok = -1;
407                         else {
408                                 subarray = get_member_info(md);
409                                 ok = st->ss->load_super(st, dfd, NULL);
410                         }
411                         close(dfd);
412                         if (ok != 0)
413                                 continue;
414                         info = st->ss->container_content(st, subarray);
415
416                         if (md->devnum >= 0)
417                                 path = map_dev(MD_MAJOR, md->devnum, 0);
418                         else
419                                 path = map_dev(mdp, (-1-md->devnum)<< 6, 0);
420                         if (path == NULL ||
421                             strncmp(path, "/dev/md/", 8) != 0) {
422                                 /* We would really like a name that provides
423                                  * an MD_DEVNAME for udev.
424                                  * The name needs to be unique both in /dev/md/
425                                  * and in this mapfile.
426                                  * It needs to match watch -I or -As would come
427                                  * up with.
428                                  * That means:
429                                  *   Check if array is in mdadm.conf 
430                                  *        - if so use that.
431                                  *   determine trustworthy from homehost etc
432                                  *   find a unique name based on metadata name.
433                                  *   
434                                  */
435                                 struct mddev_ident *match = conf_match(st, info,
436                                                                        NULL, 0,
437                                                                        NULL);
438                                 struct stat stb;
439                                 if (match && match->devname && match->devname[0] == '/') {
440                                         path = match->devname;
441                                         if (path[0] != '/') {
442                                                 strcpy(namebuf, "/dev/md/");
443                                                 strcat(namebuf, path);
444                                                 path = namebuf;
445                                         }
446                                 } else {
447                                         int unum = 0;
448                                         char *sep = "_";
449                                         const char *name;
450                                         int conflict = 1;
451                                         if ((homehost == NULL ||
452                                              st->ss->match_home(st, homehost) != 1) &&
453                                             st->ss->match_home(st, "any") != 1 &&
454                                             (require_homehost
455                                              || ! conf_name_is_free(info->name)))
456                                                 /* require a numeric suffix */
457                                                 unum = 0;
458                                         else
459                                                 /* allow name to be used as-is if no conflict */
460                                                 unum = -1;
461                                         name = info->name;
462                                         if (!*name) {
463                                                 name = st->ss->name;
464                                                 if (!isdigit(name[strlen(name)-1]) &&
465                                                     unum == -1) {
466                                                         unum = 0;
467                                                         sep = "";
468                                                 }
469                                         }
470                                         if (strchr(name, ':'))
471                                                 /* probably a uniquifying
472                                                  * hostname prefix.  Allow
473                                                  * without a suffix
474                                                  */
475                                                 unum = -1;
476
477                                         while (conflict) {
478                                                 if (unum >= 0)
479                                                         sprintf(namebuf, "/dev/md/%s%s%d",
480                                                                 name, sep, unum);
481                                                 else
482                                                         sprintf(namebuf, "/dev/md/%s",
483                                                                 name);
484                                                 unum++;
485                                                 if (lstat(namebuf, &stb) != 0 &&
486                                                     (map == NULL ||
487                                                      !map_by_name(&map, namebuf+8)))
488                                                         conflict = 0;
489                                         }
490                                         path = namebuf;
491                                 }
492                         }
493                         map_add(&map, md->devnum,
494                                 info->text_version,
495                                 info->uuid, path);
496                         st->ss->free_super(st);
497                         free(info);
498                         break;
499                 }
500                 sysfs_free(sra);
501         }
502         /* Only trigger a change if we wrote a new map file */
503         if (map_write(map))
504                 for (md = mdstat ; md ; md = md->next) {
505                         struct mdinfo *sra = sysfs_read(-1, md->devnum,
506                                                         GET_VERSION);
507                         if (sra)
508                                 sysfs_uevent(sra, "change");
509                         sysfs_free(sra);
510                 }
511         map_free(map);
512         free_mdstat(mdstat);
513 }