libzypp 9.41.1

PathInfo.cc

Go to the documentation of this file.
00001 /*---------------------------------------------------------------------\
00002 |                          ____ _   __ __ ___                          |
00003 |                         |__  / \ / / . \ . \                         |
00004 |                           / / \ V /|  _/  _/                         |
00005 |                          / /__ | | | | | |                           |
00006 |                         /_____||_| |_| |_|                           |
00007 |                                                                      |
00008 \---------------------------------------------------------------------*/
00013 #include <sys/types.h> // for ::minor, ::major macros
00014 #include <utime.h>     // for ::utime
00015 #include <sys/statvfs.h>
00016 
00017 #include <iostream>
00018 #include <fstream>
00019 #include <iomanip>
00020 
00021 #include "zypp/base/Logger.h"
00022 #include "zypp/base/String.h"
00023 #include "zypp/base/IOStream.h"
00024 #include "zypp/base/Errno.h"
00025 
00026 #include "zypp/ExternalProgram.h"
00027 #include "zypp/PathInfo.h"
00028 #include "zypp/Digest.h"
00029 #include "zypp/TmpPath.h"
00030 
00031 using std::endl;
00032 using std::string;
00033 
00035 namespace zypp
00036 { 
00037 
00038   namespace filesystem
00039   { 
00040 
00041     /******************************************************************
00042      **
00043      ** FUNCTION NAME : operator<<
00044      ** FUNCTION TYPE : std::ostream &
00045     */
00046     std::ostream & operator<<( std::ostream & str, FileType obj )
00047     {
00048       switch ( obj ) {
00049 #define EMUMOUT(T) case T: return str << #T; break
00050         EMUMOUT( FT_NOT_AVAIL );
00051         EMUMOUT( FT_NOT_EXIST );
00052         EMUMOUT( FT_FILE );
00053         EMUMOUT( FT_DIR );
00054         EMUMOUT( FT_CHARDEV );
00055         EMUMOUT( FT_BLOCKDEV );
00056         EMUMOUT( FT_FIFO );
00057         EMUMOUT( FT_LINK );
00058         EMUMOUT( FT_SOCKET );
00059 #undef EMUMOUT
00060       }
00061       return str;
00062     }
00063 
00065     //
00066     //  METHOD NAME : StatMode::fileType
00067     //  METHOD TYPE : FileType
00068     //
00069     FileType StatMode::fileType() const
00070     {
00071       if ( isFile() )
00072         return FT_FILE;
00073       if ( isDir() )
00074         return FT_DIR;
00075       if ( isLink() )
00076         return FT_LINK;
00077       if ( isChr() )
00078         return FT_CHARDEV;
00079       if ( isBlk() )
00080         return FT_BLOCKDEV;
00081       if ( isFifo() )
00082         return FT_FIFO;
00083       if ( isSock() )
00084         return FT_SOCKET ;
00085 
00086       return FT_NOT_AVAIL;
00087     }
00088 
00089     /******************************************************************
00090      **
00091      ** FUNCTION NAME : operator<<
00092      ** FUNCTION TYPE : std::ostream &
00093     */
00094     std::ostream & operator<<( std::ostream & str, const StatMode & obj )
00095     {
00096       iostr::IosFmtFlagsSaver autoResoreState( str );
00097 
00098       char t = '?';
00099       if ( obj.isFile() )
00100         t = '-';
00101       else if ( obj.isDir() )
00102         t = 'd';
00103       else if ( obj.isLink() )
00104         t = 'l';
00105       else if ( obj.isChr() )
00106         t = 'c';
00107       else if ( obj.isBlk() )
00108         t = 'b';
00109       else if ( obj.isFifo() )
00110         t = 'p';
00111       else if ( obj.isSock() )
00112         t = 's';
00113 
00114       str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
00115       return str;
00116     }
00117 
00119     //
00120     //  Class : PathInfo
00121     //
00123 
00125     //
00126     //  METHOD NAME : PathInfo::PathInfo
00127     //  METHOD TYPE : Constructor
00128     //
00129     PathInfo::PathInfo()
00130     : mode_e( STAT )
00131     , error_i( -1 )
00132     {}
00133 
00135     //
00136     //  METHOD NAME : PathInfo::PathInfo
00137     //  METHOD TYPE : Constructor
00138     //
00139     PathInfo::PathInfo( const Pathname & path, Mode initial )
00140     : path_t( path )
00141     , mode_e( initial )
00142     , error_i( -1 )
00143     {
00144       operator()();
00145     }
00146 
00148     //
00149     //  METHOD NAME : PathInfo::PathInfo
00150     //  METHOD TYPE : Constructor
00151     //
00152     PathInfo::PathInfo( const std::string & path, Mode initial )
00153     : path_t( path )
00154     , mode_e( initial )
00155     , error_i( -1 )
00156     {
00157       operator()();
00158     }
00159 
00161     //
00162     //  METHOD NAME : PathInfo::PathInfo
00163     //  METHOD TYPE : Constructor
00164     //
00165     PathInfo::PathInfo( const char * path, Mode initial )
00166     : path_t( path )
00167     , mode_e( initial )
00168     , error_i( -1 )
00169     {
00170       operator()();
00171     }
00172 
00174     //
00175     //  METHOD NAME : PathInfo::~PathInfo
00176     //  METHOD TYPE : Destructor
00177     //
00178     PathInfo::~PathInfo()
00179     {
00180     }
00181 
00183     //
00184     //  METHOD NAME : PathInfo::operator()
00185     //  METHOD TYPE : bool
00186     //
00187     bool PathInfo::operator()()
00188     {
00189       if ( path_t.empty() ) {
00190         error_i = -1;
00191       } else {
00192         switch ( mode_e ) {
00193         case STAT:
00194           error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
00195           break;
00196         case LSTAT:
00197           error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
00198           break;
00199         }
00200         if ( error_i == -1 )
00201           error_i = errno;
00202       }
00203       return !error_i;
00204     }
00205 
00207     //
00208     //  METHOD NAME : PathInfo::fileType
00209     //  METHOD TYPE : File_type
00210     //
00211     FileType PathInfo::fileType() const
00212     {
00213       if ( isExist() )
00214         return asStatMode().fileType();
00215       return FT_NOT_EXIST;
00216     }
00217 
00219     //
00220     //  METHOD NAME : PathInfo::userMay
00221     //  METHOD TYPE : mode_t
00222     //
00223     mode_t PathInfo::userMay() const
00224     {
00225       if ( !isExist() )
00226         return 0;
00227       if ( owner() == getuid() ) {
00228         return( uperm()/0100 );
00229       } else if ( group() == getgid() ) {
00230         return( gperm()/010 );
00231       }
00232       return operm();
00233     }
00234 
00235     /******************************************************************
00236      **
00237      ** FUNCTION NAME : PathInfo::major
00238      ** FUNCTION TYPE : unsigned int
00239      */
00240     unsigned int PathInfo::major() const
00241     {
00242       return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
00243     }
00244 
00245     /******************************************************************
00246      **
00247      ** FUNCTION NAME : PathInfo::minor
00248      ** FUNCTION TYPE : unsigned int
00249      */
00250     unsigned int PathInfo::minor() const
00251     {
00252       return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
00253     }
00254 
00255     /******************************************************************
00256      **
00257      ** FUNCTION NAME : operator<<
00258      ** FUNCTION TYPE :  std::ostream &
00259     */
00260     std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
00261     {
00262       iostr::IosFmtFlagsSaver autoResoreState( str );
00263 
00264       str << obj.asString() << "{";
00265       if ( !obj.isExist() ) {
00266         str << Errno( obj.error() );
00267       } else {
00268         str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
00269 
00270         if ( obj.isFile() )
00271           str << " size " << obj.size();
00272       }
00273 
00274       return str << "}";
00275     }
00276 
00278     //
00279     //  filesystem utilities
00280     //
00282 
00283     /******************************************************************
00284      **
00285      ** FUNCTION NAME : _Log_Result
00286      ** FUNCTION TYPE : int
00287      **
00288      ** DESCRIPTION : Helper function to log return values.
00289     */
00290 #define _Log_Result MIL << endl, __Log_Result
00291     inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
00292     {
00293       if ( res )
00294       {
00295         if ( rclass )
00296           WAR << " FAILED: " << rclass << " " << res << endl;
00297         else
00298           WAR << " FAILED: " << str::strerror( res ) << endl;
00299       }
00300       return res;
00301     }
00302 
00304     //
00305     //  METHOD NAME : PathInfo::mkdir
00306     //  METHOD TYPE : int
00307     //
00308     int mkdir( const Pathname & path, unsigned mode )
00309     {
00310       MIL << "mkdir " << path << ' ' << str::octstring( mode );
00311       if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
00312         return _Log_Result( errno );
00313       }
00314       return _Log_Result( 0 );
00315     }
00316 
00318     //
00319     //  METHOD NAME : assert_dir()
00320     //  METHOD TYPE : int
00321     //
00322     int assert_dir( const Pathname & path, unsigned mode )
00323     {
00324       if ( path.empty() )
00325         return ENOENT;
00326 
00327       { // Handle existing paths in advance.
00328         PathInfo pi( path );
00329         if ( pi.isDir() )
00330           return 0;
00331         if ( pi.isExist() )
00332           return EEXIST;
00333       }
00334 
00335       string spath = path.asString()+"/";
00336       string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
00337       string::size_type pos = string::npos;
00338       int ret = 0;
00339 
00340       while ( (pos = spath.find('/',lastpos)) != string::npos )
00341       {
00342         string dir( spath.substr(0,pos) );
00343         ret = ::mkdir( dir.c_str(), mode );
00344         if ( ret == -1 )
00345         {
00346           if ( errno == EEXIST ) // ignore errors about already existing paths
00347             ret = 0;
00348           else
00349           {
00350             ret = errno;
00351             WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
00352           }
00353         }
00354         else
00355         {
00356           MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
00357         }
00358         lastpos = pos+1;
00359       }
00360 
00361       return ret;
00362     }
00363 
00365     //
00366     //  METHOD NAME : rmdir
00367     //  METHOD TYPE : int
00368     //
00369     int rmdir( const Pathname & path )
00370     {
00371       MIL << "rmdir " << path;
00372       if ( ::rmdir( path.asString().c_str() ) == -1 ) {
00373         return _Log_Result( errno );
00374       }
00375       return _Log_Result( 0 );
00376     }
00377 
00379     //
00380     //  METHOD NAME : recursive_rmdir
00381     //  METHOD TYPE : int
00382     //
00383     static int recursive_rmdir_1( const Pathname & dir )
00384     {
00385       DIR * dp;
00386       struct dirent * d;
00387 
00388       if ( ! (dp = opendir( dir.c_str() )) )
00389         return _Log_Result( errno );
00390 
00391       while ( (d = readdir(dp)) )
00392       {
00393         std::string direntry = d->d_name;
00394         if ( direntry == "." || direntry == ".." )
00395           continue;
00396         Pathname new_path( dir / d->d_name );
00397 
00398         struct stat st;
00399         if ( ! lstat( new_path.c_str(), &st ) )
00400         {
00401           if ( S_ISDIR( st.st_mode ) )
00402             recursive_rmdir_1( new_path );
00403           else
00404             ::unlink( new_path.c_str() );
00405         }
00406       }
00407       closedir( dp );
00408 
00409       if ( ::rmdir( dir.c_str() ) < 0 )
00410         return errno;
00411 
00412       return 0;
00413     }
00415     int recursive_rmdir( const Pathname & path )
00416     {
00417       MIL << "recursive_rmdir " << path << ' ';
00418       PathInfo p( path );
00419 
00420       if ( !p.isExist() ) {
00421         return _Log_Result( 0 );
00422       }
00423 
00424       if ( !p.isDir() ) {
00425         return _Log_Result( ENOTDIR );
00426       }
00427 
00428       return _Log_Result( recursive_rmdir_1( path ) );
00429     }
00430 
00432     //
00433     //  METHOD NAME : clean_dir
00434     //  METHOD TYPE : int
00435     //
00436     int clean_dir( const Pathname & path )
00437     {
00438       MIL << "clean_dir " << path << ' ';
00439       PathInfo p( path );
00440 
00441       if ( !p.isExist() ) {
00442         return _Log_Result( 0 );
00443       }
00444 
00445       if ( !p.isDir() ) {
00446         return _Log_Result( ENOTDIR );
00447       }
00448 
00449       string cmd( str::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
00450       ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
00451       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
00452         MIL << "  " << output;
00453       }
00454       int ret = prog.close();
00455       return _Log_Result( ret, "returned" );
00456     }
00457 
00459     //
00460     //  METHOD NAME : copy_dir
00461     //  METHOD TYPE : int
00462     //
00463     int copy_dir( const Pathname & srcpath, const Pathname & destpath )
00464     {
00465       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
00466 
00467       PathInfo sp( srcpath );
00468       if ( !sp.isDir() ) {
00469         return _Log_Result( ENOTDIR );
00470       }
00471 
00472       PathInfo dp( destpath );
00473       if ( !dp.isDir() ) {
00474         return _Log_Result( ENOTDIR );
00475       }
00476 
00477       PathInfo tp( destpath + srcpath.basename() );
00478       if ( tp.isExist() ) {
00479         return _Log_Result( EEXIST );
00480       }
00481 
00482 
00483       const char *const argv[] = {
00484         "/bin/cp",
00485         "-dR",
00486         "--",
00487         srcpath.asString().c_str(),
00488         destpath.asString().c_str(),
00489         NULL
00490       };
00491       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
00492       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
00493         MIL << "  " << output;
00494       }
00495       int ret = prog.close();
00496       return _Log_Result( ret, "returned" );
00497     }
00498 
00500     //
00501     //  METHOD NAME : copy_dir_content
00502     //  METHOD TYPE : int
00503     //
00504     int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
00505     {
00506       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
00507 
00508       PathInfo sp( srcpath );
00509       if ( !sp.isDir() ) {
00510         return _Log_Result( ENOTDIR );
00511       }
00512 
00513       PathInfo dp( destpath );
00514       if ( !dp.isDir() ) {
00515         return _Log_Result( ENOTDIR );
00516       }
00517 
00518       if ( srcpath == destpath ) {
00519         return _Log_Result( EEXIST );
00520       }
00521 
00522       std::string src( srcpath.asString());
00523       src += "/.";
00524       const char *const argv[] = {
00525         "/bin/cp",
00526         "-dR",
00527         "--",
00528         src.c_str(),
00529         destpath.asString().c_str(),
00530         NULL
00531       };
00532       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
00533       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
00534         MIL << "  " << output;
00535       }
00536       int ret = prog.close();
00537       return _Log_Result( ret, "returned" );
00538     }
00539 
00541     //
00542     //  METHOD NAME : readdir
00543     //  METHOD TYPE : int
00544     //
00545     int readdir( std::list<std::string> & retlist,
00546                  const Pathname & path, bool dots )
00547     {
00548       retlist.clear();
00549 
00550       MIL << "readdir " << path << ' ';
00551 
00552       DIR * dir = ::opendir( path.asString().c_str() );
00553       if ( ! dir ) {
00554         return _Log_Result( errno );
00555       }
00556 
00557       struct dirent *entry;
00558       while ( (entry = ::readdir( dir )) != 0 ) {
00559 
00560         if ( entry->d_name[0] == '.' ) {
00561           if ( !dots )
00562             continue;
00563           if ( entry->d_name[1] == '\0'
00564                || (    entry->d_name[1] == '.'
00565                     && entry->d_name[2] == '\0' ) )
00566             continue;
00567         }
00568         retlist.push_back( entry->d_name );
00569       }
00570 
00571       ::closedir( dir );
00572 
00573       return _Log_Result( 0 );
00574     }
00575 
00576 
00578     //
00579     //  METHOD NAME : readdir
00580     //  METHOD TYPE : int
00581     //
00582     int readdir( std::list<Pathname> & retlist,
00583                  const Pathname & path, bool dots )
00584     {
00585       retlist.clear();
00586 
00587       std::list<string> content;
00588       int res = readdir( content, path, dots );
00589 
00590       if ( !res ) {
00591         for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
00592           retlist.push_back( path + *it );
00593         }
00594       }
00595 
00596       return res;
00597     }
00598 
00600     //
00601     //  METHOD NAME : readdir
00602     //  METHOD TYPE : int
00603     //
00604 
00605     bool DirEntry::operator==( const DirEntry &rhs ) const
00606     {
00607       // if one of the types is not known, use the name only
00608       if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
00609         return ( name == rhs.name );
00610       return ((name == rhs.name ) && (type == rhs.type));
00611     }
00612 
00613     int readdir( DirContent & retlist, const Pathname & path,
00614                  bool dots, PathInfo::Mode statmode )
00615     {
00616       retlist.clear();
00617 
00618       std::list<string> content;
00619       int res = readdir( content, path, dots );
00620 
00621       if ( !res ) {
00622         for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
00623           PathInfo p( path + *it, statmode );
00624           retlist.push_back( DirEntry( *it, p.fileType() ) );
00625         }
00626       }
00627 
00628       return res;
00629     }
00630 
00632     //
00633     //  METHOD NAME : is_empty_dir
00634     //  METHOD TYPE : int
00635     //
00636     int is_empty_dir(const Pathname & path)
00637     {
00638       DIR * dir = ::opendir( path.asString().c_str() );
00639       if ( ! dir ) {
00640         return _Log_Result( errno );
00641       }
00642 
00643       struct dirent *entry;
00644       while ( (entry = ::readdir( dir )) != NULL )
00645       {
00646         std::string name(entry->d_name);
00647 
00648         if ( name == "." || name == "..")
00649           continue;
00650 
00651         break;
00652       }
00653       ::closedir( dir );
00654 
00655       return entry != NULL ? -1 : 0;
00656     }
00657 
00659     //
00660     //  METHOD NAME : unlink
00661     //  METHOD TYPE : int
00662     //
00663     int unlink( const Pathname & path )
00664     {
00665       MIL << "unlink " << path;
00666       if ( ::unlink( path.asString().c_str() ) == -1 ) {
00667         return _Log_Result( errno );
00668       }
00669       return _Log_Result( 0 );
00670     }
00671 
00673     //
00674     //  METHOD NAME : rename
00675     //  METHOD TYPE : int
00676     //
00677     int rename( const Pathname & oldpath, const Pathname & newpath )
00678     {
00679       MIL << "rename " << oldpath << " -> " << newpath;
00680       if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
00681         return _Log_Result( errno );
00682       }
00683       return _Log_Result( 0 );
00684     }
00685 
00687     //
00688     //  METHOD NAME : exchange
00689     //  METHOD TYPE : int
00690     //
00691     int exchange( const Pathname & lpath, const Pathname & rpath )
00692     {
00693       MIL << "exchange " << lpath << " <-> " << rpath;
00694       if ( lpath.empty() || rpath.empty() )
00695         return _Log_Result( EINVAL );
00696 
00697       PathInfo linfo( lpath );
00698       PathInfo rinfo( rpath );
00699 
00700       if ( ! linfo.isExist() )
00701       {
00702         if ( ! rinfo.isExist() )
00703           return _Log_Result( 0 ); // both don't exist.
00704 
00705         // just rename rpath -> lpath
00706         int ret = assert_dir( lpath.dirname() );
00707         if ( ret != 0 )
00708           return _Log_Result( ret );
00709         if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
00710           return _Log_Result( errno );
00711         }
00712         return _Log_Result( 0 );
00713       }
00714 
00715       // HERE: lpath exists:
00716       if ( ! rinfo.isExist() )
00717       {
00718         // just rename lpath -> rpath
00719         int ret = assert_dir( rpath.dirname() );
00720         if ( ret != 0 )
00721           return _Log_Result( ret );
00722         if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
00723           return _Log_Result( errno );
00724         }
00725         return _Log_Result( 0 );
00726       }
00727 
00728       // HERE: both exist
00729       TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
00730       if ( ! tmpfile )
00731         return _Log_Result( errno );
00732       Pathname tmp( tmpfile.path() );
00733       ::unlink( tmp.c_str() );
00734 
00735       if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
00736         return _Log_Result( errno );
00737       }
00738       if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
00739         ::rename( tmp.c_str(), lpath.c_str() );
00740         return _Log_Result( errno );
00741       }
00742       if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
00743         ::rename( lpath.c_str(), rpath.c_str() );
00744         ::rename( tmp.c_str(), lpath.c_str() );
00745         return _Log_Result( errno );
00746       }
00747       return _Log_Result( 0 );
00748     }
00749 
00751     //
00752     //  METHOD NAME : copy
00753     //  METHOD TYPE : int
00754     //
00755     int copy( const Pathname & file, const Pathname & dest )
00756     {
00757       MIL << "copy " << file << " -> " << dest << ' ';
00758 
00759       PathInfo sp( file );
00760       if ( !sp.isFile() ) {
00761         return _Log_Result( EINVAL );
00762       }
00763 
00764       PathInfo dp( dest );
00765       if ( dp.isDir() ) {
00766         return _Log_Result( EISDIR );
00767       }
00768 
00769       const char *const argv[] = {
00770         "/bin/cp",
00771         "--remove-destination",
00772         "--",
00773         file.asString().c_str(),
00774         dest.asString().c_str(),
00775         NULL
00776       };
00777       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
00778       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
00779         MIL << "  " << output;
00780       }
00781       int ret = prog.close();
00782       return _Log_Result( ret, "returned" );
00783     }
00784 
00786     //
00787     //  METHOD NAME : symlink
00788     //  METHOD TYPE : int
00789     //
00790     int symlink( const Pathname & oldpath, const Pathname & newpath )
00791     {
00792       MIL << "symlink " << newpath << " -> " << oldpath;
00793       if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
00794         return _Log_Result( errno );
00795       }
00796       return _Log_Result( 0 );
00797     }
00798 
00800     //
00801     //  METHOD NAME : hardlink
00802     //  METHOD TYPE : int
00803     //
00804     int hardlink( const Pathname & oldpath, const Pathname & newpath )
00805     {
00806       MIL << "hardlink " << newpath << " -> " << oldpath;
00807       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
00808         return _Log_Result( errno );
00809       }
00810       return _Log_Result( 0 );
00811     }
00812 
00814     //
00815     //  METHOD NAME : hardlink
00816     //  METHOD TYPE : int
00817     //
00818     int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
00819     {
00820       MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
00821 
00822       PathInfo pi( oldpath, PathInfo::LSTAT );
00823       if ( pi.isLink() )
00824       {
00825         // dont hardlink symlinks!
00826         return copy( oldpath, newpath );
00827       }
00828 
00829       pi.lstat( newpath );
00830       if ( pi.isExist() )
00831       {
00832         int res = unlink( newpath );
00833         if ( res != 0 )
00834           return _Log_Result( res );
00835       }
00836 
00837       // Here: no symlink, no newpath
00838       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
00839       {
00840         switch ( errno )
00841         {
00842           case EXDEV: // oldpath  and  newpath are not on the same mounted file system
00843             return copy( oldpath, newpath );
00844             break;
00845         }
00846         return _Log_Result( errno );
00847       }
00848       return _Log_Result( 0 );
00849     }
00850 
00852     //
00853     //  METHOD NAME : readlink
00854     //  METHOD TYPE : int
00855     //
00856     int readlink( const Pathname & symlink_r, Pathname & target_r )
00857     {
00858       static const ssize_t bufsiz = 2047;
00859       static char buf[bufsiz+1];
00860       ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
00861       if ( ret == -1 )
00862       {
00863         target_r = Pathname();
00864         MIL << "readlink " << symlink_r;
00865         return _Log_Result( errno );
00866       }
00867       buf[ret] = '\0';
00868       target_r = buf;
00869       return 0;
00870     }
00871 
00873     //
00874     //  METHOD NAME : expandlink
00875     //  METHOD TYPE : Pathname
00876     //
00877     Pathname expandlink( const Pathname & path_r )
00878     {
00879       static const unsigned int level_limit = 256;
00880       static unsigned int count;
00881       Pathname path(path_r);
00882       PathInfo info(path_r, PathInfo::LSTAT);
00883 
00884       for (count = level_limit; info.isLink() && count; count--)
00885       {
00886         DBG << "following symlink " << path;
00887         path = path.dirname() / readlink(path);
00888         DBG << "->" << path << std::endl;
00889         info = PathInfo(path, PathInfo::LSTAT);
00890       }
00891 
00892       // expand limit reached
00893       if (count == 0)
00894       {
00895         ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
00896         return Pathname();
00897       }
00898       // symlink
00899       else if (count < level_limit)
00900       {
00901         // check for a broken link
00902         if (PathInfo(path).isExist())
00903           return path;
00904         // broken link, return an empty path
00905         else
00906         {
00907           ERR << path << " is broken (expanded from " << path_r << ")" << endl;
00908           return Pathname();
00909         }
00910       }
00911 
00912       // not a symlink, return the original pathname
00913       DBG << "not a symlink" << endl;
00914       return path;
00915     }
00916 
00918     //
00919     //  METHOD NAME : copy_file2dir
00920     //  METHOD TYPE : int
00921     //
00922     int copy_file2dir( const Pathname & file, const Pathname & dest )
00923     {
00924       MIL << "copy_file2dir " << file << " -> " << dest << ' ';
00925 
00926       PathInfo sp( file );
00927       if ( !sp.isFile() ) {
00928         return _Log_Result( EINVAL );
00929       }
00930 
00931       PathInfo dp( dest );
00932       if ( !dp.isDir() ) {
00933         return _Log_Result( ENOTDIR );
00934       }
00935 
00936       const char *const argv[] = {
00937         "/bin/cp",
00938         "--",
00939         file.asString().c_str(),
00940         dest.asString().c_str(),
00941         NULL
00942       };
00943       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
00944       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
00945         MIL << "  " << output;
00946       }
00947       int ret = prog.close();
00948       return _Log_Result( ret, "returned" );
00949     }
00950 
00952     //
00953     //  METHOD NAME : md5sum
00954     //  METHOD TYPE : std::string
00955     //
00956     std::string md5sum( const Pathname & file )
00957     {
00958       if ( ! PathInfo( file ).isFile() ) {
00959         return string();
00960       }
00961       std::ifstream istr( file.asString().c_str() );
00962       if ( ! istr ) {
00963         return string();
00964       }
00965       return Digest::digest( "MD5", istr );
00966     }
00967 
00969     //
00970     //  METHOD NAME : sha1sum
00971     //  METHOD TYPE : std::string
00972     //
00973     std::string sha1sum( const Pathname & file )
00974     {
00975       return checksum(file, "SHA1");
00976     }
00977 
00979     //
00980     //  METHOD NAME : checksum
00981     //  METHOD TYPE : std::string
00982     //
00983     std::string checksum( const Pathname & file, const std::string &algorithm )
00984     {
00985       if ( ! PathInfo( file ).isFile() ) {
00986         return string();
00987       }
00988       std::ifstream istr( file.asString().c_str() );
00989       if ( ! istr ) {
00990         return string();
00991       }
00992       return Digest::digest( algorithm, istr );
00993     }
00994 
00995     bool is_checksum( const Pathname & file, const CheckSum &checksum )
00996     {
00997       return ( filesystem::checksum(file,  checksum.type()) == checksum.checksum() );
00998     }
00999 
01001     //
01002     //  METHOD NAME : erase
01003     //  METHOD TYPE : int
01004     //
01005     int erase( const Pathname & path )
01006     {
01007       int res = 0;
01008       PathInfo p( path, PathInfo::LSTAT );
01009       if ( p.isExist() )
01010         {
01011           if ( p.isDir() )
01012             res = recursive_rmdir( path );
01013           else
01014             res = unlink( path );
01015         }
01016       return res;
01017     }
01018 
01020     //
01021     //  METHOD NAME : chmod
01022     //  METHOD TYPE : int
01023     //
01024     int chmod( const Pathname & path, mode_t mode )
01025     {
01026       MIL << "chmod " << path << ' ' << str::octstring( mode );
01027       if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
01028         return _Log_Result( errno );
01029       }
01030       return _Log_Result( 0 );
01031     }
01032 
01033     int addmod( const Pathname & path, mode_t mode )
01034     {
01035       mode_t omode( PathInfo( path ).st_mode() );
01036       mode_t tmode( omode | mode );
01037       if ( omode != mode )
01038         return chmod( path, tmode );
01039       return 0;
01040     }
01041 
01042     int delmod( const Pathname & path, mode_t mode )
01043     {
01044       mode_t omode( PathInfo( path ).st_mode() );
01045       mode_t tmode( omode & ~mode );
01046       if ( omode != mode )
01047         return chmod( path, tmode );
01048       return 0;
01049     }
01050 
01052     //
01053     //  METHOD NAME : zipType
01054     //  METHOD TYPE : ZIP_TYPE
01055     //
01056     ZIP_TYPE zipType( const Pathname & file )
01057     {
01058       ZIP_TYPE ret = ZT_NONE;
01059 
01060       int fd = open( file.asString().c_str(), O_RDONLY );
01061 
01062       if ( fd != -1 ) {
01063         const int magicSize = 3;
01064         unsigned char magic[magicSize];
01065         memset( magic, 0, magicSize );
01066         if ( read( fd, magic, magicSize ) == magicSize ) {
01067           if ( magic[0] == 0037 && magic[1] == 0213 ) {
01068             ret = ZT_GZ;
01069           } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
01070             ret = ZT_BZ2;
01071           }
01072         }
01073         close( fd );
01074       }
01075 
01076       return ret;
01077     }
01078 
01080     //
01081     //  METHOD NAME : df
01082     //  METHOD TYPE : ByteCount
01083     //
01084     ByteCount df( const Pathname & path_r )
01085     {
01086       ByteCount ret( -1 );
01087       struct statvfs sb;
01088       if ( statvfs( path_r.c_str(), &sb ) == 0 )
01089         {
01090           ret = sb.f_bfree * sb.f_bsize;
01091         }
01092       return ret;
01093     }
01094 
01096     //
01097     //  METHOD NAME : getUmask
01098     //  METHOD TYPE : mode_t
01099     //
01100     mode_t getUmask()
01101     {
01102       mode_t mask = ::umask( 0022 );
01103       ::umask( mask );
01104       return mask;
01105     }
01106 
01108     //
01109     //  METHOD NAME : getUmask
01110     //  METHOD TYPE : mode_t
01111     //
01112     int assert_file( const Pathname & path, unsigned mode )
01113     {
01114       int ret = assert_dir( path.dirname() );
01115       MIL << "assert_file " << str::octstring( mode ) << " " << path;
01116       if ( ret != 0 )
01117         return _Log_Result( ret );
01118 
01119       PathInfo pi( path );
01120       if ( pi.isExist() )
01121         return _Log_Result( pi.isFile() ? 0 : EEXIST );
01122 
01123       int fd = ::creat( path.c_str(), mode );
01124       if ( fd == -1 )
01125         return _Log_Result( errno );
01126 
01127       ::close( fd );
01128       return _Log_Result( 0 );
01129     }
01130 
01132     //
01133     //  METHOD NAME : touch
01134     //  METHOD TYPE : int
01135     //
01136     int touch (const Pathname & path)
01137     {
01138       MIL << "touch " << path;
01139       struct ::utimbuf times;
01140       times.actime = ::time( 0 );
01141       times.modtime = ::time( 0 );
01142       if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
01143         return _Log_Result( errno );
01144       }
01145       return _Log_Result( 0 );
01146     }
01147 
01149   } // namespace filesystem
01152 } // namespace zypp