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