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