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