Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.78
diff -u -8 -p -r1.78 system.admin.inc
--- modules/system/system.admin.inc	1 Jul 2008 20:36:40 -0000	1.78
+++ modules/system/system.admin.inc	16 Jul 2008 16:25:40 -0000
@@ -1443,17 +1443,17 @@ function system_file_system_settings() {
     '#after_build' => array('system_check_directory'),
   );
 
   $form['file_directory_temp'] = array(
     '#type' => 'textfield',
     '#title' => t('Temporary directory'),
     '#default_value' => file_directory_temp(),
     '#maxlength' => 255,
-    '#description' => t('A file system path where uploaded files will be stored during previews.'),
+    '#description' => t('A file system path where temporary files may be stored.'),
     '#after_build' => array('system_check_directory'),
   );
 
   $form['file_downloads'] = array(
     '#type' => 'radios',
     '#title' => t('Download method'),
     '#default_value' => variable_get('file_downloads', FILE_DOWNLOADS_PUBLIC),
     '#options' => array(FILE_DOWNLOADS_PUBLIC => t('Public - files are available using HTTP directly.'), FILE_DOWNLOADS_PRIVATE => t('Private - files are transferred by Drupal.')),
@@ -2258,9 +2258,9 @@ function theme_system_themes_form($form)
     }
     $rows[] = $row;
   }
 
   $header = array(t('Screenshot'), t('Name'), t('Version'), t('Enabled'), t('Default'), t('Operations'));
   $output = theme('table', $header, $rows);
   $output .= drupal_render($form);
   return $output;
