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 .
2018-01-05 05:31:28 +03:00
* Copyright ( C ) 2004 - 2018 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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 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"
2012-02-23 17:11:07 +04:00
# include "lvmetad.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
2018-01-02 16:37:12 +03:00
static struct dm_pool * _labeller_mem ;
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-10 18:01:58 +03:00
int label_init ( void )
{
2018-01-02 16:37:12 +03:00
if ( ! ( _labeller_mem = dm_pool_create ( " label scan " , 128 ) ) ) {
log_error ( " Labeller pool creation failed. " ) ;
return 0 ;
}
2008-11-04 01:14:30 +03:00
dm_list_init ( & _labellers ) ;
2018-01-02 16:37:12 +03:00
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
{
2012-02-08 15:12:18 +04:00
struct labeller_i * li , * tli ;
2002-01-10 18:01:58 +03:00
2012-02-08 15:12:18 +04:00
dm_list_iterate_items_safe ( li , tli , & _labellers ) {
dm_list_del ( & li - > list ) ;
2002-11-18 17:04:08 +03:00
li - > l - > ops - > destroy ( li - > l ) ;
2012-02-08 15:12:18 +04:00
dm_free ( 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 ) ;
2018-01-02 16:37:12 +03:00
dm_pool_destroy ( _labeller_mem ) ;
2001-12-14 16:15:15 +03:00
}
2013-07-29 17:58:18 +04:00
int label_register_handler ( struct labeller * handler )
2001-12-11 14:42:30 +03:00
{
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
2013-07-29 17:58:18 +04:00
if ( ! ( li = _alloc_li ( handler - > fmt - > name , handler ) ) )
2008-01-30 16:19:47 +03:00
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
2015-03-19 02:43:02 +03:00
static void _update_lvmcache_orphan ( struct lvmcache_info * info )
{
struct lvmcache_vgsummary vgsummary_orphan = {
. vgname = lvmcache_fmt ( info ) - > orphan_vg_name ,
} ;
memcpy ( & vgsummary_orphan . vgid , lvmcache_fmt ( info ) - > orphan_vg_name , strlen ( lvmcache_fmt ( info ) - > orphan_vg_name ) ) ;
2015-08-18 14:48:53 +03:00
if ( ! lvmcache_update_vgname_and_id ( info , & vgsummary_orphan ) )
stack ;
2015-03-19 02:43:02 +03:00
}
2018-01-02 19:01:10 +03:00
struct find_labeller_params {
struct device * dev ;
uint64_t scan_sector ; /* Sector to be scanned */
uint64_t label_sector ; /* Sector where label found */
2018-01-09 02:08:22 +03:00
lvm_callback_fn_t process_label_data_fn ;
void * process_label_data_context ;
2018-01-02 19:01:10 +03:00
struct label * * result ;
2018-01-05 05:31:28 +03:00
int ret ;
2018-01-02 19:01:10 +03:00
} ;
2018-01-07 06:43:25 +03:00
static void _set_label_read_result ( int failed , void * context , void * data )
2018-01-02 17:19:20 +03:00
{
2018-01-07 06:43:25 +03:00
struct find_labeller_params * flp = context ;
2018-01-02 19:01:10 +03:00
struct label * * result = flp - > result ;
2018-01-09 02:08:22 +03:00
if ( failed ) {
flp - > ret = 0 ;
2018-01-02 17:58:21 +03:00
goto_out ;
2018-01-09 02:08:22 +03:00
}
2018-01-02 17:58:21 +03:00
2018-01-02 17:19:20 +03:00
if ( result & & * result ) {
2018-01-02 19:01:10 +03:00
( * result ) - > dev = flp - > dev ;
( * result ) - > sector = flp - > label_sector ;
2018-01-02 17:19:20 +03:00
}
2018-01-02 17:58:21 +03:00
out :
2018-01-02 19:01:10 +03:00
if ( ! dev_close ( flp - > dev ) )
2018-01-02 17:58:21 +03:00
stack ;
2018-01-09 02:08:22 +03:00
if ( flp - > ret & & flp - > process_label_data_fn )
flp - > process_label_data_fn ( 0 , flp - > process_label_data_context , NULL ) ;
2018-01-02 17:19:20 +03:00
}
2018-01-05 17:24:10 +03:00
static void _find_labeller ( int failed , void * context , void * data )
2001-12-11 14:42:30 +03:00
{
2018-01-05 17:24:10 +03:00
struct find_labeller_params * flp = context ;
char * readbuf = data ;
2018-01-02 19:01:10 +03:00
struct device * dev = flp - > dev ;
uint64_t scan_sector = flp - > scan_sector ;
struct label * * result = flp - > result ;
2018-01-02 17:58:21 +03:00
char labelbuf [ LABEL_SIZE ] __attribute__ ( ( aligned ( 8 ) ) ) ;
2002-01-10 18:01:58 +03:00
struct labeller_i * li ;
2018-01-02 17:58:21 +03:00
struct labeller * l = NULL ; /* Set when a labeller claims the label */
2002-11-18 17:04:08 +03:00
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 ;
2018-01-05 05:31:28 +03:00
2018-01-09 02:08:22 +03:00
if ( failed ) {
log_debug_devs ( " %s: Failed to read label area " , dev_name ( dev ) ) ;
_set_label_read_result ( 1 , flp , NULL ) ;
return ;
}
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 ) {
2018-01-02 19:01:10 +03:00
lh = ( struct label_header * ) ( readbuf + ( sector < < SECTOR_SHIFT ) ) ;
2002-11-18 17:04:08 +03:00
2006-05-10 01:23:51 +04:00
if ( ! strncmp ( ( char * ) lh - > id , LABEL_ID , sizeof ( lh - > id ) ) ) {
2018-01-02 17:58:21 +03:00
if ( l ) {
2002-11-18 17:04:08 +03:00
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 ) {
2016-11-01 13:02:01 +03:00
log_very_verbose ( " %s: Label for sector % " PRIu64
" found at sector % " PRIu64
" - ignoring " , dev_name ( dev ) ,
( uint64_t ) xlate64 ( lh - > sector_xl ) ,
sector + scan_sector ) ;
2002-11-18 17:04:08 +03:00
continue ;
}
2010-09-27 23:09:34 +04:00
if ( calc_crc ( INITIAL_CRC , ( uint8_t * ) & lh - > offset_xl , LABEL_SIZE -
( ( uint8_t * ) & lh - > offset_xl - ( uint8_t * ) lh ) ) ! =
2002-11-18 17:04:08 +03:00
xlate32 ( lh - > crc_xl ) ) {
2016-11-01 13:02:01 +03:00
log_very_verbose ( " Label checksum incorrect on %s - "
" ignoring " , dev_name ( dev ) ) ;
2002-11-18 17:04:08 +03:00
continue ;
}
2018-01-02 17:58:21 +03:00
if ( l )
2002-11-18 17:04:08 +03:00
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 ) ) {
2011-06-01 19:30:36 +04:00
log_very_verbose ( " %s: %s label detected at "
" sector % " PRIu64 ,
dev_name ( dev ) , li - > name ,
sector + scan_sector ) ;
2018-01-02 17:58:21 +03:00
if ( l ) {
2002-11-18 17:04:08 +03:00
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 ;
}
2017-12-19 04:12:18 +03:00
memcpy ( labelbuf , lh , LABEL_SIZE ) ;
2018-01-02 19:01:10 +03:00
flp - > label_sector = sector + scan_sector ;
2018-01-02 17:58:21 +03:00
l = li - > l ;
2002-11-18 17:04:08 +03:00
break ;
}
}
}
2018-01-02 17:58:21 +03:00
if ( ! l ) {
2016-06-06 22:04:17 +03:00
if ( ( info = lvmcache_info_from_pvid ( dev - > pvid , dev , 0 ) ) )
2015-03-19 02:43:02 +03:00
_update_lvmcache_orphan ( info ) ;
2002-11-18 17:04:08 +03:00
log_very_verbose ( " %s: No label detected " , dev_name ( dev ) ) ;
2018-01-05 05:31:28 +03:00
flp - > ret = 0 ;
2018-01-07 06:43:25 +03:00
_set_label_read_result ( 1 , flp , NULL ) ;
2018-01-02 17:58:21 +03:00
} else
2018-01-09 02:08:22 +03:00
( void ) ( l - > ops - > read ) ( l , dev , labelbuf , result , & _set_label_read_result , flp ) ;
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
{
2017-12-19 04:12:18 +03:00
char labelbuf [ LABEL_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 ;
2017-08-11 22:32:04 +03:00
struct lvmcache_info * info ;
2018-01-10 00:13:46 +03:00
char * readbuf = NULL ;
2002-11-18 17:04:08 +03:00
2017-12-19 04:12:18 +03:00
memset ( labelbuf , 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 ) ;
2018-01-10 00:13:46 +03:00
if ( ! ( readbuf = dev_read ( dev , UINT64_C ( 0 ) , LABEL_SCAN_SIZE , DEV_IO_LABEL ) ) ) {
2013-01-08 02:30:29 +04:00
log_debug_devs ( " %s: Failed to read label area " , dev_name ( dev ) ) ;
2002-11-18 17:04:08 +03:00
goto out ;
}
/* Scan first few sectors for anything looking like a label */
for ( sector = 0 ; sector < LABEL_SCAN_SECTORS ;
sector + = LABEL_SIZE > > SECTOR_SHIFT ) {
2018-01-10 00:13:46 +03:00
lh = ( struct label_header * ) ( readbuf + ( sector < < SECTOR_SHIFT ) ) ;
2002-11-18 17:04:08 +03:00
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 ) {
2016-11-01 13:02:01 +03:00
log_very_verbose ( " %s: Wiping label at sector % " PRIu64 ,
dev_name ( dev ) , sector ) ;
2017-12-19 04:12:18 +03:00
if ( dev_write ( dev , sector < < SECTOR_SHIFT , LABEL_SIZE , DEV_IO_LABEL , labelbuf ) ) {
2017-08-11 22:32:04 +03:00
/* Also remove the PV record from cache. */
info = lvmcache_info_from_pvid ( dev - > pvid , dev , 0 ) ;
if ( info )
lvmcache_del ( info ) ;
} else {
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
}
2018-01-09 02:08:22 +03:00
static int _label_read ( struct device * dev , uint64_t scan_sector , struct label * * result ,
lvm_callback_fn_t process_label_data_fn , void * process_label_data_context )
2001-12-11 14:42:30 +03:00
{
2006-04-11 17:55:59 +04:00
struct lvmcache_info * info ;
2018-01-02 19:01:10 +03:00
struct find_labeller_params * flp ;
2001-12-11 14:42:30 +03:00
2016-06-06 22:04:17 +03:00
if ( ( info = lvmcache_info_from_pvid ( dev - > pvid , dev , 1 ) ) ) {
log_debug_devs ( " Reading label from lvmcache for %s " , dev_name ( dev ) ) ;
2012-02-23 17:11:07 +04:00
* result = lvmcache_get_label ( info ) ;
2018-01-09 02:08:22 +03:00
if ( process_label_data_fn )
process_label_data_fn ( 0 , process_label_data_context , NULL ) ;
2008-01-30 02:45:48 +03:00
return 1 ;
}
2018-01-02 19:01:10 +03:00
if ( ! ( flp = dm_pool_zalloc ( _labeller_mem , sizeof * flp ) ) ) {
log_error ( " find_labeller_params allocation failed. " ) ;
return 0 ;
}
flp - > dev = dev ;
flp - > scan_sector = scan_sector ;
flp - > result = result ;
2018-01-09 02:08:22 +03:00
flp - > process_label_data_fn = process_label_data_fn ;
flp - > process_label_data_context = process_label_data_context ;
2018-01-05 05:31:28 +03:00
flp - > ret = 1 ;
2018-01-02 19:01:10 +03:00
/* Ensure result is always wiped as a precaution */
if ( result )
* result = NULL ;
2016-06-06 22:04:17 +03:00
log_debug_devs ( " Reading label from device %s " , dev_name ( dev ) ) ;
2011-05-28 13:48:14 +04:00
if ( ! dev_open_readonly ( dev ) ) {
2002-11-18 17:04:08 +03:00
stack ;
2006-04-11 17:55:59 +04:00
2016-06-06 22:04:17 +03:00
if ( ( info = lvmcache_info_from_pvid ( dev - > pvid , dev , 0 ) ) )
2015-03-19 02:43:02 +03:00
_update_lvmcache_orphan ( info ) ;
2006-04-11 17:55:59 +04:00
2018-01-02 17:58:21 +03:00
return 0 ;
2001-12-11 14:42:30 +03:00
}
2018-01-09 02:08:22 +03:00
if ( ! ( dev_read_callback ( dev , scan_sector < < SECTOR_SHIFT , LABEL_SCAN_SIZE , DEV_IO_LABEL , _find_labeller , flp ) ) ) {
2018-01-02 19:01:10 +03:00
log_debug_devs ( " %s: Failed to read label area " , dev_name ( dev ) ) ;
2018-01-07 06:43:25 +03:00
_set_label_read_result ( 1 , flp , NULL ) ;
2018-01-02 19:01:10 +03:00
return 0 ;
}
2018-01-05 05:31:28 +03:00
return flp - > ret ;
2002-11-18 17:04:08 +03:00
}
2018-01-02 17:19:20 +03:00
int label_read ( struct device * dev , struct label * * result , uint64_t scan_sector )
{
2018-01-09 02:08:22 +03:00
return _label_read ( dev , scan_sector , result , NULL , NULL ) ;
}
int label_read_callback ( struct dm_pool * mem , struct device * dev , uint64_t scan_sector ,
lvm_callback_fn_t process_label_data_fn , void * process_label_data_context )
{
struct label * * result ; /* FIXME Eliminate this */
2018-01-09 04:50:23 +03:00
if ( ! ( result = dm_pool_zalloc ( mem , sizeof ( * result ) ) ) ) {
2018-01-09 02:08:22 +03:00
log_error ( " Couldn't allocate memory for internal result pointer. " ) ;
return 0 ;
}
return _label_read ( dev , scan_sector , result , process_label_data_fn , process_label_data_context ) ;
2018-01-02 17:19:20 +03:00
}
2002-11-18 17:04:08 +03:00
/* Caller may need to use label_get_handler to create label struct! */
int label_write ( struct device * dev , struct label * label )
{
2010-07-09 19:34:40 +04: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 ) {
2009-07-16 00:02:46 +04:00
log_error ( " Label handler does not support label writes " ) ;
2003-08-27 01:12:06 +04:00
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
2010-09-27 23:09:34 +04:00
lh - > crc_xl = xlate32 ( calc_crc ( INITIAL_CRC , ( uint8_t * ) & lh - > offset_xl , LABEL_SIZE -
( ( uint8_t * ) & lh - > offset_xl - ( uint8_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
2016-11-01 13:02:01 +03:00
log_very_verbose ( " %s: Writing label to sector % " PRIu64 " with stored offset % "
PRIu32 " . " , dev_name ( dev ) , label - > sector ,
xlate32 ( lh - > offset_xl ) ) ;
2017-12-05 02:18:56 +03:00
if ( ! dev_write ( dev , label - > sector < < SECTOR_SHIFT , LABEL_SIZE , DEV_IO_LABEL , buf ) ) {
2013-01-08 02:30:29 +04:00
log_debug_devs ( " Failed to write label to %s " , dev_name ( dev ) ) ;
2002-11-18 17:04:08 +03:00
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-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 ;
2010-10-01 01:06:50 +04:00
if ( ! ( label = dm_zalloc ( sizeof ( * label ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " label allocaction failed " ) ;
return NULL ;
}
label - > labeller = labeller ;
labeller - > ops - > initialise_label ( labeller , label ) ;
return label ;
2001-12-11 19:49:40 +03:00
}