Index: modules/simpletest/tests/file.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v
retrieving revision 1.53
diff -u -r1.53 file.test
--- modules/simpletest/tests/file.test	11 Apr 2010 18:33:44 -0000	1.53
+++ modules/simpletest/tests/file.test	25 Apr 2010 04:47:02 -0000
@@ -2251,11 +2251,6 @@
     $instance = file_stream_wrapper_get_instance_by_uri('public://foo');
     $this->assertEqual('DrupalPublicStreamWrapper', get_class($instance), t('Got correct class type for public URI.'));
 
-    // Test file_stream_wrapper_uri_normalize().
-    $uri = 'public:///' . file_directory_path() . '/foo/bar/';
-    $uri = file_stream_wrapper_uri_normalize($uri);
-    $this->assertEqual('public://foo/bar', $uri, t('Got a properly normalized URI @uri', array('@uri' => $uri)));
-
     // Test file_uri_target().
     $this->assertEqual(file_uri_target('public://foo/bar.txt'), 'foo/bar.txt', t('Got a valid stream target from public://foo/bar.txt.'));
     $this->assertFalse(file_uri_target('foo/bar.txt'), t('foo/bar.txt is not a valid stream.'));
Index: includes/stream_wrappers.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/stream_wrappers.inc,v
retrieving revision 1.13
diff -u -r1.13 stream_wrappers.inc
--- includes/stream_wrappers.inc	26 Mar 2010 17:14:45 -0000	1.13
+++ includes/stream_wrappers.inc	25 Apr 2010 04:47:01 -0000
@@ -146,6 +146,24 @@
   public function getExternalUrl();
 
   /**
+   * Returns the local writable target of the resource within the stream.
+   *
+   * This function should be used in place of calls to realpath() or similar
+   * functions when attempting to determine the location of a file. While
+   * functions like realpath() may return the location of a read-only file, this
+   * method may return a URI or path suitable for writing that is completely
+   * separate from the URI used for reading.
+   *
+   * @param $uri
+   *   Optional URI.
+   *
+   * @return
+   *   Returns a string representing a location suitable for writing of a file,
+   *   or FALSE if unable to write to the file such as with read-only streams.
+   */
+  public function getTarget($uri = NULL);
+
+  /**
    * Returns the MIME type of the resource.
    *
    * @param $uri
@@ -187,6 +205,21 @@
    *   wrapper does not provide an implementation.
    */
   public function realpath();
+
+  /**
+   * Gets the name of the directory from a given path.
+   *
+   * This method is usually access through drupal_dirname(), which wraps around
+   * the normal PHP dirname() function, which does not support stream wrappers.
+   *
+   * @param $uri
+   *   An optional URI.
+   * @return
+   *   A string containing the directory name, or FALSE if not applicable.
+   *
+   * @see drupal_dirname()
+   */
+  public function dirname($uri = NULL);
 }
 
 
@@ -248,6 +281,20 @@
   }
 
   /**
+   *  Base implementation of getTarget().
+   */
+  function getTarget($uri = NULL) {
+    if (!isset($uri)) {
+      $uri = $this->uri;
+    }
+
+    list($scheme, $target) = explode('://', $uri, 2);
+
+    // Remove erroneous leading or trailing, forward-slashes and backslashes.
+    return trim($target, '\/');
+  }
+
+  /**
    * Base implementation of getMimeType().
    */
   static function getMimeType($uri, $mapping = NULL) {
@@ -303,7 +350,7 @@
     if (!isset($uri)) {
       $uri = $this->uri;
     }
-    $path = $this->getDirectoryPath() . '/' . file_uri_target($uri);
+    $path = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
     $realpath = realpath($path);
     if (!$realpath) {
       // This file does not yet exist.
@@ -492,6 +539,31 @@
   }
 
   /**
+   * Gets the name of the directory from a given path.
+   *
+   * This method is usually access through drupal_dirname(), which wraps around
+   * the PHP dirname() function because it does not support stream wrappers.
+   *
+   * @param $uri
+   *   A URI or path.
+   * @return
+   *   A string containing the directory name.
+   *
+   * @see drupal_dirname()
+   */
+  public function dirname($uri = NULL) {
+    list($scheme, $target) = explode('://', $uri, 2);
+    $target  = $this->getTarget($uri);
+    $dirname = dirname($target);
+
+    if ($dirname == '.') {
+      $dirname = '';
+    }
+
+    return $scheme . '://' . $dirname;
+  }
+
+  /**
    * Support for mkdir().
    *
    * @param $uri
@@ -510,7 +582,7 @@
     if ($recursive) {
       // $this->getLocalPath() fails if $uri has multiple levels of directories
       // that do not yet exist.
-      $localpath = $this->getDirectoryPath() . '/' . file_uri_target($uri);
+      $localpath = $this->getDirectoryPath() . '/' . $this->getTarget($uri);
     }
     else {
       $localpath = $this->getLocalPath($uri);
@@ -638,7 +710,7 @@
    * Return the HTML URI of a public file.
    */
   function getExternalUrl() {
-    $path = str_replace('\\', '/', file_uri_target($this->uri));
+    $path = str_replace('\\', '/', $this->getTarget());
     return $GLOBALS['base_url'] . '/' . self::getDirectoryPath() . '/' . drupal_encode_path($path);
   }
 }
