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