Imported Upstream version 14.35.0
[platform/upstream/libzypp.git] / zypp / PathInfo.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PathInfo.cc
10  *
11 */
12
13 #include <sys/types.h> // for ::minor, ::major macros
14 #include <utime.h>     // for ::utime
15 #include <sys/statvfs.h>
16
17 #include <iostream>
18 #include <fstream>
19 #include <iomanip>
20
21 #include "zypp/base/LogTools.h"
22 #include "zypp/base/String.h"
23 #include "zypp/base/IOStream.h"
24 #include "zypp/base/StrMatcher.h"
25 #include "zypp/base/Errno.h"
26
27 #include "zypp/AutoDispose.h"
28 #include "zypp/ExternalProgram.h"
29 #include "zypp/PathInfo.h"
30 #include "zypp/Digest.h"
31 #include "zypp/TmpPath.h"
32
33 using std::endl;
34 using std::string;
35
36 ///////////////////////////////////////////////////////////////////
37 namespace zypp
38 { /////////////////////////////////////////////////////////////////
39   ///////////////////////////////////////////////////////////////////
40   namespace filesystem
41   { /////////////////////////////////////////////////////////////////
42
43     /******************************************************************
44      **
45      ** FUNCTION NAME : operator<<
46      ** FUNCTION TYPE : std::ostream &
47     */
48     std::ostream & operator<<( std::ostream & str, FileType obj )
49     {
50       switch ( obj ) {
51 #define EMUMOUT(T) case T: return str << #T; break
52         EMUMOUT( FT_NOT_AVAIL );
53         EMUMOUT( FT_NOT_EXIST );
54         EMUMOUT( FT_FILE );
55         EMUMOUT( FT_DIR );
56         EMUMOUT( FT_CHARDEV );
57         EMUMOUT( FT_BLOCKDEV );
58         EMUMOUT( FT_FIFO );
59         EMUMOUT( FT_LINK );
60         EMUMOUT( FT_SOCKET );
61 #undef EMUMOUT
62       }
63       return str;
64     }
65
66     ///////////////////////////////////////////////////////////////////
67     //
68     //  METHOD NAME : StatMode::fileType
69     //  METHOD TYPE : FileType
70     //
71     FileType StatMode::fileType() const
72     {
73       if ( isFile() )
74         return FT_FILE;
75       if ( isDir() )
76         return FT_DIR;
77       if ( isLink() )
78         return FT_LINK;
79       if ( isChr() )
80         return FT_CHARDEV;
81       if ( isBlk() )
82         return FT_BLOCKDEV;
83       if ( isFifo() )
84         return FT_FIFO;
85       if ( isSock() )
86         return FT_SOCKET ;
87
88       return FT_NOT_AVAIL;
89     }
90
91     /******************************************************************
92      **
93      ** FUNCTION NAME : operator<<
94      ** FUNCTION TYPE : std::ostream &
95     */
96     std::ostream & operator<<( std::ostream & str, const StatMode & obj )
97     {
98       iostr::IosFmtFlagsSaver autoResoreState( str );
99
100       char t = '?';
101       if ( obj.isFile() )
102         t = '-';
103       else if ( obj.isDir() )
104         t = 'd';
105       else if ( obj.isLink() )
106         t = 'l';
107       else if ( obj.isChr() )
108         t = 'c';
109       else if ( obj.isBlk() )
110         t = 'b';
111       else if ( obj.isFifo() )
112         t = 'p';
113       else if ( obj.isSock() )
114         t = 's';
115
116       str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
117       return str;
118     }
119
120     ///////////////////////////////////////////////////////////////////
121     //
122     //  Class : PathInfo
123     //
124     ///////////////////////////////////////////////////////////////////
125
126     ///////////////////////////////////////////////////////////////////
127     //
128     //  METHOD NAME : PathInfo::PathInfo
129     //  METHOD TYPE : Constructor
130     //
131     PathInfo::PathInfo()
132     : mode_e( STAT )
133     , error_i( -1 )
134     {}
135
136     ///////////////////////////////////////////////////////////////////
137     //
138     //  METHOD NAME : PathInfo::PathInfo
139     //  METHOD TYPE : Constructor
140     //
141     PathInfo::PathInfo( const Pathname & path, Mode initial )
142     : path_t( path )
143     , mode_e( initial )
144     , error_i( -1 )
145     {
146       operator()();
147     }
148
149     ///////////////////////////////////////////////////////////////////
150     //
151     //  METHOD NAME : PathInfo::PathInfo
152     //  METHOD TYPE : Constructor
153     //
154     PathInfo::PathInfo( const std::string & path, Mode initial )
155     : path_t( path )
156     , mode_e( initial )
157     , error_i( -1 )
158     {
159       operator()();
160     }
161
162     ///////////////////////////////////////////////////////////////////
163     //
164     //  METHOD NAME : PathInfo::PathInfo
165     //  METHOD TYPE : Constructor
166     //
167     PathInfo::PathInfo( const char * path, Mode initial )
168     : path_t( path )
169     , mode_e( initial )
170     , error_i( -1 )
171     {
172       operator()();
173     }
174
175     ///////////////////////////////////////////////////////////////////
176     //
177     //  METHOD NAME : PathInfo::~PathInfo
178     //  METHOD TYPE : Destructor
179     //
180     PathInfo::~PathInfo()
181     {
182     }
183
184     ///////////////////////////////////////////////////////////////////
185     //
186     //  METHOD NAME : PathInfo::operator()
187     //  METHOD TYPE : bool
188     //
189     bool PathInfo::operator()()
190     {
191       if ( path_t.empty() ) {
192         error_i = -1;
193       } else {
194         switch ( mode_e ) {
195         case STAT:
196           error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
197           break;
198         case LSTAT:
199           error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
200           break;
201         }
202         if ( error_i == -1 )
203           error_i = errno;
204       }
205       return !error_i;
206     }
207
208     ///////////////////////////////////////////////////////////////////
209     //
210     //  METHOD NAME : PathInfo::fileType
211     //  METHOD TYPE : File_type
212     //
213     FileType PathInfo::fileType() const
214     {
215       if ( isExist() )
216         return asStatMode().fileType();
217       return FT_NOT_EXIST;
218     }
219
220     ///////////////////////////////////////////////////////////////////
221     //
222     //  METHOD NAME : PathInfo::userMay
223     //  METHOD TYPE : mode_t
224     //
225     mode_t PathInfo::userMay() const
226     {
227       if ( !isExist() )
228         return 0;
229       if ( owner() == geteuid() ) {
230         return( uperm()/0100 );
231       } else if ( group() == getegid() ) {
232         return( gperm()/010 );
233       }
234       return operm();
235     }
236
237     /******************************************************************
238      **
239      ** FUNCTION NAME : PathInfo::devMajor
240      ** FUNCTION TYPE : unsigned int
241      */
242     unsigned int PathInfo::devMajor() const
243     {
244       return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
245     }
246
247     /******************************************************************
248      **
249      ** FUNCTION NAME : PathInfo::devMinor
250      ** FUNCTION TYPE : unsigned int
251      */
252     unsigned int PathInfo::devMinor() const
253     {
254       return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
255     }
256
257     unsigned int PathInfo::major() const
258     { INT << "Cleanup the code: This method is deprecated" << endl; return devMajor(); }
259     unsigned int PathInfo::minor() const
260     { INT << "Cleanup the code: This method is deprecated" << endl; return devMinor(); }
261
262     /******************************************************************
263      **
264      ** FUNCTION NAME : operator<<
265      ** FUNCTION TYPE :  std::ostream &
266     */
267     std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
268     {
269       iostr::IosFmtFlagsSaver autoResoreState( str );
270
271       str << obj.asString() << "{";
272       if ( !obj.isExist() ) {
273         str << Errno( obj.error() );
274       } else {
275         str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
276
277         if ( obj.isFile() )
278           str << " size " << obj.size();
279       }
280
281       return str << "}";
282     }
283
284     ///////////////////////////////////////////////////////////////////
285     //
286     //  filesystem utilities
287     //
288     ///////////////////////////////////////////////////////////////////
289
290     /******************************************************************
291      **
292      ** FUNCTION NAME : _Log_Result
293      ** FUNCTION TYPE : int
294      **
295      ** DESCRIPTION : Helper function to log return values.
296     */
297 #define _Log_Result MIL << endl, __Log_Result
298     inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
299     {
300       if ( res )
301       {
302         if ( rclass )
303           WAR << " FAILED: " << rclass << " " << res << endl;
304         else
305           WAR << " FAILED: " << str::strerror( res ) << endl;
306       }
307       return res;
308     }
309
310     ///////////////////////////////////////////////////////////////////
311     //
312     //  METHOD NAME : PathInfo::mkdir
313     //  METHOD TYPE : int
314     //
315     int mkdir( const Pathname & path, unsigned mode )
316     {
317       MIL << "mkdir " << path << ' ' << str::octstring( mode );
318       if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
319         return _Log_Result( errno );
320       }
321       return _Log_Result( 0 );
322     }
323
324     ///////////////////////////////////////////////////////////////////
325     //
326     //  METHOD NAME : assert_dir()
327     //  METHOD TYPE : int
328     //
329     int assert_dir( const Pathname & path, unsigned mode )
330     {
331       if ( path.empty() )
332         return ENOENT;
333
334       { // Handle existing paths in advance.
335         PathInfo pi( path );
336         if ( pi.isDir() )
337           return 0;
338         if ( pi.isExist() )
339           return EEXIST;
340       }
341
342       string spath = path.asString()+"/";
343       string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
344       string::size_type pos = string::npos;
345       int ret = 0;
346
347       while ( (pos = spath.find('/',lastpos)) != string::npos )
348       {
349         string dir( spath.substr(0,pos) );
350         ret = ::mkdir( dir.c_str(), mode );
351         if ( ret == -1 )
352         {
353           if ( errno == EEXIST ) // ignore errors about already existing paths
354             ret = 0;
355           else
356           {
357             ret = errno;
358             WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
359           }
360         }
361         else
362         {
363           MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
364         }
365         lastpos = pos+1;
366       }
367
368       return ret;
369     }
370
371     ///////////////////////////////////////////////////////////////////
372     //
373     //  METHOD NAME : rmdir
374     //  METHOD TYPE : int
375     //
376     int rmdir( const Pathname & path )
377     {
378       MIL << "rmdir " << path;
379       if ( ::rmdir( path.asString().c_str() ) == -1 ) {
380         return _Log_Result( errno );
381       }
382       return _Log_Result( 0 );
383     }
384
385     ///////////////////////////////////////////////////////////////////
386     //
387     //  METHOD NAME : recursive_rmdir
388     //  METHOD TYPE : int
389     //
390     static int recursive_rmdir_1( const Pathname & dir )
391     {
392       DIR * dp;
393       struct dirent * d;
394
395       if ( ! (dp = opendir( dir.c_str() )) )
396         return _Log_Result( errno );
397
398       while ( (d = readdir(dp)) )
399       {
400         std::string direntry = d->d_name;
401         if ( direntry == "." || direntry == ".." )
402           continue;
403         Pathname new_path( dir / d->d_name );
404
405         struct stat st;
406         if ( ! lstat( new_path.c_str(), &st ) )
407         {
408           if ( S_ISDIR( st.st_mode ) )
409             recursive_rmdir_1( new_path );
410           else
411             ::unlink( new_path.c_str() );
412         }
413       }
414       closedir( dp );
415
416       if ( ::rmdir( dir.c_str() ) < 0 )
417         return errno;
418
419       return 0;
420     }
421     ///////////////////////////////////////////////////////////////////
422     int recursive_rmdir( const Pathname & path )
423     {
424       MIL << "recursive_rmdir " << path << ' ';
425       PathInfo p( path );
426
427       if ( !p.isExist() ) {
428         return _Log_Result( 0 );
429       }
430
431       if ( !p.isDir() ) {
432         return _Log_Result( ENOTDIR );
433       }
434
435       return _Log_Result( recursive_rmdir_1( path ) );
436     }
437
438     ///////////////////////////////////////////////////////////////////
439     //
440     //  METHOD NAME : clean_dir
441     //  METHOD TYPE : int
442     //
443     int clean_dir( const Pathname & path )
444     {
445       MIL << "clean_dir " << path << ' ';
446       PathInfo p( path );
447
448       if ( !p.isExist() ) {
449         return _Log_Result( 0 );
450       }
451
452       if ( !p.isDir() ) {
453         return _Log_Result( ENOTDIR );
454       }
455
456       string cmd( str::form( "cd '%s' && rm -rf --preserve-root -- *", path.asString().c_str() ) );
457       ExternalProgram prog( cmd, ExternalProgram::Stderr_To_Stdout );
458       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
459         MIL << "  " << output;
460       }
461       int ret = prog.close();
462       return _Log_Result( ret, "returned" );
463     }
464
465     ///////////////////////////////////////////////////////////////////
466     //
467     //  METHOD NAME : copy_dir
468     //  METHOD TYPE : int
469     //
470     int copy_dir( const Pathname & srcpath, const Pathname & destpath )
471     {
472       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
473
474       PathInfo sp( srcpath );
475       if ( !sp.isDir() ) {
476         return _Log_Result( ENOTDIR );
477       }
478
479       PathInfo dp( destpath );
480       if ( !dp.isDir() ) {
481         return _Log_Result( ENOTDIR );
482       }
483
484       PathInfo tp( destpath + srcpath.basename() );
485       if ( tp.isExist() ) {
486         return _Log_Result( EEXIST );
487       }
488
489
490       const char *const argv[] = {
491         "/bin/cp",
492         "-dR",
493         "--",
494         srcpath.asString().c_str(),
495         destpath.asString().c_str(),
496         NULL
497       };
498       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
499       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
500         MIL << "  " << output;
501       }
502       int ret = prog.close();
503       return _Log_Result( ret, "returned" );
504     }
505
506     ///////////////////////////////////////////////////////////////////
507     //
508     //  METHOD NAME : copy_dir_content
509     //  METHOD TYPE : int
510     //
511     int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
512     {
513       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
514
515       PathInfo sp( srcpath );
516       if ( !sp.isDir() ) {
517         return _Log_Result( ENOTDIR );
518       }
519
520       PathInfo dp( destpath );
521       if ( !dp.isDir() ) {
522         return _Log_Result( ENOTDIR );
523       }
524
525       if ( srcpath == destpath ) {
526         return _Log_Result( EEXIST );
527       }
528
529       std::string src( srcpath.asString());
530       src += "/.";
531       const char *const argv[] = {
532         "/bin/cp",
533         "-dR",
534         "--",
535         src.c_str(),
536         destpath.asString().c_str(),
537         NULL
538       };
539       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
540       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
541         MIL << "  " << output;
542       }
543       int ret = prog.close();
544       return _Log_Result( ret, "returned" );
545     }
546
547     ///////////////////////////////////////////////////////////////////////
548     // dirForEach
549     ///////////////////////////////////////////////////////////////////////
550
551     const StrMatcher & matchNoDots()
552     {
553       static StrMatcher noDots( "[^.]*", Match::GLOB );
554       return noDots;
555     }
556
557     int dirForEach( const Pathname & dir_r, function<bool(const Pathname &, const char *const)> fnc_r )
558     {
559       if ( ! fnc_r )
560         return 0;
561
562       AutoDispose<DIR *> dir( ::opendir( dir_r.c_str() ),
563                               []( DIR * dir_r ) { if ( dir_r ) ::closedir( dir_r ); } );
564
565       MIL << "readdir " << dir_r << ' ';
566       if ( ! dir )
567         return _Log_Result( errno );
568       MIL << endl; // close line before callbacks are invoked.
569
570       int ret = 0;
571       for ( struct dirent * entry = ::readdir( dir ); entry; entry = ::readdir( dir ) )
572       {
573         if ( entry->d_name[0] == '.' && ( entry->d_name[1] == '\0' || ( entry->d_name[1] == '.' && entry->d_name[2] == '\0' ) ) )
574           continue; // omitt . and ..
575
576         if ( ! fnc_r( dir_r, entry->d_name ) )
577         {
578           ret = -1;
579           break;
580         }
581       }
582       return ret;
583     }
584
585     int dirForEach( const Pathname & dir_r, const StrMatcher & matcher_r, function<bool( const Pathname &, const char *const)> fnc_r )
586     {
587       if ( ! fnc_r )
588         return 0;
589
590       bool nodots = ( &matcher_r == &matchNoDots() );
591       return dirForEach( dir_r,
592                          [&]( const Pathname & dir_r, const char *const name_r )->bool
593                          {
594                            if ( ( nodots && name_r[0] == '.' ) || ! matcher_r( name_r ) )
595                              return true;
596                            return fnc_r( dir_r, name_r );
597                          } );
598     }
599
600     ///////////////////////////////////////////////////////////////////
601     // readdir
602     ///////////////////////////////////////////////////////////////////
603
604     int readdir( std::list<std::string> & retlist_r, const Pathname & path_r, bool dots_r )
605     {
606       retlist_r.clear();
607       return dirForEach( path_r,
608                          [&]( const Pathname & dir_r, const char *const name_r )->bool
609                          {
610                            if ( dots_r || name_r[0] != '.' )
611                              retlist_r.push_back( name_r );
612                            return true;
613                          } );
614     }
615
616
617     int readdir( std::list<Pathname> & retlist_r, const Pathname & path_r, bool dots_r )
618     {
619       retlist_r.clear();
620       return dirForEach( path_r,
621                          [&]( const Pathname & dir_r, const char *const name_r )->bool
622                          {
623                            if ( dots_r || name_r[0] != '.' )
624                              retlist_r.push_back( dir_r/name_r );
625                            return true;
626                          } );
627     }
628
629     bool DirEntry::operator==( const DirEntry &rhs ) const
630     {
631       // if one of the types is not known, use the name only
632       if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
633         return ( name == rhs.name );
634       return ((name == rhs.name ) && (type == rhs.type));
635     }
636
637     int readdir( DirContent & retlist_r, const Pathname & path_r, bool dots_r, PathInfo::Mode statmode_r )
638     {
639       retlist_r.clear();
640       return dirForEach( path_r,
641                          [&]( const Pathname & dir_r, const char *const name_r )->bool
642                          {
643                            if ( dots_r || name_r[0] != '.' )
644                              retlist_r.push_back( DirEntry( name_r, PathInfo( dir_r/name_r, statmode_r ).fileType() ) );
645                            return true;
646                          } );
647     }
648
649     std::ostream & operator<<( std::ostream & str, const DirContent & obj )
650     { return dumpRange( str, obj.begin(), obj.end() ); }
651
652     ///////////////////////////////////////////////////////////////////
653     // is_empty_dir
654     ///////////////////////////////////////////////////////////////////
655
656     int is_empty_dir( const Pathname & path_r )
657     {
658       return dirForEach( path_r,
659                          [&]( const Pathname & dir_r, const char *const name_r )->bool
660                          { return false; } );
661     }
662
663     ///////////////////////////////////////////////////////////////////
664     //
665     //  METHOD NAME : unlink
666     //  METHOD TYPE : int
667     //
668     int unlink( const Pathname & path )
669     {
670       MIL << "unlink " << path;
671       if ( ::unlink( path.asString().c_str() ) == -1 ) {
672         return _Log_Result( errno );
673       }
674       return _Log_Result( 0 );
675     }
676
677     ///////////////////////////////////////////////////////////////////
678     //
679     //  METHOD NAME : rename
680     //  METHOD TYPE : int
681     //
682     int rename( const Pathname & oldpath, const Pathname & newpath )
683     {
684       MIL << "rename " << oldpath << " -> " << newpath;
685       if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
686         return _Log_Result( errno );
687       }
688       return _Log_Result( 0 );
689     }
690
691     ///////////////////////////////////////////////////////////////////
692     //
693     //  METHOD NAME : exchange
694     //  METHOD TYPE : int
695     //
696     int exchange( const Pathname & lpath, const Pathname & rpath )
697     {
698       MIL << "exchange " << lpath << " <-> " << rpath;
699       if ( lpath.empty() || rpath.empty() )
700         return _Log_Result( EINVAL );
701
702       PathInfo linfo( lpath );
703       PathInfo rinfo( rpath );
704
705       if ( ! linfo.isExist() )
706       {
707         if ( ! rinfo.isExist() )
708           return _Log_Result( 0 ); // both don't exist.
709
710         // just rename rpath -> lpath
711         int ret = assert_dir( lpath.dirname() );
712         if ( ret != 0 )
713           return _Log_Result( ret );
714         if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
715           return _Log_Result( errno );
716         }
717         return _Log_Result( 0 );
718       }
719
720       // HERE: lpath exists:
721       if ( ! rinfo.isExist() )
722       {
723         // just rename lpath -> rpath
724         int ret = assert_dir( rpath.dirname() );
725         if ( ret != 0 )
726           return _Log_Result( ret );
727         if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
728           return _Log_Result( errno );
729         }
730         return _Log_Result( 0 );
731       }
732
733       // HERE: both exist
734       TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
735       if ( ! tmpfile )
736         return _Log_Result( errno );
737       Pathname tmp( tmpfile.path() );
738       ::unlink( tmp.c_str() );
739
740       if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
741         return _Log_Result( errno );
742       }
743       if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
744         ::rename( tmp.c_str(), lpath.c_str() );
745         return _Log_Result( errno );
746       }
747       if ( ::rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
748         ::rename( lpath.c_str(), rpath.c_str() );
749         ::rename( tmp.c_str(), lpath.c_str() );
750         return _Log_Result( errno );
751       }
752       return _Log_Result( 0 );
753     }
754
755     ///////////////////////////////////////////////////////////////////
756     //
757     //  METHOD NAME : copy
758     //  METHOD TYPE : int
759     //
760     int copy( const Pathname & file, const Pathname & dest )
761     {
762       MIL << "copy " << file << " -> " << dest << ' ';
763
764       PathInfo sp( file );
765       if ( !sp.isFile() ) {
766         return _Log_Result( EINVAL );
767       }
768
769       PathInfo dp( dest );
770       if ( dp.isDir() ) {
771         return _Log_Result( EISDIR );
772       }
773
774       const char *const argv[] = {
775         "/bin/cp",
776         "--remove-destination",
777         "--",
778         file.asString().c_str(),
779         dest.asString().c_str(),
780         NULL
781       };
782       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
783       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
784         MIL << "  " << output;
785       }
786       int ret = prog.close();
787       return _Log_Result( ret, "returned" );
788     }
789
790     ///////////////////////////////////////////////////////////////////
791     //
792     //  METHOD NAME : symlink
793     //  METHOD TYPE : int
794     //
795     int symlink( const Pathname & oldpath, const Pathname & newpath )
796     {
797       MIL << "symlink " << newpath << " -> " << oldpath;
798       if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
799         return _Log_Result( errno );
800       }
801       return _Log_Result( 0 );
802     }
803
804     ///////////////////////////////////////////////////////////////////
805     //
806     //  METHOD NAME : hardlink
807     //  METHOD TYPE : int
808     //
809     int hardlink( const Pathname & oldpath, const Pathname & newpath )
810     {
811       MIL << "hardlink " << newpath << " -> " << oldpath;
812       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
813         return _Log_Result( errno );
814       }
815       return _Log_Result( 0 );
816     }
817
818     ///////////////////////////////////////////////////////////////////
819     //
820     //  METHOD NAME : hardlink
821     //  METHOD TYPE : int
822     //
823     int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
824     {
825       MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
826
827       PathInfo pi( oldpath, PathInfo::LSTAT );
828       if ( pi.isLink() )
829       {
830         // dont hardlink symlinks!
831         return copy( oldpath, newpath );
832       }
833
834       pi.lstat( newpath );
835       if ( pi.isExist() )
836       {
837         int res = unlink( newpath );
838         if ( res != 0 )
839           return _Log_Result( res );
840       }
841
842       // Here: no symlink, no newpath
843       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
844       {
845         switch ( errno )
846         {
847           case EPERM: // /proc/sys/fs/protected_hardlink in proc(5)
848           case EXDEV: // oldpath  and  newpath are not on the same mounted file system
849             return copy( oldpath, newpath );
850             break;
851         }
852         return _Log_Result( errno );
853       }
854       return _Log_Result( 0 );
855     }
856
857     ///////////////////////////////////////////////////////////////////
858     //
859     //  METHOD NAME : readlink
860     //  METHOD TYPE : int
861     //
862     int readlink( const Pathname & symlink_r, Pathname & target_r )
863     {
864       static const ssize_t bufsiz = 2047;
865       static char buf[bufsiz+1];
866       ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
867       if ( ret == -1 )
868       {
869         target_r = Pathname();
870         MIL << "readlink " << symlink_r;
871         return _Log_Result( errno );
872       }
873       buf[ret] = '\0';
874       target_r = buf;
875       return 0;
876     }
877
878     ///////////////////////////////////////////////////////////////////
879     //
880     //  METHOD NAME : expandlink
881     //  METHOD TYPE : Pathname
882     //
883     Pathname expandlink( const Pathname & path_r )
884     {
885       static const unsigned int level_limit = 256;
886       static unsigned int count;
887       Pathname path(path_r);
888       PathInfo info(path_r, PathInfo::LSTAT);
889
890       for (count = level_limit; info.isLink() && count; count--)
891       {
892         DBG << "following symlink " << path;
893         path = path.dirname() / readlink(path);
894         DBG << "->" << path << std::endl;
895         info = PathInfo(path, PathInfo::LSTAT);
896       }
897
898       // expand limit reached
899       if (count == 0)
900       {
901         ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
902         return Pathname();
903       }
904       // symlink
905       else if (count < level_limit)
906       {
907         // check for a broken link
908         if (PathInfo(path).isExist())
909           return path;
910         // broken link, return an empty path
911         else
912         {
913           ERR << path << " is broken (expanded from " << path_r << ")" << endl;
914           return Pathname();
915         }
916       }
917
918       // not a symlink, return the original pathname
919       DBG << "not a symlink" << endl;
920       return path;
921     }
922
923     ///////////////////////////////////////////////////////////////////
924     //
925     //  METHOD NAME : copy_file2dir
926     //  METHOD TYPE : int
927     //
928     int copy_file2dir( const Pathname & file, const Pathname & dest )
929     {
930       MIL << "copy_file2dir " << file << " -> " << dest << ' ';
931
932       PathInfo sp( file );
933       if ( !sp.isFile() ) {
934         return _Log_Result( EINVAL );
935       }
936
937       PathInfo dp( dest );
938       if ( !dp.isDir() ) {
939         return _Log_Result( ENOTDIR );
940       }
941
942       const char *const argv[] = {
943         "/bin/cp",
944         "--",
945         file.asString().c_str(),
946         dest.asString().c_str(),
947         NULL
948       };
949       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
950       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
951         MIL << "  " << output;
952       }
953       int ret = prog.close();
954       return _Log_Result( ret, "returned" );
955     }
956
957     ///////////////////////////////////////////////////////////////////
958     //
959     //  METHOD NAME : md5sum
960     //  METHOD TYPE : std::string
961     //
962     std::string md5sum( const Pathname & file )
963     {
964       if ( ! PathInfo( file ).isFile() ) {
965         return string();
966       }
967       std::ifstream istr( file.asString().c_str() );
968       if ( ! istr ) {
969         return string();
970       }
971       return Digest::digest( "MD5", istr );
972     }
973
974     ///////////////////////////////////////////////////////////////////
975     //
976     //  METHOD NAME : sha1sum
977     //  METHOD TYPE : std::string
978     //
979     std::string sha1sum( const Pathname & file )
980     {
981       return checksum(file, "SHA1");
982     }
983
984     ///////////////////////////////////////////////////////////////////
985     //
986     //  METHOD NAME : checksum
987     //  METHOD TYPE : std::string
988     //
989     std::string checksum( const Pathname & file, const std::string &algorithm )
990     {
991       if ( ! PathInfo( file ).isFile() ) {
992         return string();
993       }
994       std::ifstream istr( file.asString().c_str() );
995       if ( ! istr ) {
996         return string();
997       }
998       return Digest::digest( algorithm, istr );
999     }
1000
1001     bool is_checksum( const Pathname & file, const CheckSum &checksum )
1002     {
1003       return ( filesystem::checksum(file,  checksum.type()) == checksum.checksum() );
1004     }
1005
1006     ///////////////////////////////////////////////////////////////////
1007     //
1008     //  METHOD NAME : erase
1009     //  METHOD TYPE : int
1010     //
1011     int erase( const Pathname & path )
1012     {
1013       int res = 0;
1014       PathInfo p( path, PathInfo::LSTAT );
1015       if ( p.isExist() )
1016         {
1017           if ( p.isDir() )
1018             res = recursive_rmdir( path );
1019           else
1020             res = unlink( path );
1021         }
1022       return res;
1023     }
1024
1025     ///////////////////////////////////////////////////////////////////
1026     //
1027     //  METHOD NAME : chmod
1028     //  METHOD TYPE : int
1029     //
1030     int chmod( const Pathname & path, mode_t mode )
1031     {
1032       MIL << "chmod " << path << ' ' << str::octstring( mode );
1033       if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
1034         return _Log_Result( errno );
1035       }
1036       return _Log_Result( 0 );
1037     }
1038
1039     int addmod( const Pathname & path, mode_t mode )
1040     {
1041       mode_t omode( PathInfo( path ).st_mode() );
1042       mode_t tmode( omode | mode );
1043       if ( omode != mode )
1044         return chmod( path, tmode );
1045       return 0;
1046     }
1047
1048     int delmod( const Pathname & path, mode_t mode )
1049     {
1050       mode_t omode( PathInfo( path ).st_mode() );
1051       mode_t tmode( omode & ~mode );
1052       if ( omode != mode )
1053         return chmod( path, tmode );
1054       return 0;
1055     }
1056
1057    //////////////////////////////////////////////////////////////////
1058     //
1059     //  METHOD NAME : zipType
1060     //  METHOD TYPE : ZIP_TYPE
1061     //
1062     ZIP_TYPE zipType( const Pathname & file )
1063     {
1064       ZIP_TYPE ret = ZT_NONE;
1065
1066       int fd = open( file.asString().c_str(), O_RDONLY|O_CLOEXEC );
1067
1068       if ( fd != -1 ) {
1069         const int magicSize = 3;
1070         unsigned char magic[magicSize];
1071         memset( magic, 0, magicSize );
1072         if ( read( fd, magic, magicSize ) == magicSize ) {
1073           if ( magic[0] == 0037 && magic[1] == 0213 ) {
1074             ret = ZT_GZ;
1075           } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
1076             ret = ZT_BZ2;
1077           }
1078         }
1079         close( fd );
1080       }
1081
1082       return ret;
1083     }
1084
1085     ///////////////////////////////////////////////////////////////////
1086     //
1087     //  METHOD NAME : df
1088     //  METHOD TYPE : ByteCount
1089     //
1090     ByteCount df( const Pathname & path_r )
1091     {
1092       ByteCount ret( -1 );
1093       struct statvfs sb;
1094       if ( statvfs( path_r.c_str(), &sb ) == 0 )
1095         {
1096           ret = sb.f_bfree * sb.f_bsize;
1097         }
1098       return ret;
1099     }
1100
1101     ///////////////////////////////////////////////////////////////////
1102     //
1103     //  METHOD NAME : getUmask
1104     //  METHOD TYPE : mode_t
1105     //
1106     mode_t getUmask()
1107     {
1108       mode_t mask = ::umask( 0022 );
1109       ::umask( mask );
1110       return mask;
1111     }
1112
1113     ///////////////////////////////////////////////////////////////////
1114     //
1115     //  METHOD NAME : getUmask
1116     //  METHOD TYPE : mode_t
1117     //
1118     int assert_file( const Pathname & path, unsigned mode )
1119     {
1120       int ret = assert_dir( path.dirname() );
1121       MIL << "assert_file " << str::octstring( mode ) << " " << path;
1122       if ( ret != 0 )
1123         return _Log_Result( ret );
1124
1125       PathInfo pi( path );
1126       if ( pi.isExist() )
1127         return _Log_Result( pi.isFile() ? 0 : EEXIST );
1128
1129       int fd = ::creat( path.c_str(), mode );
1130       if ( fd == -1 )
1131         return _Log_Result( errno );
1132
1133       ::close( fd );
1134       return _Log_Result( 0 );
1135     }
1136
1137     ///////////////////////////////////////////////////////////////////
1138     //
1139     //  METHOD NAME : touch
1140     //  METHOD TYPE : int
1141     //
1142     int touch (const Pathname & path)
1143     {
1144       MIL << "touch " << path;
1145       struct ::utimbuf times;
1146       times.actime = ::time( 0 );
1147       times.modtime = ::time( 0 );
1148       if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
1149         return _Log_Result( errno );
1150       }
1151       return _Log_Result( 0 );
1152     }
1153
1154     /////////////////////////////////////////////////////////////////
1155   } // namespace filesystem
1156   ///////////////////////////////////////////////////////////////////
1157   /////////////////////////////////////////////////////////////////
1158 } // namespace zypp
1159 ///////////////////////////////////////////////////////////////////