- expandlink(Pathname) added
authorJan Kupec <jkupec@suse.cz>
Wed, 2 Jan 2008 17:02:39 +0000 (17:02 +0000)
committerJan Kupec <jkupec@suse.cz>
Wed, 2 Jan 2008 17:02:39 +0000 (17:02 +0000)
- TODO: could be made so that it throws on bad links (better error
  reporting)
- TODO: cyclic links protection can be improved by remembering paths
  alredy visited and checking them (again better error reporting and
  performance)

tests/zypp/PathInfo.cc
zypp/PathInfo.cc
zypp/PathInfo.h

index 1187700404c9ba293ef6a3835ca380d9b6182010..13e627e76ae70271d4c4f8522631363a4a830c4b 100644 (file)
@@ -71,13 +71,59 @@ void pathinfo_is_exist_test()
 void pathinfo_misc_test()
 {
   TmpDir dir;
-  
-  
-  
+
   PathInfo info(dir.path());
   BOOST_CHECK(info.isDir());
 }
 
+void pathinfo_expandlink_test()
+{
+  TmpDir dir;
+
+  // ---- not a link
+
+  // create a file
+  Pathname file(dir / "file");
+  ofstream str(file.asString().c_str(),ofstream::out);
+  str << "foo bar" << endl;
+  str.flush();
+  str.close();
+
+  // expandlink should return the original Pathname if it does not point to a link
+  BOOST_CHECK_EQUAL( file, filesystem::expandlink(file) );
+
+  // ---- valid link
+
+  // create a link to that file
+  Pathname link1(dir / "link1");
+  BOOST_CHECK_EQUAL( filesystem::symlink(file, link1), 0);
+
+  // does the link expand to the file?
+  BOOST_CHECK_EQUAL( file, filesystem::expandlink(link1) );
+
+  // ---- broken link
+
+  // create a link to a non-existent file
+  Pathname brokenlink(dir / "brokenlink");
+  Pathname non_existent(dir / "non-existent");
+  BOOST_CHECK_EQUAL( filesystem::symlink(non_existent, brokenlink), 0);
+  PathInfo info(brokenlink, PathInfo::LSTAT);
+  BOOST_CHECK(info.isLink());
+
+  // expandlink should return an empty Pathname for a broken link
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(brokenlink) );
+
+  // ---- cyclic link
+
+  // make the 'non-existent' a link to 'brokenlink' :O)
+  BOOST_CHECK_EQUAL( filesystem::symlink(brokenlink, non_existent), 0);
+  // expandlink should return an empty Pathname for such a cyclic link
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(brokenlink) );
+  BOOST_CHECK_EQUAL( Pathname(), filesystem::expandlink(non_existent) );
+
+  cout << brokenlink << " -> " << filesystem::expandlink(brokenlink) << endl;
+}
+
 test_suite*
 init_unit_test_suite( int, char* [] )
 {
@@ -85,6 +131,7 @@ init_unit_test_suite( int, char* [] )
     test->add( BOOST_TEST_CASE( &pathinfo_checksum_test ), 0 /* expected zero error */ );
     test->add( BOOST_TEST_CASE( &pathinfo_misc_test ), 0 /* expected zero error */ );
     test->add( BOOST_TEST_CASE( &pathinfo_is_exist_test ), 0 /* expected zero error */ );
+    test->add( BOOST_TEST_CASE( &pathinfo_expandlink_test ), 0 /* expected zero error */ );
     return test;
 }
 
index 9b673bad3026eff21efd6acc0aab54ac7e9301c9..ff9db2899f88e3bf8a4667fe4bf8a1f1a8ea7d2c 100644 (file)
@@ -754,6 +754,50 @@ namespace zypp
       return 0;
     }
 
+    ///////////////////////////////////////////////////////////////////
+    //
+    //  METHOD NAME : expandlink
+    //  METHOD TYPE : Pathname
+    //
+    Pathname expandlink( const Pathname & path_r )
+    {
+      static const unsigned int level_limit = 256;
+      static unsigned int count;
+      Pathname path(path_r);
+      PathInfo info(path_r, PathInfo::LSTAT);
+
+      for (count = level_limit; info.isLink() && count; count--)
+      {
+        DBG << "following symlink " << path << std::endl;
+        path = readlink(path);
+        info = PathInfo(path, PathInfo::LSTAT);
+      }
+
+      // expand limit reached
+      if (count == 0)
+      {
+        ERR << "Expand level limit reached. Probably a cyclic symbolic link." << endl;
+        return Pathname();
+      }
+      // symlink
+      else if (count < level_limit)
+      {
+        // check for a broken link
+        if (PathInfo(path).isExist())
+          return path;
+        // broken link, return and empty path
+        else
+        {
+          ERR << path << " is broken (expanded from " << path_r << ")" << endl;
+          return Pathname();
+        }
+      }
+
+      // not a symlink, return the original pathname
+      DBG << "not a symlink" << endl;
+      return path;
+    }
+
     ///////////////////////////////////////////////////////////////////
     //
     // METHOD NAME : copy_file2dir
index 2f537d21bfcc40827b85b0fc7c670749e900948a..15c3344658be4904db3a889e43ec569d32a78dc8 100644 (file)
@@ -581,6 +581,20 @@ namespace zypp
       return target;
     }
 
+    /**
+     * Recursively follows the symlink pointed to by \a path_r and returns
+     * the Pathname to the real file or directory pointed to by the link.
+     * 
+     * There is a recursion limit of 256 iterations to protect against a cyclic
+     * link.
+     *
+     * @return Pathname of the file or directory pointed to by the given link
+     *   if it is a valid link. If \a path_r is not a link, an exact copy of
+     *   it is returned. If \a path_r is a broken or a cyclic link, an empty
+     *   Pathname is returned and the event logged.
+     */ 
+    Pathname expandlink( const Pathname & path_r );
+
     /**
      * Like 'cp file dest'. Copy file to dest dir.
      *