2002-01-10 15:22:17 +03:00
/*
2008-01-30 17:00:02 +03:00
* Copyright ( C ) 2001 - 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 .
2002-01-10 15:22:17 +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-01-10 15:22:17 +03:00
*/
2002-11-18 17:01:16 +03:00
# include "lib.h"
2002-01-10 15:22:17 +03:00
# include "lvm-file.h"
# include "lvm-string.h"
# include <unistd.h>
# include <sys/stat.h>
# include <sys/file.h>
# include <fcntl.h>
2002-03-12 01:23:24 +03:00
# include <dirent.h>
2002-01-10 15:22:17 +03:00
/*
* Creates a temporary filename , and opens a descriptor to the
* file . Both the filename and descriptor are needed so we can
* rename the file after successfully writing it . Grab
* NFS - supported exclusive fcntl discretionary lock .
*/
2008-12-07 07:27:56 +03:00
int create_temp_name ( const char * dir , char * buffer , size_t len , int * fd ,
unsigned * seed )
2002-01-10 15:22:17 +03:00
{
int i , num ;
pid_t pid ;
char hostname [ 255 ] ;
2011-06-08 12:49:53 +04:00
char * p ;
2002-01-10 15:22:17 +03:00
struct flock lock = {
2006-05-10 01:23:51 +04:00
. l_type = F_WRLCK ,
. l_whence = 0 ,
. l_start = 0 ,
. l_len = 0
2002-01-10 15:22:17 +03:00
} ;
2008-12-07 07:27:56 +03:00
num = rand_r ( seed ) ;
2002-01-10 15:22:17 +03:00
pid = getpid ( ) ;
if ( gethostname ( hostname , sizeof ( hostname ) ) < 0 ) {
log_sys_error ( " gethostname " , " " ) ;
strcpy ( hostname , " nohostname " ) ;
}
2011-06-08 12:49:53 +04:00
else {
/* Replace any '/' with '?' found in the hostname. */
p = hostname ;
while ( ( p = strchr ( p , ' / ' ) ) )
* p = ' ? ' ;
}
2002-01-10 15:22:17 +03:00
for ( i = 0 ; i < 20 ; i + + , num + + ) {
2006-08-21 16:54:53 +04:00
if ( dm_snprintf ( buffer , len , " %s/.lvm_%s_%d_%d " ,
2002-01-10 15:22:17 +03:00
dir , hostname , pid , num ) = = - 1 ) {
2009-07-16 00:02:46 +04:00
log_error ( " Not enough space to build temporary file "
" string. " ) ;
2002-01-10 15:22:17 +03:00
return 0 ;
}
* fd = open ( buffer , O_CREAT | O_EXCL | O_WRONLY | O_APPEND ,
S_IRUSR | S_IRGRP | S_IROTH |
S_IWUSR | S_IWGRP | S_IWOTH ) ;
if ( * fd < 0 )
continue ;
if ( ! fcntl ( * fd , F_SETLK , & lock ) )
return 1 ;
2007-01-25 17:37:48 +03:00
if ( close ( * fd ) )
log_sys_error ( " close " , buffer ) ;
2002-01-10 15:22:17 +03:00
}
return 0 ;
}
/*
* NFS - safe rename of a temporary file to a common name , designed
* to avoid race conditions and not overwrite the destination if
* it exists .
*
* Try to create the new filename as a hard link to the original .
* Check the link count of the original file to see if it worked .
* ( Assumes nothing else touches our temporary file ! ) If it
* worked , unlink the old filename .
*/
int lvm_rename ( const char * old , const char * new )
{
struct stat buf ;
2005-03-11 01:31:10 +03:00
if ( link ( old , new ) ) {
log_error ( " %s: rename to %s failed: %s " , old , new ,
strerror ( errno ) ) ;
return 0 ;
}
2002-01-10 15:22:17 +03:00
if ( stat ( old , & buf ) ) {
log_sys_error ( " stat " , old ) ;
return 0 ;
}
if ( buf . st_nlink ! = 2 ) {
log_error ( " %s: rename to %s failed " , old , new ) ;
return 0 ;
}
if ( unlink ( old ) ) {
log_sys_error ( " unlink " , old ) ;
return 0 ;
}
return 1 ;
}
2002-02-11 18:42:34 +03:00
int path_exists ( const char * path )
{
struct stat info ;
if ( ! * path )
return 0 ;
if ( stat ( path , & info ) < 0 )
return 0 ;
return 1 ;
}
int dir_exists ( const char * path )
{
struct stat info ;
if ( ! * path )
return 0 ;
if ( stat ( path , & info ) < 0 )
return 0 ;
if ( ! S_ISDIR ( info . st_mode ) )
return 0 ;
return 1 ;
}
2002-03-12 01:23:24 +03:00
int is_empty_dir ( const char * dir )
{
struct dirent * dirent ;
DIR * d ;
if ( ! ( d = opendir ( dir ) ) ) {
log_sys_error ( " opendir " , dir ) ;
return 0 ;
}
while ( ( dirent = readdir ( d ) ) )
if ( strcmp ( dirent - > d_name , " . " ) & & strcmp ( dirent - > d_name , " .. " ) )
break ;
if ( closedir ( d ) ) {
log_sys_error ( " closedir " , dir ) ;
}
return dirent ? 0 : 1 ;
}
2002-12-06 01:51:15 +03:00
void sync_dir ( const char * file )
{
int fd ;
char * dir , * c ;
2005-10-17 03:03:59 +04:00
if ( ! ( dir = dm_strdup ( file ) ) ) {
2002-12-06 01:51:15 +03:00
log_error ( " sync_dir failed in strdup " ) ;
return ;
}
if ( ! dir_exists ( dir ) ) {
c = dir + strlen ( dir ) ;
while ( * c ! = ' / ' & & c > dir )
c - - ;
2004-06-19 23:24:33 +04:00
if ( c = = dir )
* c + + = ' . ' ;
2002-12-06 01:51:15 +03:00
* c = ' \0 ' ;
}
if ( ( fd = open ( dir , O_RDONLY ) ) = = - 1 ) {
log_sys_error ( " open " , dir ) ;
goto out ;
}
2005-09-01 22:37:22 +04:00
if ( fsync ( fd ) & & ( errno ! = EROFS ) & & ( errno ! = EINVAL ) )
2002-12-06 01:51:15 +03:00
log_sys_error ( " fsync " , dir ) ;
2007-01-25 17:37:48 +03:00
if ( close ( fd ) )
log_sys_error ( " close " , dir ) ;
2002-12-06 01:51:15 +03:00
out :
2005-10-17 03:03:59 +04:00
dm_free ( dir ) ;
2002-12-06 01:51:15 +03:00
}
2006-11-04 06:34:10 +03:00
/*
* Attempt to obtain fcntl lock on a file , if necessary creating file first
* or waiting .
* Returns file descriptor on success , else - 1.
* mode is F_WRLCK or F_RDLCK
*/
int fcntl_lock_file ( const char * file , short lock_type , int warn_if_read_only )
{
int lockfd ;
2007-02-28 21:27:13 +03:00
char * dir ;
char * c ;
2006-11-04 06:34:10 +03:00
struct flock lock = {
. l_type = lock_type ,
. l_whence = 0 ,
. l_start = 0 ,
. l_len = 0
} ;
2007-02-28 21:27:13 +03:00
if ( ! ( dir = dm_strdup ( file ) ) ) {
log_error ( " fcntl_lock_file failed in strdup. " ) ;
return - 1 ;
}
if ( ( c = strrchr ( dir , ' / ' ) ) )
* c = ' \0 ' ;
2007-07-28 16:26:21 +04:00
if ( ! dm_create_dir ( dir ) ) {
2007-04-26 20:44:59 +04:00
dm_free ( dir ) ;
2007-02-28 21:27:13 +03:00
return - 1 ;
2007-04-26 20:44:59 +04:00
}
dm_free ( dir ) ;
2007-02-28 21:27:13 +03:00
2006-11-04 06:34:10 +03:00
log_very_verbose ( " Locking %s (%s, %hd) " , file ,
( lock_type = = F_WRLCK ) ? " F_WRLCK " : " F_RDLCK " ,
lock_type ) ;
if ( ( lockfd = open ( file , O_RDWR | O_CREAT , 0777 ) ) < 0 ) {
/* EACCES has been reported on NFS */
if ( warn_if_read_only | | ( errno ! = EROFS & & errno ! = EACCES ) )
log_sys_error ( " open " , file ) ;
else
stack ;
return - 1 ;
}
if ( fcntl ( lockfd , F_SETLKW , & lock ) ) {
log_sys_error ( " fcntl " , file ) ;
2012-02-27 15:28:47 +04:00
if ( close ( lockfd ) )
log_sys_error ( " close " , file ) ;
2006-11-04 06:34:10 +03:00
return - 1 ;
}
return lockfd ;
}
void fcntl_unlock_file ( int lockfd )
{
struct flock lock = {
. l_type = F_UNLCK ,
. l_whence = 0 ,
. l_start = 0 ,
. l_len = 0
} ;
log_very_verbose ( " Unlocking fd %d " , lockfd ) ;
if ( fcntl ( lockfd , F_SETLK , & lock ) = = - 1 )
log_error ( " fcntl unlock failed on fd %d: %s " , lockfd ,
strerror ( errno ) ) ;
if ( close ( lockfd ) )
log_error ( " lock file close failed on fd %d: %s " , lockfd ,
strerror ( errno ) ) ;
}
2007-07-24 21:48:08 +04:00
int lvm_fclose ( FILE * fp , const char * filename )
{
if ( ! dm_fclose ( fp ) )
return 0 ;
if ( errno = = 0 )
log_error ( " %s: write error " , filename ) ;
else
log_sys_error ( " write error " , filename ) ;
return EOF ;
}