-}
\ No newline at end of file
+}
Index: modules/system/image.gd.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/image.gd.inc,v
retrieving revision 1.1
diff -u -8 -p -r1.1 image.gd.inc
--- modules/system/image.gd.inc	8 Jul 2008 01:08:15 -0000	1.1
+++ modules/system/image.gd.inc	16 Jul 2008 16:25:40 -0000
@@ -191,31 +191,40 @@ function image_gd_open($file, $extension
 }
 
 /**
  * GD helper to write an image resource to a destination file.
  *
  * @param $res
  *   An image resource created with image_gd_open().
  * @param $destination
- *   A string file path where the iamge should be saved.
+ *   A string file path where the image should be saved.
  * @param $extension
  *   A string containing one of the following extensions: gif, jpg, jpeg, png.
  * @return
  *   Boolean indicating success.
  */
 function image_gd_close($res, $destination, $extension) {
   $extension = str_replace('jpg', 'jpeg', $extension);
   $close_func = 'image' . $extension;
   if (!function_exists($close_func)) {
     return FALSE;
   }
+
+  // These functions do not support stream wrappers
+  $output_file = file_is_wrapper($destination) ? tempnam(file_realpath(file_directory_temp()), 'image') : $destination;
   if ($extension == 'jpeg') {
-    return $close_func($res, $destination, variable_get('image_jpeg_quality', 75));
+    $ok = $close_func($res, $output_file, variable_get('image_jpeg_quality', 75));
   }
   else {
-    return $close_func($res, $destination);
+    $ok = $close_func($res, $output_file);
+  }
+
+  if ($ok && $destination != $output_file) {
+    $ok = copy($output_file, $destination);
+    @unlink($output_file);
   }
+  return $ok;
 }
 
 /**
  * @} End of "ingroup image".
  */
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.127
diff -u -8 -p -r1.127 file.inc
--- includes/file.inc	5 Jul 2008 18:34:29 -0000	1.127
+++ includes/file.inc	16 Jul 2008 16:25:40 -0000
@@ -66,16 +66,67 @@ define('FILE_STATUS_TEMPORARY', 0);
  *
  * If you wish to add custom statuses for use by contrib modules please expand
  * as binary flags and consider the first 8 bits reserved.
  * (0,1,2,4,8,16,32,64,128).
  */
 define('FILE_STATUS_PERMANENT', 1);
 
 /**
+ * Get canonicalized absolute path of a file or directory. The Windows path
+ * separator "\" is converted to "/", and consecutive "/" characters are stripped.
+ * If path is a directory, the trailing "/" is stripped.
+ * For regular files:
+ * - Symbolic links are expanded.
+ * - "/./" and "/../" segments are resolved.
+ * For paths with protocol/wrapper prefix (e.g. "mywrapper://foo/bar.txt"):
+ * - Paths containing "/../" are blocked (FALSE is returned).
+ * - Paths are assumed to be case-sensitive (no case normalization is done).
+ *
+ * @code
+ *   // Returns "/foo/bar/boo", or FALSE if the file does not exist:
+ *   file_realpath('/foo//bar/./baz/..\\boo');
+ *
+ *   // Returns FALSE due to "/../":
+ *   file_realpath('mywrapper://foo/bar/../baz');
+ * @endcode
+ *
+ * @param $path A string containing a path to a file or directory.
+ * @return A string containing the absolute path to the file/directory,
+ *         or FALSE if file/directory does not exist.
+ */
+function file_realpath($path) {
+  // Does $path include an explicit protocol/wrapper prefix "foo://" (not a
+  // Windows drive letter "C:/temp")?
+  if (preg_match('@^([a-z0-9.+-]{2,})://(.*)@i', $path, $reg)) {
+    // Replace "\" and "//" with "/", except when "//" is preceded by a colon
+    // (this indicates a nested stream wrapper prefix, e.g. "foo://bar://".
+    $wrappedPath = preg_replace('@(?<!:)[/\\\\]+@', '/', $reg[2]);
+    // Look for ".." separated by "/" or string boundary
+    if (preg_match('@(?<=^|/)\.\.(?=/|$)@', $wrappedPath)) {
+      return FALSE;
+    }
+    $path = $reg[1] . '://' . $wrappedPath;
+    return file_exists($path) ? rtrim($path, '/') : FALSE;
+  }
+  else {
+    return realpath($path);
+  }
+}
+
+/**
+ * Checks whether the path uses a wrapper prefix, e.g. "foo://".
+ */
+function file_is_wrapper($path) {
+  // A wrapper prefix is at least two characters so that it can be distinguised
+  // from a Windows drive letter, "C:/temp"?
+  return preg_match('@^([a-z0-9.+-]{2,})://@i', $path);
+}
+
+/**
  * Create the download path to a file.
  *
  * @param $path A string containing the path of the file to generate URL for.
  * @return A string containing a URL that can be used to download the file.
  */
 function file_create_url($path) {
   // Strip file_directory_path from $path. We only include relative paths in urls.
   if (strpos($path, file_directory_path() . '/') === 0) {
@@ -133,17 +184,17 @@ function file_create_path($dest = 0) {
  *   work, a form error will be set preventing them from saving the settings.
  * @return FALSE when directory not found, or TRUE when directory exists.
  */
 function file_check_directory(&$directory, $mode = 0, $form_item = NULL) {
   $directory = rtrim($directory, '/\\');
 
   // Check if directory exists.
   if (!is_dir($directory)) {
-    if (($mode & FILE_CREATE_DIRECTORY) && @mkdir($directory)) {
+    if (($mode & FILE_CREATE_DIRECTORY) && @mkdir($directory, 0775)) {
       @chmod($directory, 0775); // Necessary for non-webserver users.
     }
     else {
       if ($form_item) {
         form_set_error($form_item, t('The directory %directory does not exist.', array('%directory' => $directory)));
         watchdog('file system', 'The directory %directory does not exist.', array('%directory' => $directory), WATCHDOG_ERROR);
       }
       return FALSE;
@@ -158,17 +209,17 @@ function file_check_directory(&$director
       return FALSE;
     }
   }
 
   if ((file_directory_path() == $directory || file_directory_temp() == $directory) && !is_file("$directory/.htaccess")) {
     $htaccess_lines = "SetHandler Drupal_Security_Do_Not_Remove_See_SA_2006_006\nOptions None\nOptions +FollowSymLinks";
     if (($fp = fopen("$directory/.htaccess", 'w')) && fputs($fp, $htaccess_lines)) {
       fclose($fp);
-      chmod($directory . '/.htaccess', 0664);
+      @chmod($directory . '/.htaccess', 0664);
     }
     else {
       $variables = array('%directory' => $directory, '!htaccess' => '<br />' . nl2br(check_plain($htaccess_lines)));
       form_set_error($form_item, t("Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables));
       watchdog('security', "Security warning: Couldn't write .htaccess file. Please create a .htaccess file in your %directory directory which contains the following lines: <code>!htaccess</code>", $variables, WATCHDOG_ERROR);
     }
   }
 
@@ -209,25 +260,33 @@ function file_check_path(&$path) {
  *   file_check_location('/www/example.com/files/../../../etc/passwd', '/www/example.com/files');
  * @endcode
  *
  * @param $source A string set to the file to check.
  * @param $directory A string where the file should be located.
  * @return FALSE for invalid path or the real path of the source.
  */
 function file_check_location($source, $directory = '') {
-  $check = realpath($source);
+  $check = file_realpath($source);
   if ($check) {
     $source = $check;
   }
   else {
     // This file does not yet exist
-    $source = realpath(dirname($source)) . '/' . basename($source);
+    $dirname = file_realpath(dirname($source));
+    $basename = basename($source);
+
+    // Make sure $source is located in an existing directory
+    if (!$dirname || $basename == "..") {
+      return 0;
+    }
+
+    $source = $dirname . '/' . $basename;
   }
-  $directory = realpath($directory);
+  $directory = file_realpath($directory);
   if ($directory && strpos($source, $directory) !== 0) {
     return FALSE;
   }
   return $source;
 }
 
 /**
  * Copies a file to a new location. This is a powerful function that in many ways
@@ -266,30 +325,30 @@ function file_copy(&$source, $dest = 0, 
   if (is_object($source)) {
     $file = $source;
     $source = $file->filepath;
     if (!$basename) {
       $basename = $file->filename;
     }
   }
 
-  $source = realpath($source);
+  $source = file_realpath($source);
   if (!file_exists($source)) {
     drupal_set_message(t('The selected file %file could not be copied, because no file by that name exists. Please check that you supplied the correct filename.', array('%file' => $source)), 'error');
     return FALSE;
   }
 
   // If the destination file is not specified then use the filename of the source file.
   $basename = $basename ? $basename : basename($source);
   $dest = $directory . '/' . $basename;
 
   // 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($dest)) {
+  if ($source != file_realpath($dest)) {
     if (!$dest = file_destination($dest, $replace)) {
       drupal_set_message(t('The selected file %file could not be copied, because a file by that name already exists in the destination.', array('%file' => $source)), 'error');
       return FALSE;
     }
 
     if (!@copy($source, $dest)) {
       drupal_set_message(t('The selected file %file could not be copied.', array('%file' => $source)), 'error');
       return FALSE;
@@ -783,17 +842,17 @@ function file_validate_image_resolution(
  *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is unique
  *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
  *
  * @return A string containing the resulting filename or FALSE on error
  */
 function file_save_data($data, $dest, $replace = FILE_EXISTS_RENAME) {
   $temp = file_directory_temp();
   // On Windows, tempnam() requires an absolute path, so we use realpath().
-  $file = tempnam(realpath($temp), 'file');
+  $file = tempnam(file_realpath($temp), 'file');
   if (!$fp = fopen($file, 'wb')) {
     drupal_set_message(t('The file could not be created.'), 'error');
     return FALSE;
   }
   fwrite($fp, $data);
   fclose($fp);
 
   if (!file_move($file, $dest, $replace)) {
@@ -943,17 +1002,20 @@ function file_scan_directory($dir, $mask
 
     closedir($handle);
   }
 
   return $files;
 }
 
 /**
- * Determine the default temporary directory.
+ * Determine the default temporary directory. This may be used for storing
+ * temporary files within a single request.
+ * If a temporary file is to be used in several requests, it should be saved in
+ * file_directory_path with its status set to FILE_STATUS_TEMPORARY.
  *
  * @return A string containing a temp directory.
  */
 function file_directory_temp() {
   $temporary_directory = variable_get('file_directory_temp', NULL);
 
   if (is_null($temporary_directory)) {
     $directories = array();
Index: includes/file.test
===================================================================
RCS file: includes/file.test
diff -N includes/file.test
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/file.test	16 Jul 2008 16:25:40 -0000
@@ -0,0 +1,160 @@
+<?php
+// $Id: file.test,v 1.1 2008/05/05 18:23:31 foo Exp $
+
+require_once './includes/file.dummystreamwrapper.inc';
+
+class FileTestCase extends DrupalWebTestCase {
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Filesystem functions'),
+      'description' => t('Create, delete, rename etc.'),
+      'group' => t('File')
+    );
+  }
+
+  /**
+   * Test file_realpath().
+   */
+  function testFileRealpath() {
+    $file_directory_path = file_realpath(file_directory_path());
+    $this->assertTrue(is_dir($file_directory_path));
+    $this->assertNotEqual(substr($file_directory_path, 0, -1), '/');
+
+    $this->assertFalse(file_realpath($file_directory_path . '/not-found'));
+
+    $file = $file_directory_path . '/foo.txt';
+    file_put_contents($file, 'lorem ipsum');
+    $this->assertTrue(is_file($file));
+    $this->assertFalse(is_dir($file));
+    $this->assertEqual(file_realpath($file), $file);
+
+    $this->assertEqual(file_realpath($file_directory_path . '//foo.txt'),
+                       $file);
+  }
+
+  /**
+   * Test file_check_directory().
+   */
+  function testFileCheckDirectory() {
+    $this->assertTrue(file_check_directory(file_directory_path()));
+    $this->assertTrue(file_exists(file_directory_path() . '/.htaccess'));
+
+    $dir = file_directory_path() . '/dir';
+    $this->assertFalse(file_check_directory($dir));
+    $this->assertTrue(file_check_directory($dir, FILE_CREATE_DIRECTORY));
+    $this->assertTrue(file_check_directory($dir));
+  }
+
+  /**
+   * Test file_check_location().
+   */
+  function testFileCheckLocation() {
+    $file = file_directory_path() . '/foo/bar.txt';
+    mkdir(dirname($file), 0777, true);
+    file_put_contents($file, 'lorem ipsum');
+
+    $this->assertTrue(file_check_location(file_directory_path() . '/', file_directory_path()));
+    $this->assertTrue(file_check_location($file, file_directory_path()));
+    $this->assertFalse(file_check_location(file_directory_path() . '/..', file_directory_path()));
+  }
+
+  /**
+   * Test file_save_data().
+   */
+  function testFileSaveData() {
+    $file_directory_path = file_directory_path(); 
+    $file = file_directory_path() . '/foo.txt';
+    $file_0 = file_directory_path() . '/foo_0.txt';
+    $this->assertEqual(file_save_data('abc', $file, FILE_EXISTS_REPLACE), $file);
+    $this->assertEqual(filesize($file), 3);
+
+    $this->assertEqual(file_save_data('abcd', $file, FILE_EXISTS_REPLACE), $file);
+    $this->assertEqual(filesize($file), 4);
+
+    $this->assertFalse(file_save_data('abcde', $file, FILE_EXISTS_ERROR));
+    $this->assertEqual(filesize($file), 4);
+
+    $this->assertEqual(file_save_data('abcdef', $file, FILE_EXISTS_RENAME), $file_0);
+    $this->assertEqual(filesize($file_0), 6);
+  }
+
+  /**
+   * Test file_scan_directory().
+   */
+  function testFileScanDirectory() {
+    $files = array(
+      'foobar.txt',
+      'subdir1/foo.txt',
+      'subdir2/subsubdir/bar.txt',
+    );
+    foreach ($files as &$file) {
+      $file = file_directory_path() . '/' . $file;
+      if (!is_dir(dirname($file))) {
+        mkdir(dirname($file), 0777, true);
+      }
+      file_put_contents($file, 'lorem ipsum');
+    }
+    $files_found = array_keys(file_scan_directory(file_directory_path(), '.*'));
+    sort($files);
+    sort($files_found);
+    $this->assertEqual($files, $files_found);
+  }
+}
+
+
+/**
+ * Inherits FileTestCase to allow running of all tests using a dummy stream wrapper.
+ */
+class FileStreamWrapperTestCase extends FileTestCase {
+  private $prefix;
+  private $original_directory_path;
+
+  /**
+   * Implementation of getInfo().
+   */
+  function getInfo() {
+    return array(
+      'name' => t('Filesystem functions (stream wrapper)'),
+      'description' => t('Run all file tests with file_directory_path set to a path with a stream wrapper prefix.'),
+      'group' => t('File')
+    );
+  }
+
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp();
+    $this->prefix = 'dummy-stream-wrapper-' . uniqid();
+    stream_wrapper_register($this->prefix, 'FileDummyStreamWrapper');
+    $this->original_directory_path = file_directory_path();
+    variable_set('file_directory_path', $this->prefix . '://' . file_realpath(file_directory_path()));
+  }
+
+  /**
+   * Implementation of tearDown().
+   */
+  function tearDown() {
+    variable_set('file_directory_path', $this->original_directory_path);
+    stream_wrapper_unregister($this->prefix);
+    parent::tearDown();
+  }
+
+  /**
+   * Test file_realpath().
+   */
+  function testStreamWrapperFileRealpath() {
+    $file = file_directory_path() . '/foo/bar.txt';
+    mkdir(dirname($file), 0777, true);
+
+    file_put_contents($file, 'lorem ipsum');
+
+    // "/../" is forbidden
+    $this->assertFalse(file_realpath(file_directory_path() . '/../foo/bar.txt'));
+    $this->assertFalse(file_realpath(file_directory_path() . '/..'));
+  }
+}
+
Index: includes/file.dummystreamwrapper.inc
===================================================================
RCS file: includes/file.dummystreamwrapper.inc
diff -N includes/file.dummystreamwrapper.inc
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ includes/file.dummystreamwrapper.inc	16 Jul 2008 16:25:40 -0000
@@ -0,0 +1,275 @@
+<?php
+// $Id: file.dummystreamwrapper.inc,v 1.1 2008/05/05 18:23:31 foo Exp $
+
+/**
+ * Dummy stream wrapper that just strips the wrapper prefix and passes all
+ * requests on. E.g. "dummy-wrapper:///tmp/foo" maps to "/tmp/foo". Will also
+ * work nested, e.g. "dummy-wrapper://mywrapper://foo.txt" maps to
+ * "mywrapper://foo.txt".
+ * The dummy stream wrapper is used for unit testing functions that access files.
+ * The class implements the methods required by PHP's stream_wrapper_register().
+ */
+class FileDummyStreamWrapper {
+  /**
+   * A file handle to the nested file opened by stream_open().
+   */
+  private $fileHandle;
+
+  /**
+   * A directory handle to the nested file opened by dir_opendor().
+   */
+  private $directoryHandle;
+
+  /**
+   * Support for fopen(), file_get_contents(), file_put_contents() etc.
+   *
+   * @param $path
+   *   A string containing the path to the file to open
+   * @param $mode
+   *   The file mode ("r", "wb" etc.)
+   * @param $options
+   *   A bit mask of STREAM_USE_PATH and STREAM_REPORT_ERRORS
+   * @param &$opened_path
+   *   A string containing the path actually opened
+   * @return
+   *  TRUE if file was opened successfully
+   */
+  public function stream_open($path, $mode, $options, &$opened_path) {
+     $nestedPath = self::getNestedPath($path);
+    if ($options & STREAM_REPORT_ERRORS) {
+      $this->fileHandle = fopen($nestedPath, $mode);
+    } else {
+      $this->fileHandle = @fopen($nestedPath, $mode);
+    }
+    return (bool) $this->fileHandle;
+  }
+
+  /**
+   * Support for fread(), file_get_contents() etc.
+   *
+   * @param $count
+   *    Maximum number of bytes to be read
+   * @return
+   *  The string that was read, or FALSE in case of an error
+   */
+  public function stream_read($count) {
+    return fread($this->fileHandle, $count);
+  }
+
+  /**
+   * Support for fwrite(), file_put_contents() etc.
+   *
+   * @param $data
+   *   The string to be written
+   * @return
+   *   The number of bytes written
+   */
+  public function stream_write($data) {
+    return fwrite($this->fileHandle, $data);
+  }
+
+  /**
+   * Support for feof().
+   *
+   * @return
+   *   TRUE if end-of-file has been reached
+   */
+  public function stream_eof() {
+    return feof($this->fileHandle);
+  }
+
+  /**
+   * Support for fseek().
+   * 
+   * @param $offset
+   *   The byte offset to got to
+   * @param $whence
+   *   SEEK_SET, SEEK_CUR, or SEEK_END
+   * @return
+   *   TRUE on success
+   */
+  public function stream_seek($offset, $whence) {
+    return fseek($this->fileHandle, $offset, $whence);
+  }
+
+  /**
+   * Support for fflush().
+   *
+   * @return
+   *   TRUE if data was successfully stored (or there was no data to store)
+   */
+  public function stream_flush() {
+    return fflush($this->fileHandle);
+  }
+
+  /**
+   * Support for ftell().
+   *
+   * @return
+   *   The current offset in bytes from the beginning of file
+   */
+  public function stream_tell() {
+    return ftell($this->fileHandle);
+  }
+
+  /**
+   * Support for fstat().
+   *
+   * @return
+   *   An array with file status, or false in case of an error - see fstat()
+   *   for a description of this array
+   */
+  public function stream_stat() {
+    return fstat($this->fileHandle);
+  }
+
+  /**
+   * Support for fclose().
+   */
+  public function stream_close() {
+    return fclose($this->fileHandle);
+  }
+
+  /**
+   * Support for unlink().
+   *
+   * @param $path
+   *   A string containing the path to the file to delete
+   * @return
+   *   TRUE if file was successfully deleted
+   */
+  public function unlink($path) {
+    return unlink(self::getNestedPath($path));
+  }
+
+  /**
+   * Support for rename().
+   *
+   * @param $fromPath
+   *   The path to the file to rename
+   * @param $toPath
+   *   The new path to the file
+   *
+   * @return
+   *   TRUE, if file was successfully renamed
+   */
+  public function rename($fromPath, $toPath) {
+    return rename(self::getNestedPath($fromPath), self::getNestedPath($toPath));
+  }
+
+  /**
+   * Support for mkdir().
+   *
+   * @param $path
+   *   A string containing the path to the directory to create
+   * @param $mode
+   *   Permission flags - see mkdir()
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS and STREAM_MKDIR_RECURSIVE
+   * @return
+   *   TRUE if directory was successfully created
+   */
+  public function mkdir($path, $mode, $options) {
+    $nestedPath = self::getNestedPath($path);
+    $recursive = (bool) $options & STREAM_MKDIR_RECURSIVE;
+    if ($options & STREAM_REPORT_ERRORS) {
+        return mkdir($nestedPath, $mode, $recursive);
+    } else {
+      return @mkdir($nestedPath, $mode, $recursive);
+    }
+  }
+
+  /**
+   * Support for rmdir().
+   *
+   * @param $path
+   *   A string containing the path to the directory to delete
+   * @param $options
+   *   A bit mask of STREAM_REPORT_ERRORS
+   * @return bool true if directory was successfully removed
+   */
+  public function rmdir($path, $options) {
+    $nestedPath = self::getNestedPath($path);
+    if ($options & STREAM_REPORT_ERRORS) {
+      return rmdir($nestedPath);
+    } else {
+      return @rmdir($nestedPath);
+    }
+  }
+
+  /**
+   * Support for stat().
+   *
+   * @param $path
+   *   A string containing the path to get information about
+   * @param $flags
+   *   A bit mask of STREAM_URL_STAT_LINK and STREAM_URL_STAT_QUIET
+   * @return
+   *   An array with file status, or FALSE in case of an error - see fstat()
+   *   for a description of this array
+   */
+  public function url_stat($path, $flags) {
+      $nestedPath = self::getNestedPath($path);
+      return ($flags & STREAM_URL_STAT_QUIET)
+          ? (file_exists($nestedPath) ? stat($nestedPath) : false)
+          : stat($nestedPath);
+  }
+
+  /**
+   * Support for opendir().
+   *
+   * @param $path
+   *   A string containing the path to the directory to open
+   * @param $options
+   *   Unknown (parameter is not documented in PHP Manual)
+   * @return
+   *   TRUE on success
+   */
+  public function dir_opendir($path, $options) {
+    $this->dirHandle = opendir(self::getNestedPath($path));
+    return (bool) $this->dirHandle;
+  }
+
+  /**
+   * Support for readdir().
+   *
+   * @return
+   *   The next filename, or FALSE if there are no more files in the directory
+   */
+  public function dir_readdir() {
+    return readdir($this->dirHandle);
+  }
+
+  /**
+   * Support for rewinddir().
+   *
+   * @return
+   *   TRUE on success
+   */
+  public function dir_rewinddir() {
+    return rewinddir($this->dirHandle);
+  }
+
+  /**
+   * Support for closedir().
+   *
+   * @return
+   *   TRUE on success
+   */
+  public function dir_closedir() {
+    return closedir($this->dirHandle);
+  }
+
+  /**
+   * Strips the stream wrapper prefix from the specified path.
+   *
+   * @param $path
+   *    A string containing a path with a stream wrapper prefix
+   * @return
+   *    The without a stream wrapper prefix
+   */
+  private static function getNestedPath($path) {
+    return preg_replace('@^([a-z0-9.+-]{2,})://@i', '', $path);
+  }
+}
+
