? .DS_Store
? .cache
? .cvsignore
? .git
? .project
? .settings
? modules/.DS_Store
? sites/all/modules
? sites/default/files
? sites/default/settings.php
? sites/default/test
Index: includes/common.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/common.inc,v
retrieving revision 1.793
diff -u -p -r1.793 common.inc
--- includes/common.inc	14 Sep 2008 21:14:21 -0000	1.793
+++ includes/common.inc	15 Sep 2008 23:07:59 -0000
@@ -1850,7 +1850,7 @@ function drupal_build_css_cache($types, 
     $data = implode('', $matches[0]) . $data;
 
     // Create the CSS file.
-    file_save_data($data, $csspath . '/' . $filename, FILE_EXISTS_REPLACE);
+    file_unmanaged_save_data($data, $csspath . '/' . $filename, FILE_EXISTS_REPLACE);
   }
   return $csspath . '/' . $filename;
 }
@@ -1951,7 +1951,7 @@ function _drupal_load_stylesheet($matche
  * Delete all cached CSS files.
  */
 function drupal_clear_css_cache() {
-  file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
+  file_scan_directory(file_create_path('css'), '.*', array('.', '..', 'CVS'), 'file_unmanaged_delete', TRUE);
 }
 
 /**
@@ -2314,7 +2314,7 @@ function drupal_build_js_cache($files, $
     }
 
     // Create the JS file.
-    file_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
+    file_unmanaged_save_data($contents, $jspath . '/' . $filename, FILE_EXISTS_REPLACE);
   }
 
   return $jspath . '/' . $filename;
@@ -2324,7 +2324,7 @@ function drupal_build_js_cache($files, $
  * Delete all cached JS files.
  */
 function drupal_clear_js_cache() {
-  file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_delete', TRUE);
+  file_scan_directory(file_create_path('js'), '.*', array('.', '..', 'CVS'), 'file_unmanaged_delete', TRUE);
   variable_set('javascript_parsed', array());
 }
 
Index: includes/file.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/file.inc,v
retrieving revision 1.131
diff -u -p -r1.131 file.inc
--- includes/file.inc	15 Sep 2008 09:28:49 -0000	1.131
+++ includes/file.inc	15 Sep 2008 23:08:00 -0000
@@ -254,7 +254,134 @@ function file_check_location($source, $d
 }
 
 /**
- * Copy a file to a new location.
+ * Load a file object from the database.
+ *
+ * @param $param
+ *   Either the id of a file or an array of conditions to match against in the
+ *   database query
+ * @param $reset
+ *   Whether to reset the internal file_load cache.
+ * @return
+ *   A file object.
+ */
+function file_load($param, $reset = NULL) {
+  static $files = array();
+
+  if ($reset) {
+    $files = array();
+  }
+
+  $arguments = array();
+  if (is_numeric($param)) {
+    if (isset($files[(string) $param])) {
+      return is_object($files[$param]) ? clone $files[$param] : $files[$param];
+    }
+    $cond = 'f.fid = %d';
+    $arguments[] = $param;
+  }
+  elseif (is_array($param)) {
+    // Turn the conditions into a query.
+    $cond = array();
+    foreach ($param as $key => $value) {
+      $cond[] = 'f.' . db_escape_table($key) . " = '%s'";
+      $arguments[] = $value;
+    }
+    $cond = implode(' AND ', $cond);
+  }
+  else {
+    return FALSE;
+  }
+  $file = db_fetch_object(db_query('SELECT f.* FROM {files} f WHERE ' . $cond, $arguments));
+
+  if ($file && $file->fid) {
+    // Allow modules to add or change the file object.
+    module_invoke_all('file_load', $file);
+
+    // Cache the fully loaded value.
+    $files[(string) $file->fid] = clone $file;
+  }
+
+  return $file;
+}
+
+/**
+ * Save a file object to the database.
+ *
+ * If the $file->fid is not set a new record will be added. Re-saving an
+ * existing file will not change its status.
+ *
+ * @param $file
+ *   A file object returned by file_load().
+ * @return
+ *   The updated file object.
+ */
+function file_save($file) {
+  $file->timestamp = $_SERVER['REQUEST_TIME'];
+  $file->filesize = filesize($file->filepath);
+
+  if (empty($file->fid)) {
+    drupal_write_record('files', $file);
+    // Inform modules about the newly added file.
+    module_invoke_all('file_insert', $file);
+  }
+  else {
+    drupal_write_record('files', $file, 'fid');
+    // Inform modules that the file has been updated.
+    module_invoke_all('file_update', $file);
+  }
+
+  return $file;
+}
+
+/**
+ * Copy a file to a new location and adds a file record to the database.
+ *
+ * This function should be used when manipulating files that have records
+ * stored in the database. This is a powerful function that in many ways
+ * performs like an advanced version of copy().
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Checks that $source is not equal to $destination; if they are an error
+ *   is reported.
+ * - If file already exists in $destination either the call will error out,
+ *   replace the file or rename the file based on the $replace parameter.
+ * - Adds the new file to the files database. If the source file is a
+ *   temporary file, the resulting file will also be a temporary file.
+ *   @see file_save_upload about temporary files.
+ *
+ * @param $source
+ *   A file object.
+ * @param $destination
+ *   A string containing the directory $source should be copied to. If this
+ *   value is omitted, Drupal's 'files' directory will be used.
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *                          unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ * @return
+ *   File object if the copy is successful, or FALSE in the event of an error.
+ *
+ * @see file_unmanaged_copy()
+ */
+function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  if ($filepath = file_unmanaged_copy($source->filepath, $destination, $replace)) {
+    $file = clone $source;
+    $file->fid      = NULL;
+    $file->filename = basename($filepath);
+    $file->filepath = $filepath;
+    if ($file = file_save($file)) {
+      // Inform modules that the file has been copied.
+      module_invoke_all('file_copy', $file, $source);
+      return $file;
+    }
+  }
+  return FALSE;
+}
+
+/**
+ * Copy a file to a new location without calling any hooks or making any
+ * changes to the database.
  *
  * This is a powerful function that in many ways performs like an advanced
  * version of copy().
@@ -277,8 +404,10 @@ function file_check_location($source, $d
  *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
  * @return
  *   The path to the new file, or FALSE in the event of an error.
+ *
+ * @see file_copy()
  */
