Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / PathInfo.cc
index c09ddf0..5cbc827 100644 (file)
 */
 
 #include <sys/types.h> // for ::minor, ::major macros
+#include <utime.h>     // for ::utime
+#include <sys/statvfs.h>
 
 #include <iostream>
 #include <fstream>
 #include <iomanip>
 
-#include <boost/filesystem/operations.hpp>
-#include <boost/filesystem/exception.hpp>
-
-#include "zypp/base/Logger.h"
+#include "zypp/base/LogTools.h"
 #include "zypp/base/String.h"
 #include "zypp/base/IOStream.h"
+#include "zypp/base/StrMatcher.h"
+#include "zypp/base/Errno.h"
 
+#include "zypp/AutoDispose.h"
 #include "zypp/ExternalProgram.h"
 #include "zypp/PathInfo.h"
 #include "zypp/Digest.h"
+#include "zypp/TmpPath.h"
 
-
+using std::endl;
 using std::string;
 
 ///////////////////////////////////////////////////////////////////
@@ -223,9 +226,9 @@ namespace zypp
     {
       if ( !isExist() )
         return 0;
-      if ( owner() == getuid() ) {
+      if ( owner() == geteuid() ) {
         return( uperm()/0100 );
-      } else if ( group() == getgid() ) {
+      } else if ( group() == getegid() ) {
         return( gperm()/010 );
       }
       return operm();
@@ -233,22 +236,22 @@ namespace zypp
 
     /******************************************************************
      **
-     **        FUNCTION NAME : PathInfo::major
+     **        FUNCTION NAME : PathInfo::devMajor
      **        FUNCTION TYPE : unsigned int
      */
-    unsigned int PathInfo::major() const
+    unsigned int PathInfo::devMajor() const
     {
-      return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
+      return isBlk() || isChr() ? major(statbuf_C.st_rdev) : 0;
     }
 
     /******************************************************************
      **
-     **        FUNCTION NAME : PathInfo::minor
+     **        FUNCTION NAME : PathInfo::devMinor
      **        FUNCTION TYPE : unsigned int
      */
-    unsigned int PathInfo::minor() const
+    unsigned int PathInfo::devMinor() const
     {
-      return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
+      return isBlk() || isChr() ? minor(statbuf_C.st_rdev) : 0;
     }
 
     /******************************************************************
@@ -262,17 +265,15 @@ namespace zypp
 
       str << obj.asString() << "{";
       if ( !obj.isExist() ) {
-        str << "does not exist}";
+        str << Errno( obj.error() );
       } else {
         str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
 
         if ( obj.isFile() )
           str << " size " << obj.size();
-
-        str << "}";
       }
 
-      return str;
+      return str << "}";
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -281,20 +282,21 @@ namespace zypp
     //
     ///////////////////////////////////////////////////////////////////
 
-    /******************************************************************
-     **
-     **        FUNCTION NAME : _Log_Result
-     **        FUNCTION TYPE : int
-     **
-     **        DESCRIPTION : Helper function to log return values.
-    */
-    inline int _Log_Result( const int res, const char * rclass = "errno" )
-    {
-      if ( res )
-        DBG << " FAILED: " << rclass << " " << res;
-      DBG << std::endl;
-      return 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 ( res )
+       {
+         if ( rclass )
+           WAR << " FAILED: " << rclass << " " << res << endl;
+         else
+           WAR << " FAILED: " << str::strerror( res ) << endl;
+       }
+       return res;
+      }
+    } // namespace
 
     ///////////////////////////////////////////////////////////////////
     //
@@ -303,11 +305,11 @@ namespace zypp
     //
     int mkdir( const Pathname & path, unsigned mode )
     {
-      DBG << "mkdir " << path << ' ' << str::octstring( mode );
+      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 );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -317,36 +319,43 @@ namespace zypp
     //
     int assert_dir( const Pathname & path, unsigned mode )
     {
-      string::size_type pos, lastpos = 0;
-      string spath = path.asString()+"/";
-      int ret = 0;
-
-      if(path.empty())
+      if ( path.empty() )
         return ENOENT;
 
-      // skip ./
-      if(path.relative())
-        lastpos=2;
-      // skip /
-      else
-        lastpos=1;
+      { // Handle existing paths in advance.
+        PathInfo pi( path );
+        if ( pi.isDir() )
+          return 0;
+        if ( pi.isExist() )
+          return EEXIST;
+      }
+
+      string spath = path.asString()+"/";
+      string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
+      string::size_type pos = string::npos;
+      int ret = 0;
 
-      //    DBG << "about to create " << spath << endl;
-      while((pos = spath.find('/',lastpos)) != string::npos )
+      while ( (pos = spath.find('/',lastpos)) != string::npos )
+      {
+        string dir( spath.substr(0,pos) );
+        ret = ::mkdir( dir.c_str(), mode );
+        if ( ret == -1 )
+        {
+          if ( errno == EEXIST ) // ignore errors about already existing paths
+            ret = 0;
+          else
+          {
+            ret = errno;
+            WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
+          }
+        }
+        else
         {
-          string dir = spath.substr(0,pos);
-          ret = ::mkdir(dir.c_str(), mode);
-          if(ret == -1)
-            {
-              // ignore errors about already existing directorys
-              if(errno == EEXIST)
-                ret=0;
-              else
-                ret=errno;
-            }
-          //   DBG << "creating directory " << dir << (ret?" failed":" succeeded") << endl;
-          lastpos = pos+1;
+          MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
         }
+        lastpos = pos+1;
+      }
+
       return ret;
     }
 
@@ -357,11 +366,11 @@ namespace zypp
     //
     int rmdir( const Pathname & path )
     {
-      DBG << "rmdir " << path;
+      MIL << "rmdir " << path;
       if ( ::rmdir( path.asString().c_str() ) == -1 ) {
-        return _Log_Result( errno );
+        return logResult( errno );
       }
-      return _Log_Result( 0 );
+      return logResult( 0 );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -369,31 +378,52 @@ namespace zypp
     // METHOD NAME : recursive_rmdir
     // METHOD TYPE : int
     //
+    static int recursive_rmdir_1( const Pathname & dir, bool removeDir = true )
+    {
+      DIR * dp;
+      struct dirent * d;
+
+      if ( ! (dp = opendir( dir.c_str() )) )
+        return logResult( errno );
+
+      while ( (d = readdir(dp)) )
+      {
+        std::string direntry = d->d_name;
+        if ( direntry == "." || direntry == ".." )
+          continue;
+        Pathname new_path( dir / d->d_name );
+
+        struct stat st;
+        if ( ! lstat( new_path.c_str(), &st ) )
+        {
+          if ( S_ISDIR( st.st_mode ) )
+            recursive_rmdir_1( new_path );
+          else
+            ::unlink( new_path.c_str() );
+        }
+      }
+      closedir( dp );
+
+      if ( removeDir && ::rmdir( dir.c_str() ) < 0 )
+        return errno;
+
+      return 0;
+    }
+    ///////////////////////////////////////////////////////////////////
     int recursive_rmdir( const Pathname & path )
     {
-      DBG << "recursive_rmdir " << path << ' ';
+      MIL << "recursive_rmdir " << path << ' ';
       PathInfo p( path );
 
       if ( !p.isExist() ) {
-        return _Log_Result( 0 );
+        return logResult( 0 );
       }
 
       if ( !p.isDir() ) {
-        return _Log_Result( ENOTDIR );
+        return logResult( ENOTDIR );
       }
 
-      try
-        {
-          boost::filesystem::path bp( path.asString(), boost::filesystem::native );
-          boost::filesystem::remove_all( bp );
-        }
-      catch ( boost::filesystem::filesystem_error & excpt )
-        {
-          DBG << " FAILED: " << excpt.what() << std::endl;
-          return -1;
-        }
-
-      return _Log_Result( 0 );
+      return logResult( recursive_rmdir_1( path ) );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -403,24 +433,18 @@ namespace zypp
     //
     int clean_dir( const Pathname & path )
     {
-      DBG << "clean_dir " << path << ' ';
+      MIL << "clean_dir " << path << ' ';
       PathInfo p( path );
 
       if ( !p.isExist() ) {
-        return _Log_Result( 0 );
+        return logResult( 0 );
       }
 
       if ( !p.isDir() ) {
-        return _Log_Result( ENOTDIR );
+        return logResult( ENOTDIR );
       }
 
-      string cmd( str::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
-      ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
-      for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
-        DBG << "  " << output;
-      }
-      int ret = prog.close();
-      return _Log_Result( ret, "returned" );
+      return logResult( recursive_rmdir_1( path, false/* don't remove path itself */ ) );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -430,21 +454,21 @@ namespace zypp
     //
     int copy_dir( const Pathname & srcpath, const Pathname & destpath )
     {
-      DBG << "copy_dir " << srcpath << " -> " << destpath << ' ';
+      MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
 
       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 );
       }
 
 
@@ -458,10 +482,10 @@ namespace zypp
       };
       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
-        DBG << "  " << output;
+        MIL << "  " << output;
       }
       int ret = prog.close();
-      return _Log_Result( ret, "returned" );
+      return logResult( ret, "returned" );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -471,20 +495,20 @@ namespace zypp
     //
     int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
     {
-      DBG << "copy_dir " << srcpath << " -> " << destpath << ' ';
+      MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
 
       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());
@@ -499,119 +523,126 @@ namespace zypp
       };
       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
