diff --git includes/file.inc includes/file.inc
index 1677e29..09ceeb7 100644
--- includes/file.inc
+++ includes/file.inc
@@ -207,16 +207,10 @@ function file_stream_wrapper_valid_scheme($scheme) {
  *   "sample/test.txt".
  */
 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;
 }
 
 /**
@@ -240,15 +234,9 @@ function file_stream_wrapper_uri_normalize($uri) {
   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;
 }
@@ -1863,14 +1851,7 @@ function drupal_dirname($uri) {
   $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);
diff --git includes/stream_wrappers.inc includes/stream_wrappers.inc
index 196b84a..da88619 100644
--- includes/stream_wrappers.inc
+++ includes/stream_wrappers.inc
@@ -146,6 +146,20 @@ interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
   public function getExternalUrl();
 
   /**
+   * Returns the local target of the resource within the stream.
+   *
+   * @param $uri
+   *   Optional URI.
+   *
+   * @return
+   *  Returns a string representing the write location or path of the file or
+   *  resource as it is referenced within the stream; or FALSE if that
+   *  information is inaccessible or irrelevant, for instance with read-only
+   *  streams.
+   */
+  public function getTarget($uri = NULL);
+
+  /**
    * Returns the MIME type of the resource.
    *
    * @param $uri
@@ -187,6 +201,25 @@ interface DrupalStreamWrapperInterface extends StreamWrapperInterface {
    *   wrapper does not provide an implementation.
    */
   public function realpath();
+
+  /**
+    * Gets the name of the directory from a given path.
+    *
+    * PHP's dirname() does not properly pass streams, so this function fills
+    * that gap.
+    *
+    * Compatibility: normal paths and stream wrappers.
+    * @see http://drupal.org/node/515192
+    *
+    * @param $uri
+    *   An optional URI.
+    * @return
+    *   A string containing the directory name, or FALSE if not applicable.
+    *
+    * @see dirname()
+    * @ingroup php_wrappers
+    */
+  public function dirname($uri = NULL);
 }
 
 
@@ -248,6 +281,20 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
   }
 
   /**
+   *  Base implementation of getTarget().
+   */
+  function getTarget($uri = NULL) {
+    if (!isset($uri)) {
+      $uri = $this->uri;
+    }
+
+    list($scheme, $target) = explode('://', $uri, 2);
+
+    // Remove erroneous beginning or ending forward and backslashes.
+    return trim($target, '\/');
+  }
+
+  /**
    * Base implementation of getMimeType().
    */
   static function getMimeType($uri, $mapping = NULL) {
@@ -303,7 +350,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
     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,36 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
   }
 
   /**
+    * Gets the name of the directory from a given path.
+    *
+    * PHP's dirname() does not properly pass streams, so this function fills
+    * that gap. It is backwards compatible with normal paths and will use
+    * PHP's dirname() as a fallback.
+    *
+    * Compatibility: normal paths and stream wrappers.
+    * @see http://drupal.org/node/515192
+    *
+    * @param $uri
+    *   A URI or path.
+    * @return
+    *   A string containing the directory name.
+    *
+    * @see dirname()
+    * @ingroup php_wrappers
+    */
+  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 +587,7 @@ abstract class DrupalLocalStreamWrapper implements DrupalStreamWrapperInterface
     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 +715,7 @@ class DrupalPublicStreamWrapper extends DrupalLocalStreamWrapper {
    * 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 +743,7 @@ class DrupalPrivateStreamWrapper extends DrupalLocalStreamWrapper {
    * 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 +768,7 @@ class DrupalTemporaryStreamWrapper extends DrupalLocalStreamWrapper {
    * 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));
   }
 }