-function file_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+function file_unmanaged_copy($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
   $source = 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' => $source)), 'error');
@@ -360,7 +489,50 @@ function file_destination($destination, 
 }
 
 /**
- * Move a file to a new location.
+ * Move a file to a new location and update the file's database entry.
+ *
+ * Moving a file is performed by copying the file to the new location and then
+ * deleting the original.
+ * - Checks if $source and $destination are valid and readable/writable.
+ * - Performs a file move if $source is not equal to $destination.
+ * - If file already exists in $destination either the call will error out,
+ *   replace the file or rename the file based on the $replace parameter.
+ * - Adds the new file to the files database.
+ *
+ * @param $source
+ *   A file object.
+ * @param $destination
+ *   A string containing the directory $source should be copied to. If this
+ *   value is omitted, Drupal's 'files' directory will be used.
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *                          unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ * @return
+ *   Resulting file object for success, or FALSE in the event of an error.
+ *
+ * @see file_unmanaged_move()
+ */
+function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  if ($filepath = file_unmanaged_move($source->filepath, $destination, $replace)) {
+    $file = clone $source;
+    $file->filename = basename($filepath);
+    $file->filepath = $filepath;
+    if ($file = file_save($file)) {
+      // Inform modules that the file has been moved.
+      module_invoke_all('file_move', $file, $source);
+      return $file;
+    }
+    drupal_set_message(t('The removal of the original file %file has failed.', array('%file' => $source->filepath)), 'error');
+  }
+  return FALSE;
+}
+
+/**
+ * Move a file to a new location without calling any hooks or making any
+ * changes to the database.
  *
  * @param $source
  *   A string specifying the file location of the original file.
@@ -375,10 +547,12 @@ function file_destination($destination, 
  *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
  * @return
  *   The filepath of the moved file, or FALSE in the event of an error.
+ *
+ * @see file_move()
  */
