*
*/
-#include <sys/types.h> // for ::minor, ::major macros
#include <utime.h> // for ::utime
#include <sys/statvfs.h>
+#include <sys/sysmacros.h> // for ::minor, ::major macros
#include <iostream>
#include <fstream>
//
///////////////////////////////////////////////////////////////////
- /******************************************************************
- **
- ** FUNCTION NAME : _Log_Result
- ** FUNCTION TYPE : int
- **
- ** DESCRIPTION : Helper function to log return values.
- */
-#define _Log_Result MIL << endl, __Log_Result
- inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
- {
- if ( res )
+#define logResult MIL << endl, doLogResult
+ namespace {
+ /** Helper function to log return values. */
+ inline int doLogResult( const int res, const char * rclass = 0 /*errno*/ )
{
- if ( rclass )
- WAR << " FAILED: " << rclass << " " << res << endl;
- else
- WAR << " FAILED: " << str::strerror( res ) << endl;
+ if ( res )
+ {
+ if ( rclass )
+ WAR << " FAILED: " << rclass << " " << res << endl;
+ else
+ WAR << " FAILED: " << str::strerror( res ) << endl;
+ }
+ return res;
}
- return res;
- }
+ } // namespace
///////////////////////////////////////////////////////////////////
//
{
MIL << "mkdir " << path << ' ' << str::octstring( mode );
if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
{
MIL << "rmdir " << path;
if ( ::rmdir( path.asString().c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
struct dirent * d;
if ( ! (dp = opendir( dir.c_str() )) )
- return _Log_Result( errno );
+ return logResult( errno );
while ( (d = readdir(dp)) )
{
PathInfo p( path );
if ( !p.isExist() ) {
- return _Log_Result( 0 );
+ return logResult( 0 );
}
if ( !p.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
- return _Log_Result( recursive_rmdir_1( path ) );
+ return logResult( recursive_rmdir_1( path ) );
}
///////////////////////////////////////////////////////////////////
PathInfo p( path );
if ( !p.isExist() ) {
- return _Log_Result( 0 );
+ return logResult( 0 );
}
if ( !p.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
- return _Log_Result( recursive_rmdir_1( path, false/* don't remove path itself */ ) );
+ return logResult( recursive_rmdir_1( path, false/* don't remove path itself */ ) );
}
///////////////////////////////////////////////////////////////////
PathInfo sp( srcpath );
if ( !sp.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
PathInfo dp( destpath );
if ( !dp.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
PathInfo tp( destpath + srcpath.basename() );
if ( tp.isExist() ) {
- return _Log_Result( EEXIST );
+ return logResult( EEXIST );
}
MIL << " " << output;
}
int ret = prog.close();
- return _Log_Result( ret, "returned" );
+ return logResult( ret, "returned" );
}
///////////////////////////////////////////////////////////////////
PathInfo sp( srcpath );
if ( !sp.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
PathInfo dp( destpath );
if ( !dp.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
if ( srcpath == destpath ) {
- return _Log_Result( EEXIST );
+ return logResult( EEXIST );
}
std::string src( srcpath.asString());
MIL << " " << output;
}
int ret = prog.close();
- return _Log_Result( ret, "returned" );
+ return logResult( ret, "returned" );
}
///////////////////////////////////////////////////////////////////////
MIL << "readdir " << dir_r << ' ';
if ( ! dir )
- return _Log_Result( errno );
+ return logResult( errno );
MIL << endl; // close line before callbacks are invoked.
int ret = 0;
{
MIL << "unlink " << path;
if ( ::unlink( path.asString().c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
+ namespace
+ {
+ int safe_rename( const Pathname & oldpath, const Pathname & newpath )
+ {
+ int ret = ::rename( oldpath.asString().c_str(), newpath.asString().c_str() );
+
+ // rename(2) can fail on OverlayFS. Fallback to using mv(1), which is
+ // explicitly mentioned in the kernel docs to deal correctly with OverlayFS.
+ if ( ret == -1 && errno == EXDEV ) {
+ const char *const argv[] = {
+ "/usr/bin/mv",
+ oldpath.asString().c_str(),
+ newpath.asString().c_str(),
+ NULL
+ };
+ ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
+ for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
+ MIL << " " << output;
+ }
+ ret = prog.close();
+ }
+
+ return ret;
+ }
+ } // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
//
// METHOD NAME : rename
// METHOD TYPE : int
int rename( const Pathname & oldpath, const Pathname & newpath )
{
MIL << "rename " << oldpath << " -> " << newpath;
- if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
- return _Log_Result( errno );
+ if ( safe_rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
{
MIL << "exchange " << lpath << " <-> " << rpath;
if ( lpath.empty() || rpath.empty() )
- return _Log_Result( EINVAL );
+ return logResult( EINVAL );
PathInfo linfo( lpath );
PathInfo rinfo( rpath );
if ( ! linfo.isExist() )
{
if ( ! rinfo.isExist() )
- return _Log_Result( 0 ); // both don't exist.
+ return logResult( 0 ); // both don't exist.
// just rename rpath -> lpath
int ret = assert_dir( lpath.dirname() );
if ( ret != 0 )
- return _Log_Result( ret );
- if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( ret );
+ if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
// HERE: lpath exists:
// just rename lpath -> rpath
int ret = assert_dir( rpath.dirname() );
if ( ret != 0 )
- return _Log_Result( ret );
- if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( ret );
+ if ( safe_rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
// HERE: both exist
TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
if ( ! tmpfile )
- return _Log_Result( errno );
+ return logResult( errno );
Pathname tmp( tmpfile.path() );
::unlink( tmp.c_str() );
- if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
- return _Log_Result( errno );
+ if ( safe_rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
+ return logResult( errno );
}
- if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
- ::rename( tmp.c_str(), lpath.c_str() );
- return _Log_Result( errno );
+ if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+ safe_rename( tmp.c_str(), lpath.c_str() );
+ return logResult( errno );
}
- if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
- ::rename( lpath.c_str(), rpath.c_str() );
- ::rename( tmp.c_str(), lpath.c_str() );
- return _Log_Result( errno );
+ if ( safe_rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
+ safe_rename( lpath.c_str(), rpath.c_str() );
+ safe_rename( tmp.c_str(), lpath.c_str() );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
PathInfo sp( file );
if ( !sp.isFile() ) {
- return _Log_Result( EINVAL );
+ return logResult( EINVAL );
}
PathInfo dp( dest );
if ( dp.isDir() ) {
- return _Log_Result( EISDIR );
+ return logResult( EISDIR );
}
const char *const argv[] = {
MIL << " " << output;
}
int ret = prog.close();
- return _Log_Result( ret, "returned" );
+ return logResult( ret, "returned" );
}
///////////////////////////////////////////////////////////////////
{
MIL << "symlink " << newpath << " -> " << oldpath;
if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
{
MIL << "hardlink " << newpath << " -> " << oldpath;
if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
if ( pi.isLink() )
{
// dont hardlink symlinks!
+ MIL << " => copy" << endl;
return copy( oldpath, newpath );
}
{
int res = unlink( newpath );
if ( res != 0 )
- return _Log_Result( res );
+ return logResult( res );
}
// Here: no symlink, no newpath
{
case EPERM: // /proc/sys/fs/protected_hardlink in proc(5)
case EXDEV: // oldpath and newpath are not on the same mounted file system
+ MIL << " => copy" << endl;
return copy( oldpath, newpath );
break;
}
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
{
target_r = Pathname();
MIL << "readlink " << symlink_r;
- return _Log_Result( errno );
+ return logResult( errno );
}
buf[ret] = '\0';
target_r = buf;
PathInfo sp( file );
if ( !sp.isFile() ) {
- return _Log_Result( EINVAL );
+ return logResult( EINVAL );
}
PathInfo dp( dest );
if ( !dp.isDir() ) {
- return _Log_Result( ENOTDIR );
+ return logResult( ENOTDIR );
}
const char *const argv[] = {
MIL << " " << output;
}
int ret = prog.close();
- return _Log_Result( ret, "returned" );
+ return logResult( ret, "returned" );
}
///////////////////////////////////////////////////////////////////
{
MIL << "chmod " << path << ' ' << str::octstring( mode );
if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
int addmod( const Pathname & path, mode_t mode )
int fd = open( file.asString().c_str(), O_RDONLY|O_CLOEXEC );
if ( fd != -1 ) {
- const int magicSize = 3;
+ const int magicSize = 5;
unsigned char magic[magicSize];
memset( magic, 0, magicSize );
if ( read( fd, magic, magicSize ) == magicSize ) {
ret = ZT_GZ;
} else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
ret = ZT_BZ2;
+ } else if ( magic[0] == '\0' && magic[1] == 'Z' && magic[2] == 'C' && magic[3] == 'K' && magic[4] == '1') {
+ ret = ZT_ZCHNK;
+
}
}
close( fd );
int ret = assert_dir( path.dirname() );
MIL << "assert_file " << str::octstring( mode ) << " " << path;
if ( ret != 0 )
- return _Log_Result( ret );
+ return logResult( ret );
PathInfo pi( path );
if ( pi.isExist() )
- return _Log_Result( pi.isFile() ? 0 : EEXIST );
+ return logResult( pi.isFile() ? 0 : EEXIST );
int fd = ::creat( path.c_str(), mode );
if ( fd == -1 )
- return _Log_Result( errno );
+ return logResult( errno );
+
+ ::close( fd );
+ return logResult( 0 );
+ }
+
+ int assert_file_mode( const Pathname & path, unsigned mode )
+ {
+ int ret = assert_dir( path.dirname() );
+ MIL << "assert_file_mode " << str::octstring( mode ) << " " << path;
+ if ( ret != 0 )
+ return logResult( ret );
+ PathInfo pi( path );
+ if ( pi.isExist() )
+ {
+ if ( ! pi.isFile() )
+ return logResult( EEXIST );
+
+ mode = applyUmaskTo( mode );
+ if ( pi.st_mode() != mode )
+ return chmod( path, mode );
+
+ return logResult( 0 );
+ }
+
+ int fd = ::creat( path.c_str(), mode );
+ if ( fd == -1 )
+ return logResult( errno );
::close( fd );
- return _Log_Result( 0 );
+ return logResult( 0 );
}
///////////////////////////////////////////////////////////////////
times.actime = ::time( 0 );
times.modtime = ::time( 0 );
if ( ::utime( path.asString().c_str(), × ) == -1 ) {
- return _Log_Result( errno );
+ return logResult( errno );
}
- return _Log_Result( 0 );
+ return logResult( 0 );
}
/////////////////////////////////////////////////////////////////