Initial import of binutils 2.22 on the new vendor branch
[dragonfly.git] / contrib / lvm2 / dist / lib / label / label.c
1 /*      $NetBSD: label.c,v 1.1.1.2 2009/12/02 00:26:32 haad Exp $       */
2
3 /*
4  * Copyright (C) 2002-2004 Sistina Software, Inc. All rights reserved.
5  * Copyright (C) 2004-2007 Red Hat, Inc. All rights reserved.
6  *
7  * This file is part of LVM2.
8  *
9  * This copyrighted material is made available to anyone wishing to use,
10  * modify, copy, or redistribute it subject to the terms and conditions
11  * of the GNU Lesser General Public License v.2.1.
12  *
13  * You should have received a copy of the GNU Lesser General Public License
14  * along with this program; if not, write to the Free Software Foundation,
15  * Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16  */
17
18 #include "lib.h"
19 #include "label.h"
20 #include "crc.h"
21 #include "xlate.h"
22 #include "lvmcache.h"
23 #include "metadata.h"
24
25 #include <sys/stat.h>
26 #include <fcntl.h>
27 #include <unistd.h>
28
29 /* FIXME Allow for larger labels?  Restricted to single sector currently */
30
31 /*
32  * Internal labeller struct.
33  */
34 struct labeller_i {
35         struct dm_list list;
36
37         struct labeller *l;
38         char name[0];
39 };
40
41 static struct dm_list _labellers;
42
43 static struct labeller_i *_alloc_li(const char *name, struct labeller *l)
44 {
45         struct labeller_i *li;
46         size_t len;
47
48         len = sizeof(*li) + strlen(name) + 1;
49
50         if (!(li = dm_malloc(len))) {
51                 log_error("Couldn't allocate memory for labeller list object.");
52                 return NULL;
53         }
54
55         li->l = l;
56         strcpy(li->name, name);
57
58         return li;
59 }
60
61 static void _free_li(struct labeller_i *li)
62 {
63         dm_free(li);
64 }
65
66 int label_init(void)
67 {
68         dm_list_init(&_labellers);
69         return 1;
70 }
71
72 void label_exit(void)
73 {
74         struct dm_list *c, *n;
75         struct labeller_i *li;
76
77         for (c = _labellers.n; c && c != &_labellers; c = n) {
78                 n = c->n;
79                 li = dm_list_item(c, struct labeller_i);
80                 li->l->ops->destroy(li->l);
81                 _free_li(li);
82         }
83
84         dm_list_init(&_labellers);
85 }
86
87 int label_register_handler(const char *name, struct labeller *handler)
88 {
89         struct labeller_i *li;
90
91         if (!(li = _alloc_li(name, handler)))
92                 return_0;
93
94         dm_list_add(&_labellers, &li->list);
95         return 1;
96 }
97
98 struct labeller *label_get_handler(const char *name)
99 {
100         struct labeller_i *li;
101
102         dm_list_iterate_items(li, &_labellers)
103                 if (!strcmp(li->name, name))
104                         return li->l;
105
106         return NULL;
107 }
108
109 static struct labeller *_find_labeller(struct device *dev, char *buf,
110                                        uint64_t *label_sector,
111                                        uint64_t scan_sector)
112 {
113         struct labeller_i *li;
114         struct labeller *r = NULL;
115         struct label_header *lh;
116         struct lvmcache_info *info;
117         uint64_t sector;
118         int found = 0;
119         char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8)));
120
121         if (!dev_read(dev, scan_sector << SECTOR_SHIFT,
122                       LABEL_SCAN_SIZE, readbuf)) {
123                 log_debug("%s: Failed to read label area", dev_name(dev));
124                 goto out;
125         }
126
127         /* Scan a few sectors for a valid label */
128         for (sector = 0; sector < LABEL_SCAN_SECTORS;
129              sector += LABEL_SIZE >> SECTOR_SHIFT) {
130                 lh = (struct label_header *) (readbuf +
131                                               (sector << SECTOR_SHIFT));
132
133                 if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
134                         if (found) {
135                                 log_error("Ignoring additional label on %s at "
136                                           "sector %" PRIu64, dev_name(dev),
137                                           sector + scan_sector);
138                         }
139                         if (xlate64(lh->sector_xl) != sector + scan_sector) {
140                                 log_info("%s: Label for sector %" PRIu64
141                                          " found at sector %" PRIu64
142                                          " - ignoring", dev_name(dev),
143                                          (uint64_t)xlate64(lh->sector_xl),
144                                          sector + scan_sector);
145                                 continue;
146                         }
147                         if (calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE -
148                                      ((uintptr_t) &lh->offset_xl - (uintptr_t) lh)) !=
149                             xlate32(lh->crc_xl)) {
150                                 log_info("Label checksum incorrect on %s - "
151                                          "ignoring", dev_name(dev));
152                                 continue;
153                         }
154                         if (found)
155                                 continue;
156                 }
157
158                 dm_list_iterate_items(li, &_labellers) {
159                         if (li->l->ops->can_handle(li->l, (char *) lh,
160                                                    sector + scan_sector)) {
161                                 log_very_verbose("%s: %s label detected",
162                                                  dev_name(dev), li->name);
163                                 if (found) {
164                                         log_error("Ignoring additional label "
165                                                   "on %s at sector %" PRIu64,
166                                                   dev_name(dev),
167                                                   sector + scan_sector);
168                                         continue;
169                                 }
170                                 r = li->l;
171                                 memcpy(buf, lh, LABEL_SIZE);
172                                 if (label_sector)
173                                         *label_sector = sector + scan_sector;
174                                 found = 1;
175                                 break;
176                         }
177                 }
178         }
179
180       out:
181         if (!found) {
182                 if ((info = info_from_pvid(dev->pvid, 0)))
183                         lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
184                                                       info->fmt->orphan_vg_name,
185                                                       0, NULL);
186                 log_very_verbose("%s: No label detected", dev_name(dev));
187         }
188
189         return r;
190 }
191
192 /* FIXME Also wipe associated metadata area headers? */
193 int label_remove(struct device *dev)
194 {
195         char buf[LABEL_SIZE] __attribute((aligned(8)));
196         char readbuf[LABEL_SCAN_SIZE] __attribute((aligned(8)));
197         int r = 1;
198         uint64_t sector;
199         int wipe;
200         struct labeller_i *li;
201         struct label_header *lh;
202
203         memset(buf, 0, LABEL_SIZE);
204
205         log_very_verbose("Scanning for labels to wipe from %s", dev_name(dev));
206
207         if (!dev_open(dev))
208                 return_0;
209
210         /*
211          * We flush the device just in case someone is stupid
212          * enough to be trying to import an open pv into lvm.
213          */
214         dev_flush(dev);
215
216         if (!dev_read(dev, UINT64_C(0), LABEL_SCAN_SIZE, readbuf)) {
217                 log_debug("%s: Failed to read label area", dev_name(dev));
218                 goto out;
219         }
220
221         /* Scan first few sectors for anything looking like a label */
222         for (sector = 0; sector < LABEL_SCAN_SECTORS;
223              sector += LABEL_SIZE >> SECTOR_SHIFT) {
224                 lh = (struct label_header *) (readbuf +
225                                               (sector << SECTOR_SHIFT));
226
227                 wipe = 0;
228
229                 if (!strncmp((char *)lh->id, LABEL_ID, sizeof(lh->id))) {
230                         if (xlate64(lh->sector_xl) == sector)
231                                 wipe = 1;
232                 } else {
233                         dm_list_iterate_items(li, &_labellers) {
234                                 if (li->l->ops->can_handle(li->l, (char *) lh,
235                                                            sector)) {
236                                         wipe = 1;
237                                         break;
238                                 }
239                         }
240                 }
241
242                 if (wipe) {
243                         log_info("%s: Wiping label at sector %" PRIu64,
244                                  dev_name(dev), sector);
245                         if (!dev_write(dev, sector << SECTOR_SHIFT, LABEL_SIZE,
246                                        buf)) {
247                                 log_error("Failed to remove label from %s at "
248                                           "sector %" PRIu64, dev_name(dev),
249                                           sector);
250                                 r = 0;
251                         }
252                 }
253         }
254
255       out:
256         if (!dev_close(dev))
257                 stack;
258
259         return r;
260 }
261
262 int label_read(struct device *dev, struct label **result,
263                 uint64_t scan_sector)
264 {
265         char buf[LABEL_SIZE] __attribute((aligned(8)));
266         struct labeller *l;
267         uint64_t sector;
268         struct lvmcache_info *info;
269         int r = 0;
270
271         if ((info = info_from_pvid(dev->pvid, 1))) {
272                 log_debug("Using cached label for %s", dev_name(dev));
273                 *result = info->label;
274                 return 1;
275         }
276
277         if (!dev_open(dev)) {
278                 stack;
279
280                 if ((info = info_from_pvid(dev->pvid, 0)))
281                         lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
282                                                       info->fmt->orphan_vg_name,
283                                                       0, NULL);
284
285                 return r;
286         }
287
288         if (!(l = _find_labeller(dev, buf, &sector, scan_sector)))
289                 goto out;
290
291         if ((r = (l->ops->read)(l, dev, buf, result)) && result && *result)
292                 (*result)->sector = sector;
293
294       out:
295         if (!dev_close(dev))
296                 stack;
297
298         return r;
299 }
300
301 /* Caller may need to use label_get_handler to create label struct! */
302 int label_write(struct device *dev, struct label *label)
303 {
304         char buf[LABEL_SIZE] __attribute((aligned(8)));
305         struct label_header *lh = (struct label_header *) buf;
306         int r = 1;
307
308         if (!label->labeller->ops->write) {
309                 log_error("Label handler does not support label writes");
310                 return 0;
311         }
312
313         if ((LABEL_SIZE + (label->sector << SECTOR_SHIFT)) > LABEL_SCAN_SIZE) {
314                 log_error("Label sector %" PRIu64 " beyond range (%ld)",
315                           label->sector, LABEL_SCAN_SECTORS);
316                 return 0;
317         }
318
319         memset(buf, 0, LABEL_SIZE);
320
321         strncpy((char *)lh->id, LABEL_ID, sizeof(lh->id));
322         lh->sector_xl = xlate64(label->sector);
323         lh->offset_xl = xlate32(sizeof(*lh));
324
325         if (!(label->labeller->ops->write)(label, buf))
326                 return_0;
327
328         lh->crc_xl = xlate32(calc_crc(INITIAL_CRC, &lh->offset_xl, LABEL_SIZE -
329                                       ((uintptr_t) &lh->offset_xl - (uintptr_t) lh)));
330
331         if (!dev_open(dev))
332                 return_0;
333
334         log_info("%s: Writing label to sector %" PRIu64 " with stored offset %"
335                  PRIu32 ".", dev_name(dev), label->sector,
336                  xlate32(lh->offset_xl));
337         if (!dev_write(dev, label->sector << SECTOR_SHIFT, LABEL_SIZE, buf)) {
338                 log_debug("Failed to write label to %s", dev_name(dev));
339                 r = 0;
340         }
341
342         if (!dev_close(dev))
343                 stack;
344
345         return r;
346 }
347
348 /* Unused */
349 int label_verify(struct device *dev)
350 {
351         struct labeller *l;
352         char buf[LABEL_SIZE] __attribute((aligned(8)));
353         uint64_t sector;
354         struct lvmcache_info *info;
355         int r = 0;
356
357         if (!dev_open(dev)) {
358                 if ((info = info_from_pvid(dev->pvid, 0)))
359                         lvmcache_update_vgname_and_id(info, info->fmt->orphan_vg_name,
360                                                       info->fmt->orphan_vg_name,
361                                                       0, NULL);
362
363                 return_0;
364         }
365
366         if (!(l = _find_labeller(dev, buf, &sector, UINT64_C(0))))
367                 goto out;
368
369         r = l->ops->verify ? l->ops->verify(l, buf, sector) : 1;
370
371       out:
372         if (!dev_close(dev))
373                 stack;
374
375         return r;
376 }
377
378 void label_destroy(struct label *label)
379 {
380         label->labeller->ops->destroy_label(label->labeller, label);
381         dm_free(label);
382 }
383
384 struct label *label_create(struct labeller *labeller)
385 {
386         struct label *label;
387
388         if (!(label = dm_malloc(sizeof(*label)))) {
389                 log_error("label allocaction failed");
390                 return NULL;
391         }
392         memset(label, 0, sizeof(*label));
393
394         label->labeller = labeller;
395
396         labeller->ops->initialise_label(labeller, label);
397
398         return label;
399 }