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