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 .
* Copyright ( C ) 2004 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
* of the GNU General Public License v .2 .
*
* You should have received a copy of the GNU General Public License
* 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-01-10 18:01:58 +03:00
# include "list.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 {
struct 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
2002-01-10 18:01:58 +03:00
static struct 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
2002-01-10 18:01:58 +03:00
if ( ! ( li = dbg_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
{
2002-01-10 18:01:58 +03:00
dbg_free ( li ) ;
}
2001-12-11 14:42:30 +03:00
2002-01-10 18:01:58 +03:00
int label_init ( void )
{
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
{
2002-01-10 18:01:58 +03:00
struct 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 ;
2002-01-11 13:43:32 +03:00
li = 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
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 ;
if ( ! ( li = _alloc_li ( name , handler ) ) ) {
2001-12-11 14:42:30 +03:00
stack ;
2001-12-11 19:49:40 +03:00
return 0 ;
2001-12-11 14:42:30 +03:00
}
2002-01-10 18:01:58 +03:00
list_add ( & _labellers , & li - > list ) ;
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 list * lih ;
struct labeller_i * li ;
2001-12-11 14:42:30 +03:00
2002-04-24 22:20:51 +04:00
list_iterate ( lih , & _labellers ) {
2002-01-10 18:01:58 +03:00
li = list_item ( lih , struct labeller_i ) ;
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 ,
uint64_t * label_sector )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct list * lih ;
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 ;
char readbuf [ LABEL_SCAN_SIZE ] ;
2001-12-11 14:42:30 +03:00
2003-07-05 02:34:56 +04:00
if ( ! dev_open ( dev ) ) {
2002-11-18 17:04:08 +03:00
stack ;
2005-03-22 01:40:35 +03:00
if ( ( info = info_from_pvid ( dev - > pvid ) ) )
lvmcache_update_vgname ( info , ORPHAN ) ;
2002-11-18 17:04:08 +03:00
return NULL ;
2002-01-10 18:01:58 +03:00
}
2001-12-11 14:42:30 +03:00
2003-07-05 02:34:56 +04:00
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 a valid label */
for ( sector = 0 ; sector < LABEL_SCAN_SECTORS ;
sector + = LABEL_SIZE > > SECTOR_SHIFT ) {
lh = ( struct label_header * ) ( readbuf +
( sector < < SECTOR_SHIFT ) ) ;
if ( ! strncmp ( lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
if ( found ) {
log_error ( " Ignoring additional label on %s at "
" sector % " PRIu64 , dev_name ( dev ) ,
sector ) ;
}
if ( xlate64 ( lh - > sector_xl ) ! = sector ) {
log_info ( " %s: Label for sector % " PRIu64
" found at sector % " PRIu64
" - ignoring " , dev_name ( dev ) ,
xlate64 ( lh - > sector_xl ) , sector ) ;
continue ;
}
if ( calc_crc ( INITIAL_CRC , & lh - > offset_xl , LABEL_SIZE -
( ( void * ) & lh - > offset_xl - ( void * ) lh ) ) ! =
xlate32 ( lh - > crc_xl ) ) {
log_info ( " Label checksum incorrect on %s - "
" ignoring " , dev_name ( dev ) ) ;
continue ;
}
if ( found )
continue ;
}
list_iterate ( lih , & _labellers ) {
li = list_item ( lih , struct labeller_i ) ;
if ( li - > l - > ops - > can_handle ( li - > l , ( char * ) lh , sector ) ) {
log_very_verbose ( " %s: %s label detected " ,
dev_name ( dev ) , li - > name ) ;
if ( found ) {
log_error ( " Ignoring additional label "
" on %s at sector % " PRIu64 ,
dev_name ( dev ) , sector ) ;
continue ;
}
r = li - > l ;
memcpy ( buf , lh , LABEL_SIZE ) ;
if ( label_sector )
* label_sector = sector ;
found = 1 ;
break ;
}
}
}
2005-03-22 01:40:35 +03:00
out :
if ( ! found ) {
if ( ( info = info_from_pvid ( dev - > pvid ) ) )
lvmcache_update_vgname ( info , ORPHAN ) ;
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
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
}
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
{
2002-11-18 17:04:08 +03:00
char buf [ LABEL_SIZE ] ;
char readbuf [ LABEL_SCAN_SIZE ] ;
int r = 1 ;
uint64_t sector ;
int wipe ;
struct list * lih ;
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 ) ) ;
2003-07-05 02:34:56 +04:00
if ( ! dev_open ( dev ) ) {
2002-01-10 18:01:58 +03:00
stack ;
return 0 ;
2001-12-11 19:49:40 +03:00
}
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 ;
if ( ! strncmp ( lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
if ( xlate64 ( lh - > sector_xl ) = = sector )
wipe = 1 ;
} else {
list_iterate ( lih , & _labellers ) {
li = list_item ( lih , struct labeller_i ) ;
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
}
2002-11-18 17:04:08 +03:00
/* FIXME Avoid repeated re-reading if cache lock held */
2002-01-11 13:43:32 +03:00
int label_read ( struct device * dev , struct label * * result )
2001-12-11 14:42:30 +03:00
{
2002-11-18 17:04:08 +03:00
char buf [ LABEL_SIZE ] ;
struct labeller * l ;
uint64_t sector ;
2002-01-15 13:24:48 +03:00
int r ;
2001-12-11 14:42:30 +03:00
2002-11-18 17:04:08 +03:00
if ( ! ( l = _find_labeller ( dev , buf , & sector ) ) ) {
stack ;
return 0 ;
2001-12-11 14:42:30 +03: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 ;
return r ;
}
/* Caller may need to use label_get_handler to create label struct! */
int label_write ( struct device * dev , struct label * label )
{
char buf [ LABEL_SIZE ] ;
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 ) ;
strncpy ( lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ;
lh - > sector_xl = xlate64 ( label - > sector ) ;
lh - > offset_xl = xlate32 ( sizeof ( * lh ) ) ;
2005-03-10 23:23:36 +03:00
if ( ! ( label - > labeller - > ops - > write ) ( label , buf ) ) {
2003-08-27 01:12:06 +04:00
stack ;
2002-11-18 17:04:08 +03:00
return 0 ;
2003-08-27 01:12:06 +04:00
}
2002-11-18 17:04:08 +03:00
lh - > crc_xl = xlate32 ( calc_crc ( INITIAL_CRC , & lh - > offset_xl , LABEL_SIZE -
( ( void * ) & lh - > offset_xl - ( void * ) lh ) ) ) ;
2003-07-05 02:34:56 +04:00
if ( ! dev_open ( dev ) ) {
2002-11-18 17:04:08 +03:00
stack ;
return 0 ;
}
log_info ( " %s: Writing label to sector % " PRIu64 , dev_name ( dev ) ,
label - > sector ) ;
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
}
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 ;
2002-11-18 17:04:08 +03:00
char buf [ LABEL_SIZE ] ;
uint64_t sector ;
2001-12-11 14:42:30 +03:00
2002-11-18 17:04:08 +03:00
if ( ! ( l = _find_labeller ( dev , buf , & sector ) ) ) {
2001-12-11 14:42:30 +03:00
stack ;
2002-01-10 18:01:58 +03:00
return 0 ;
2001-12-11 14:42:30 +03:00
}
2003-08-27 01:12:06 +04:00
return ( ( l - > ops - > verify ) ? l - > ops - > verify ( l , buf , sector ) : 1 ) ;
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 ) ;
dbg_free ( label ) ;
}
struct label * label_create ( struct labeller * labeller )
{
struct label * label ;
if ( ! ( label = dbg_malloc ( sizeof ( * label ) ) ) ) {
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
}