8 file.inc file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS)
7 file.inc file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS)

Checks that the directory exists and is writable.

Directories need to have execute permissions to be considered a directory by FTP servers, etc.


$directory: A string reference containing the name of a directory path or URI. A trailing slash will be trimmed from a path.

$options: A bitmask to indicate if the directory should be created if it does not exist (FILE_CREATE_DIRECTORY) or made writable if it is read-only (FILE_MODIFY_PERMISSIONS).

Return value

TRUE if the directory exists (or was created) and is writable. FALSE otherwise.

Related topics

24 calls to file_prepare_directory()
color_scheme_form_submit in modules/color/color.module
Form submission handler for color_scheme_form().
DrupalUnitTestCase::setUp in modules/simpletest/drupal_web_test_case.php
Sets up unit test environment.
DrupalWebTestCase::prepareEnvironment in modules/simpletest/drupal_web_test_case.php
Prepares the current environment for running the test.
drupal_build_css_cache in includes/common.inc
Aggregates and optimizes CSS files into a cache file in the files directory.
drupal_build_js_cache in includes/common.inc
Aggregates JavaScript files into a cache file in the files directory.

... See full list


includes/file.inc, line 430
API for handling file uploads and server file management.


function file_prepare_directory(&$directory, $options = FILE_MODIFY_PERMISSIONS) {
  if (!file_stream_wrapper_valid_scheme(file_uri_scheme($directory))) {
    // Only trim if we're not dealing with a stream.
    $directory = rtrim($directory, '/\\');

  // Check if directory exists.
  if (!is_dir($directory)) {
    // Let mkdir() recursively create directories and use the default directory
    // permissions.
    if (($options & FILE_CREATE_DIRECTORY) && @drupal_mkdir($directory, NULL, TRUE)) {
      return drupal_chmod($directory);
    return FALSE;
  // The directory exists, so check to see if it is writable.
  $writable = is_writable($directory);
  if (!$writable && ($options & FILE_MODIFY_PERMISSIONS)) {
    return drupal_chmod($directory);

  return $writable;


This is the Drupal7 version of Drupal6 file_check_directory()

If you get

Deprecated function: Call-time pass-by-reference has been deprecated

The correct function calling is
file_prepare_directory($directory, $options = FILE_MODIFY_PERMISSIONS);
Without the "&" in front of the directory variable

Why is $directory passed by reference?

Is there a particular reason for the pass by reference?

Good question. I don't see any reason why $directory is a reference, it doesn't appear to be modified at all by the function.
I was trying to pass the value into the function directly as a string and got a 500 server error. Because it's a reference I had to create a variable first and pass that in. Not a problem really, but it took me a little while to figure it out what the problem was.

Not allowed:

file_prepare_directory('public://mydir', FILE_CREATE_DIRECTORY);


$mydir = 'public://mydir';
file_prepare_directory($mydir, FILE_CREATE_DIRECTORY);

$directory pass by reference is for cleanup of the string passed in. Check :437 therein

= rtrim($directory, '/\\');

The only way I could add a file with IIS from the /admin/content/file page was to change the line

$writable = is_writable($directory);


$writable = is_writable(drupal_realpath($directory));

Patch to follow in coming days.

If you have a valid file_uri_scheme (e.g. public) and the directory exists then an attempt is made to use is_writable('public://mydirectory/myfile') which will return FALSE as we are passing a URI instead of full filename to is_writable

Using drupal_realpath($directory) will not always work either as passing a full filename to drupal_realpath instead of a URI will return FALSE from drupal_realpath.

I would suggest this:

$writable = is_writable(drupal_realpath($directory) == FALSE ? $directory : drupal_realpath($directory));

Another solution (for Drupal 7)

You can solve this issue by defining your own Stream Wrapper.

 * Implements hook_stream_wrappers().
function MYMODULE_stream_wrappers() {
  return array(
'syspublic' => array(
'name' => t("Default distribution files"),
'class' => 'SysPublicStreamWrapper',
'description' => t("Visible, readable and writeable local path"),

 * Default sys (syspublic://) stream wrapper class.
class SysPublicStreamWrapper extends DrupalPublicStreamWrapper {
  public function
getDirectoryPath() {

Via: Writing Stream Wrappers

Under the covers, this uses a recursive PHP mkdir() call. As stated in the comments on http://uk3.php.net/mkdir, only the lowest directory in the tree gets the permissions - all the others get something else.

If you want all directories in the tree to get the permissions you specify, then you have to recurse through the path and set them yourself.

A "caution" for combing the FILE_CREATE_DIRECTORY and FILE_MODIFY_PERMISSIONS bit masks…you have to use the OR operator (||), not the AND operator (&&). This got me! Hope it will help someone else (:

Use | (the bitwise operator), not || (the boolean operator).