diff --git includes/file.inc includes/file.inc
index fd80845..45757db 100644
--- includes/file.inc
+++ includes/file.inc
@@ -230,7 +230,7 @@ function file_check_path(&$path) {
 
   // Check if path is a possible dir/file.
   $filename = basename($path);
-  $path = dirname($path);
+  $path     = drupal_dirname($path);
   if (file_check_directory($path)) {
     return $filename;
   }
@@ -259,15 +259,15 @@ function file_check_path(&$path) {
  *   path of the source.
  */
 function file_check_location($source, $directory = '') {
-  $check = realpath($source);
+  $check = drupal_realpath($source);
   if ($check) {
     $source = $check;
   }
   else {
     // This file does not yet exist.
-    $source = realpath(dirname($source)) . '/' . basename($source);
+    $source = drupal_realpath(drupal_dirname($source)) . '/' . basename($source);
   }
-  $directory = realpath($directory);
+  $directory = drupal_realpath($directory);
   if ($directory && strpos($source, $directory) !== 0) {
     return FALSE;
   }
@@ -465,7 +465,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
   $original_source = $source;
   $original_destination = $destination;
 
-  $source = realpath($source);
+  $source = drupal_realpath($source);
   if (!file_exists($source)) {
     drupal_set_message(t('The specified file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $original_source)), 'error');
     return FALSE;
@@ -493,7 +493,7 @@ function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXIST
   // Make sure source and destination filenames are not the same, makes no
   // sense to copy it if they are. In fact copying the file will most likely
   // result in a 0 byte file. Which is bad. Real bad.
-  if ($source == realpath($destination)) {
+  if ($source == drupal_realpath($destination)) {
     drupal_set_message(t('The specified file %file was not copied because it would overwrite itself.', array('%file' => $source)), 'error');
     return FALSE;
   }
@@ -533,7 +533,7 @@ function file_destination($destination, $replace) {
 
       case FILE_EXISTS_RENAME:
         $basename = basename($destination);
-        $directory = dirname($destination);
+        $directory = drupal_dirname($destination);
         $destination = file_create_filename($basename, $directory);
         break;
 
@@ -1596,7 +1596,7 @@ function file_get_mimetype($filename, $mapping = NULL) {
 }
 
 /**
- * Set the permissions on a file or directory.
+ * Sets the permissions on a stream (URI).
  *
  * This function will use the 'file_chmod_directory' and 'file_chmod_file'
  * variables for the default modes for directories and uploaded/generated files.
@@ -1605,17 +1605,28 @@ function file_get_mimetype($filename, $mapping = NULL) {
  * these files, and give group write permissions so webserver group members
  * (e.g. a vhost account) can alter files uploaded and owned by the webserver.
  *
- * @param $path
- *   String containing the path to a file or directory.
- * @param $mode
+ * PHP's chmod does not support stream wrappers so we use our wrapper implementation
+ * which interfaces with chmod() by default. Contrib wrappers may override this
+ * bahavior in their implementations as needed.
+ *
+ * @see http://php.net/manual/en/function.chmod.php
+ *
+ * Compatibility: normal paths and stream wrappers.
+ * @see http://drupal.org/node/515192
+ *
+ * @param string $uri
+ *   A string containing the URI to verify. If this value is omitted,
+ *   Drupal's public files directory will be used [public://].
+ * @param int $mode
  *   Integer value for the permissions. Consult PHP chmod() documentation for
  *   more information.
- * @return
+ * @return bool
  *   TRUE for success, FALSE in the event of an error.
  */
-function drupal_chmod($path, $mode = NULL) {
+function drupal_chmod($uri, $mode = NULL) {
+
   if (!isset($mode)) {
-    if (is_dir($path)) {
+    if (is_dir($uri)) {
       $mode = variable_get('file_chmod_directory', 0775);
     }
     else {
@@ -1623,14 +1634,88 @@ function drupal_chmod($path, $mode = NULL) {
     }
   }
 
-  if (@chmod($path, $mode)) {
-    return TRUE;
+  // If this URI is a stream, pass it off to the appropriate stream wrapper.
+  // Otherwise, attempt PHP's chmod. This allows use of drupal_chmod even
+  // for unmanaged files outside of the stream wrapper interface.
+  if ($wrapper = DrupalStreamWrapperRegistry::getInstanceByUri($uri)) {
+    if ($wrapper->chmod($mode)) {
+      return TRUE;
+    }
+  }
+  else {
+    if (@chmod($uri, $mode)) {
+      return TRUE;
+    }
   }
 
-  watchdog('file', 'The file permissions could not be set on %path.', array('%path' => $path), WATCHDOG_ERROR);
+  watchdog('file', 'The file permissions could not be set on %uri.', array('%uri' => $uri), WATCHDOG_ERROR);
   return FALSE;
 }
 
 /**
+ * Gets canonicalized absolute path of a file or directory
+ *
+ * PHP's realpath() does not properly support streams, so this function
+ * fills that gap. If a stream wrapped URI is provided, it will be passed
+ * to the registered wrapper for handling. If the URI does not contain a
+ * scheme or the wrapper implementation does not implement realpath, then
+ * FALSE will be returned.
+ *
+ * @see http://php.net/manual/en/function.realpath.php
+ *
+ * Compatibility: normal paths and stream wrappers.
+ * @see http://drupal.org/node/515192
+ *
+ * @param string $uri
+ *   A string containing the URI to verify. If this value is omitted,
+ *   Drupal's public files directory will be used [public://].
+ * @return mixed
+ *   Returns the absolute pathname, or FALSE on failure.
+ */
+function drupal_realpath($uri) {
+
+  // If this URI is a stream, pass it off to the appropriate stream wrapper.
+  // Otherwise, attempt PHP's realpath. This allows use of drupal_realpath even
+  // for unmanaged files outside of the stream wrapper interface.
+  if ($wrapper = DrupalStreamWrapperRegistry::getInstanceByUri($uri)) {
+    return $wrapper->realpath();
+  }
+  else {
+    return realpath($uri);
+  }
+}
+
+/**
+ * Gets the name of the directory from a given path.
+ *
+ * PHP's dirname() does not properly pase 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
+ *
+ * PHP's dirname doesn't parse a stream as expected. This will fallback to
+ * dirname if it's not a valid stream.
+ */
+function drupal_dirname($uri) {
+
+  if ($scheme = DrupalStreamWrapperRegistry::getValidStreamScheme($uri)) {
+    $target  = DrupalStreamWrapperRegistry::getStreamTarget($uri);
+    $dirname = dirname($target);
+
+    if ($dirname == '.') {
+      $dirname = '';
+    }
+
+    return $scheme . '://' . $dirname;
+  }
+  else {
+    return dirname($uri);
+  }
+
+}
+
+/**
  * @} End of "defgroup file".
  */
diff --git includes/filetransfer/filetransfer.inc includes/filetransfer/filetransfer.inc
index 588526c..e5c9ff8 100644
--- includes/filetransfer/filetransfer.inc
+++ includes/filetransfer/filetransfer.inc
@@ -114,7 +114,7 @@ abstract class FileTransfer {
    *   A path to check against the jail.
    */
   protected final function checkPath($path) {
-    if (realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) {
+    if (drupal_realpath(substr($path, 0, strlen($this->jail))) !== $this->jail) {
       throw new FileTransferException('@directory is outside of the @jail', NULL, array('@directory' => $path, '@jail' => $this->jail));
     }
   }
diff --git modules/image/image.module modules/image/image.module
index f0411ef..f1c0451 100644
--- modules/image/image.module
+++ modules/image/image.module
@@ -373,7 +373,7 @@ function image_style_create_derivative($style, $source, $destination) {
  *   An image style array.
  */
 function image_style_flush($style) {
-  $style_directory = realpath(file_directory_path() . '/styles/' . $style['name']);
+  $style_directory = drupal_realpath(file_directory_path() . '/styles/' . $style['name']);
   if (is_dir($style_directory)) {
     file_unmanaged_delete_recursive($style_directory);
   }
diff --git modules/simpletest/drupal_web_test_case.php modules/simpletest/drupal_web_test_case.php
index 1e77669..e06d321 100644
--- modules/simpletest/drupal_web_test_case.php
+++ modules/simpletest/drupal_web_test_case.php
@@ -1416,7 +1416,7 @@ class DrupalWebTestCase extends DrupalTestCase {
             // is broken. This is a less than elegant workaround. Alternatives
             // are being explored at #253506.
             foreach ($upload as $key => $file) {
-              $file = realpath($file);
+              $file = drupal_realpath($file);
               if ($file && is_file($file)) {
                 $post[$key] = '@' . $file;
               }
diff --git modules/simpletest/tests/error.test modules/simpletest/tests/error.test
index f9319b9..9e4ed6d 100644
--- modules/simpletest/tests/error.test
+++ modules/simpletest/tests/error.test
@@ -26,21 +26,21 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase {
       '%message' => 'Undefined variable: bananas',
       '%function' => 'error_test_generate_warnings()',
       '%line' => 44,
-      '%file' => realpath('modules/simpletest/tests/error_test.module'),
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
     );
     $error_warning = array(
       '%type' => 'Warning',
       '%message' => 'Division by zero',
       '%function' => 'error_test_generate_warnings()',
       '%line' => 46,
-      '%file' => realpath('modules/simpletest/tests/error_test.module'),
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
     );
     $error_user_notice = array(
       '%type' => 'User notice',
       '%message' => 'Drupal is awesome',
       '%function' => 'error_test_generate_warnings()',
       '%line' => 48,
-      '%file' => realpath('modules/simpletest/tests/error_test.module'),
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
     );
 
     // Set error reporting to collect notices.
@@ -77,14 +77,14 @@ class DrupalErrorHandlerUnitTest extends DrupalWebTestCase {
       '%message' => 'Drupal is awesome',
       '%function' => 'error_test_trigger_exception()',
       '%line' => 57,
-      '%file' => realpath('modules/simpletest/tests/error_test.module'),
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
     );
     $error_pdo_exception = array(
       '%type' => 'PDOException',
       '%message' => 'SELECT * FROM bananas_are_awesome',
       '%function' => 'error_test_trigger_pdo_exception()',
       '%line' => 65,
-      '%file' => realpath('modules/simpletest/tests/error_test.module'),
+      '%file' => drupal_realpath('modules/simpletest/tests/error_test.module'),
     );
 
     $this->drupalGet('error-test/trigger-exception');
diff --git modules/simpletest/tests/file.test modules/simpletest/tests/file.test
index 30408c7..4b8928b 100644
--- modules/simpletest/tests/file.test
+++ modules/simpletest/tests/file.test
@@ -435,7 +435,7 @@ class FileValidatorTest extends DrupalWebTestCase {
     if (image_get_toolkit()) {
       // Copy the image so that the original doesn't get resized.
       $temp_dir = file_directory_temp();
-      copy(realpath('misc/druplicon.png'), realpath($temp_dir) . '/druplicon.png');
+      copy(drupal_realpath('misc/druplicon.png'), drupal_realpath($temp_dir) . '/druplicon.png');
       $this->image->filepath = $temp_dir . '/druplicon.png';
 
       $errors = file_validate_image_resolution($this->image, '10x5');
@@ -445,7 +445,7 @@ class FileValidatorTest extends DrupalWebTestCase {
       $this->assertTrue($info['width'] <= 10, t('Image scaled to correct width.'), 'File');
       $this->assertTrue($info['height'] <= 5, t('Image scaled to correct height.'), 'File');
 
-      unlink(realpath($temp_dir . '/druplicon.png'));
+      unlink(drupal_realpath($temp_dir . '/druplicon.png'));
     }
     else {
       // TODO: should check that the error is returned if no toolkit is available.
@@ -539,14 +539,14 @@ class FileUnmanagedSaveDataTest extends FileTestCase {
     $filepath = file_unmanaged_save_data($contents);
     $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
     $this->assertEqual(file_directory_path(), dirname($filepath), t("File was placed in Drupal's files directory."));
-    $this->assertEqual($contents, file_get_contents(realpath($filepath)), t('Contents of the file are correct.'));
+    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
 
     // Provide a filename.
     $filepath = file_unmanaged_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE);
     $this->assertTrue($filepath, t('Unnamed file saved correctly.'));
-    $this->assertEqual(file_directory_path(), dirname($filepath), t("File was placed in Drupal's files directory."));
+    $this->assertEqual(file_directory_path(), drupal_dirname($filepath), t("File was placed in Drupal's files directory."));
     $this->assertEqual('asdf.txt', basename($filepath), t('File was named correctly.'));
-    $this->assertEqual($contents, file_get_contents(realpath($filepath)), t('Contents of the file are correct.'));
+    $this->assertEqual($contents, file_get_contents(drupal_realpath($filepath)), t('Contents of the file are correct.'));
     $this->assertFilePermissions($filepath, variable_get('file_chmod_file', 0664));
   }
 }
@@ -586,7 +586,7 @@ class FileSaveUploadTest extends FileHookTestCase {
     // Upload with replace to gurantee there's something there.
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => realpath($this->image->filepath)
+      'files[file_test_upload]' => drupal_realpath($this->image->filepath)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -613,7 +613,7 @@ class FileSaveUploadTest extends FileHookTestCase {
     // Upload a second file.
     $max_fid_before = db_query('SELECT MAX(fid) AS fid FROM {files}')->fetchField();
     $image2 = current($this->drupalGetTestFiles('image'));
-    $edit = array('files[file_test_upload]' => realpath($image2->filepath));
+    $edit = array('files[file_test_upload]' => drupal_realpath($image2->filepath));
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
     $this->assertRaw(t('You WIN!'));
@@ -638,7 +638,7 @@ class FileSaveUploadTest extends FileHookTestCase {
   function testExistingRename() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_RENAME,
-      'files[file_test_upload]' => realpath($this->image->filepath)
+      'files[file_test_upload]' => drupal_realpath($this->image->filepath)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -654,7 +654,7 @@ class FileSaveUploadTest extends FileHookTestCase {
   function testExistingReplace() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_REPLACE,
-      'files[file_test_upload]' => realpath($this->image->filepath)
+      'files[file_test_upload]' => drupal_realpath($this->image->filepath)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
@@ -670,7 +670,7 @@ class FileSaveUploadTest extends FileHookTestCase {
   function testExistingError() {
     $edit = array(
       'file_test_replace' => FILE_EXISTS_ERROR,
-      'files[file_test_upload]' => realpath($this->image->filepath)
+      'files[file_test_upload]' => drupal_realpath($this->image->filepath)
     );
     $this->drupalPost('file-test/upload', $edit, t('Submit'));
     $this->assertResponse(200, t('Received a 200 response for posted test file.'));
diff --git modules/user/user.test modules/user/user.test
index a661258..345b681 100644
--- modules/user/user.test
+++ modules/user/user.test
@@ -706,7 +706,7 @@ class UserPictureTestCase extends DrupalWebTestCase {
   }
 
   function saveUserPicture($image) {
-    $edit = array('files[picture_upload]' => realpath($image->filepath));
+    $edit = array('files[picture_upload]' => drupal_realpath($image->filepath));
     $this->drupalPost('user/' . $this->user->uid . '/edit', $edit, t('Save'));
 
     $img_info = image_get_info($image->filepath);
diff --git scripts/run-tests.sh scripts/run-tests.sh
index 42ea657..09e1c77 100755
--- scripts/run-tests.sh
+++ scripts/run-tests.sh
@@ -272,7 +272,7 @@ function simpletest_script_init($server_software) {
   $_SERVER['PHP_SELF'] = $path .'/index.php';
   $_SERVER['HTTP_USER_AGENT'] = 'Drupal command line';
 
-  chdir(realpath(dirname(__FILE__) . '/..'));
+  chdir(drupal_realpath(dirname(__FILE__) . '/..'));
   define('DRUPAL_ROOT', getcwd());
   require_once DRUPAL_ROOT . '/includes/bootstrap.inc';
 }
@@ -396,7 +396,7 @@ function simpletest_script_get_test_list() {
     elseif ($args['file']) {
       $files = array();
       foreach ($args['test_names'] as $file) {
-        $files[realpath($file)] = 1;
+        $files[drupal_realpath($file)] = 1;
       }
 
       // Check for valid class names.
