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 ,
2016-01-21 13:49:46 +03:00
* Inc . , 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA
2002-01-10 15:22:17 +03:00
*/
2018-05-14 12:30:20 +03:00
# include "lib/misc/lib.h"
# include "lib/misc/lvm-file.h"
2002-01-10 15:22:17 +03:00
# 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
{
2013-06-30 01:10:33 +04:00
const struct flock lock = { . l_type = F_WRLCK } ;
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
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 " , " " ) ;
2024-04-09 12:43:14 +03:00
dm_strncpy ( hostname , " nohostname " , sizeof ( hostname ) ) ;
2013-06-30 01:10:33 +04:00
} else {
2011-06-08 12:49:53 +04:00
/* 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 ;
}
2024-02-01 16:14:02 +03:00
int dir_create ( const char * path , int mode )
{
int r ;
log_debug ( " Creating directory %s. " , path ) ;
dm_prepare_selinux_context ( path , S_IFDIR ) ;
r = mkdir ( path , mode ) ;
dm_prepare_selinux_context ( NULL , 0 ) ;
if ( r = = 0 )
return 1 ;
if ( errno = = EEXIST ) {
if ( dir_exists ( path ) )
return 1 ;
log_error ( " Path %s is not a directory. " , path ) ;
} else
log_sys_error ( " mkdir " , path ) ;
return 0 ;
}
int dir_create_recursive ( const char * path , int mode )
{
int r = 0 ;
char * orig , * s ;
orig = s = strdup ( path ) ;
if ( ! s ) {
log_error ( " Failed to duplicate directory path %s. " , path ) ;
return 0 ;
}
while ( ( s = strchr ( s , ' / ' ) ) ! = NULL ) {
* s = ' \0 ' ;
if ( * orig & & ! dir_exists ( orig ) & & ! dir_create ( orig , mode ) )
goto_out ;
* s + + = ' / ' ;
}
if ( ! dir_exists ( path ) & & ! dir_create ( path , mode ) )
goto_out ;
r = 1 ;
out :
free ( orig ) ;
return r ;
}
2002-12-06 01:51:15 +03:00
void sync_dir ( const char * file )
{
int fd ;
char * dir , * c ;
2018-06-08 15:40:53 +03:00
if ( ! ( dir = 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 :
2018-06-08 15:40:53 +03:00
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 )
{
2013-06-30 01:10:33 +04:00
const struct flock lock = { . l_type = lock_type } ;
2006-11-04 06:34:10 +03:00
int lockfd ;
2007-02-28 21:27:13 +03:00
char * dir ;
char * c ;
2006-11-04 06:34:10 +03:00
2018-06-08 15:40:53 +03:00
if ( ! ( dir = strdup ( file ) ) ) {
2007-02-28 21:27:13 +03:00
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 ) ) {
2018-06-08 15:40:53 +03:00
free ( dir ) ;
2007-02-28 21:27:13 +03:00
return - 1 ;
2007-04-26 20:44:59 +04:00
}
2018-06-08 15:40:53 +03:00
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 ) )
2013-06-30 01:10:33 +04:00
log_sys_error ( " close " , file ) ;
2006-11-04 06:34:10 +03:00
return - 1 ;
}
return lockfd ;
}
void fcntl_unlock_file ( int lockfd )
{
2013-06-30 01:10:33 +04:00
const struct flock lock = { . l_type = F_UNLCK } ;
2006-11-04 06:34:10 +03:00
log_very_verbose ( " Unlocking fd %d " , lockfd ) ;
if ( fcntl ( lockfd , F_SETLK , & lock ) = = - 1 )
2013-06-30 01:10:33 +04:00
log_sys_error ( " fcntl " , " " ) ;
2006-11-04 06:34:10 +03:00
if ( close ( lockfd ) )
2013-06-30 01:10:33 +04:00
log_sys_error ( " close " , " " ) ;
2006-11-04 06:34:10 +03:00
}
2007-07-24 21:48:08 +04:00
int lvm_fclose ( FILE * fp , const char * filename )
{
if ( ! dm_fclose ( fp ) )
return 0 ;
2013-06-30 01:10:33 +04:00
2007-07-24 21:48:08 +04:00
if ( errno = = 0 )
log_error ( " %s: write error " , filename ) ;
else
log_sys_error ( " write error " , filename ) ;
2013-06-30 01:10:33 +04:00
2007-07-24 21:48:08 +04:00
return EOF ;
}
2015-03-18 12:54:08 +03:00
void lvm_stat_ctim ( struct timespec * ctim , const struct stat * buf )
{
# ifdef HAVE_STAT_ST_CTIM
* ctim = buf - > st_ctim ;
# else
ctim - > tv_sec = buf - > st_ctime ;
ctim - > tv_nsec = 0 ;
# endif
}