2002-11-18 17:04:08 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2002 - 2004 Sistina Software , Inc . All rights reserved .
2007-08-21 00:55:30 +04:00
* Copyright ( C ) 2004 - 2006 Red Hat , Inc . All rights reserved .
2002-11-18 17:04:08 +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
2002-11-18 17:04:08 +03:00
*/
# include "lib.h"
# include "format-text.h"
# include "layout.h"
# include "label.h"
# include "xlate.h"
2004-05-05 01:25:57 +04:00
# include "lvmcache.h"
2002-11-18 17:04:08 +03:00
# include <sys/stat.h>
# include <fcntl.h>
2010-07-09 19:34:40 +04:00
static int _text_can_handle ( struct labeller * l __attribute__ ( ( unused ) ) ,
2006-12-01 02:11:42 +03:00
void * buf ,
2010-07-09 19:34:40 +04:00
uint64_t sector __attribute__ ( ( unused ) ) )
2002-11-18 17:04:08 +03:00
{
struct label_header * lh = ( struct label_header * ) buf ;
2006-05-10 01:23:51 +04:00
if ( ! strncmp ( ( char * ) lh - > type , LVM2_LABEL , sizeof ( lh - > type ) ) )
2002-11-18 17:04:08 +03:00
return 1 ;
return 0 ;
}
2006-12-01 02:11:42 +03:00
static int _text_write ( struct label * label , void * buf )
2002-11-18 17:04:08 +03:00
{
struct label_header * lh = ( struct label_header * ) buf ;
struct pv_header * pvhdr ;
2003-07-05 02:34:56 +04:00
struct lvmcache_info * info ;
2002-11-18 17:04:08 +03:00
struct disk_locn * pvh_dlocn_xl ;
struct metadata_area * mda ;
struct mda_context * mdac ;
struct data_area_list * da ;
2010-07-09 19:34:40 +04:00
char buffer [ 64 ] __attribute__ ( ( aligned ( 8 ) ) ) ;
2009-02-21 02:19:28 +03:00
int da1 , mda1 , mda2 ;
2002-11-18 17:04:08 +03:00
/* FIXME Move to where label is created */
strncpy ( label - > type , LVM2_LABEL , sizeof ( label - > type ) ) ;
2006-05-10 01:23:51 +04:00
strncpy ( ( char * ) lh - > type , label - > type , sizeof ( label - > type ) ) ;
2002-11-18 17:04:08 +03:00
2011-02-18 17:34:41 +03:00
pvhdr = ( struct pv_header * ) ( ( char * ) buf + xlate32 ( lh - > offset_xl ) ) ;
2003-07-05 02:34:56 +04:00
info = ( struct lvmcache_info * ) label - > info ;
2002-11-18 17:04:08 +03:00
pvhdr - > device_size_xl = xlate64 ( info - > device_size ) ;
memcpy ( pvhdr - > pv_uuid , & info - > dev - > pvid , sizeof ( struct id ) ) ;
2009-02-21 02:19:28 +03:00
if ( ! id_write_format ( ( const struct id * ) pvhdr - > pv_uuid , buffer ,
sizeof ( buffer ) ) ) {
stack ;
buffer [ 0 ] = ' \0 ' ;
}
2002-11-18 17:04:08 +03:00
pvh_dlocn_xl = & pvhdr - > disk_areas_xl [ 0 ] ;
/* List of data areas (holding PEs) */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( da , & info - > das ) {
2002-11-18 17:04:08 +03:00
pvh_dlocn_xl - > offset = xlate64 ( da - > disk_locn . offset ) ;
pvh_dlocn_xl - > size = xlate64 ( da - > disk_locn . size ) ;
pvh_dlocn_xl + + ;
}
/* NULL-termination */
2003-08-20 19:48:27 +04:00
pvh_dlocn_xl - > offset = xlate64 ( UINT64_C ( 0 ) ) ;
pvh_dlocn_xl - > size = xlate64 ( UINT64_C ( 0 ) ) ;
2002-11-18 17:04:08 +03:00
pvh_dlocn_xl + + ;
/* List of metadata area header locations */
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( mda , & info - > mdas ) {
2002-11-18 17:04:08 +03:00
mdac = ( struct mda_context * ) mda - > metadata_locn ;
if ( mdac - > area . dev ! = info - > dev )
continue ;
pvh_dlocn_xl - > offset = xlate64 ( mdac - > area . start ) ;
pvh_dlocn_xl - > size = xlate64 ( mdac - > area . size ) ;
pvh_dlocn_xl + + ;
}
/* NULL-termination */
2003-08-20 19:48:27 +04:00
pvh_dlocn_xl - > offset = xlate64 ( UINT64_C ( 0 ) ) ;
pvh_dlocn_xl - > size = xlate64 ( UINT64_C ( 0 ) ) ;
2002-11-18 17:04:08 +03:00
2009-02-21 02:19:28 +03:00
/* Create debug message with da and mda locations */
if ( xlate64 ( pvhdr - > disk_areas_xl [ 0 ] . offset ) | |
xlate64 ( pvhdr - > disk_areas_xl [ 0 ] . size ) )
da1 = 0 ;
else
da1 = - 1 ;
mda1 = da1 + 2 ;
mda2 = mda1 + 1 ;
if ( ! xlate64 ( pvhdr - > disk_areas_xl [ mda1 ] . offset ) & &
! xlate64 ( pvhdr - > disk_areas_xl [ mda1 ] . size ) )
mda1 = mda2 = 0 ;
else if ( ! xlate64 ( pvhdr - > disk_areas_xl [ mda2 ] . offset ) & &
! xlate64 ( pvhdr - > disk_areas_xl [ mda2 ] . size ) )
mda2 = 0 ;
log_debug ( " %s: Preparing PV label header %s size % " PRIu64 " with "
" %s%.* " PRIu64 " %s%.* " PRIu64 " %s "
" %s%.* " PRIu64 " %s%.* " PRIu64 " %s "
" %s%.* " PRIu64 " %s%.* " PRIu64 " %s " ,
dev_name ( info - > dev ) , buffer , info - > device_size ,
( da1 > - 1 ) ? " da1 ( " : " " ,
( da1 > - 1 ) ? 1 : 0 ,
( da1 > - 1 ) ? xlate64 ( pvhdr - > disk_areas_xl [ da1 ] . offset ) > > SECTOR_SHIFT : 0 ,
( da1 > - 1 ) ? " s, " : " " ,
( da1 > - 1 ) ? 1 : 0 ,
( da1 > - 1 ) ? xlate64 ( pvhdr - > disk_areas_xl [ da1 ] . size ) > > SECTOR_SHIFT : 0 ,
( da1 > - 1 ) ? " s) " : " " ,
mda1 ? " mda1 ( " : " " ,
mda1 ? 1 : 0 ,
mda1 ? xlate64 ( pvhdr - > disk_areas_xl [ mda1 ] . offset ) > > SECTOR_SHIFT : 0 ,
mda1 ? " s, " : " " ,
mda1 ? 1 : 0 ,
mda1 ? xlate64 ( pvhdr - > disk_areas_xl [ mda1 ] . size ) > > SECTOR_SHIFT : 0 ,
mda1 ? " s) " : " " ,
mda2 ? " mda2 ( " : " " ,
mda2 ? 1 : 0 ,
mda2 ? xlate64 ( pvhdr - > disk_areas_xl [ mda2 ] . offset ) > > SECTOR_SHIFT : 0 ,
mda2 ? " s, " : " " ,
mda2 ? 1 : 0 ,
mda2 ? xlate64 ( pvhdr - > disk_areas_xl [ mda2 ] . size ) > > SECTOR_SHIFT : 0 ,
mda2 ? " s) " : " " ) ;
2009-02-22 22:00:26 +03:00
if ( da1 < 0 ) {
2009-12-16 22:22:11 +03:00
log_error ( INTERNAL_ERROR " %s label header currently requires "
2009-02-22 22:00:26 +03:00
" a data area. " , dev_name ( info - > dev ) ) ;
return 0 ;
}
2002-11-18 17:04:08 +03:00
return 1 ;
}
2008-11-04 01:14:30 +03:00
int add_da ( struct dm_pool * mem , struct dm_list * das ,
2002-11-18 17:04:08 +03:00
uint64_t start , uint64_t size )
{
struct data_area_list * dal ;
if ( ! mem ) {
2005-10-17 03:03:59 +04:00
if ( ! ( dal = dm_malloc ( sizeof ( * dal ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct data_area_list allocation failed " ) ;
return 0 ;
}
} else {
2005-10-17 03:03:59 +04:00
if ( ! ( dal = dm_pool_alloc ( mem , sizeof ( * dal ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct data_area_list allocation failed " ) ;
return 0 ;
}
}
dal - > disk_locn . offset = start ;
dal - > disk_locn . size = size ;
2008-11-04 01:14:30 +03:00
dm_list_add ( das , & dal - > list ) ;
2002-11-18 17:04:08 +03:00
return 1 ;
}
2008-11-04 01:14:30 +03:00
void del_das ( struct dm_list * das )
2002-11-18 17:04:08 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * dah , * tmp ;
2002-11-18 17:04:08 +03:00
struct data_area_list * da ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_safe ( dah , tmp , das ) {
da = dm_list_item ( dah , struct data_area_list ) ;
dm_list_del ( & da - > list ) ;
2005-10-17 03:03:59 +04:00
dm_free ( da ) ;
2002-11-18 17:04:08 +03:00
}
}
2010-06-30 16:17:24 +04:00
/* FIXME: refactor this function with other mda constructor code */
2008-11-04 01:14:30 +03:00
int add_mda ( const struct format_type * fmt , struct dm_pool * mem , struct dm_list * mdas ,
2010-06-30 16:17:24 +04:00
struct device * dev , uint64_t start , uint64_t size , unsigned ignored )
2002-11-18 17:04:08 +03:00
{
/* FIXME List size restricted by pv_header SECTOR_SIZE */
struct metadata_area * mdal ;
struct mda_lists * mda_lists = ( struct mda_lists * ) fmt - > private ;
struct mda_context * mdac ;
if ( ! mem ) {
2005-10-17 03:03:59 +04:00
if ( ! ( mdal = dm_malloc ( sizeof ( struct metadata_area ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct mda_list allocation failed " ) ;
return 0 ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( mdac = dm_malloc ( sizeof ( struct mda_context ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct mda_context allocation failed " ) ;
2005-10-17 03:03:59 +04:00
dm_free ( mdal ) ;
2002-11-18 17:04:08 +03:00
return 0 ;
}
} else {
2005-10-17 03:03:59 +04:00
if ( ! ( mdal = dm_pool_alloc ( mem , sizeof ( struct metadata_area ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct mda_list allocation failed " ) ;
return 0 ;
}
2005-10-17 03:03:59 +04:00
if ( ! ( mdac = dm_pool_alloc ( mem , sizeof ( struct mda_context ) ) ) ) {
2002-11-18 17:04:08 +03:00
log_error ( " struct mda_context allocation failed " ) ;
return 0 ;
}
}
mdal - > ops = mda_lists - > raw_ops ;
mdal - > metadata_locn = mdac ;
2010-10-05 21:34:05 +04:00
mdal - > status = 0 ;
2002-11-18 17:04:08 +03:00
mdac - > area . dev = dev ;
mdac - > area . start = start ;
mdac - > area . size = size ;
2007-11-05 20:17:55 +03:00
mdac - > free_sectors = UINT64_C ( 0 ) ;
2002-11-18 17:04:08 +03:00
memset ( & mdac - > rlocn , 0 , sizeof ( mdac - > rlocn ) ) ;
2010-06-30 16:17:24 +04:00
mda_set_ignored ( mdal , ignored ) ;
2002-11-18 17:04:08 +03:00
2008-11-04 01:14:30 +03:00
dm_list_add ( mdas , & mdal - > list ) ;
2002-11-18 17:04:08 +03:00
return 1 ;
}
2008-11-04 01:14:30 +03:00
void del_mdas ( struct dm_list * mdas )
2002-11-18 17:04:08 +03:00
{
2008-11-04 01:14:30 +03:00
struct dm_list * mdah , * tmp ;
2002-11-18 17:04:08 +03:00
struct metadata_area * mda ;
2008-11-04 01:14:30 +03:00
dm_list_iterate_safe ( mdah , tmp , mdas ) {
mda = dm_list_item ( mdah , struct metadata_area ) ;
2005-10-17 03:03:59 +04:00
dm_free ( mda - > metadata_locn ) ;
2008-11-04 01:14:30 +03:00
dm_list_del ( & mda - > list ) ;
2005-10-17 03:03:59 +04:00
dm_free ( mda ) ;
2002-11-18 17:04:08 +03:00
}
}
2010-07-09 19:34:40 +04:00
static int _text_initialise_label ( struct labeller * l __attribute__ ( ( unused ) ) ,
2006-05-11 21:58:58 +04:00
struct label * label )
2002-11-18 17:04:08 +03:00
{
strncpy ( label - > type , LVM2_LABEL , sizeof ( label - > type ) ) ;
return 1 ;
}
2006-12-01 02:11:42 +03:00
static int _text_read ( struct labeller * l , struct device * dev , void * buf ,
2002-11-18 17:04:08 +03:00
struct label * * label )
{
struct label_header * lh = ( struct label_header * ) buf ;
struct pv_header * pvhdr ;
2003-07-05 02:34:56 +04:00
struct lvmcache_info * info ;
2002-11-18 17:04:08 +03:00
struct disk_locn * dlocn_xl ;
uint64_t offset ;
struct metadata_area * mda ;
2006-04-11 17:55:59 +04:00
struct id vgid ;
2002-11-18 17:04:08 +03:00
struct mda_context * mdac ;
2006-04-11 17:55:59 +04:00
const char * vgname ;
2009-11-25 01:55:55 +03:00
uint64_t vgstatus ;
2006-04-13 21:32:24 +04:00
char * creation_host ;
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
struct mda_header * mdah ;
2002-11-18 17:04:08 +03:00
2011-02-18 17:34:41 +03:00
pvhdr = ( struct pv_header * ) ( ( char * ) buf + xlate32 ( lh - > offset_xl ) ) ;
2002-11-18 17:04:08 +03:00
2008-02-06 18:47:28 +03:00
if ( ! ( info = lvmcache_add ( l , ( char * ) pvhdr - > pv_uuid , dev ,
FMT_TEXT_ORPHAN_VG_NAME ,
FMT_TEXT_ORPHAN_VG_NAME , 0 ) ) )
2006-04-11 17:55:59 +04:00
return_0 ;
2002-11-18 17:04:08 +03:00
* label = info - > label ;
info - > device_size = xlate64 ( pvhdr - > device_size_xl ) ;
if ( info - > das . n )
del_das ( & info - > das ) ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & info - > das ) ;
2002-11-18 17:04:08 +03:00
if ( info - > mdas . n )
del_mdas ( & info - > mdas ) ;
2008-11-04 01:14:30 +03:00
dm_list_init ( & info - > mdas ) ;
2002-11-18 17:04:08 +03:00
/* Data areas holding the PEs */
dlocn_xl = pvhdr - > disk_areas_xl ;
while ( ( offset = xlate64 ( dlocn_xl - > offset ) ) ) {
2006-05-11 21:58:58 +04:00
add_da ( NULL , & info - > das , offset ,
2002-11-18 17:04:08 +03:00
xlate64 ( dlocn_xl - > size ) ) ;
dlocn_xl + + ;
}
/* Metadata area headers */
dlocn_xl + + ;
while ( ( offset = xlate64 ( dlocn_xl - > offset ) ) ) {
add_mda ( info - > fmt , NULL , & info - > mdas , dev , offset ,
2010-06-30 16:17:24 +04:00
xlate64 ( dlocn_xl - > size ) , 0 ) ;
2002-11-18 17:04:08 +03:00
dlocn_xl + + ;
}
2008-11-04 01:14:30 +03:00
dm_list_iterate_items ( mda , & info - > mdas ) {
2002-11-18 17:04:08 +03:00
mdac = ( struct mda_context * ) mda - > metadata_locn ;
2011-05-28 13:48:14 +04:00
if ( ! dev_open_readonly ( mdac - > area . dev ) ) {
2010-06-29 00:31:18 +04:00
mda_set_ignored ( mda , 1 ) ;
2010-06-29 00:30:46 +04:00
stack ;
continue ;
}
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
if ( ! ( mdah = raw_read_mda_header ( info - > fmt , & mdac - > area ) ) ) {
stack ;
goto close_dev ;
}
2010-06-29 00:31:18 +04:00
mda_set_ignored ( mda , rlocn_is_ignored ( mdah - > raw_locns ) ) ;
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
2010-06-29 00:34:24 +04:00
if ( mda_is_ignored ( mda ) ) {
2010-06-30 23:28:35 +04:00
log_debug ( " Ignoring mda on device %s at offset % " PRIu64 ,
2010-06-30 17:51:11 +04:00
dev_name ( mdac - > area . dev ) ,
mdac - > area . start ) ;
2010-06-29 00:34:24 +04:00
if ( ! dev_close ( mdac - > area . dev ) )
stack ;
continue ;
}
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
if ( ( vgname = vgname_from_mda ( info - > fmt , mdah ,
& mdac - > area ,
2007-11-05 20:17:55 +03:00
& vgid , & vgstatus , & creation_host ,
& mdac - > free_sectors ) ) & &
2006-04-11 21:42:15 +04:00
! lvmcache_update_vgname_and_id ( info , vgname ,
2006-04-13 21:32:24 +04:00
( char * ) & vgid , vgstatus ,
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
creation_host ) ) {
if ( ! dev_close ( mdac - > area . dev ) )
stack ;
2006-04-11 20:00:26 +04:00
return_0 ;
Allow raw_read_mda_header to be called from text_label.c.
We'd like to pass in mda_header to vgname_from_mda(). In order to
do this, we need to call raw_read_mda_header() from text_label.c,
_text_read(), which gets called from the label_read() path, and
peers into the metadata and update vginfo cache. We should check
the disable bit here, and if set, not peer into the vg metadata,
thus reducing the I/O to disk.
In the process, move vgname_from_mda() to layout.h, since the fn
only gets called from format_text code, and we need the mda_header
definition from the private layout.h.
Signed-off-by: Dave Wysochanski <dwysocha@redhat.com>
2010-06-29 00:31:01 +04:00
}
close_dev :
2010-06-29 00:30:46 +04:00
if ( ! dev_close ( mdac - > area . dev ) )
stack ;
2002-11-18 17:04:08 +03:00
}
info - > status & = ~ CACHE_INVALID ;
return 1 ;
}
2010-07-09 19:34:40 +04:00
static void _text_destroy_label ( struct labeller * l __attribute__ ( ( unused ) ) ,
2006-05-11 21:58:58 +04:00
struct label * label )
2002-11-18 17:04:08 +03:00
{
2003-07-05 02:34:56 +04:00
struct lvmcache_info * info = ( struct lvmcache_info * ) label - > info ;
2002-11-18 17:04:08 +03:00
if ( info - > mdas . n )
del_mdas ( & info - > mdas ) ;
if ( info - > das . n )
del_das ( & info - > das ) ;
}
2006-04-19 19:33:07 +04:00
static void _fmt_text_destroy ( struct labeller * l )
2002-11-18 17:04:08 +03:00
{
2005-10-17 03:03:59 +04:00
dm_free ( l ) ;
2002-11-18 17:04:08 +03:00
}
struct label_ops _text_ops = {
2006-05-10 01:23:51 +04:00
. can_handle = _text_can_handle ,
. write = _text_write ,
. read = _text_read ,
. verify = _text_can_handle ,
. initialise_label = _text_initialise_label ,
. destroy_label = _text_destroy_label ,
. destroy = _fmt_text_destroy ,
2002-11-18 17:04:08 +03:00
} ;
2002-12-20 02:25:55 +03:00
struct labeller * text_labeller_create ( const struct format_type * fmt )
2002-11-18 17:04:08 +03:00
{
struct labeller * l ;
2005-10-17 03:03:59 +04:00
if ( ! ( l = dm_malloc ( sizeof ( * l ) ) ) ) {
2009-07-16 00:02:46 +04:00
log_error ( " Couldn't allocate labeller object. " ) ;
2002-11-18 17:04:08 +03:00
return NULL ;
}
l - > ops = & _text_ops ;
2002-12-20 02:25:55 +03:00
l - > private = ( const void * ) fmt ;
2002-11-18 17:04:08 +03:00
return l ;
}