1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PathInfo.cc
13 #include <sys/types.h> // for ::minor, ::major macros
14 #include <utime.h> // for ::utime
15 #include <sys/statvfs.h>
21 #include "zypp/base/Logger.h"
22 #include "zypp/base/String.h"
23 #include "zypp/base/IOStream.h"
25 #include "zypp/ExternalProgram.h"
26 #include "zypp/PathInfo.h"
27 #include "zypp/Digest.h"
28 #include "zypp/TmpPath.h"
33 ///////////////////////////////////////////////////////////////////
35 { /////////////////////////////////////////////////////////////////
36 ///////////////////////////////////////////////////////////////////
38 { /////////////////////////////////////////////////////////////////
40 /******************************************************************
42 ** FUNCTION NAME : operator<<
43 ** FUNCTION TYPE : std::ostream &
45 std::ostream & operator<<( std::ostream & str, FileType obj )
48 #define EMUMOUT(T) case T: return str << #T; break
49 EMUMOUT( FT_NOT_AVAIL );
50 EMUMOUT( FT_NOT_EXIST );
53 EMUMOUT( FT_CHARDEV );
54 EMUMOUT( FT_BLOCKDEV );
63 ///////////////////////////////////////////////////////////////////
65 // METHOD NAME : StatMode::fileType
66 // METHOD TYPE : FileType
68 FileType StatMode::fileType() const
88 /******************************************************************
90 ** FUNCTION NAME : operator<<
91 ** FUNCTION TYPE : std::ostream &
93 std::ostream & operator<<( std::ostream & str, const StatMode & obj )
95 iostr::IosFmtFlagsSaver autoResoreState( str );
100 else if ( obj.isDir() )
102 else if ( obj.isLink() )
104 else if ( obj.isChr() )
106 else if ( obj.isBlk() )
108 else if ( obj.isFifo() )
110 else if ( obj.isSock() )
113 str << t << " " << std::setfill( '0' ) << std::setw( 4 ) << std::oct << obj.perm();
117 ///////////////////////////////////////////////////////////////////
121 ///////////////////////////////////////////////////////////////////
123 ///////////////////////////////////////////////////////////////////
125 // METHOD NAME : PathInfo::PathInfo
126 // METHOD TYPE : Constructor
133 ///////////////////////////////////////////////////////////////////
135 // METHOD NAME : PathInfo::PathInfo
136 // METHOD TYPE : Constructor
138 PathInfo::PathInfo( const Pathname & path, Mode initial )
146 ///////////////////////////////////////////////////////////////////
148 // METHOD NAME : PathInfo::PathInfo
149 // METHOD TYPE : Constructor
151 PathInfo::PathInfo( const std::string & path, Mode initial )
159 ///////////////////////////////////////////////////////////////////
161 // METHOD NAME : PathInfo::PathInfo
162 // METHOD TYPE : Constructor
164 PathInfo::PathInfo( const char * path, Mode initial )
172 ///////////////////////////////////////////////////////////////////
174 // METHOD NAME : PathInfo::~PathInfo
175 // METHOD TYPE : Destructor
177 PathInfo::~PathInfo()
181 ///////////////////////////////////////////////////////////////////
183 // METHOD NAME : PathInfo::operator()
184 // METHOD TYPE : bool
186 bool PathInfo::operator()()
188 if ( path_t.empty() ) {
193 error_i = ::stat( path_t.asString().c_str(), &statbuf_C );
196 error_i = ::lstat( path_t.asString().c_str(), &statbuf_C );
205 ///////////////////////////////////////////////////////////////////
207 // METHOD NAME : PathInfo::fileType
208 // METHOD TYPE : File_type
210 FileType PathInfo::fileType() const
213 return asStatMode().fileType();
217 ///////////////////////////////////////////////////////////////////
219 // METHOD NAME : PathInfo::userMay
220 // METHOD TYPE : mode_t
222 mode_t PathInfo::userMay() const
226 if ( owner() == getuid() ) {
227 return( uperm()/0100 );
228 } else if ( group() == getgid() ) {
229 return( gperm()/010 );
234 /******************************************************************
236 ** FUNCTION NAME : PathInfo::major
237 ** FUNCTION TYPE : unsigned int
239 unsigned int PathInfo::major() const
241 return isBlk() || isChr() ? ::major(statbuf_C.st_rdev) : 0;
244 /******************************************************************
246 ** FUNCTION NAME : PathInfo::minor
247 ** FUNCTION TYPE : unsigned int
249 unsigned int PathInfo::minor() const
251 return isBlk() || isChr() ? ::minor(statbuf_C.st_rdev) : 0;
254 /******************************************************************
256 ** FUNCTION NAME : operator<<
257 ** FUNCTION TYPE : std::ostream &
259 std::ostream & operator<<( std::ostream & str, const PathInfo & obj )
261 iostr::IosFmtFlagsSaver autoResoreState( str );
263 str << obj.asString() << "{";
264 if ( !obj.isExist() ) {
265 str << "does not exist}";
267 str << obj.asStatMode() << " " << std::dec << obj.owner() << "/" << obj.group();
270 str << " size " << obj.size();
278 ///////////////////////////////////////////////////////////////////
280 // filesystem utilities
282 ///////////////////////////////////////////////////////////////////
284 /******************************************************************
286 ** FUNCTION NAME : _Log_Result
287 ** FUNCTION TYPE : int
289 ** DESCRIPTION : Helper function to log return values.
291 #define _Log_Result MIL << endl, __Log_Result
292 inline int __Log_Result( const int res, const char * rclass = 0 /*errno*/ )
297 WAR << " FAILED: " << rclass << " " << res << endl;
299 WAR << " FAILED: " << str::strerror( res ) << endl;
304 ///////////////////////////////////////////////////////////////////
306 // METHOD NAME : PathInfo::mkdir
309 int mkdir( const Pathname & path, unsigned mode )
311 MIL << "mkdir " << path << ' ' << str::octstring( mode );
312 if ( ::mkdir( path.asString().c_str(), mode ) == -1 ) {
313 return _Log_Result( errno );
315 return _Log_Result( 0 );
318 ///////////////////////////////////////////////////////////////////
320 // METHOD NAME : assert_dir()
323 int assert_dir( const Pathname & path, unsigned mode )
328 { // Handle existing paths in advance.
336 string spath = path.asString()+"/";
337 string::size_type lastpos = ( path.relative() ? 2 : 1 ); // skip leasding './' or '/'
338 string::size_type pos = string::npos;
341 while ( (pos = spath.find('/',lastpos)) != string::npos )
343 string dir( spath.substr(0,pos) );
344 ret = ::mkdir( dir.c_str(), mode );
347 if ( errno == EEXIST ) // ignore errors about already existing paths
352 WAR << " FAILED: mkdir " << dir << ' ' << str::octstring( mode ) << " errno " << ret << endl;
357 MIL << "mkdir " << dir << ' ' << str::octstring( mode ) << endl;
365 ///////////////////////////////////////////////////////////////////
367 // METHOD NAME : rmdir
370 int rmdir( const Pathname & path )
372 MIL << "rmdir " << path;
373 if ( ::rmdir( path.asString().c_str() ) == -1 ) {
374 return _Log_Result( errno );
376 return _Log_Result( 0 );
379 ///////////////////////////////////////////////////////////////////
381 // METHOD NAME : recursive_rmdir
384 static int recursive_rmdir_1( const Pathname & dir )
389 if ( ! (dp = opendir( dir.c_str() )) )
390 return _Log_Result( errno );
392 while ( (d = readdir(dp)) )
394 std::string direntry = d->d_name;
395 if ( direntry == "." || direntry == ".." )
397 Pathname new_path( dir / d->d_name );
400 if ( ! lstat( new_path.c_str(), &st ) )
402 if ( S_ISDIR( st.st_mode ) )
403 recursive_rmdir_1( new_path );
405 ::unlink( new_path.c_str() );
410 if ( ::rmdir( dir.c_str() ) < 0 )
415 ///////////////////////////////////////////////////////////////////
416 int recursive_rmdir( const Pathname & path )
418 MIL << "recursive_rmdir " << path << ' ';
421 if ( !p.isExist() ) {
422 return _Log_Result( 0 );
426 return _Log_Result( ENOTDIR );
429 return _Log_Result( recursive_rmdir_1( path ) );
432 ///////////////////////////////////////////////////////////////////
434 // METHOD NAME : clean_dir
437 int clean_dir( const Pathname & path )
439 MIL << "clean_dir " << path << ' ';
442 if ( !p.isExist() ) {
443 return _Log_Result( 0 );
447 return _Log_Result( ENOTDIR );
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;
455 int ret = prog.close();
456 return _Log_Result( ret, "returned" );
459 ///////////////////////////////////////////////////////////////////
461 // METHOD NAME : copy_dir
464 int copy_dir( const Pathname & srcpath, const Pathname & destpath )
466 MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
468 PathInfo sp( srcpath );
470 return _Log_Result( ENOTDIR );
473 PathInfo dp( destpath );
475 return _Log_Result( ENOTDIR );
478 PathInfo tp( destpath + srcpath.basename() );
479 if ( tp.isExist() ) {
480 return _Log_Result( EEXIST );
484 const char *const argv[] = {
488 srcpath.asString().c_str(),
489 destpath.asString().c_str(),
492 ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
493 for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
494 MIL << " " << output;
496 int ret = prog.close();
497 return _Log_Result( ret, "returned" );
500 ///////////////////////////////////////////////////////////////////
502 // METHOD NAME : copy_dir_content
505 int copy_dir_content(const Pathname & srcpath, const Pathname & destpath)
507 MIL << "copy_dir " << srcpath << " -> " << destpath << ' ';
509 PathInfo sp( srcpath );
511 return _Log_Result( ENOTDIR );
514 PathInfo dp( destpath );
516 return _Log_Result( ENOTDIR );
519 if ( srcpath == destpath ) {
520 return _Log_Result( EEXIST );
523 std::string src( srcpath.asString());
525 const char *const argv[] = {
530 destpath.asString().c_str(),
533 ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
534 for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
535 MIL << " " << output;
537 int ret = prog.close();
538 return _Log_Result( ret, "returned" );
541 ///////////////////////////////////////////////////////////////////
543 // METHOD NAME : readdir
546 int readdir( std::list<std::string> & retlist,
547 const Pathname & path, bool dots )
551 MIL << "readdir " << path << ' ';
553 DIR * dir = ::opendir( path.asString().c_str() );
555 return _Log_Result( errno );
558 struct dirent *entry;
559 while ( (entry = ::readdir( dir )) != 0 ) {
561 if ( entry->d_name[0] == '.' ) {
564 if ( entry->d_name[1] == '\0'
565 || ( entry->d_name[1] == '.'
566 && entry->d_name[2] == '\0' ) )
569 retlist.push_back( entry->d_name );
574 return _Log_Result( 0 );
578 ///////////////////////////////////////////////////////////////////
580 // METHOD NAME : readdir
583 int readdir( std::list<Pathname> & retlist,
584 const Pathname & path, bool dots )
588 std::list<string> content;
589 int res = readdir( content, path, dots );
592 for ( std::list<string>::const_iterator it = content.begin(); it != content.end(); ++it ) {
593 retlist.push_back( path + *it );
600 ///////////////////////////////////////////////////////////////////
602 // METHOD NAME : readdir
606 bool DirEntry::operator==( const DirEntry &rhs ) const
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));
614 int readdir( DirContent & retlist, const Pathname & path,
615 bool dots, PathInfo::Mode statmode )
619 std::list<string> content;
620 int res = readdir( content, path, dots );
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() ) );
632 ///////////////////////////////////////////////////////////////////
634 // METHOD NAME : is_empty_dir
637 int is_empty_dir(const Pathname & path)
639 DIR * dir = ::opendir( path.asString().c_str() );
641 return _Log_Result( errno );
644 struct dirent *entry;
645 while ( (entry = ::readdir( dir )) != NULL )
647 std::string name(entry->d_name);
649 if ( name == "." || name == "..")
656 return entry != NULL ? -1 : 0;
659 ///////////////////////////////////////////////////////////////////
661 // METHOD NAME : unlink
664 int unlink( const Pathname & path )
666 MIL << "unlink " << path;
667 if ( ::unlink( path.asString().c_str() ) == -1 ) {
668 return _Log_Result( errno );
670 return _Log_Result( 0 );
673 ///////////////////////////////////////////////////////////////////
675 // METHOD NAME : rename
678 int rename( const Pathname & oldpath, const Pathname & newpath )
680 MIL << "rename " << oldpath << " -> " << newpath;
681 if ( ::rename( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
682 return _Log_Result( errno );
684 return _Log_Result( 0 );
687 ///////////////////////////////////////////////////////////////////
689 // METHOD NAME : exchange
692 int exchange( const Pathname & lpath, const Pathname & rpath )
694 MIL << "exchange " << lpath << " <-> " << rpath;
695 if ( lpath.empty() || rpath.empty() )
696 return _Log_Result( EINVAL );
698 PathInfo linfo( lpath );
699 PathInfo rinfo( rpath );
701 if ( ! linfo.isExist() )
703 if ( ! rinfo.isExist() )
704 return _Log_Result( 0 ); // both don't exist.
706 // just rename rpath -> lpath
707 int ret = assert_dir( lpath.dirname() );
709 return _Log_Result( ret );
710 if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
711 return _Log_Result( errno );
713 return _Log_Result( 0 );
716 // HERE: lpath exists:
717 if ( ! rinfo.isExist() )
719 // just rename lpath -> rpath
720 int ret = assert_dir( rpath.dirname() );
722 return _Log_Result( ret );
723 if ( ::rename( lpath.c_str(), rpath.c_str() ) == -1 ) {
724 return _Log_Result( errno );
726 return _Log_Result( 0 );
730 TmpFile tmpfile( TmpFile::makeSibling( rpath ) );
732 return _Log_Result( errno );
733 Pathname tmp( tmpfile.path() );
734 ::unlink( tmp.c_str() );
736 if ( ::rename( lpath.c_str(), tmp.c_str() ) == -1 ) {
737 return _Log_Result( errno );
739 if ( ::rename( rpath.c_str(), lpath.c_str() ) == -1 ) {
740 ::rename( tmp.c_str(), lpath.c_str() );
741 return _Log_Result( errno );
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 );
748 return _Log_Result( 0 );
751 ///////////////////////////////////////////////////////////////////
753 // METHOD NAME : copy
756 int copy( const Pathname & file, const Pathname & dest )
758 MIL << "copy " << file << " -> " << dest << ' ';
761 if ( !sp.isFile() ) {
762 return _Log_Result( EINVAL );
767 return _Log_Result( EISDIR );
770 const char *const argv[] = {
773 file.asString().c_str(),
774 dest.asString().c_str(),
777 ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
778 for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
779 MIL << " " << output;
781 int ret = prog.close();
782 return _Log_Result( ret, "returned" );
785 ///////////////////////////////////////////////////////////////////
787 // METHOD NAME : symlink
790 int symlink( const Pathname & oldpath, const Pathname & newpath )
792 MIL << "symlink " << newpath << " -> " << oldpath;
793 if ( ::symlink( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
794 return _Log_Result( errno );
796 return _Log_Result( 0 );
799 ///////////////////////////////////////////////////////////////////
801 // METHOD NAME : hardlink
804 int hardlink( const Pathname & oldpath, const Pathname & newpath )
806 MIL << "hardlink " << newpath << " -> " << oldpath;
807 if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 ) {
808 return _Log_Result( errno );
810 return _Log_Result( 0 );
813 ///////////////////////////////////////////////////////////////////
815 // METHOD NAME : hardlink
818 int hardlinkCopy( const Pathname & oldpath, const Pathname & newpath )
820 MIL << "hardlinkCopy " << oldpath << " -> " << newpath;
821 if ( ::link( oldpath.asString().c_str(), newpath.asString().c_str() ) == -1 )
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 );
829 case EXDEV: // oldpath and newpath are not on the same mounted file system
830 return copy( oldpath, newpath );
833 return _Log_Result( errno );
835 return _Log_Result( 0 );
838 ///////////////////////////////////////////////////////////////////
840 // METHOD NAME : readlink
843 int readlink( const Pathname & symlink_r, Pathname & target_r )
845 static const ssize_t bufsiz = 2047;
846 static char buf[bufsiz+1];
847 ssize_t ret = ::readlink( symlink_r.c_str(), buf, bufsiz );
850 target_r = Pathname();
851 MIL << "readlink " << symlink_r;
852 return _Log_Result( errno );
859 ///////////////////////////////////////////////////////////////////
861 // METHOD NAME : expandlink
862 // METHOD TYPE : Pathname
864 Pathname expandlink( const Pathname & path_r )
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);
871 for (count = level_limit; info.isLink() && count; count--)
873 DBG << "following symlink " << path;
874 path = path.dirname() / readlink(path);
875 DBG << "->" << path << std::endl;
876 info = PathInfo(path, PathInfo::LSTAT);
879 // expand limit reached
882 ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
886 else if (count < level_limit)
888 // check for a broken link
889 if (PathInfo(path).isExist())
891 // broken link, return an empty path
894 ERR << path << " is broken (expanded from " << path_r << ")" << endl;
899 // not a symlink, return the original pathname
900 DBG << "not a symlink" << endl;
904 ///////////////////////////////////////////////////////////////////
906 // METHOD NAME : copy_file2dir
909 int copy_file2dir( const Pathname & file, const Pathname & dest )
911 MIL << "copy_file2dir " << file << " -> " << dest << ' ';
914 if ( !sp.isFile() ) {
915 return _Log_Result( EINVAL );
920 return _Log_Result( ENOTDIR );
923 const char *const argv[] = {
926 file.asString().c_str(),
927 dest.asString().c_str(),
930 ExternalProgram prog( argv, ExternalProgram::Stderr_To_Stdout );
931 for ( string output( prog.receiveLine() ); output.length(); output = prog.receiveLine() ) {
932 MIL << " " << output;
934 int ret = prog.close();
935 return _Log_Result( ret, "returned" );
938 ///////////////////////////////////////////////////////////////////
940 // METHOD NAME : md5sum
941 // METHOD TYPE : std::string
943 std::string md5sum( const Pathname & file )
945 if ( ! PathInfo( file ).isFile() ) {
948 std::ifstream istr( file.asString().c_str() );
952 return Digest::digest( "MD5", istr );
955 ///////////////////////////////////////////////////////////////////
957 // METHOD NAME : sha1sum
958 // METHOD TYPE : std::string
960 std::string sha1sum( const Pathname & file )
962 return checksum(file, "SHA1");
965 ///////////////////////////////////////////////////////////////////
967 // METHOD NAME : checksum
968 // METHOD TYPE : std::string
970 std::string checksum( const Pathname & file, const std::string &algorithm )
972 if ( ! PathInfo( file ).isFile() ) {
975 std::ifstream istr( file.asString().c_str() );
979 return Digest::digest( algorithm, istr );
982 bool is_checksum( const Pathname & file, const CheckSum &checksum )
984 return ( filesystem::checksum(file, checksum.type()) == checksum.checksum() );
987 ///////////////////////////////////////////////////////////////////
989 // METHOD NAME : erase
992 int erase( const Pathname & path )
995 PathInfo p( path, PathInfo::LSTAT );
999 res = recursive_rmdir( path );
1001 res = unlink( path );
1006 ///////////////////////////////////////////////////////////////////
1008 // METHOD NAME : chmod
1009 // METHOD TYPE : int
1011 int chmod( const Pathname & path, mode_t mode )
1013 MIL << "chmod " << path << ' ' << str::octstring( mode );
1014 if ( ::chmod( path.asString().c_str(), mode ) == -1 ) {
1015 return _Log_Result( errno );
1017 return _Log_Result( 0 );
1020 ///////////////////////////////////////////////////////////////////
1022 // METHOD NAME : zipType
1023 // METHOD TYPE : ZIP_TYPE
1025 ZIP_TYPE zipType( const Pathname & file )
1027 ZIP_TYPE ret = ZT_NONE;
1029 int fd = open( file.asString().c_str(), O_RDONLY );
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 ) {
1038 } else if ( magic[0] == 'B' && magic[1] == 'Z' && magic[2] == 'h' ) {
1048 ///////////////////////////////////////////////////////////////////
1051 // METHOD TYPE : ByteCount
1053 ByteCount df( const Pathname & path_r )
1055 ByteCount ret( -1 );
1057 if ( statvfs( path_r.c_str(), &sb ) == 0 )
1059 ret = sb.f_bfree * sb.f_bsize;
1064 ///////////////////////////////////////////////////////////////////
1066 // METHOD NAME : getUmask
1067 // METHOD TYPE : mode_t
1071 mode_t mask = ::umask( 0022 );
1076 ///////////////////////////////////////////////////////////////////
1078 // METHOD NAME : getUmask
1079 // METHOD TYPE : mode_t
1081 int assert_file( const Pathname & path, unsigned mode )
1083 int ret = assert_dir( path.dirname() );
1084 MIL << "assert_file " << str::octstring( mode ) << " " << path;
1086 return _Log_Result( ret );
1088 PathInfo pi( path );
1090 return _Log_Result( pi.isFile() ? 0 : EEXIST );
1092 int fd = ::creat( path.c_str(), mode );
1094 return _Log_Result( errno );
1097 return _Log_Result( 0 );
1100 ///////////////////////////////////////////////////////////////////
1102 // METHOD NAME : touch
1103 // METHOD TYPE : int
1105 int touch (const Pathname & path)
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(), × ) == -1 ) {
1112 return _Log_Result( errno );
1114 return _Log_Result( 0 );
1117 /////////////////////////////////////////////////////////////////
1118 } // namespace filesystem
1119 ///////////////////////////////////////////////////////////////////
1120 /////////////////////////////////////////////////////////////////
1122 ///////////////////////////////////////////////////////////////////