-        DBG << "  " << output;
+        MIL << "  " << output;
       }
       int ret = prog.close();
-      return _Log_Result( ret, "returned" );
+      return logResult( ret, "returned" );
     }
 
-    ///////////////////////////////////////////////////////////////////
-    //
-    // METHOD NAME : readdir
-    //  METHOD TYPE : int
-    //
-    int readdir( std::list<std::string> & retlist,
-                 const Pathname & path, bool dots )
+    ///////////////////////////////////////////////////////////////////////
+    // dirForEach
+    ///////////////////////////////////////////////////////////////////////
+
+    const StrMatcher & matchNoDots()
     {
-      retlist.clear();
+      static StrMatcher noDots( "[^.]*", Match::GLOB );
+      return noDots;
+    }
 
-      DBG << "readdir " << path << ' ';
+    int dirForEach( const Pathname & dir_r, function<bool(const Pathname &, const char *const)> fnc_r )
+    {
+      if ( ! fnc_r )
+       return 0;
 
-      DIR * dir = ::opendir( path.asString().c_str() );
-      if ( ! dir ) {
-        return _Log_Result( errno );
-      }
+      AutoDispose<DIR *> dir( ::opendir( dir_r.c_str() ),
+                             []( DIR * dir_r ) { if ( dir_r ) ::closedir( dir_r ); } );
 
-      struct dirent *entry;
-      while ( (entry = ::readdir( dir )) != 0 ) {
+      MIL << "readdir " << dir_r << ' ';
+      if ( ! dir )
+       return logResult( errno );
+      MIL << endl; // close line before callbacks are invoked.
 
-        if ( entry->d_name[0] == '.' ) {
-          if ( !dots )
-            continue;
-          if ( entry->d_name[1] == '\0'
-               || (    entry->d_name[1] == '.'
-                    && entry->d_name[2] == '\0' ) )
-            continue;
-        }
-        retlist.push_back( entry->d_name );
+      int ret = 0;
+      for ( struct dirent * entry = ::readdir( dir ); entry; entry = ::readdir( dir ) )
+      {
+        if ( entry->d_name[0] == '.' && ( entry->d_name[1] == '\0' || ( entry->d_name[1] == '.' && entry->d_name[2] == '\0' ) ) )
+         continue; // omitt . and ..
+
+       if ( ! fnc_r( dir_r, entry->d_name ) )
+       {
+         ret = -1;
+         break;
+       }
       }
-
-      ::closedir( dir );
-
-      return _Log_Result( 0 );
+      return ret;
     }
 
-
-    ///////////////////////////////////////////////////////////////////
-    //
-    // METHOD NAME : readdir
-    // METHOD TYPE : int
-    //
-    int readdir( std::list<Pathname> & retlist,
-                 const Pathname & path, bool dots )
+    int dirForEach( const Pathname & dir_r, const StrMatcher & matcher_r, function<bool( const Pathname &, const char *const)> fnc_r )
     {
-      retlist.clear();
-
-      std::list<string> content;
-      int res = readdir( content, path, dots );
-
-      if ( !res ) {
-        for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
-          retlist.push_back( path + *it );
-        }
-      }
-
-      return res;
+      if ( ! fnc_r )
+       return 0;
+
+      bool nodots = ( &matcher_r == &matchNoDots() );
+      return dirForEach( dir_r,
+                        [&]( const Pathname & dir_r, const char *const name_r )->bool
+                        {
+                          if ( ( nodots && name_r[0] == '.' ) || ! matcher_r( name_r ) )
+                            return true;
+                          return fnc_r( dir_r, name_r );
+                        } );
     }
 
     ///////////////////////////////////////////////////////////////////