@@ -666,7 +738,7 @@
    * Return the HTML URI of a private file.
    */
   function getExternalUrl() {
-    $path = str_replace('\\', '/', file_uri_target($this->uri));
+    $path = str_replace('\\', '/', $this->getTarget());
     return url('system/files/' . $path, array('absolute' => TRUE));
   }
 }
@@ -691,7 +763,7 @@
    * Overrides getExternalUrl().
    */
   public function getExternalUrl() {
-    $path = str_replace('\\', '/', file_uri_target($this->uri));
+    $path = str_replace('\\', '/', $this->getTarget());
     return url('system/temporary/' . $path, array('absolute' => TRUE));
   }
 }
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.207
diff -u -r1.207 file.inc
--- includes/file.inc	10 Apr 2010 17:30:15 -0000	1.207
+++ includes/file.inc	25 Apr 2010 04:47:00 -0000
@@ -165,6 +165,8 @@
  * @return
  *   A string containing the name of the scheme, or FALSE if none. For example,
  *   the URI "public://example.txt" would return "public".
+ *
+ * @see file_uri_target()
  */
 function file_uri_scheme($uri) {
   $data = explode('://', $uri, 2);
@@ -205,18 +207,14 @@
  *   A string containing the target (path), or FALSE if none.
  *   For example, the URI "public://sample/test.txt" would return
  *   "sample/test.txt".
+ *
+ * @see file_uri_scheme()
  */
 function file_uri_target($uri) {
-  $data = explode('://', $uri, 2);
-
-  if (count($data) != 2) {
-    return FALSE;
+  if ($scheme = file_uri_scheme($uri)) {
+    return file_stream_wrapper_get_instance_by_scheme($scheme)->getTarget($uri);
   }
-
-  // Remove erroneous beginning forward slash.
-  $data[1] = ltrim($data[1], '\/');
-
-  return $data[1];
+  return FALSE;
 }
 
 /**
@@ -225,7 +223,6 @@
  * A stream is referenced as "scheme://target".
  *
  * The following actions are taken:
- * - Remove all occurrences of the wrapper's directory path
  * - Remove trailing slashes from target
  * - Trim erroneous leading slashes from target. e.g. ":///" becomes "://".
  *
@@ -240,15 +237,9 @@
   if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
     $target = file_uri_target($uri);
 
-    // Remove all occurrences of the wrapper's directory path.
-    $directory_path = file_stream_wrapper_get_instance_by_scheme($scheme)->getDirectoryPath();
-    $target = str_replace($directory_path, '', $target);
-
-    // Trim trailing slashes from target.
-    $target = rtrim($target, '/');
-
-    // Trim erroneous leading slashes from target.
-    $uri = $scheme . '://' . ltrim($target, '/');
+    if ($target !== FALSE) {
+      $uri = $scheme . '://' . $target;
+    }
   }
   return $uri;
 }
@@ -1867,14 +1858,7 @@
   $scheme = file_uri_scheme($uri);
 
   if ($scheme && file_stream_wrapper_valid_scheme($scheme)) {
-    $target  = file_uri_target($uri);
-    $dirname = dirname($target);
-
-    if ($dirname == '.') {
-      $dirname = '';
-    }
-
-    return $scheme . '://' . $dirname;
+    return file_stream_wrapper_get_instance_by_scheme($scheme)->dirname($uri);
   }
   else {
     return dirname($uri);
