Imported Upstream version 17.25.4
[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 <utime.h>     // for ::utime
14 #include <sys/statvfs.h>
15 #include <sys/sysmacros.h> // for ::minor, ::major macros
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     /******************************************************************
258      **
259      ** FUNCTION NAME : operator<<
260      ** FUNCTION TYPE :  std::ostream &
261     */
262     std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
263     {
264       iostr::IosFmtFlagsSaver autoResoreState( str );
265
266       str << obj.asString() << "{";
267       if ( !obj.isExist() ) {
268         str << Errno( obj.error() );
269       } else {
270         str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
271
272         if ( obj.isFile() )
273           str << " size " << obj.size();
274       }
275
276       return str << "}";
277     }
278
279     ///////////////////////////////////////////////////////////////////
280     //
281     //  filesystem utilities
282     //
283     ///////////////////////////////////////////////////////////////////
284
285 #define logResult MIL << endl, doLogResult
286     namespace {
287       /**  Helper function to log return values. */
288       inline int doLogResult( const int res, const char * rclass = 0 /*errno*/ )
289       {
290         if ( res )
291         {
292           if ( rclass )
293             WAR << " FAILED: " << rclass << " " << res << endl;
294           else
295             WAR << " FAILED: " << str::strerror( res ) << endl;
296         }
297         return res;
298       }
299     } // namespace
300
301     ///////////////////////////////////////////////////////////////////
302     //
303     //  METHOD NAME : PathInfo::mkdir
304     //  METHOD TYPE : int
305     //
306     int mkdir( const Pathname & path, unsigned mode )
307     {
308       MIL << "mkdir " << path << ' ' << str::octstring( mode );
309       if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
310         return logResult( errno );
311       }
312       return logResult( 0 );
313     }
314
315     ///////////////////////////////////////////////////////////////////
316     //
317     //  METHOD NAME : assert_dir()
318     //  METHOD TYPE : int
319     //
320     int assert_dir( const Pathname & path, unsigned mode )
321     {
322       if ( path.empty() )
323         return ENOENT;
324
325       { // Handle existing paths in advance.
326         PathInfo pi( path );
327         if ( pi.isDir() )
328           return 0;
329         if ( pi.isExist() )
330           return EEXIST;
331       }
332
333       string spath = path.asString()+"/";
334       std::string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
335       std::string::size_type pos = std::string::npos;
336       int ret = 0;
337
338       while ( (pos = spath.find('/',lastpos)) != std::string::npos )
339       {
340         string dir( spath.substr(0,pos) );
341         ret = ::mkdir( dir.c_str(), mode );
342         if ( ret == -1 )
343         {
344           if ( errno == EEXIST ) // ignore errors about already existing paths
345             ret = 0;
346           else
347           {
348             ret = errno;
349             WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
350           }
351         }
352         else
353         {
354           MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
355         }
356         lastpos = pos+1;
357       }
358
359       return ret;
360     }
361
362     ///////////////////////////////////////////////////////////////////
363     //
364     //  METHOD NAME : rmdir
365     //  METHOD TYPE : int
366     //
367     int rmdir( const Pathname & path )
368     {
369       MIL << "rmdir " << path;
370       if ( ::rmdir( path.asString().c_str() ) == -1 ) {
371         return logResult( errno );
372       }
373       return logResult( 0 );
374     }
375
376     ///////////////////////////////////////////////////////////////////
377     //
378     //  METHOD NAME : recursive_rmdir
379     //  METHOD TYPE : int
380     //
381     static int recursive_rmdir_1( const Pathname & dir, bool removeDir = true )
382     {
383       DIR * dp;
384       struct dirent * d;
385
386       if ( ! (dp = opendir( dir.c_str() )) )
387         return logResult( errno );
388
389       while ( (d = readdir(dp)) )
390       {
391         std::string direntry = d->d_name;
392         if ( direntry == "." || direntry == ".." )
393           continue;
394         Pathname new_path( dir / d->d_name );
395
396         struct stat st;
397         if ( ! lstat( new_path.c_str(), &st ) )
398         {
399           if ( S_ISDIR( st.st_mode ) )
400             recursive_rmdir_1( new_path );
401           else
402             ::unlink( new_path.c_str() );
403         }
404       }
405       closedir( dp );
406
407       if ( removeDir && ::rmdir( dir.c_str() ) < 0 )
408         return errno;
409
410       return 0;
411     }
412     ///////////////////////////////////////////////////////////////////
413     int recursive_rmdir( const Pathname & path )
414     {
415       MIL << "recursive_rmdir " << path << ' ';
416       PathInfo p( path );
417
418       if ( !p.isExist() ) {
419         return logResult( 0 );
420       }
421
422       if ( !p.isDir() ) {
423         return logResult( ENOTDIR );
424       }
425
426       p.lstat();        // get dir symlinks
427       if ( !p.isDir() ) {
428         MIL << "unlink symlink ";
429         if ( ::unlink( path.asString().c_str() ) == -1 ) {
430           return logResult( errno );
431         }
432         return logResult( 0 );
433       }
434
435       return logResult( 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 logResult( 0 );
450       }
451
452       if ( !p.isDir() ) {
453         return logResult( ENOTDIR );
454       }
455
456       return logResult( recursive_rmdir_1( path, false/* don't remove path itself */ ) );
457     }
458
459     ///////////////////////////////////////////////////////////////////
460     //
461     //  METHOD NAME : copy_dir
462     //  METHOD TYPE : int
463     //
464     int copy_dir( const Pathname & srcpath, const Pathname & destpath )
465     {
466       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
467
468       PathInfo sp( srcpath );
469       if ( !sp.isDir() ) {
470         return logResult( ENOTDIR );
471       }
472
473       PathInfo dp( destpath );
474       if ( !dp.isDir() ) {
475         return logResult( ENOTDIR );
476       }
477
478       PathInfo tp( destpath + srcpath.basename() );
479       if ( tp.isExist() ) {
480         return logResult( EEXIST );
481       }
482
483
484       const char *const argv[] = {
485         "/bin/cp",
486         "-dR",
487         "--",
488         srcpath.asString().c_str(),
489         destpath.asString().c_str(),
490         NULL
491       };
492       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
493       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
494         MIL << "  " << output;
495       }
496       int ret = prog.close();
497       return logResult( ret, "returned" );
498     }
499
500     ///////////////////////////////////////////////////////////////////
501     //
502     //  METHOD NAME : copy_dir_content
503     //  METHOD TYPE : int
504     //
505     int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
506     {
507       MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
508
509       PathInfo sp( srcpath );
510       if ( !sp.isDir() ) {
511         return logResult( ENOTDIR );
512       }
513
514       PathInfo dp( destpath );
515       if ( !dp.isDir() ) {
516         return logResult( ENOTDIR );
517       }
518
519       if ( srcpath == destpath ) {
520         return logResult( EEXIST );
521       }
522
523       std::string src( srcpath.asString());
524       src += "/.";
525       const char *const argv[] = {
526         "/bin/cp",
527         "-dR",
528         "--",
529         src.c_str(),
530         destpath.asString().c_str(),
531         NULL
532       };
533       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
534       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
535         MIL << "  " << output;
536       }
537       int ret = prog.close();
538       return logResult( ret, "returned" );
539     }
540
541     ///////////////////////////////////////////////////////////////////////
542     // dirForEach
543     ///////////////////////////////////////////////////////////////////////
544
545     const StrMatcher & matchNoDots()
546     {
547       static StrMatcher noDots( "[^.]*", Match::GLOB );
548       return noDots;
549     }
550
551     int dirForEach( const Pathname & dir_r, function<bool(const Pathname &, const char *const)> fnc_r )
552     {
553       if ( ! fnc_r )
554         return 0;
555
556       AutoDispose<DIR *> dir( ::opendir( dir_r.c_str() ),
557                               []( DIR * dir_r ) { if ( dir_r ) ::closedir( dir_r ); } );
558
559       MIL << "readdir " << dir_r << ' ';
560       if ( ! dir )
561         return logResult( errno );
562       MIL << endl; // close line before callbacks are invoked.
563
564       int ret = 0;
565       for ( struct dirent * entry = ::readdir( dir ); entry; entry = ::readdir( dir ) )
566       {
567         if ( entry->d_name[0] == '.' && ( entry->d_name[1] == '\0' || ( entry->d_name[1] == '.' && entry->d_name[2] == '\0' ) ) )
568           continue; // omitt . and ..
569
570         if ( ! fnc_r( dir_r, entry->d_name ) )
571         {
572           ret = -1;
573           break;
574         }
575       }
576       return ret;
577     }
578
579     int dirForEach( const Pathname & dir_r, const StrMatcher & matcher_r, function<bool( const Pathname &, const char *const)> fnc_r )
580     {
581       if ( ! fnc_r )
582         return 0;
583
584       bool nodots = ( &matcher_r == &matchNoDots() );
585       return dirForEach( dir_r,
586                          [&]( const Pathname & dir_r, const char *const name_r )->bool
587                          {
588                            if ( ( nodots && name_r[0] == '.' ) || ! matcher_r( name_r ) )
589                              return true;
590                            return fnc_r( dir_r, name_r );
591                          } );
592     }
593
594     ///////////////////////////////////////////////////////////////////
595     // readdir
596     ///////////////////////////////////////////////////////////////////
597
598     int readdir( std::list<std::string> & retlist_r, const Pathname & path_r, bool dots_r )
599     {
600       retlist_r.clear();
601       return dirForEach( path_r,
602                          [&]( const Pathname & dir_r, const char *const name_r )->bool
603                          {
604                            if ( dots_r || name_r[0] != '.' )
605                              retlist_r.push_back( name_r );
606                            return true;
607                          } );
608     }
609
610
611     int readdir( std::list<Pathname> & retlist_r, const Pathname & path_r, bool dots_r )
612     {
613       retlist_r.clear();
614       return dirForEach( path_r,
615                          [&]( const Pathname & dir_r, const char *const name_r )->bool
616                          {
617                            if ( dots_r || name_r[0] != '.' )
618                              retlist_r.push_back( dir_r/name_r );
619                            return true;
620                          } );
621     }
622
623     bool DirEntry::operator==( const DirEntry &rhs ) const
624     {
625       // if one of the types is not known, use the name only
626       if ( type == FT_NOT_AVAIL || rhs.type == FT_NOT_AVAIL )
627         return ( name == rhs.name );
628       return ((name == rhs.name ) && (type == rhs.type));
629     }
630
631     int readdir( DirContent & retlist_r, const Pathname & path_r, bool dots_r, PathInfo::Mode statmode_r )
632     {
633       retlist_r.clear();
634       return dirForEach( path_r,
635                          [&]( const Pathname & dir_r, const char *const name_r )->bool
636                          {
637                            if ( dots_r || name_r[0] != '.' )
638                              retlist_r.push_back( DirEntry( name_r, PathInfo( dir_r/name_r, statmode_r ).fileType() ) );
639                            return true;
640                          } );
641     }
642
643     std::ostream & operator<<( std::ostream & str, const DirContent & obj )
644     { return dumpRange( str, obj.begin(), obj.end() ); }
645
646     ///////////////////////////////////////////////////////////////////
647     // is_empty_dir
648     ///////////////////////////////////////////////////////////////////
649
650     int is_empty_dir( const Pathname & path_r )
651     {
652       return dirForEach( path_r,
653                          [&]( const Pathname & dir_r, const char *const name_r )->bool
654                          { return false; } );
655     }
656
657     ///////////////////////////////////////////////////////////////////
658     //
659     //  METHOD NAME : unlink
660     //  METHOD TYPE : int
661     //
662     int unlink( const Pathname & path )
663     {
664       MIL << "unlink " << path;
665       if ( ::unlink( path.asString().c_str() ) == -1 ) {
666         return logResult( errno );
667       }
668       return logResult( 0 );
669     }
670
671     ///////////////////////////////////////////////////////////////////
672     namespace
673     {
674       int safe_rename( const Pathname & oldpath, const Pathname & newpath )
675       {
676         int ret = ::rename( oldpath.asString().c_str(), newpath.asString().c_str() );
677
678         // rename(2) can fail on OverlayFS. Fallback to using mv(1), which is
679         // explicitly mentioned in the kernel docs to deal correctly with OverlayFS.
680         if ( ret == -1 && errno == EXDEV ) {
681           const char *const argv[] = {
682             "/usr/bin/mv",
683             oldpath.asString().c_str(),
684             newpath.asString().c_str(),
685             NULL
686           };
687           ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
688           for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
689             MIL << "  " << output;
690           }
691           ret = prog.close();
692         }
693
694         return ret;
695       }
696     } // namespace
697     ///////////////////////////////////////////////////////////////////
698
699     ///////////////////////////////////////////////////////////////////
700     //
701     //  METHOD NAME : rename
702     //  METHOD TYPE : int
703     //
704     int rename( const Pathname & oldpath, const Pathname & newpath )
705     {
706       MIL << "rename " << oldpath << " -> " << newpath;
707       if ( safe_rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
708         return logResult( errno );
709       }
710       return logResult( 0 );
711     }
712
713     ///////////////////////////////////////////////////////////////////
714     //
715     //  METHOD NAME : exchange
716     //  METHOD TYPE : int
717     //
718     int exchange( const Pathname & lpath, const Pathname & rpath )
719     {
720       MIL << "exchange " << lpath << " <-> " << rpath;
721       if ( lpath.empty() || rpath.empty() )
722         return logResult( EINVAL );
723
724       PathInfo linfo( lpath );
725       PathInfo rinfo( rpath );
726
727       if ( ! linfo.isExist() )
728       {
729         if ( ! rinfo.isExist() )
730           return logResult( 0 ); // both don't exist.
731
732         // just rename rpath -> lpath
733         int ret = assert_dir( lpath.dirname() );
734         if ( ret != 0 )
735           return logResult( ret );
736         if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
737           return logResult( errno );
738         }
739         return logResult( 0 );
740       }
741
742       // HERE: lpath exists:
743       if ( ! rinfo.isExist() )
744       {
745         // just rename lpath -> rpath
746         int ret = assert_dir( rpath.dirname() );
747         if ( ret != 0 )
748           return logResult( ret );
749         if ( safe_rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
750           return logResult( errno );
751         }
752         return logResult( 0 );
753       }
754
755       // HERE: both exist
756       TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
757       if ( ! tmpfile )
758         return logResult( errno );
759       Pathname tmp( tmpfile.path() );
760       ::unlink( tmp.c_str() );
761
762       if ( safe_rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
763         return logResult( errno );
764       }
765       if ( safe_rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
766         safe_rename( tmp.c_str(), lpath.c_str() );
767         return logResult( errno );
768       }
769       if ( safe_rename( tmp.c_str(), rpath.c_str() ) == -1 ) {
770         safe_rename( lpath.c_str(), rpath.c_str() );
771         safe_rename( tmp.c_str(), lpath.c_str() );
772         return logResult( errno );
773       }
774       return logResult( 0 );
775     }
776
777     ///////////////////////////////////////////////////////////////////
778     //
779     //  METHOD NAME : copy
780     //  METHOD TYPE : int
781     //
782     int copy( const Pathname & file, const Pathname & dest )
783     {
784       MIL << "copy " << file << " -> " << dest << ' ';
785
786       PathInfo sp( file );
787       if ( !sp.isFile() ) {
788         return logResult( EINVAL );
789       }
790
791       PathInfo dp( dest );
792       if ( dp.isDir() ) {
793         return logResult( EISDIR );
794       }
795
796       const char *const argv[] = {
797         "/bin/cp",
798         "--remove-destination",
799         "--",
800         file.asString().c_str(),
801         dest.asString().c_str(),
802         NULL
803       };
804       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
805       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
806         MIL << "  " << output;
807       }
808       int ret = prog.close();
809       return logResult( ret, "returned" );
810     }
811
812     ///////////////////////////////////////////////////////////////////
813     //
814     //  METHOD NAME : symlink
815     //  METHOD TYPE : int
816     //
817     int symlink( const Pathname & oldpath, const Pathname & newpath )
818     {
819       MIL << "symlink " << newpath << " -> " << oldpath;
820       if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
821         return logResult( errno );
822       }
823       return logResult( 0 );
824     }
825
826     ///////////////////////////////////////////////////////////////////
827     //
828     //  METHOD NAME : hardlink
829     //  METHOD TYPE : int
830     //
831     int hardlink( const Pathname & oldpath, const Pathname & newpath )
832     {
833       MIL << "hardlink " << newpath << " -> " << oldpath;
834       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
835         return logResult( errno );
836       }
837       return logResult( 0 );
838     }
839
840     ///////////////////////////////////////////////////////////////////
841     //
842     //  METHOD NAME : hardlink
843     //  METHOD TYPE : int
844     //
845     int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
846     {
847       MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
848
849       PathInfo pi( oldpath, PathInfo::LSTAT );
850       if ( pi.isLink() )
851       {
852         // dont hardlink symlinks!
853         MIL << " => copy" << endl;
854         return copy( oldpath, newpath );
855       }
856
857       pi.lstat( newpath );
858       if ( pi.isExist() )
859       {
860         int res = unlink( newpath );
861         if ( res != 0 )
862           return logResult( res );
863       }
864
865       // Here: no symlink, no newpath
866       if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
867       {
868         switch ( errno )
869         {
870           case EPERM: // /proc/sys/fs/protected_hardlink in proc(5)
871           case EXDEV: // oldpath  and  newpath are not on the same mounted file system
872             MIL << " => copy" << endl;
873             return copy( oldpath, newpath );
874             break;
875         }
876         return logResult( errno );
877       }
878       return logResult( 0 );
879     }
880
881     ///////////////////////////////////////////////////////////////////
882     //
883     //  METHOD NAME : readlink
884     //  METHOD TYPE : int
885     //
886     int readlink( const Pathname & symlink_r, Pathname & target_r )
887     {
888       static const ssize_t bufsiz = 2047;
889       static char buf[bufsiz+1];
890       ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
891       if ( ret == -1 )
892       {
893         target_r = Pathname();
894         MIL << "readlink " << symlink_r;
895         return logResult( errno );
896       }
897       buf[ret] = '\0';
898       target_r = buf;
899       return 0;
900     }
901
902     ///////////////////////////////////////////////////////////////////
903     //
904     //  METHOD NAME : expandlink
905     //  METHOD TYPE : Pathname
906     //
907     Pathname expandlink( const Pathname & path_r )
908     {
909       static const unsigned int level_limit = 256;
910       static unsigned int count;
911       Pathname path(path_r);
912       PathInfo info(path_r, PathInfo::LSTAT);
913
914       for (count = level_limit; info.isLink() && count; count--)
915       {
916         DBG << "following symlink " << path;
917         path = path.dirname() / readlink(path);
918         DBG << "->" << path << std::endl;
919         info = PathInfo(path, PathInfo::LSTAT);
920       }
921
922       // expand limit reached
923       if (count == 0)
924       {
925         ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
926         return Pathname();
927       }
928       // symlink
929       else if (count < level_limit)
930       {
931         // check for a broken link
932         if (PathInfo(path).isExist())
933           return path;
934         // broken link, return an empty path
935         else
936         {
937           ERR << path << " is broken (expanded from " << path_r << ")" << endl;
938           return Pathname();
939         }
940       }
941
942       // not a symlink, return the original pathname
943       DBG << "not a symlink" << endl;
944       return path;
945     }
946
947     ///////////////////////////////////////////////////////////////////
948     //
949     //  METHOD NAME : copy_file2dir
950     //  METHOD TYPE : int
951     //
952     int copy_file2dir( const Pathname & file, const Pathname & dest )
953     {
954       MIL << "copy_file2dir " << file << " -> " << dest << ' ';
955
956       PathInfo sp( file );
957       if ( !sp.isFile() ) {
958         return logResult( EINVAL );
959       }
960
961       PathInfo dp( dest );
962       if ( !dp.isDir() ) {
963         return logResult( ENOTDIR );
964       }
965
966       const char *const argv[] = {
967         "/bin/cp",
968         "--",
969         file.asString().c_str(),
970         dest.asString().c_str(),
971         NULL
972       };
973       ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
974       for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
975         MIL << "  " << output;
976       }
977       int ret = prog.close();
978       return logResult( ret, "returned" );
979     }
980
981     ///////////////////////////////////////////////////////////////////
982     //
983     //  METHOD NAME : md5sum
984     //  METHOD TYPE : std::string
985     //
986     std::string md5sum( const Pathname & file )
987     {
988       if ( ! PathInfo( file ).isFile() ) {
989         return string();
990       }
991       std::ifstream istr( file.asString().c_str() );
992       if ( ! istr ) {
993         return string();
994       }
995       return Digest::digest( "MD5", istr );
996     }
997
998     ///////////////////////////////////////////////////////////////////
999     //
1000     //  METHOD NAME : sha1sum
1001     //  METHOD TYPE : std::string
1002     //
1003     std::string sha1sum( const Pathname & file )
1004     {
1005       return checksum(file, "SHA1");
1006     }
1007
1008     ///////////////////////////////////////////////////////////////////
1009     //
1010     //  METHOD NAME : checksum
1011     //  METHOD TYPE : std::string
1012     //
1013     std::string checksum( const Pathname & file, const std::string &algorithm )
1014     {
1015       if ( ! PathInfo( file ).isFile() ) {
1016         return string();
1017       }
1018       std::ifstream istr( file.asString().c_str() );
1019       if ( ! istr ) {
1020         return string();
1021       }
1022       return Digest::digest( algorithm, istr );
1023     }
1024
1025     bool is_checksum( const Pathname & file, const CheckSum &checksum )
1026     {
1027       return ( filesystem::checksum(file,  checksum.type()) == checksum.checksum() );
1028     }
1029
1030     ///////////////////////////////////////////////////////////////////
1031     //
1032     //  METHOD NAME : erase
1033     //  METHOD TYPE : int
1034     //
1035     int erase( const Pathname & path )
1036     {
1037       int res = 0;
1038       PathInfo p( path, PathInfo::LSTAT );
1039       if ( p.isExist() )
1040         {
1041           if ( p.isDir() )
1042             res = recursive_rmdir( path );
1043           else
1044             res = unlink( path );
1045         }
1046       return res;
1047     }
1048
1049     ///////////////////////////////////////////////////////////////////
1050     //
1051     //  METHOD NAME : chmod
1052     //  METHOD TYPE : int
1053     //
1054     int chmod( const Pathname & path, mode_t mode )
1055     {
1056       MIL << "chmod " << path << ' ' << str::octstring( mode );
1057       if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
1058         return logResult( errno );
1059       }
1060       return logResult( 0 );
1061     }
1062
1063     int addmod( const Pathname & path, mode_t mode )
1064     {
1065       mode_t omode( PathInfo( path ).st_mode() );
1066       mode_t tmode( omode | mode );
1067       if ( omode != mode )
1068         return chmod( path, tmode );
1069       return 0;
1070     }
1071
1072     int delmod( const Pathname & path, mode_t mode )
1073     {
1074       mode_t omode( PathInfo( path ).st_mode() );
1075       mode_t tmode( omode & ~mode );
1076       if ( omode != mode )
1077         return chmod( path, tmode );
1078       return 0;
1079     }
1080
1081    //////////////////////////////////////////////////////////////////
1082     //
1083     //  METHOD NAME : zipType
1084     //  METHOD TYPE : ZIP_TYPE
1085     //
1086     ZIP_TYPE zipType( const Pathname & file )
1087     {
1088       ZIP_TYPE ret = ZT_NONE;
1089
1090       int fd = open( file.asString().c_str(), O_RDONLY|O_CLOEXEC );
1091
1092       if ( fd != -1 ) {
1093         const int magicSize = 5;
1094         unsigned char magic[magicSize];
1095         memset( magic, 0, magicSize );
1096         if ( read( fd, magic, magicSize ) == magicSize ) {
1097           if ( magic[0] == 0037 && magic[1] == 0213 ) {
1098             ret = ZT_GZ;
1099           } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
1100             ret = ZT_BZ2;
1101           } else if ( magic[0] == '\0' && magic[1] == 'Z' && magic[2] == 'C' && magic[3] == 'K' && magic[4] == '1') {
1102             ret = ZT_ZCHNK;
1103
1104           }
1105         }
1106         close( fd );
1107       }
1108
1109       return ret;
1110     }
1111
1112     ///////////////////////////////////////////////////////////////////
1113     //
1114     //  METHOD NAME : df
1115     //  METHOD TYPE : ByteCount
1116     //
1117     ByteCount df( const Pathname & path_r )
1118     {
1119       ByteCount ret( -1 );
1120       struct statvfs sb;
1121       if ( statvfs( path_r.c_str(), &sb ) == 0 )
1122         {
1123           ret = sb.f_bfree * sb.f_bsize;
1124         }
1125       return ret;
1126     }
1127
1128     ///////////////////////////////////////////////////////////////////
1129     //
1130     //  METHOD NAME : getUmask
1131     //  METHOD TYPE : mode_t
1132     //
1133     mode_t getUmask()
1134     {
1135       mode_t mask = ::umask( 0022 );
1136       ::umask( mask );
1137       return mask;
1138     }
1139
1140     ///////////////////////////////////////////////////////////////////
1141     //
1142     //  METHOD NAME : getUmask
1143     //  METHOD TYPE : mode_t
1144     //
1145     int assert_file( const Pathname & path, unsigned mode )
1146     {
1147       int ret = assert_dir( path.dirname() );
1148       MIL << "assert_file " << str::octstring( mode ) << " " << path;
1149       if ( ret != 0 )
1150         return logResult( ret );
1151
1152       PathInfo pi( path );
1153       if ( pi.isExist() )
1154         return logResult( pi.isFile() ? 0 : EEXIST );
1155
1156       int fd = ::creat( path.c_str(), mode );
1157       if ( fd == -1 )
1158         return logResult( errno );
1159
1160       ::close( fd );
1161       return logResult( 0 );
1162     }
1163
1164     int assert_file_mode( const Pathname & path, unsigned mode )
1165     {
1166       int ret = assert_dir( path.dirname() );
1167       MIL << "assert_file_mode " << str::octstring( mode ) << " " << path;
1168       if ( ret != 0 )
1169         return logResult( ret );
1170
1171       PathInfo pi( path );
1172       if ( pi.isExist() )
1173       {
1174         if ( ! pi.isFile() )
1175           return logResult( EEXIST );
1176
1177         mode = applyUmaskTo( mode );
1178         if ( pi.st_mode() != mode )
1179           return chmod( path, mode );
1180
1181         return logResult( 0 );
1182       }
1183
1184       int fd = ::creat( path.c_str(), mode );
1185       if ( fd == -1 )
1186         return logResult( errno );
1187       ::close( fd );
1188       return logResult( 0 );
1189     }
1190
1191     ///////////////////////////////////////////////////////////////////
1192     //
1193     //  METHOD NAME : touch
1194     //  METHOD TYPE : int
1195     //
1196     int touch (const Pathname & path)
1197     {
1198       MIL << "touch " << path;
1199       struct ::utimbuf times;
1200       times.actime = ::time( 0 );
1201       times.modtime = ::time( 0 );
1202       if ( ::utime( path.asString().c_str(), &times ) == -1 ) {
1203         return logResult( errno );
1204       }
1205       return logResult( 0 );
1206     }
1207
1208     /////////////////////////////////////////////////////////////////
1209   } // namespace filesystem
1210   ///////////////////////////////////////////////////////////////////
1211   /////////////////////////////////////////////////////////////////
1212 } // namespace zypp
1213 ///////////////////////////////////////////////////////////////////