-    //
-    // METHOD NAME : readdir
-    // METHOD TYPE : int
-    //
-    int readdir( DirContent & retlist, const Pathname & path,
-                 bool dots, PathInfo::Mode statmode )
-    {
-      retlist.clear();
+    // readdir
+    ///////////////////////////////////////////////////////////////////
 
-      std::list<string> content;
-      int res = readdir( content, path, dots );
+    int readdir( std::list<std::string> & retlist_r, const Pathname & path_r, bool dots_r )
+    {
+      retlist_r.clear();
+      return dirForEach( path_r,
+                        [&]( const Pathname & dir_r, const char *const name_r )->bool
+                        {
+                          if ( dots_r || name_r[0] != '.' )
+                            retlist_r.push_back( name_r );
+                          return true;
+                        } );
+    }
 
-      if ( !res ) {
-        for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
-          PathInfo p( path + *it, statmode );
-          retlist.push_back( DirEntry( *it, p.fileType() ) );
-        }
-      }
 
-      return res;
+    int readdir( std::list<Pathname> & retlist_r, const Pathname & path_r, bool dots_r )
+    {
+      retlist_r.clear();
+      return dirForEach( path_r,
+                        [&]( const Pathname & dir_r, const char *const name_r )->bool
+                        {
+                          if ( dots_r || name_r[0] != '.' )
+                            retlist_r.push_back( dir_r/name_r );
+                          return true;
+                        } );
     }
 
