------------------------------------------------------------------------
authorStephan Kulow <coolo@suse.de>
Fri, 8 Feb 2008 08:52:29 +0000 (08:52 +0000)
committerStephan Kulow <coolo@suse.de>
Fri, 8 Feb 2008 08:52:29 +0000 (08:52 +0000)
r8177 | jkupec | 2008-01-02 18:02:39 +0100 (Mi, 02 Jan 2008) | 8 lines

- expandlink(Pathname) added
- 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 1187700..13e627e 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 9b673ba..ff9db28 100644 (file)
@@ -756,6 +756,50 @@ namespace zypp
 
     ///////////////////////////////////////////////////////////////////
     //
+    //  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
     // METHOD TYPE : int
     //
index 2f537d2..15c3344 100644 (file)
@@ -582,6 +582,20 @@ namespace zypp
     }
 
     /**
+     * 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.
      *
      * @return 0 on success, EINVAL if file is not a file, ENOTDIR if dest