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 
_alloc_li(const char * name,struct labeller * l)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 
_free_li(struct labeller_i * li)61 static void _free_li(struct labeller_i *li)
62 {
63           dm_free(li);
64 }
65 
label_init(void)66 int label_init(void)
67 {
68           dm_list_init(&_labellers);
69           return 1;
70 }
71 
label_exit(void)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 
label_register_handler(const char * name,struct labeller * handler)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 
label_get_handler(const char * name)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 
_find_labeller(struct device * dev,char * buf,uint64_t * label_sector,uint64_t scan_sector)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? */
label_remove(struct device * dev)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 
label_read(struct device * dev,struct label ** result,uint64_t scan_sector)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! */
label_write(struct device * dev,struct label * label)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 */
label_verify(struct device * dev)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 
label_destroy(struct label * label)378 void label_destroy(struct label *label)
379 {
380           label->labeller->ops->destroy_label(label->labeller, label);
381           dm_free(label);
382 }
383 
label_create(struct labeller * labeller)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 }
400