-    ///////////////////////////////////////////////////////////////////
-    //
-    // METHOD NAME : is_empty_dir
-    // METHOD TYPE : int
-    //
-    int is_empty_dir(const Pathname & path)
+    bool DirEntry::operator==( const DirEntry &rhs ) const
     {
-      DIR * dir = ::opendir( path.asString().c_str() );
-      if ( ! dir ) {
-        return _Log_Result( errno );
-      }
+      // if one of the types is not known, use the name only
+      if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
+        return ( name == rhs.name );
+      return ((name == rhs.name ) && (type == rhs.type));
+    }
 
-      struct dirent *entry;
-      while ( (entry = ::readdir( dir )) != NULL )
-      {
-        std::string name(entry->d_name);
+    int readdir( DirContent & retlist_r, const Pathname & path_r, bool dots_r, PathInfo::Mode statmode_r )
+    {
+      retlist_r.clear();
+      return dirForEach( path_r,
+                        [&]( const Pathname & dir_r, const char *const name_r )->bool
+                        {
+                          if ( dots_r || name_r[0] != '.' )
+                            retlist_r.push_back( DirEntry( name_r, PathInfo( dir_r/name_r, statmode_r ).fileType() ) );
+                          return true;
+                        } );
+    }
 
-        if ( name == "." || name == "..")
-         continue;
+    std::ostream & operator<<( std::ostream & str, const DirContent & obj )
+    { return dumpRange( str, obj.begin(), obj.end() ); }
 
-        break;
-      }
-      ::closedir( dir );
+    ///////////////////////////////////////////////////////////////////
+    // is_empty_dir
+    ///////////////////////////////////////////////////////////////////
 
