2001-12-11 14:42:30 +03:00
/*
2004-03-30 23:35:44 +04:00
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2007 Red Hat , Inc . All rights reserved .
2001-12-11 14:42:30 +03:00
*
2004-03-30 23:35:44 +04:00
* This file is part of LVM2 .
*
* This copyrighted material is made available to anyone wishing to use ,
* modify , copy , or redistribute it subject to the terms and conditions
2007-08-21 00:55:30 +04:00
* of the GNU Lesser General Public License v .2 .1 .
2004-03-30 23:35:44 +04:00
*
2007-08-21 00:55:30 +04:00
* You should have received a copy of the GNU Lesser General Public License
2004-03-30 23:35:44 +04:00
* along with this program ; if not , write to the Free Software Foundation ,
* Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
2001-12-11 14:42:30 +03:00
*/
2002-11-18 17:04:08 +03:00
# include "lib.h"
2001-12-11 14:42:30 +03:00
# include "label.h"
2002-11-18 17:04:08 +03:00
# include "crc.h"
# include "xlate.h"
2003-07-05 02:34:56 +04:00
# include "lvmcache.h"
2004-05-05 01:25:57 +04:00
# include "metadata.h"
2002-11-18 17:04:08 +03:00
# include <sys/stat.h>
# include <fcntl.h>
# include <unistd.h>
/* FIXME Allow for larger labels? Restricted to single sector currently */
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
/*
* Internal labeller struct .
*/
struct labeller_i {
2008-11-04 01:14:30 +03:00
struct dm_list list ;
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
struct labeller * l ;
char name [ 0 ] ;
} ;
2001-12-13 11:40:47 +03:00
2008-11-04 01:14:30 +03:00
static struct dm_list _labellers ;
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
static struct labeller_i * _alloc_li ( const char * name , struct labeller * l )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
size_t len ;
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
len = sizeof ( * li ) + strlen ( name ) + 1 ;
2001-12-11 14:42:30 +03:00
2005-10-17 03:03:59 +04:00
if ( ! ( li = dm_malloc ( len ) ) ) {
2002-01-16 03:01:36 +03:00
log_error ( " Couldn't allocate memory for labeller list object. " ) ;
2002-01-10 18:01:58 +03:00
return NULL ;
}
li - > l = l ;
strcpy ( li - > name , name ) ;
return li ;
2001-12-12 12:05:44 +03:00
}
2001-12-11 14:42:30 +03:00
2002-01-11 13:43:32 +03:00
static void _free_li ( struct labeller_i * li )
2001-12-12 12:05:44 +03:00
{
2005-10-17 03:03:59 +04:00
dm_free ( li ) ;
2002-01-10 18:01:58 +03:00
}
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
int label_init ( void )
{
2008-11-04 01:14:30 +03:00
dm_list_init ( & _labellers ) ;
2002-01-10 19:48:28 +03:00
return 1 ;
2001-12-11 14:42:30 +03:00
}
2002-01-10 18:01:58 +03:00
void label_exit ( void )
2001-12-14 16:15:15 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * c , * n ;
2002-01-11 13:43:32 +03:00
struct labeller_i * li ;
2002-01-10 18:01:58 +03:00
for ( c = _labellers . n ; c ! = & _labellers ; c = n ) {
n = c - > n ;
2008-11-04 01:14:30 +03:00
li = dm_list_item ( c , struct labeller_i ) ;
2002-11-18 17:04:08 +03:00
li - > l - > ops - > destroy ( li - > l ) ;
2002-01-11 13:43:32 +03:00
_free_li ( li ) ;
2002-01-10 18:01:58 +03:00
}
2004-05-05 01:25:57 +04:00
2008-11-04 01:14:30 +03:00
dm_list_init ( & _labellers ) ;
2001-12-14 16:15:15 +03:00
}
2002-01-10 18:01:58 +03:00
int label_register_handler ( const char * name , struct labeller * handler )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
2008-01-30 16:19:47 +03:00
if ( ! ( li = _alloc_li ( name , handler ) ) )
return_0 ;
2001-12-11 14:42:30 +03:00
2008-11-04 01:14:30 +03:00
dm_list_add ( & _labellers , & li - > list ) ;
2002-01-10 18:01:58 +03:00
return 1 ;
2001-12-11 14:42:30 +03:00
}
2002-01-10 18:01:58 +03:00
struct labeller * label_get_handler ( const char * name )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
2001-12-11 14:42:30 +03:00
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( li , & _labellers )
2002-01-10 18:01:58 +03:00
if ( ! strcmp ( li - > name , name ) )
return li - > l ;
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
return NULL ;
}
2001-12-11 14:42:30 +03:00
2002-11-18 17:04:08 +03:00
static struct labeller * _find_labeller ( struct device * dev , char * buf ,
2007-04-23 22:21:01 +04:00
uint64_t * label_sector ,
uint64_t scan_sector )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
2002-11-18 17:04:08 +03:00
struct labeller * r = NULL ;
struct label_header * lh ;
2005-03-22 01:40:35 +03:00
struct lvmcache_info * info ;
2002-11-18 17:04:08 +03:00
uint64_t sector ;
int found = 0 ;
2006-12-01 02:11:42 +03:00
char readbuf [ LABEL_SCAN_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
2001-12-11 14:42:30 +03:00
2007-04-23 22:21:01 +04:00
if ( ! dev_read ( dev , scan_sector < < SECTOR_SHIFT ,
LABEL_SCAN_SIZE , readbuf ) ) {
2002-11-18 17:04:08 +03:00
log_debug ( " %s: Failed to read label area " , dev_name ( dev ) ) ;
2008-09-15 21:06:55 +04:00
goto out ;
2002-11-18 17:04:08 +03:00
}
2007-04-23 22:21:01 +04:00
/* Scan a few sectors for a valid label */
2002-11-18 17:04:08 +03:00
for ( sector = 0 ; sector < LABEL_SCAN_SECTORS ;
sector + = LABEL_SIZE > > SECTOR_SHIFT ) {
lh = ( struct label_header * ) ( readbuf +
( sector < < SECTOR_SHIFT ) ) ;
2006-05-10 01:23:51 +04:00
if ( ! strncmp ( ( char * ) lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
2002-11-18 17:04:08 +03:00
if ( found ) {
log_error ( " Ignoring additional label on %s at "
" sector % " PRIu64 , dev_name ( dev ) ,
2007-04-23 22:21:01 +04:00
sector + scan_sector ) ;
2002-11-18 17:04:08 +03:00
}
2007-04-23 22:21:01 +04:00
if ( xlate64 ( lh - > sector_xl ) ! = sector + scan_sector ) {
2002-11-18 17:04:08 +03:00
log_info ( " %s: Label for sector % " PRIu64
" found at sector % " PRIu64
" - ignoring " , dev_name ( dev ) ,
2007-04-23 22:21:01 +04:00
xlate64 ( lh - > sector_xl ) ,
sector + scan_sector ) ;
2002-11-18 17:04:08 +03:00
continue ;
}
if ( calc_crc ( INITIAL_CRC , & lh - > offset_xl , LABEL_SIZE -
2008-01-31 15:35:31 +03:00
( ( uintptr_t ) & lh - > offset_xl - ( uintptr_t ) lh ) ) ! =
2002-11-18 17:04:08 +03:00
xlate32 ( lh - > crc_xl ) ) {
log_info ( " Label checksum incorrect on %s - "
" ignoring " , dev_name ( dev ) ) ;
continue ;
}
if ( found )
continue ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( li , & _labellers ) {
2007-04-23 22:21:01 +04:00
if ( li - > l - > ops - > can_handle ( li - > l , ( char * ) lh ,
sector + scan_sector ) ) {
2002-11-18 17:04:08 +03:00
log_very_verbose ( " %s: %s label detected " ,
dev_name ( dev ) , li - > name ) ;
if ( found ) {
log_error ( " Ignoring additional label "
" on %s at sector % " PRIu64 ,
2007-04-23 22:21:01 +04:00
dev_name ( dev ) ,
sector + scan_sector ) ;
2002-11-18 17:04:08 +03:00
continue ;
}
r = li - > l ;
memcpy ( buf , lh , LABEL_SIZE ) ;
if ( label_sector )
2007-04-23 22:21:01 +04:00
* label_sector = sector + scan_sector ;
2002-11-18 17:04:08 +03:00
found = 1 ;
break ;
}
}
}
2005-03-22 01:40:35 +03:00
out :
if ( ! found ) {
2008-01-30 02:45:48 +03:00
if ( ( info = info_from_pvid ( dev - > pvid , 0 ) ) )
2008-02-06 18:47:28 +03:00
lvmcache_update_vgname_and_id ( info , info - > fmt - > orphan_vg_name ,
info - > fmt - > orphan_vg_name ,
2006-04-13 21:32:24 +04:00
0 , NULL ) ;
2002-11-18 17:04:08 +03:00
log_very_verbose ( " %s: No label detected " , dev_name ( dev ) ) ;
2005-03-22 01:40:35 +03:00
}
2002-11-18 17:04:08 +03:00
return r ;
2001-12-11 14:42:30 +03:00
}
2002-11-18 17:04:08 +03:00
/* FIXME Also wipe associated metadata area headers? */
2002-01-11 13:43:32 +03:00
int label_remove ( struct device * dev )
2001-12-11 14:42:30 +03:00
{
2006-12-01 02:11:42 +03:00
char buf [ LABEL_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
char readbuf [ LABEL_SCAN_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-11-18 17:04:08 +03:00
int r = 1 ;
uint64_t sector ;
int wipe ;
struct labeller_i * li ;
struct label_header * lh ;
memset ( buf , 0 , LABEL_SIZE ) ;
2002-01-10 18:01:58 +03:00
2002-11-18 17:04:08 +03:00
log_very_verbose ( " Scanning for labels to wipe from %s " , dev_name ( dev ) ) ;
2008-01-30 16:19:47 +03:00
if ( ! dev_open ( dev ) )
return_0 ;
2001-12-11 14:42:30 +03:00
2003-07-05 02:34:56 +04:00
/*
* We flush the device just in case someone is stupid
* enough to be trying to import an open pv into lvm .
*/
dev_flush ( dev ) ;
if ( ! dev_read ( dev , UINT64_C ( 0 ) , LABEL_SCAN_SIZE , readbuf ) ) {
2002-11-18 17:04:08 +03:00
log_debug ( " %s: Failed to read label area " , dev_name ( dev ) ) ;
goto out ;
}
/* Scan first few sectors for anything looking like a label */
for ( sector = 0 ; sector < LABEL_SCAN_SECTORS ;
sector + = LABEL_SIZE > > SECTOR_SHIFT ) {
lh = ( struct label_header * ) ( readbuf +
( sector < < SECTOR_SHIFT ) ) ;
wipe = 0 ;
2006-05-10 01:23:51 +04:00
if ( ! strncmp ( ( char * ) lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
2002-11-18 17:04:08 +03:00
if ( xlate64 ( lh - > sector_xl ) = = sector )
wipe = 1 ;
} else {
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( li , & _labellers ) {
2002-11-18 17:04:08 +03:00
if ( li - > l - > ops - > can_handle ( li - > l , ( char * ) lh ,
sector ) ) {
wipe = 1 ;
break ;
}
}
}
if ( wipe ) {
log_info ( " %s: Wiping label at sector % " PRIu64 ,
dev_name ( dev ) , sector ) ;
2003-07-05 02:34:56 +04:00
if ( ! dev_write ( dev , sector < < SECTOR_SHIFT , LABEL_SIZE ,
buf ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " Failed to remove label from %s at "
" sector % " PRIu64 , dev_name ( dev ) ,
sector ) ;
r = 0 ;
}
}
}
out :
if ( ! dev_close ( dev ) )
stack ;
return r ;
2001-12-11 14:42:30 +03:00
}
2007-04-23 22:21:01 +04:00
int label_read ( struct device * dev , struct label * * result ,
uint64_t scan_sector )
2001-12-11 14:42:30 +03:00
{
2006-12-01 02:11:42 +03:00
char buf [ LABEL_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-11-18 17:04:08 +03:00
struct labeller * l ;
uint64_t sector ;
2006-04-11 17:55:59 +04:00
struct lvmcache_info * info ;
int r = 0 ;
2001-12-11 14:42:30 +03:00
2008-01-30 02:45:48 +03:00
if ( ( info = info_from_pvid ( dev - > pvid , 1 ) ) ) {
log_debug ( " Using cached label for %s " , dev_name ( dev ) ) ;
* result = info - > label ;
return 1 ;
}
2006-04-11 17:55:59 +04:00
if ( ! dev_open ( dev ) ) {
2002-11-18 17:04:08 +03:00
stack ;
2006-04-11 17:55:59 +04:00
2008-01-30 02:45:48 +03:00
if ( ( info = info_from_pvid ( dev - > pvid , 0 ) ) )
2008-02-06 18:47:28 +03:00
lvmcache_update_vgname_and_id ( info , info - > fmt - > orphan_vg_name ,
info - > fmt - > orphan_vg_name ,
2006-04-13 21:32:24 +04:00
0 , NULL ) ;
2006-04-11 17:55:59 +04:00
2006-06-09 02:15:49 +04:00
return r ;
2001-12-11 14:42:30 +03:00
}
2007-04-23 22:21:01 +04:00
if ( ! ( l = _find_labeller ( dev , buf , & sector , scan_sector ) ) )
2008-08-28 17:28:13 +04:00
goto out ;
2006-04-11 17:55:59 +04:00
2005-03-10 23:23:36 +03:00
if ( ( r = ( l - > ops - > read ) ( l , dev , buf , result ) ) & & result & & * result )
2002-11-18 17:04:08 +03:00
( * result ) - > sector = sector ;
2006-04-11 17:55:59 +04:00
out :
if ( ! dev_close ( dev ) )
stack ;
2002-11-18 17:04:08 +03:00
return r ;
}
/* Caller may need to use label_get_handler to create label struct! */
int label_write ( struct device * dev , struct label * label )
{
2006-12-01 02:11:42 +03:00
char buf [ LABEL_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-11-18 17:04:08 +03:00
struct label_header * lh = ( struct label_header * ) buf ;
int r = 1 ;
2003-08-27 01:12:06 +04:00
if ( ! label - > labeller - > ops - > write ) {
log_err ( " Label handler does not support label writes " ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
if ( ( LABEL_SIZE + ( label - > sector < < SECTOR_SHIFT ) ) > LABEL_SCAN_SIZE ) {
log_error ( " Label sector % " PRIu64 " beyond range (%ld) " ,
label - > sector , LABEL_SCAN_SECTORS ) ;
return 0 ;
}
memset ( buf , 0 , LABEL_SIZE ) ;
2006-05-10 01:23:51 +04:00
strncpy ( ( char * ) lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ;
2002-11-18 17:04:08 +03:00
lh - > sector_xl = xlate64 ( label - > sector ) ;
lh - > offset_xl = xlate32 ( sizeof ( * lh ) ) ;
2008-01-30 16:19:47 +03:00
if ( ! ( label - > labeller - > ops - > write ) ( label , buf ) )
return_0 ;
2002-11-18 17:04:08 +03:00
lh - > crc_xl = xlate32 ( calc_crc ( INITIAL_CRC , & lh - > offset_xl , LABEL_SIZE -
2008-01-31 15:35:31 +03:00
( ( uintptr_t ) & lh - > offset_xl - ( uintptr_t ) lh ) ) ) ;
2002-11-18 17:04:08 +03:00
2008-01-30 16:19:47 +03:00
if ( ! dev_open ( dev ) )
return_0 ;
2002-11-18 17:04:08 +03:00
2009-02-21 02:19:28 +03:00
log_info ( " %s: Writing label to sector % " PRIu64 " with stored offset % "
PRIu32 " . " , dev_name ( dev ) , label - > sector ,
xlate32 ( lh - > offset_xl ) ) ;
2003-07-05 02:34:56 +04:00
if ( ! dev_write ( dev , label - > sector < < SECTOR_SHIFT , LABEL_SIZE , buf ) ) {
2002-11-18 17:04:08 +03:00
log_debug ( " Failed to write label to %s " , dev_name ( dev ) ) ;
r = 0 ;
}
2003-07-05 02:34:56 +04:00
if ( ! dev_close ( dev ) )
2002-11-18 17:04:08 +03:00
stack ;
return r ;
2001-12-11 14:42:30 +03:00
}
2006-04-11 17:55:59 +04:00
/* Unused */
2002-01-11 13:43:32 +03:00
int label_verify ( struct device * dev )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller * l ;
2006-12-01 02:11:42 +03:00
char buf [ LABEL_SIZE ] __attribute ( ( aligned ( 8 ) ) ) ;
2002-11-18 17:04:08 +03:00
uint64_t sector ;
2006-04-11 17:55:59 +04:00
struct lvmcache_info * info ;
int r = 0 ;
2001-12-11 14:42:30 +03:00
2006-04-11 17:55:59 +04:00
if ( ! dev_open ( dev ) ) {
2008-01-30 02:45:48 +03:00
if ( ( info = info_from_pvid ( dev - > pvid , 0 ) ) )
2008-02-06 18:47:28 +03:00
lvmcache_update_vgname_and_id ( info , info - > fmt - > orphan_vg_name ,
info - > fmt - > orphan_vg_name ,
2006-04-13 21:32:24 +04:00
0 , NULL ) ;
2006-04-11 17:55:59 +04:00
2006-11-14 18:03:07 +03:00
return_0 ;
2001-12-11 14:42:30 +03:00
}
2007-04-23 22:21:01 +04:00
if ( ! ( l = _find_labeller ( dev , buf , & sector , UINT64_C ( 0 ) ) ) )
2008-08-28 17:28:13 +04:00
goto out ;
2006-04-11 17:55:59 +04:00
r = l - > ops - > verify ? l - > ops - > verify ( l , buf , sector ) : 1 ;
out :
if ( ! dev_close ( dev ) )
stack ;
return r ;
2001-12-11 19:49:40 +03:00
}
2002-11-18 17:04:08 +03:00
void label_destroy ( struct label * label )
2001-12-11 19:49:40 +03:00
{
2002-11-18 17:04:08 +03:00
label - > labeller - > ops - > destroy_label ( label - > labeller , label ) ;
2005-10-17 03:03:59 +04:00
dm_free ( label ) ;
2002-11-18 17:04:08 +03:00
}
struct label * label_create ( struct labeller * labeller )
{
struct label * label ;
2005-10-17 03:03:59 +04:00
if ( ! ( label = dm_malloc ( sizeof ( * label ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " label allocaction failed " ) ;
return NULL ;
}
memset ( label , 0 , sizeof ( * label ) ) ;
label - > labeller = labeller ;
labeller - > ops - > initialise_label ( labeller , label ) ;
return label ;
2001-12-11 19:49:40 +03:00
}