-function file_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
-  $filepath = file_copy($source, $destination, $replace);
-  if ($filepath == FALSE || file_delete($source) == FALSE) {
+function file_unmanaged_move($source, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  $filepath = file_unmanaged_copy($source, $destination, $replace);
+  if ($filepath == FALSE || file_unmanaged_delete($source) == FALSE) {
     return FALSE;
   }
   return $filepath;
@@ -470,17 +644,62 @@ function file_create_filename($basename,
 }
 
 /**
- * Delete a file.
+ * Delete a file and its database record.
+ *
+ * If the $force parameter is not TRUE hook_file_references() will be called
+ * to determine if the file is being used by any modules. If the file is being
+ * used is the delete will be cancled.
+ *
+ * @param $path
+ *   A file object.
+ * @param $force
+ *   Boolean indicating that the file should be deleted even if
+ *   hook_file_references() reports that the file is in use.
+ * @return mixed
+ *   TRUE for success, Array for reference count block, or FALSE in the event
+ *   of an error.
+ *
+ * @see hook_file_references()
+ * @see file_unmanaged_delete()
+ */
+function file_delete($file, $force = FALSE) {
+  // If any module returns a value from the reference hook, the file will not
+  // be deleted from Drupal, but file_delete will return a populated array that
+  // tests as TRUE.
+  if (!$force && ($references = module_invoke_all('file_references', $file))) {
+    return $references;
+  }
+
+  // Let other modules clean up any references to the deleted file.
+  module_invoke_all('file_delete', $file);
+
+  // Make sure the file is deleted before removing its row from the
+  // database, so UIs can still find the file in the database.
+  if (file_unmanaged_delete($file->filepath)) {
+    db_query('DELETE FROM {files} WHERE fid = %d', array($file->fid));
+    return TRUE;
+  }
+  return FALSE;
+}
+
+/**
+ * Delete a file without calling any hooks or making any changes to the
+ * database.
+ *
+ * This function should be used when the file to be deleted does not have an
+ * entry recorded in the files table.
  *
  * @param $path
  *   A string containing a file path.
  * @return
  *   TRUE for success or path does not exist, or FALSE in the event of an
  *   error.
+ *
+ * @see file_delete()
  */
-function file_delete($path) {
+function file_unmanaged_delete($path) {
   if (is_dir($path)) {
-    watchdog('file', t('%path is a directory and cannot be removed using file_delete().', array('%path' => $path)), WATCHDOG_ERROR);
+    watchdog('file', t('%path is a directory and cannot be removed using file_unmanaged_delete().', array('%path' => $path)), WATCHDOG_ERROR);
     return FALSE;
   }
   if (is_file($path)) {
@@ -641,15 +860,11 @@ function file_save_upload($source, $vali
       return FALSE;
     }
 
-    // If we made it this far it's safe to record this file in the database.
-    $file->status = FILE_STATUS_TEMPORARY;
-    $file->timestamp = $_SERVER['REQUEST_TIME'];
-    drupal_write_record('files', $file);
-
-    // Add file to the cache.
-    $upload_cache[$source] = $file;
-    return $file;
-
+    if ($file = file_save($file)) {
+      // Add file to the cache.
+      $upload_cache[$source] = $file;
+      return $file;
+    }
   }
   return FALSE;
 }
@@ -658,6 +873,9 @@ function file_save_upload($source, $vali
 /**
  * Check that a file meets the criteria specified by the validators.
  *
+ * After executing the validator callbacks specified hook_file_validate() will
+ * also be called to allow other modules to report errors about the file.
+ *
  * @param $file
  *   A Drupal file object.
  * @param $validators
@@ -678,7 +896,8 @@ function file_validate(&$file, $validato
     $errors = array_merge($errors, call_user_func_array($function, $args));
   }
 
-  return $errors;
+  // Let other modules perform validation on the new file.
+  return array_merge($errors, module_invoke_all('file_validate', $file));
 }
 
 /**
@@ -834,7 +1053,7 @@ function file_validate_image_resolution(
 }
 
 /**
- * Save a string to the specified destination.
+ * Save a string to the specified destination and create a database file entry.
  *
  * @param $data
  *   A string containing the contents of the file.
@@ -849,9 +1068,53 @@ function file_validate_image_resolution(
  *                          unique.
  *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
  * @return
- *   A string with the path of the resulting file, or FALSE on error.
+ *   An file object or FALSE on error
+ *
+ * @see file_unmanaged_save_data()
  */
 function file_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  global $user;
+
+  if ($filepath = file_unmanaged_save_data($data, $destination, $replace)) {
+    // Create a file object.
+    $file = new stdClass();
+    $file->filepath = $filepath;
+    $file->filename = basename($file->filepath);
+    $file->filemime = file_get_mimetype($file->filepath);
+    $file->uid      = $user->uid;
+    $file->status   = FILE_STATUS_PERMANENT;
+    return file_save($file);
+  }
+  return FALSE;
+}
+
+/**
+ * Save a string to the specified destination without calling any hooks or
+ * making any changes to the database.
+ *
+ * This function is identical to file_save_data() except the file will not be
+ * saved to the files table and none of the file_* hooks will be called.
+ *
+ * @param $data
+ *   A string containing the contents of the file.
+ * @param $destination
+ *   A string containing the destination location. If no value is provided
+ *   then a randomly name will be generated and the file saved in Drupal's
+ *   files directory.
+ * @param $replace
+ *   Replace behavior when the destination file already exists:
+ *   - FILE_EXISTS_REPLACE - Replace the existing file.
+ *   - FILE_EXISTS_RENAME - Append _{incrementing number} until the filename is
+ *                          unique.
+ *   - FILE_EXISTS_ERROR - Do nothing and return FALSE.
+ * @return
+ *   A string with the path of the resulting file, or FALSE on error.
+ *
+ * @see file_save_data()
+ */
+function file_unmanaged_save_data($data, $destination = NULL, $replace = FILE_EXISTS_RENAME) {
+  global $user;
+
   // Write the data to a temporary file.
   $temp_name = tempnam(file_directory_temp(), 'file');
   if (file_put_contents($temp_name, $data) === FALSE) {
@@ -860,7 +1123,7 @@ function file_save_data($data, $destinat
   }
 
   // Move the file to its final destination.
-  return file_move($temp_name, $destination, $replace);
+  return file_unmanaged_move($temp_name, $destination, $replace);
 }
 
 /**
@@ -881,7 +1144,9 @@ function file_save_data($data, $destinat
 function file_set_status($file, $status = FILE_STATUS_PERMANENT) {
   if (db_query('UPDATE {files} SET status = %d WHERE fid = %d', array($status, $file->fid))) {
     $file->status = $status;
-    return TRUE;
+    // Notify other modules that the file's status has changed.
+    module_invoke_all('file_status', $file);
+    return $file;
   }
   return FALSE;
 }
Index: includes/locale.inc
===================================================================
RCS file: /cvs/drupal/drupal/includes/locale.inc,v
retrieving revision 1.182
diff -u -p -r1.182 locale.inc
--- includes/locale.inc	15 Sep 2008 20:48:07 -0000	1.182
+++ includes/locale.inc	15 Sep 2008 23:08:00 -0000
@@ -2159,7 +2159,7 @@ function _locale_rebuild_js($langcode = 
 
     // Save the file.
     $dest = $dir . '/' . $language->language . '_' . $data_hash . '.js';
-    if (file_save_data($data, $dest)) {
+    if (file_unmanaged_save_data($data, $dest)) {
       $language->javascript = $data_hash;
       $status = ($status == 'deleted') ? 'updated' : 'created';
     }
Index: modules/aggregator/aggregator.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/aggregator/aggregator.test,v
retrieving revision 1.8
diff -u -p -r1.8 aggregator.test
--- modules/aggregator/aggregator.test	15 Sep 2008 21:06:06 -0000	1.8
+++ modules/aggregator/aggregator.test	15 Sep 2008 23:08:00 -0000
@@ -151,7 +151,7 @@ class AggregatorTestCase extends DrupalW
 EOF;
 
     $path = file_directory_path() . '/valid-opml.xml';
-    return file_save_data($opml, $path);
+    return file_unmanaged_save_data($opml, $path);
   }
 
   /**
@@ -168,7 +168,7 @@ EOF;
 EOF;
 
     $path = file_directory_path() . '/invalid-opml.xml';
-    return file_save_data($opml, $path);
+    return file_unmanaged_save_data($opml, $path);
   }
 
   /**
@@ -190,7 +190,7 @@ EOF;
 EOF;
 
     $path = file_directory_path() . '/empty-opml.xml';
-    return file_save_data($opml, $path);
+    return file_unmanaged_save_data($opml, $path);
   }
 
   function getRSS091Sample() {
@@ -223,7 +223,7 @@ EOF;
 EOT;
 
     $path = file_directory_path() . '/rss091.xml';
-    return file_save_data($feed, $path);
+    return file_unmanaged_save_data($feed, $path);
   }
 }
 
Index: modules/blogapi/blogapi.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/blogapi/blogapi.module,v
retrieving revision 1.124
diff -u -p -r1.124 blogapi.module
--- modules/blogapi/blogapi.module	15 Sep 2008 09:28:49 -0000	1.124
+++ modules/blogapi/blogapi.module	15 Sep 2008 23:08:00 -0000
@@ -385,7 +385,7 @@ function blogapi_metaweblog_new_media_ob
     return blogapi_error(t('No file sent.'));
   }
 
-  if (!$filepath = file_save_data($data, $name)) {
+  if (!$filepath = file_unmanaged_save_data($data, $name)) {
     return blogapi_error(t('Error storing file.'));
   }
 
Index: modules/color/color.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/color/color.module,v
retrieving revision 1.45
diff -u -p -r1.45 color.module
--- modules/color/color.module	15 Sep 2008 21:06:07 -0000	1.45
+++ modules/color/color.module	15 Sep 2008 23:08:00 -0000
@@ -308,7 +308,7 @@ function color_scheme_form_submit($form,
   foreach ($info['copy'] as $file) {
     $base = basename($file);
     $source = $paths['source'] . $file;
-    $filepath = file_copy($source, $paths['target'] . $base);
+    $filepath = file_unmanaged_copy($source, $paths['target'] . $base);
     $paths['map'][$file] = $base;
     $paths['files'][] = $filepath;
   }
@@ -435,7 +435,7 @@ function _color_rewrite_stylesheet($them
  * Save the rewritten stylesheet to disk.
  */
 function _color_save_stylesheet($file, $style, &$paths) {
-  $filepath = file_save_data($style, $file, FILE_EXISTS_REPLACE);
+  $filepath = file_unmanaged_save_data($style, $file, FILE_EXISTS_REPLACE);
   $paths['files'][] = $filepath;
 
   // Set standard file permissions for webserver-generated files.
Index: modules/simpletest/simpletest.install
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.install,v
retrieving revision 1.8
diff -u -p -r1.8 simpletest.install
--- modules/simpletest/simpletest.install	10 Sep 2008 04:13:01 -0000	1.8
+++ modules/simpletest/simpletest.install	15 Sep 2008 23:08:00 -0000
@@ -33,7 +33,7 @@ function simpletest_install() {
       $original = drupal_get_path('module', 'simpletest') . '/files';
       $files = file_scan_directory($original, '(html|image|javascript|php|sql)-.*');
       foreach ($files as $file) {
-        file_copy($file->filename, $path . '/' . $file->basename);
+        file_unmanaged_copy($file->filename, $path . '/' . $file->basename);
       }
       $generated = TRUE;
     }
Index: modules/simpletest/simpletest.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/simpletest.module,v
retrieving revision 1.12
diff -u -p -r1.12 simpletest.module
--- modules/simpletest/simpletest.module	10 Sep 2008 04:13:01 -0000	1.12
+++ modules/simpletest/simpletest.module	15 Sep 2008 23:08:00 -0000
@@ -558,7 +558,7 @@ function simpletest_clean_temporary_dire
         simpletest_clean_temporary_directory($file_path);
       }
       else {
-        file_delete($file_path);
+        file_unmanaged_delete($file_path);
       }
     }
   }
Index: modules/simpletest/tests/file.test
===================================================================
RCS file: /cvs/drupal/drupal/modules/simpletest/tests/file.test,v
retrieving revision 1.3
diff -u -p -r1.3 file.test
--- modules/simpletest/tests/file.test	15 Sep 2008 21:06:07 -0000	1.3
+++ modules/simpletest/tests/file.test	15 Sep 2008 23:08:00 -0000
@@ -33,7 +33,7 @@ class FileValidateTest extends DrupalWeb
    * Implementation of setUp().
    */
   function setUp() {
-    parent::setUp();
+    parent::setUp('file_test');
 
     $this->image = new stdClass();
     $this->image->filepath = 'misc/druplicon.png';
@@ -210,22 +210,143 @@ class FileLoadSaveTest extends DrupalWeb
     );
   }
 
-  function testFileSaveData() {
+  /**
+   * Implementation of setUp().
+   */
+  function setUp() {
+    parent::setUp('file_test');
+  }
+
+  /**
+   *  This will test saving file data to the database.
+   */
+  function testFileLoadSave() {
+    // Create a new file object.
+    $file = array(
+      'uid' => 1,
+      'filename' => 'druplicon.png',
+      'filepath' => 'misc/druplicon.png',
+      'filemime' => 'image/png',
+      'timestamp' => 1,
+      'status' => FILE_STATUS_PERMANENT,
+    );
+    $this->file = (object) $file;
+
+    // Try to load bogus stuff
+    file_test_reset_calls();
+    $this->assertFalse(file_load(-1), t("Try to load an invalid fid"));
+    $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called.'));
+    file_test_reset_calls();
+    $this->assertFalse(file_load(array('filepath' => $this->file->filepath)), t("Try to load a file that doesn't exist in the database."));
+    $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called.'));
+
+    // Save it, inserting a new record
+    file_test_reset_calls();
+    $saved_file = file_save($this->file);
+    $this->assertEqual(count(file_test_get_calls('insert')), 1, t('hook_file_insert was called.'));
+    $this->assertNotNull($saved_file, t("Saving the file should give us back a file object."), 'File');
+    $this->assertTrue($saved_file->fid > 0, t("A new file ID is set when saving a new file to the database."), 'File');
+    $this->assertEqual(db_result(db_query('SELECT COUNT(*) FROM {files} f WHERE f.fid = %d', array($saved_file->fid))), 1, t("Record exists in the database."));
+    $this->assertEqual($saved_file->filesize, filesize($this->file->filepath), t("File size was set correctly."), 'File');
+    $this->assertTrue($saved_file->timestamp > 1, t("File size was set correctly."), 'File');
+
+    // Load by path
+    file_test_reset_calls();
+    $by_path_file = file_load(array('filepath' => $this->file->filepath));
+    $this->assertEqual(count(file_test_get_calls('load')), 1, t('hook_file_load was called.'));
+    $this->assertTrue($by_path_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+    $this->assertEqual($by_path_file->fid, $this->file->fid, t("Loading by filepath got the correct fid."), 'File');
+
+    // Load by fid.
+    file_test_reset_calls();
+    $by_fid_file = file_load($this->file->fid);
+    $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called because the loaded file was cached.'));
+    $this->assertTrue($by_fid_file->file_test['loaded'], t('file_test_file_load() was able to modify the file during load.'));
+    $this->assertEqual($by_fid_file->filepath, $this->file->filepath, t("Loading by fid got the correct filepath."), 'File');
+
+    // Load again by fid to make sure the caching doesn't screw anythign up
+    file_test_reset_calls();
+    $by_fid_file = file_load($this->file->fid);
+    $this->assertEqual(count(file_test_get_calls('load')), 0, t('hook_file_load was not called since it loaded from cache.'));
+    $this->assertEqual($by_fid_file->filepath, $this->file->filepath, t("Loading by fid got the correct filepath."), 'File');
+
+    // Resave the file, updating the existing record.
+    file_test_reset_calls();
+    $resaved_file = file_save($saved_file);
+    $this->assertEqual(count(file_test_get_calls('update')), 1, t('hook_file_update was called.'));
+    $this->assertEqual($resaved_file->fid, $saved_file->fid, t("The file ID of an existing file is not changed when updating the database."), 'File');
+    $this->assertTrue($resaved_file->timestamp >= $saved_file->timestamp, t("Timestamp didn't go backwards."), 'File');
+    $count = db_result(db_query('SELECT COUNT(*) FROM {files} f WHERE f.fid = %d', array($saved_file->fid)));
+    $this->assertEqual($count, 1, t("Record still exists in the database."), 'File');
+
+    // TODO: test the reset parameter
+  }
+
+  function testFileUnmanagedSaveData() {
     $contents = $this->randomName(8);
 
     // No filename
-    $filepath = file_save_data($contents);
+    $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."));
 
     // Provide a filename
-    $filepath = file_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE);
+    $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('asdf.txt', basename($filepath), t("File was named correctly."));
     $this->assertEqual($contents, file_get_contents(realpath($filepath)), t("Contents of the file are correct."));
   }
+
+  function testFileSaveData() {
+    $contents = $this->randomName(8);
+
+    // No filename
+    $file = file_save_data($contents);
+    $this->assertTrue($file, t("Unnamed file saved correctly"));
+    $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory"));
+    $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct."));
+    $this->assertEqual($file->filemime, 'application/octet-stream', t("A MIME type was set."));
+    $this->assertEqual($file->status, FILE_STATUS_PERMANENT, t("The file's status was set to permanent."));
+
+    // Try loading the file.
+    $loaded_file = file_load($file->fid);
+    $this->assertTrue($loaded_file, t("File loaded from database."));
+
+    // Provide a filename
+    $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_REPLACE);
+    $this->assertTrue($file, t("Unnamed file saved correctly"));
+    $this->assertEqual(file_directory_path(), dirname($file->filepath), t("File was placed in Drupal's files directory."));
+    $this->assertEqual('asdf.txt', basename($file->filepath), t("File was named correctly."));
+    $this->assertEqual($contents, file_get_contents(realpath($file->filepath)), t("Contents of the file are correct."));
+
+    // Check the overwrite error.
+    $file = file_save_data($contents, 'asdf.txt', FILE_EXISTS_ERROR);
+    $this->assertFalse($file, t("Overwriting a file fails when FILE_EXISTS_ERROR is specified."));
+
+    // Clear out the error messages.
+#    drupal_get_messages();
+  }
+
+  function testFileSaveUpload() {
+    $max_fid_before = db_result(db_query("SELECT MAX(fid) AS fid FROM {files}"));
+
+    $upload_user = $this->drupalCreateUser(array('access content'));
+    $this->drupalLogin($upload_user);
+
+    $image = current($this->drupalGetTestFiles('image'));
+    $this->assertTrue(is_file($image->filename), t("the file we're going to upload exists."));
+    $edit = array('files[file_test_upload]' => realpath($image->filename));
+    $this->drupalPost('file-test/upload', $edit, t('Submit'));
+    $this->assertResponse(200, t("Received a 200 response for posted test file."));
+    $this->assertText(t('hook_file_validate() was called.'), t('hook_file_validate() was called.'));
+    $this->assertText(t('hook_file_insert() was called.'), t('hook_file_insert() was called.'));
+
+    $max_fid_after = db_result(db_query("SELECT MAX(fid) AS fid FROM {files}"));
+
+    $this->assertTrue($max_fid_after > $max_fid_before, t("A new file was created."));
+  }
 }
 
 /**
@@ -247,7 +368,7 @@ class FileDirectoryTest extends DrupalWe
    * Implementation of setUp().
    */
   function setUp() {
-    parent::setUp();
+    parent::setUp('file_test');
 
     // A directory to operate on.
     $this->directory = file_directory_path() . '/' . $this->randomName();
@@ -415,7 +536,7 @@ class FileCopyDeleteMoveTest extends Dru
    */
   function setUp() {
     // Install file_test module
-    parent::setUp();
+    parent::setUp('file_test');
 
     // A directory to operate on.
     $this->dirname = file_directory_path() . '/' . $this->randomName();
@@ -428,36 +549,71 @@ class FileCopyDeleteMoveTest extends Dru
     touch($f->filepath);
     $f->filemime = 'text/plain';
     $f->uid = 1;
-    $f->timestamp = $_SERVER['REQUEST_TIME'];
-    $f->filesize = 0;
-    drupal_write_record('files', $f);
-    $this->file = $f;
+    $this->file = file_save($f);
   }
 
+
   function testFileDelete() {
+    file_test_reset_calls();
+
+    // Check that deletion removes the file and database record.
+    $this->assertTrue(is_file($this->file->filepath), t("File exists."));
+    $this->assertTrue(file_delete($this->file), t("Delete worked."));
+    $this->assertEqual(count(file_test_get_calls('references')), 1, t('hook_file_references was called.'));
+    $this->assertEqual(count(file_test_get_calls('delete')), 1, t('hook_file_delete was called.'));
+    $this->assertFalse(file_exists($this->file->filepath), t("Test file has actually been deleted."));
+    $this->assertFalse(file_load(array('filepath' => $this->file->filepath)), t("File was removed from the database"));
+
+    // TODO: implement hook_file_references() in file_test.module and report a
+    // file in use and test the $force parameter.
+  }
+
+
+  function testFileUnmanagedDelete() {
     // Delete a regular file
     $this->assertTrue(is_file($this->file->filepath), t("File exists."));
-    $this->assertTrue(file_delete($this->file->filepath), t("Deleted worked."));
+    $this->assertTrue(file_unmanaged_delete($this->file->filepath), t("Deleted worked."));
     $this->assertFalse(file_exists($this->file->filepath), t("Test file has actually been deleted."));
   }
 
-  function testFileDelete_Missing() {
+  function testFileUnmanagedDelete_Missing() {
     // Try to delete a non-existing file
-    $this->assertTrue(file_delete(file_directory_path() . '/' . $this->randomName()), t("Returns true when deleting a non-existant file."));
+    $this->assertTrue(file_unmanaged_delete(file_directory_path() . '/' . $this->randomName()), t("Returns true when deleting a non-existant file."));
   }
 
-  function testFileDelete_Directory() {
+  function testFileUnmanagedDelete_Directory() {
     // Try to delete a directory
     $this->assertTrue(is_dir($this->dirname), t("Directory exists."));
-    $this->assertFalse(file_delete($this->dirname), t("Could not delete the delete directory."));
+    $this->assertFalse(file_unmanaged_delete($this->dirname), t("Could not delete the delete directory."));
     $this->assertTrue(file_exists($this->dirname), t("Directory has not been deleted."));
   }
 
+
   function testFileMove() {
+    file_test_reset_calls();
+    $desired_filepath = file_directory_path() . '/' . $this->randomName();
+
+    $file = file_move(clone $this->file, $desired_filepath, FILE_EXISTS_ERROR);
+    $this->assertTrue($file, t("File moved sucessfully."));
+    $this->assertEqual(count(file_test_get_calls('move')), 1, t('hook_file_move was called.'));
+    $this->assertEqual(count(file_test_get_calls('update')), 1, t('hook_file_update was called.'));
+    $this->assertEqual($file->fid, $this->file->fid, t("File id $file->fid is unchanged after move."));
+
+    $loaded_file = file_load($file->fid, TRUE);
+    $this->assertTrue($loaded_file, t("File can be loaded from the database."));
+    $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database."));
+    $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database."));
+
+    // Clean up the file so that the directory can be removed
+    @unlink($loaded_file->filepath);
+  }
+
+
+  function testFileUnmanagedMove() {
     // Moving to a new name.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving."));
     $desired_filepath = file_directory_path() . '/' . $this->randomName();
-    $new_filepath = file_move($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
+    $new_filepath = file_unmanaged_move($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
     $this->assertTrue($new_filepath, t("Move was successful."));
     $this->assertEqual($new_filepath, $desired_filepath, t("Returned expected filepath."));
     $this->assertTrue(file_exists($new_filepath), t("File exists at the new location."));
@@ -467,7 +623,7 @@ class FileCopyDeleteMoveTest extends Dru
     $desired_filepath = file_directory_path() . '/' . $this->randomName();
     $this->assertTrue(file_exists($new_filepath), t("File exists before moving."));
     $this->assertTrue(touch($desired_filepath), t('Created a file so a rename will have to happen.'));
-    $newer_filepath = file_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+    $newer_filepath = file_unmanaged_move($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
     $this->assertTrue($newer_filepath, t("Move was successful."));
     $this->assertNotEqual($newer_filepath, $desired_filepath, t("Returned expected filepath."));
     $this->assertTrue(file_exists($newer_filepath), t("File exists at the new location."));
@@ -476,24 +632,24 @@ class FileCopyDeleteMoveTest extends Dru
     // TODO: test moving to a directory (rather than full directory/file path)
   }
 
-  function testFileMove_Missing() {
+  function testFileUnmanagedMove_Missing() {
     // Move non-existant file
-    $new_filepath = file_move($this->randomName(), $this->randomName());
+    $new_filepath = file_unmanaged_move($this->randomName(), $this->randomName());
     $this->assertFalse($new_filepath, t("Moving a missing file fails"));
 
     drupal_get_messages();
   }
 
-  function testFileMove_OverwriteSelf() {
+  function testFileUnmanagedMove_OverwriteSelf() {
     // Move the file onto itself without renaming shouldn't make changes.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving."));
-    $new_filepath = file_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_REPLACE);
+    $new_filepath = file_unmanaged_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_REPLACE);
     $this->assertFalse($new_filepath, t("Moving onto itself without renaming fails."));
     $this->assertTrue(file_exists($this->file->filepath), t("File exists after moving onto itself."));
 
     // Move the file onto itself with renaming will result in a new filename.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before moving."));
-    $new_filepath = file_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
+    $new_filepath = file_unmanaged_move($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
     $this->assertTrue($new_filepath, t("Moving onto itself with renaming works."));
     $this->assertFalse(file_exists($this->file->filepath), t("Original file has been removed."));
     $this->assertTrue(file_exists($new_filepath), t("File exists after moving onto itself."));
@@ -501,10 +657,36 @@ class FileCopyDeleteMoveTest extends Dru
     drupal_get_messages();
   }
 
+
   function testFileCopy() {
+    file_test_reset_calls();
+    $desired_filepath = file_directory_path() . '/' . $this->randomName();
+
+    $file = file_copy(clone $this->file, $desired_filepath, FILE_EXISTS_ERROR);
+    $this->assertTrue($file, t("File copied sucessfully."));
+    $this->assertEqual(count(file_test_get_calls('copy')), 1, t('hook_file_copy was called.'));
+    $this->assertEqual(count(file_test_get_calls('insert')), 1, t('hook_file_insert was called.'));
+    $this->assertNotEqual($file->fid, $this->file->fid, t("A new file id was created."));
+    $this->assertNotEqual($file->filepath, $this->file->filepath, t("A new filepath was created."));
+    $this->assertEqual($file->filepath, $desired_filepath, t('The copied file object has the desired filepath'));
+    $this->assertTrue(file_exists($this->file->filepath), t('The original file still exists.'));
+    $this->assertTrue(file_exists($file->filepath), t('The copied file exists'));
+
+    // Check that the changes were actually saved to the database.
+    $loaded_file = file_load($file->fid, TRUE);
+    $this->assertTrue($loaded_file, t("File can be loaded from the database."));
+    $this->assertEqual($file->filename, $loaded_file->filename, t("File name was updated correctly in the database."));
+    $this->assertEqual($file->filepath, $loaded_file->filepath, t("File path was updated correctly in the database."));
+
+    // Clean up the file so that the directory can be removed
+    @unlink($loaded_file->filepath);
+  }
+
+
+  function testFileUnmanagedCopy() {
     // Copying to a new name.
     $desired_filepath = file_directory_path() . '/' . $this->randomName();
-    $new_filepath = file_copy($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
+    $new_filepath = file_unmanaged_copy($this->file->filepath, $desired_filepath, FILE_EXISTS_ERROR);
     $this->assertTrue($new_filepath, t("Copy was successful."));
     $this->assertEqual($new_filepath, $desired_filepath, t("Returned expected filepath."));
     $this->assertTrue(file_exists($this->file->filepath), t("Original file remains."));
@@ -513,7 +695,7 @@ class FileCopyDeleteMoveTest extends Dru
     // Copying with rename.
     $desired_filepath = file_directory_path() . '/' . $this->randomName();
     $this->assertTrue(touch($desired_filepath), t('Created a file so a rename will have to happen.'));
-    $newer_filepath = file_copy($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
+    $newer_filepath = file_unmanaged_copy($new_filepath, $desired_filepath, FILE_EXISTS_RENAME);
     $this->assertTrue($newer_filepath, t("Copy was successful."));
     $this->assertNotEqual($newer_filepath, $desired_filepath, t("Returned expected filepath."));
     $this->assertTrue(file_exists($this->file->filepath), t("Original file remains."));
@@ -522,20 +704,20 @@ class FileCopyDeleteMoveTest extends Dru
     // TODO: test copying to a directory (rather than full directory/file path)
   }
 
-  function testFileCopy_NonExistant() {
+  function testFileUnmanagedCopy_NonExistant() {
     // Copy non-existant file
     $desired_filepath = $this->randomName();
     $this->assertFalse(file_exists($desired_filepath), t("Randomly named file doesn't exists."));
-    $new_filepath = file_copy($desired_filepath, $this->randomName());
+    $new_filepath = file_unmanaged_copy($desired_filepath, $this->randomName());
     $this->assertFalse($new_filepath, t("Copying a missing file fails"));
 
     drupal_get_messages();
   }
 
-  function testFileCopy_OverwriteSelf() {
+  function testFileUnmanagedCopy_OverwriteSelf() {
     // Copy the file onto itself with renaming works.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying."));
-    $new_filepath = file_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
+    $new_filepath = file_unmanaged_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_RENAME);
     $this->assertTrue($new_filepath, t("Copying onto itself with renaming works."));
     $this->assertNotEqual($new_filepath, $this->file->filepath, t("Copied file has a new name."));
     $this->assertTrue(file_exists($this->file->filepath), t("Original file exists after copying onto itself."));
@@ -543,19 +725,19 @@ class FileCopyDeleteMoveTest extends Dru
 
     // Copy the file onto itself without renaming fails.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying."));
-    $new_filepath = file_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_ERROR);
+    $new_filepath = file_unmanaged_copy($this->file->filepath, $this->file->filepath, FILE_EXISTS_ERROR);
     $this->assertFalse($new_filepath, t("Copying onto itself without renaming fails."));
     $this->assertTrue(file_exists($this->file->filepath), t("File exists after copying onto itself."));
 
     // Copy the file into same directory without renaming fails.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying."));
-    $new_filepath = file_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_ERROR);
+    $new_filepath = file_unmanaged_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_ERROR);
     $this->assertFalse($new_filepath, t("Copying onto itself fails."));
     $this->assertTrue(file_exists($this->file->filepath), t("File exists after copying onto itself."));
 
     // Copy the file into same directory with renaming works.
     $this->assertTrue(file_exists($this->file->filepath), t("File exists before copying."));
-    $new_filepath = file_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_RENAME);
+    $new_filepath = file_unmanaged_copy($this->file->filepath, dirname($this->file->filepath), FILE_EXISTS_RENAME);
     $this->assertTrue($new_filepath, t("Copying into same directory works."));
     $this->assertNotEqual($new_filepath, $this->file->filepath, t("Copied file has a new name."));
     $this->assertTrue(file_exists($this->file->filepath), t("Original file exists after copying onto itself."));
Index: modules/simpletest/tests/file_test.info
===================================================================
RCS file: modules/simpletest/tests/file_test.info
diff -N modules/simpletest/tests/file_test.info
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/file_test.info	15 Sep 2008 23:08:00 -0000
@@ -0,0 +1,8 @@
+; $Id$
+name = "File test"
+description = "Support module for file handling tests."
+package = Testing
+version = VERSION
+core = 7.x
+files[] = file_test.module
+hidden = TRUE
Index: modules/simpletest/tests/file_test.module
===================================================================
RCS file: modules/simpletest/tests/file_test.module
diff -N modules/simpletest/tests/file_test.module
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ modules/simpletest/tests/file_test.module	15 Sep 2008 23:08:00 -0000
@@ -0,0 +1,185 @@
+<?php
+// $Id$
+
+/**
+ * @file
+ * Provides implementations of hook_file hooks. All the hooks simply set a
+ * message, which is checked for in file.test.
+ */
+
+
+/**
+ * Implementation of hook_menu().
+ */
+function file_test_menu() {
+  $items['file-test/upload'] = array(
+    'title' => t('Upload test'),
+    'page callback' => 'drupal_get_form',
+    'page arguments' => array('_file_test_form'),
+    'access arguments' => array('access content'),
+    'type' => MENU_CALLBACK,
+  );
+  return $items;
+}
+
+/**
+ * Reset/initialize the history of calls to the file_* hooks.
+ */
+function file_test_reset_calls() {
+  global $file_test_results;
+
+  $file_test_results = array(
+    'load' => array(),
+    'validate' => array(),
+    'download' => array(),
+    'references' => array(),
+    'status' => array(),
+    'insert' => array(),
+    'update' => array(),
+    'copy' => array(),
+    'move' => array(),
+    'delete' => array(),
+  );
+}
+
+/**
+ * Log the invocation of a file hook.
+ *
+ * @param $op One of the hook_file_* operations.
+ * @param $args Arguments passed to the hook.
+ */
+function _file_test_add_call($op, $args) {
+  global $file_test_results;
+  $file_test_results[$op][] = $args;
+}
+
+/**
+ * Get the values passed to a the hook calls for a given operation.
+ *
+ * @param $op One of the hook_file_* operations.
+ * @returns Array of the parameters passed to each call.
+ */
+function file_test_get_calls($op) {
+  global $file_test_results;
+  return $file_test_results[$op];
+}
+
+/**
+ * Implementation of hook_file_load().
+ */
+function file_test_file_load(&$file) {
+  // Assign a value on the object so that we can test that the $file is passed
+  // by reference.
+  $file->file_test['loaded'] = TRUE;
+  $args = func_get_args();
+  _file_test_add_call('load', $args);
+}
+
+/**
+ * Implementation of hook_file_validate().
+ */
+function file_test_file_validate(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('validate', $args);
+}
+
+/**
+ * Implementation of hook_file_status().
+ */
+function file_test_file_status(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('status', $args);
+}
+
+/**
+ * Implementation of hook_file_download().
+ */
+function file_test_file_download(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('download', $args);
+}
+
+/**
+ * Implementation of hook_file_references().
+ */
+function file_test_file_references(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('references', $args);
+}
+
+/**
+ * Implementation of hook_file_insert().
+ */
+function file_test_file_insert(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('insert', $args);
+}
+
+/**
+ * Implementation of hook_file_update().
+ */
+function file_test_file_update(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('update', $args);
+}
+
+/**
+ * Implemenation of hook_file_copy().
+ */
+function file_test_file_copy(&$file, &$source) {
+  $args = func_get_args();
+  _file_test_add_call('copy', $args);
+}
+
+/**
+ * Implemenation of hook_file_move().
+ */
+function file_test_file_move(&$file, &$source) {
+  $args = func_get_args();
+  _file_test_add_call('move', $args);
+}
+
+/**
+ * Implementation of hook_file_delete().
+ */
+function file_test_file_delete(&$file) {
+  $args = func_get_args();
+  _file_test_add_call('delete', $args);
+}
+
+
+/**
+ * Form to test file uploads.
+ */
+function _file_test_form(&$form_state) {
+  $form['#validate'][] = '_file_test_validate_upload';
+  $form['file_test_upload'] = array(
+    '#type' => 'file',
+    '#title' => t('Upload image file'),
+  );
+  $form['submit'] = array(
+    '#type' => 'submit',
+    '#value' => t('Submit'),
+  );
+  return $form;
+}
+
+/**
+ * Process the upload.
+ */
+function _file_test_validate_upload(&$form, &$form_state) {
+  // Validate the uploaded picture.
+  $validators = array(
+    'file_validate_is_image' => array(),
+  );
+
+  if ($file = file_save_upload('file_test_upload', $validators)) {
+    $form_state['values']['file_test_upload'] = $file;
+    if (count(file_test_get_calls('validate'))) {
+      drupal_set_message(t('hook_file_validate() was called.'));
+    }
+    if (count(file_test_get_calls('insert'))) {
+      drupal_set_message(t('hook_file_insert() was called.'));
+    }
+  }
+}
Index: modules/system/system.admin.inc
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.admin.inc,v
retrieving revision 1.88
diff -u -p -r1.88 system.admin.inc
--- modules/system/system.admin.inc	15 Sep 2008 09:28:50 -0000	1.88
+++ modules/system/system.admin.inc	15 Sep 2008 23:08:01 -0000
@@ -338,7 +338,7 @@ function system_theme_settings(&$form_st
     // The image was saved using file_save_upload() and was added to the
     // files table as a temporary file. We'll make a copy and let the garbage
     // collector delete the original upload.
-    if ($filepath = file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
+    if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
       $_POST['default_logo'] = 0;
       $_POST['logo_path'] = $filepath;
       $_POST['toggle_logo'] = 1;
@@ -353,7 +353,7 @@ function system_theme_settings(&$form_st
     // The image was saved using file_save_upload() and was added to the
     // files table as a temporary file. We'll make a copy and let the garbage
     // collector delete the original upload.
-    if ($filepath = file_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
+    if ($filepath = file_unmanaged_copy($file->filepath, $filename, FILE_EXISTS_REPLACE)) {
       $_POST['default_favicon'] = 0;
       $_POST['favicon_path'] = $filepath;
       $_POST['toggle_favicon'] = 1;
Index: modules/system/system.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/system/system.module,v
retrieving revision 1.619
diff -u -p -r1.619 system.module
--- modules/system/system.module	15 Sep 2008 08:49:40 -0000	1.619
+++ modules/system/system.module	15 Sep 2008 23:08:01 -0000
@@ -1408,7 +1408,7 @@ function system_cron() {
     if (file_exists($file->filepath)) {
       // If files that exist cannot be deleted, continue so the database remains
       // consistent.
-      if (!file_delete($file->filepath)) {
+      if (!file_delete($file)) {
         watchdog('file system', 'Could not delete temporary file "%path" during garbage collection', array('%path' => $file->filepath), WATCHDOG_ERROR);
         continue;
       }
Index: modules/upload/upload.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/upload/upload.module,v
retrieving revision 1.206
diff -u -p -r1.206 upload.module
--- modules/upload/upload.module	15 Sep 2008 09:28:50 -0000	1.206
+++ modules/upload/upload.module	15 Sep 2008 23:08:01 -0000
@@ -263,6 +263,38 @@ function upload_form_alter(&$form, $form
 }
 
 /**
+ * Implementation of hook_file_load().
+ */
+function upload_file_load(&$file, $source = NULL) {
+  // Add the upload specific data into the file object. 
+  $values = db_fetch_array(db_query('SELECT * FROM {upload} u WHERE u.fid = %d', $file->fid));
+  foreach ((array)$values as $key => $value) {
+    $file->{$key} = $value;
+  }
+}
+
+/**
+ * Implementation of hook_file_references().
+ */
+function upload_file_references(&$file, $source = NULL) {
+  // If upload.module is still using a file, do not let other modules delete it.
+  $count = db_result(db_query('SELECT count(*) FROM {upload} WHERE fid = %d', $file->fid));
+  if ($count) {
+    // return the name of the module and how many references it has to the file.
+    return array('upload' => $count);
+  }
+}
+ 
+/**
+ * Implementation of hook_file_delete().
+ */
+function upload_file_delete(&$file, $source = NULL) {
+  // Delete all information associated with the file.
+  db_query('DELETE FROM {upload} WHERE fid = %d', $file->fid);
+}
+
+
+/**
  * Implementation of hook_nodeapi().
  */
 function upload_nodeapi(&$node, $op, $teaser) {
@@ -406,13 +438,15 @@ function upload_save(&$node) {
       // If the file isn't used by any other revisions delete it.
       $count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE fid = %d', $fid));
       if ($count < 1) {
-        file_delete($file->filepath);
+        file_delete($file);
         db_query('DELETE FROM {files} WHERE fid = %d', $fid);
       }
 
       // Remove it from the session in the case of new uploads,
       // that you want to disassociate before node submission.
       unset($_SESSION['upload_files'][$fid]);
+      // Try to clean up a file that is no longer in use.
+      file_delete($file);
       // Move on, so the removed file won't be added to new revisions.
       continue;
     }
@@ -433,38 +467,23 @@ function upload_save(&$node) {
 }
 
 function upload_delete($node) {
-  $files = array();
-  $result = db_query('SELECT DISTINCT f.* FROM {upload} u INNER JOIN {files} f ON u.fid = f.fid WHERE u.nid = %d', $node->nid);
-  while ($file = db_fetch_object($result)) {
-    $files[$file->fid] = $file;
+  db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
+  if (!is_array($node->files)) {
+    return; 
   }
-
-  foreach ($files as $fid => $file) {
-    // Delete all files associated with the node
-    db_query('DELETE FROM {files} WHERE fid = %d', $fid);
-    file_delete($file->filepath);
+  foreach($node->files as $file) {
+    file_delete($file);
   }
-
-  // Delete all file revision information associated with the node
-  db_query('DELETE FROM {upload} WHERE nid = %d', $node->nid);
 }
 
 function upload_delete_revision($node) {
-  if (is_array($node->files)) {
-    foreach ($node->files as $file) {
-      // Check if the file will be used after this revision is deleted
-      $count = db_result(db_query('SELECT COUNT(fid) FROM {upload} WHERE fid = %d', $file->fid));
-
-      // if the file won't be used, delete it
-      if ($count < 2) {
-        db_query('DELETE FROM {files} WHERE fid = %d', $file->fid);
-        file_delete($file->filepath);
-      }
-    }
-  }
-
-  // delete the revision
   db_query('DELETE FROM {upload} WHERE vid = %d', $node->vid);
+  if (!is_array($node->files)) {
+    return;
+  }
+  foreach ($node->files as $file) {
+    file_delete($file);
+  }
 }
 
 function _upload_form($node) {
@@ -478,11 +497,11 @@ function _upload_form($node) {
   if (!empty($node->files) && is_array($node->files)) {
     $form['files']['#theme'] = 'upload_form_current';
     $form['files']['#tree'] = TRUE;
-    foreach ($node->files as $key => $file) {
+    foreach ($node->files as $file) {
       $file = (object)$file;
-      $description = file_create_url($file->filepath);
-      $description = "<small>" . check_plain($description) . "</small>";
-      $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => $description );
+      $key = $file->fid;
+
+      $form['files'][$key]['description'] = array('#type' => 'textfield', '#default_value' => !empty($file->description) ? $file->description : $file->filename, '#maxlength' => 256, '#description' => '<small>' . file_create_url($file->filepath) . '</small>');
       $form['files'][$key]['size'] = array('#markup' => format_size($file->filesize));
       $form['files'][$key]['remove'] = array('#type' => 'checkbox', '#default_value' => !empty($file->remove));
       $form['files'][$key]['list'] = array('#type' => 'checkbox',  '#default_value' => $file->list);
@@ -497,12 +516,26 @@ function _upload_form($node) {
 
   if (user_access('upload files')) {
     $limits = _upload_file_limits($user);
+
+    $limit_description = t('The maximum size of file uploads is %filesize. ', array('%filesize' => format_size($limits['file_size'])));
+    if (!empty($limits['resolution'])) {
+      if (image_get_toolkit()) {
+        $limit_description .= t('Images larger than %resolution will be resized. ', array('%resolution' => $limits['resolution']));
+      }
+      else {
+        $limit_description .= t('Images may not be larger than %resolution. ', array('%resolution' => $limits['resolution']));
+      }
+    }
+    if ($user->uid != 1) {
+      $limit_description .= t('Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions']));
+    }
+
     $form['new']['#weight'] = 10;
     $form['new']['upload'] = array(
       '#type' => 'file',
       '#title' => t('Attach new file'),
       '#size' => 40,
-      '#description' => ($limits['resolution'] ? t('Images are larger than %resolution will be resized. ', array('%resolution' => $limits['resolution'])) : '') . t('The maximum upload size is %filesize. Only files with the following extensions may be uploaded: %extensions. ', array('%extensions' => $limits['extensions'], '%filesize' => format_size($limits['file_size']))),
+      '#description' => $limit_description,
     );
     $form['new']['attach'] = array(
       '#type' => 'submit',
@@ -564,9 +597,9 @@ function upload_load($node) {
   $files = array();
 
   if ($node->vid) {
-    $result = db_query('SELECT * FROM {files} f INNER JOIN {upload} r ON f.fid = r.fid WHERE r.vid = %d ORDER BY r.weight, f.fid', $node->vid);
+    $result = db_query('SELECT u.fid FROM {upload} u WHERE u.vid = %d ORDER BY u.fid', $node->vid);
     while ($file = db_fetch_object($result)) {
-      $files[$file->fid] = $file;
+      $files[$file->fid] = file_load($file->fid);
     }
   }
 
Index: modules/user/user.module
===================================================================
RCS file: /cvs/drupal/drupal/modules/user/user.module,v
retrieving revision 1.921
diff -u -p -r1.921 user.module
--- modules/user/user.module	15 Sep 2008 15:18:59 -0000	1.921
+++ modules/user/user.module	15 Sep 2008 23:08:01 -0000
@@ -407,7 +407,7 @@ function user_validate_picture(&$form, &
   if ($file = file_save_upload('picture_upload', $validators)) {
     // Remove the old picture.
     if (isset($form_state['values']['_account']->picture) && file_exists($form_state['values']['_account']->picture)) {
-      file_delete($form_state['values']['_account']->picture);
+      file_unmanaged_delete($form_state['values']['_account']->picture);
     }
 
     // The image was saved using file_save_upload() and was added to the
@@ -415,7 +415,7 @@ function user_validate_picture(&$form, &
     // collector delete the original upload.
     $info = image_get_info($file->filepath);
     $destination = file_create_path(variable_get('user_picture_path', 'pictures') . '/picture-' . $form['#uid'] . '.' . $info['extension']);
-    if ($filepath = file_copy($file->filepath, $destination, FILE_EXISTS_REPLACE)) {
+    if ($filepath = file_unmanaged_copy($file->filepath, $destination, FILE_EXISTS_REPLACE)) {
       $form_state['values']['picture'] = $filepath;
     }
     else {
@@ -1538,7 +1538,7 @@ function _user_edit_submit($uid, &$edit)
   // Delete picture if requested, and if no replacement picture was given.
   if (!empty($edit['picture_delete'])) {
     if ($user->picture && file_exists($user->picture)) {
-      file_delete($user->picture);
+      file_unmanaged_delete($user->picture);
     }
     $edit['picture'] = '';
   }