-      return entry != NULL ? -1 : 0;
+    int is_empty_dir( const Pathname & path_r )
+    {
+      return dirForEach( path_r,
+                        [&]( const Pathname & dir_r, const char *const name_r )->bool
+                        { return false; } );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -621,11 +652,11 @@ namespace zypp
     //
     int unlink( const Pathname & path )
     {
-      DBG << "unlink " << path;
+      MIL << "unlink " << path;
       if ( ::unlink( path.asString().c_str() ) == -1 ) {
-        return _Log_Result( errno );
+        return logResult( errno );
       }
-      return _Log_Result( 0 );
+      return logResult( 0 );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -635,11 +666,75 @@ namespace zypp
     //
     int rename( const Pathname & oldpath, const Pathname & newpath )
     {
-      DBG << "rename " << oldpath << " -> " << newpath;
+      MIL << "rename " << oldpath << " -> " << newpath;
       if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
-        return _Log_Result( errno );
+        return logResult( errno );
+      }
+      return logResult( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : exchange
+    // METHOD TYPE : int
+    //
+    int exchange( const Pathname & lpath, const Pathname & rpath )
+    {
+      MIL << "exchange " << lpath << " <-> " << rpath;
+      if ( lpath.empty() || rpath.empty() )
+        return logResult( EINVAL );
+
+      PathInfo linfo( lpath );
+      PathInfo rinfo( rpath );
+
+      if ( ! linfo.isExist() )
+      {
+        if ( ! rinfo.isExist() )
+          return logResult( 0 ); // both don't exist.
+
+        // just rename rpath -> lpath
+        int ret = assert_dir( lpath.dirname() );
+        if ( ret != 0 )
+          return logResult( ret );
+        if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
+          return logResult( errno );
+        }
+        return logResult( 0 );
+      }
+
+      // HERE: lpath exists:
+      if ( ! rinfo.isExist() )
+      {
+        // just rename lpath -> rpath
+        int ret = assert_dir( rpath.dirname() );
+        if ( ret != 0 )
+          return logResult( ret );
+        if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
+          return logResult( errno );
+        }
+        return logResult( 0 );
+      }
+
+      // HERE: both exist
+      TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
+      if ( ! tmpfile )
+        return logResult( errno );
+      Pathname tmp( tmpfile.path() );
+      ::unlink( tmp.c_str() );
+
+      if ( ::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 logResult( errno );
       }
-      return _Log_Result( 0 );
+      if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
+        ::rename( lpath.c_str(), rpath.c_str() );
+        ::rename( tmp.c_str(), lpath.c_str() );
+        return logResult( errno );
+      }
+      return logResult( 0 );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -649,20 +744,21 @@ namespace zypp
     //
     int copy( const Pathname & file, const Pathname & dest )
     {
-      DBG << "copy " << file << " -> " << dest << ' ';
+      MIL << "copy " << file << " -> " << dest << ' ';
 
       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[] = {
         "/bin/cp",
+        "--remove-destination",
         "--",
         file.asString().c_str(),
         dest.asString().c_str(),
@@ -670,10 +766,10 @@ namespace zypp
       };
       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
-        DBG << "  " << output;
+        MIL << "  " << output;
       }
       int ret = prog.close();
-      return _Log_Result( ret, "returned" );
+      return logResult( ret, "returned" );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -683,11 +779,11 @@ namespace zypp
     //
     int symlink( const Pathname & oldpath, const Pathname & newpath )
     {
-      DBG << "symlink " << newpath << " -> " << oldpath;
+      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 );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -697,11 +793,116 @@ namespace zypp
     //
     int hardlink( const Pathname & oldpath, const Pathname & newpath )
     {
-      DBG << "hardlink " << newpath << " -> " << oldpath;
+      MIL << "hardlink " << newpath << " -> " << oldpath;
       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
-        return _Log_Result( errno );
+        return logResult( errno );
+      }
+      return logResult( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : hardlink
+    // METHOD TYPE : int
+    //
+    int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
+    {
+      MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
+
+      PathInfo pi( oldpath, PathInfo::LSTAT );
+      if ( pi.isLink() )
+      {
+       // dont hardlink symlinks!
+       return copy( oldpath, newpath );
+      }
+
+      pi.lstat( newpath );
+      if ( pi.isExist() )
+      {
+       int res = unlink( newpath );
+       if ( res != 0 )
+         return logResult( res );
+      }
+
+      // Here: no symlink, no newpath
+      if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
+      {
+        switch ( errno )
+        {
+         case EPERM: // /proc/sys/fs/protected_hardlink in proc(5)
+          case EXDEV: // oldpath  and  newpath are not on the same mounted file system
+            return copy( oldpath, newpath );
+            break;
+        }
+        return logResult( errno );
+      }
+      return logResult( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : readlink
+    // METHOD TYPE : int
+    //
+    int readlink( const Pathname & symlink_r, Pathname & target_r )
+    {
+      static const ssize_t bufsiz = 2047;
+      static char buf[bufsiz+1];
+      ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
+      if ( ret == -1 )
+      {
+        target_r = Pathname();
+        MIL << "readlink " << symlink_r;
+        return logResult( errno );
+      }
+      buf[ret] = '\0';
+      target_r = buf;
+      return 0;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : expandlink
+    //  METHOD TYPE : Pathname
+    //
+    Pathname expandlink( const Pathname & path_r )
+    {
+      static const unsigned int level_limit = 256;
+      static unsigned int count;
+      Pathname path(path_r);
+      PathInfo info(path_r, PathInfo::LSTAT);
+
+      for (count = level_limit; info.isLink() && count; count--)
+      {
+        DBG << "following symlink " << path;
+        path = path.dirname() / readlink(path);
+        DBG << "->" << path << std::endl;
+        info = PathInfo(path, PathInfo::LSTAT);
       }
-      return _Log_Result( 0 );
+
+      // expand limit reached
+      if (count == 0)
+      {
+        ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
+        return Pathname();
+      }
+      // symlink
+      else if (count < level_limit)
+      {
+        // check for a broken link
+        if (PathInfo(path).isExist())
+          return path;
+        // broken link, return an empty path
+        else
+        {
+          ERR << path << " is broken (expanded from " << path_r << ")" << endl;
+          return Pathname();
+        }
+      }
+
+      // not a symlink, return the original pathname
+      DBG << "not a symlink" << endl;
+      return path;
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -711,16 +912,16 @@ namespace zypp
     //
     int copy_file2dir( const Pathname & file, const Pathname & dest )
     {
-      DBG << "copy_file2dir " << file << " -> " << dest << ' ';
+      MIL << "copy_file2dir " << file << " -> " << dest << ' ';
 
       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[] = {
@@ -732,10 +933,10 @@ namespace zypp
       };
       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
-        DBG << "  " << output;
+        MIL << "  " << output;
       }
       int ret = prog.close();
-      return _Log_Result( ret, "returned" );
+      return logResult( ret, "returned" );
     }
 
     ///////////////////////////////////////////////////////////////////
@@ -813,14 +1014,32 @@ namespace zypp
     //
     int chmod( const Pathname & path, mode_t mode )
     {
-      DBG << "chmod " << path << ' ' << str::octstring( mode );
+      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 )
+    {
+      mode_t omode( PathInfo( path ).st_mode() );
+      mode_t tmode( omode | mode );
+      if ( omode != mode )
+        return chmod( path, tmode );
+      return 0;
+    }
+
+    int delmod( const Pathname & path, mode_t mode )
+    {
+      mode_t omode( PathInfo( path ).st_mode() );
+      mode_t tmode( omode & ~mode );
+      if ( omode != mode )
+        return chmod( path, tmode );
+      return 0;
+    }
+
+   //////////////////////////////////////////////////////////////////
     //
     // METHOD NAME : zipType
     // METHOD TYPE : ZIP_TYPE
@@ -829,7 +1048,7 @@ namespace zypp
     {
       ZIP_TYPE ret = ZT_NONE;
 
-      int fd = open( file.asString().c_str(), O_RDONLY );
+      int fd = open( file.asString().c_str(), O_RDONLY|O_CLOEXEC );
 
       if ( fd != -1 ) {
         const int magicSize = 3;
@@ -848,6 +1067,75 @@ namespace zypp
       return ret;
     }
 
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : df
+    // METHOD TYPE : ByteCount
+    //
+    ByteCount df( const Pathname & path_r )
+    {
+      ByteCount ret( -1 );
+      struct statvfs sb;
+      if ( statvfs( path_r.c_str(), &sb ) == 0 )
+        {
+          ret = sb.f_bfree * sb.f_bsize;
+        }
+      return ret;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : getUmask
+    // METHOD TYPE : mode_t
+    //
+    mode_t getUmask()
+    {
+      mode_t mask = ::umask( 0022 );
+      ::umask( mask );
+      return mask;
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // METHOD NAME : getUmask
+    // METHOD TYPE : mode_t
+    //
+    int assert_file( const Pathname & path, unsigned mode )
+    {
+      int ret = assert_dir( path.dirname() );
+      MIL << "assert_file " << str::octstring( mode ) << " " << path;
+      if ( ret != 0 )
+        return logResult( ret );
+
+      PathInfo pi( path );
+      if ( pi.isExist() )
+        return logResult( pi.isFile() ? 0 : EEXIST );
+
+      int fd = ::creat( path.c_str(), mode );
+      if ( fd == -1 )
+        return logResult( errno );
+
+      ::close( fd );
+      return logResult( 0 );
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : touch
+    //  METHOD TYPE : int
+    //
+    int touch (const Pathname & path)
+    {
+      MIL << "touch " << path;
+      struct ::utimbuf times;
+      times.actime = ::time( 0 );
+      times.modtime = ::time( 0 );
+      if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
+        return logResult( errno );
+      }
+      return logResult( 0 );
+    }
+
     /////////////////////////////////////////////////////////////////
   } // namespace filesystem
   ///////////////////////////////////////////////////////////////////