Index: src/wp-includes/post.php
===================================================================
--- src/wp-includes/post.php	(revision 40238)
+++ src/wp-includes/post.php	(working copy)
@@ -4914,34 +4914,25 @@
 	/** This action is documented in wp-includes/post.php */
 	do_action( 'deleted_post', $post_id );
 
-	$uploadpath = wp_get_upload_dir();
+	$dirname = trailingslashit( dirname( $file ) );
 
 	if ( ! empty($meta['thumb']) ) {
 		// Don't delete the thumb if another attachment uses it.
 		if (! $wpdb->get_row( $wpdb->prepare( "SELECT meta_id FROM $wpdb->postmeta WHERE meta_key = '_wp_attachment_metadata' AND meta_value LIKE %s AND post_id <> %d", '%' . $wpdb->esc_like( $meta['thumb'] ) . '%', $post_id)) ) {
-			$thumbfile = str_replace(basename($file), $meta['thumb'], $file);
-			/** This filter is documented in wp-includes/functions.php */
-			$thumbfile = apply_filters( 'wp_delete_file', $thumbfile );
-			@ unlink( path_join($uploadpath['basedir'], $thumbfile) );
+			wp_delete_file( $dirname . $meta['thumb'] );
 		}
 	}
 
 	// Remove intermediate and backup images if there are any.
 	if ( isset( $meta['sizes'] ) && is_array( $meta['sizes'] ) ) {
-		foreach ( $meta['sizes'] as $size => $sizeinfo ) {
-			$intermediate_file = str_replace( basename( $file ), $sizeinfo['file'], $file );
-			/** This filter is documented in wp-includes/functions.php */
-			$intermediate_file = apply_filters( 'wp_delete_file', $intermediate_file );
-			@ unlink( path_join( $uploadpath['basedir'], $intermediate_file ) );
+		foreach ( $meta['sizes'] as $size ) {
+			wp_delete_file( $dirname . $size['file'] );
 		}
 	}
 
 	if ( is_array($backup_sizes) ) {
 		foreach ( $backup_sizes as $size ) {
-			$del_file = path_join( dirname($meta['file']), $size['file'] );
-			/** This filter is documented in wp-includes/functions.php */
-			$del_file = apply_filters( 'wp_delete_file', $del_file );
-			@ unlink( path_join($uploadpath['basedir'], $del_file) );
+			wp_delete_file( $dirname . $size['file'] );
 		}
 	}
 
Index: tests/phpunit/tests/post/attachments.php
===================================================================
--- tests/phpunit/tests/post/attachments.php	(revision 40238)
+++ tests/phpunit/tests/post/attachments.php	(working copy)
@@ -59,6 +59,9 @@
 		if ( !function_exists( 'imagejpeg' ) )
 			$this->markTestSkipped( 'jpeg support unavailable' );
 
+		$prev_opt_medium_size_w = get_option( 'medium_size_w' );
+		$prev_opt_medium_size_h = get_option( 'medium_size_h' );
+
 		update_option( 'medium_size_w', 0 );
 		update_option( 'medium_size_h', 0 );
 
@@ -105,6 +108,9 @@
 		$this->assertEquals( 400, $downsize[1] );
 		$this->assertEquals( 300, $downsize[2] );
 
+		// Cleanup.
+		update_option( 'medium_size_w', $prev_opt_medium_size_w );
+		update_option( 'medium_size_h', $prev_opt_medium_size_h );
 	}
 
 	function test_insert_image_medium_sizes() {
@@ -111,6 +117,11 @@
 		if ( !function_exists( 'imagejpeg' ) )
 			$this->markTestSkipped( 'jpeg support unavailable' );
 
+		$prev_opt_medium_size_w = get_option( 'medium_size_w' );
+		$prev_opt_medium_size_h = get_option( 'medium_size_h' );
+		$prev_opt_medium_large_size_w = get_option( 'medium_large_size_w' );
+		$prev_opt_medium_large_size_h = get_option( 'medium_large_size_h' );
+
 		update_option('medium_size_w', 400);
 		update_option('medium_size_h', 0);
 
@@ -162,6 +173,12 @@
 		$this->assertEquals( '2007-06-17DSC_4173.jpg', basename($downsize[0]) );
 		$this->assertEquals( 680, $downsize[1] );
 		$this->assertEquals( 1024, $downsize[2] );
+
+		// Cleanup.
+		update_option( 'medium_size_w', $prev_opt_medium_size_w );
+		update_option( 'medium_size_h', $prev_opt_medium_size_h );
+		update_option( 'medium_large_size_w', $prev_opt_medium_large_size_w );
+		update_option( 'medium_large_size_h', $prev_opt_medium_large_size_h );
 	}
 
 
@@ -169,6 +186,11 @@
 		if ( !function_exists( 'imagejpeg' ) )
 			$this->markTestSkipped( 'jpeg support unavailable' );
 
+		$prev_opt_medium_size_w = get_option( 'medium_size_w' );
+		$prev_opt_medium_size_h = get_option( 'medium_size_h' );
+		$prev_opt_medium_large_size_w = get_option( 'medium_large_size_w' );
+		$prev_opt_medium_large_size_h = get_option( 'medium_large_size_h' );
+
 		update_option('medium_size_w', 400);
 		update_option('medium_size_h', 0);
 
@@ -197,6 +219,10 @@
 		$this->assertEquals( '2007-06-17DSC_4173-600x904.jpg', $medium_large['file'] );
 		$this->assertTrue( is_file($uploads['basedir'] . DIRECTORY_SEPARATOR . $medium_large['path']) );
 
+		$large = image_get_intermediate_size( $id, 'large' );
+		$this->assertEquals( '2007-06-17DSC_4173-680x1024.jpg', $large['file'] );
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $large['path'] ) );
+
 		$meta = wp_get_attachment_metadata($id);
 		$original = $meta['file'];
 		$this->assertTrue( is_file($uploads['basedir'] . DIRECTORY_SEPARATOR . $original) );
@@ -204,13 +230,171 @@
 		// now delete the attachment and make sure all files are gone
 		wp_delete_attachment($id);
 
-		$this->assertFalse( is_file($thumb['path']) );
-		$this->assertFalse( is_file($medium['path']) );
-		$this->assertFalse( is_file($medium_large['path']) );
-		$this->assertFalse( is_file($original) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $thumb['path'] ) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $medium['path'] ) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $medium_large['path'] ) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $large['path'] ) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $original ) );
+
+		// Cleanup.
+		update_option( 'medium_size_w', $prev_opt_medium_size_w );
+		update_option( 'medium_size_h', $prev_opt_medium_size_h );
+		update_option( 'medium_large_size_w', $prev_opt_medium_large_size_w );
+		update_option( 'medium_large_size_h', $prev_opt_medium_large_size_h );
 	}
 
 	/**
+	 * Test delete with edited image (and backup sizes).
+	 * @ticket 39476
+	 */
+	function test_insert_image_delete_with_edited_image() {
+		if ( ! function_exists( 'imagejpeg' ) ) {
+			$this->markTestSkipped( 'jpeg support unavailable' );
+		}
+
+		$prev_opt_medium_size_w = get_option( 'medium_size_w' );
+		$prev_opt_medium_size_h = get_option( 'medium_size_h' );
+
+		// Generate thumbnail only.
+		update_option( 'medium_size_w', 0 );
+		update_option( 'medium_size_h', 0 );
+
+		$contents = file_get_contents( DIR_TESTDATA . '/images/a2-small.jpg' );
+		$basename_noext = 'blah';
+
+		$upload = wp_upload_bits( $basename_noext . '.jpg', null, $contents );
+		$this->assertEmpty( $upload['error'] );
+		$basename_noext = sanitize_file_name( $basename_noext );
+
+		$id = $this->_make_attachment( $upload );
+		$uploads = wp_upload_dir();
+		$dirname = trailingslashit( dirname( get_attached_file( $id ) ) );
+		$expected_files = array();
+
+		// Check that the intermediate thumbnail exists.
+		$thumb = image_get_intermediate_size( $id, 'thumbnail' );
+		$this->assertSame( $basename_noext . '-150x150.jpg', $thumb['file'] );
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $thumb['path'] ) );
+		$expected_files[] = $uploads['basedir'] . DIRECTORY_SEPARATOR . $thumb['path'];
+
+		// Check that the intermediate medium doesn't exist.
+		$medium = image_get_intermediate_size( $id, 'medium' );
+		$this->assertEmpty( $medium );
+
+		$meta = wp_get_attachment_metadata( $id );
+		$original = $meta['file'];
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $original ) );
+		$expected_files[] = $uploads['basedir'] . DIRECTORY_SEPARATOR . $original;
+
+		// Create edited version.
+		require_once ABSPATH . 'wp-admin/includes/image-edit.php';
+		// a2-small.jpg is 400x300, so halve.
+		$_REQUEST = array( 'fwidth' => 200, 'fheight' => 150, 'target' => 'all', 'do' => 'scale' );
+		$edited = wp_save_image( $id );
+		$this->assertTrue( is_object( $edited ) );
+		$this->assertTrue( isset( $edited->thumbnail ) );
+		$this->assertTrue( is_file( $dirname . wp_basename( $edited->thumbnail ) ) );
+		$expected_files[] = $dirname . wp_basename( $edited->thumbnail );
+
+		// Backup sizes should exist.
+		$backup_sizes = get_post_meta( $id, '_wp_attachment_backup_sizes', true );
+		$this->assertTrue( is_array( $backup_sizes ) );
+		$this->assertNotEmpty( $backup_sizes['thumbnail-orig'] );
+
+		// And be same as prior meta.
+		$this->assertSame( $meta['sizes']['thumbnail'], $backup_sizes['thumbnail-orig'] );
+		$this->assertSame( wp_basename( $original ), $backup_sizes['full-orig']['file'] );
+
+		// But new meta should point to edited.
+		$new_meta = wp_get_attachment_metadata( $id );
+		$this->assertSame( $new_meta['sizes']['thumbnail']['file'], wp_basename( $edited->thumbnail ) );
+		$this->assertNotSame( $new_meta['file'], $original );
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $new_meta['file'] ) );
+		$expected_files[] = $uploads['basedir'] . DIRECTORY_SEPARATOR . $new_meta['file'];
+
+		// Passthru filter.
+		add_filter( 'wp_delete_file', array( $this, 'wp_delete_file_passthru_filter' ) );
+
+		// Now delete the attachment and make sure all files are gone.
+		wp_delete_attachment( $id );
+
+		foreach ( $expected_files as $expected_file ) {
+			$this->assertFalse( is_file( $expected_file ) );
+		}
+
+		$this->assertSame( sort( $expected_files ), sort( self::$wp_delete_files ) );
+
+		// Cleanup.
+		remove_filter( 'wp_delete_file', array( $this, 'wp_delete_file_passthru_filter' ) );
+		update_option( 'medium_size_w', $prev_opt_medium_size_w );
+		update_option( 'medium_size_h', $prev_opt_medium_size_h );
+	}
+
+	static $wp_delete_files = array();
+
+	function wp_delete_file_passthru_filter( $file ) {
+		$this->assertTrue( path_is_absolute( $file ) );
+		self::$wp_delete_files[] = $file;
+		return $file;
+	}
+
+	/**
+	 * Test delete of UTF-8 filename.
+	 * @ticket 33227
+	 * @ticket 39476
+	 */
+	function test_insert_image_delete_utf8() {
+		if ( ! function_exists( 'imagejpeg' ) ) {
+			$this->markTestSkipped( 'jpeg support unavailable' );
+		}
+
+		$prev_opt_medium_size_w = get_option( 'medium_size_w' );
+		$prev_opt_medium_size_h = get_option( 'medium_size_h' );
+
+		// Generate thumbnail only.
+		update_option( 'medium_size_w', 0 );
+		update_option( 'medium_size_h', 0 );
+
+		$prev_ctype_locale = setlocale( LC_CTYPE, 'C' ); // In case shell env has set LC_CTYPE.
+		$this->assertTrue( false !== $prev_ctype_locale );
+
+		$contents = file_get_contents( DIR_TESTDATA . '/images/a2-small.jpg' );
+		$basename_noext = 'هم اندیشی';
+
+		$upload = wp_upload_bits( $basename_noext . '.jpg', null, $contents );
+		$this->assertEmpty( $upload['error'] );
+		$basename_noext = sanitize_file_name( $basename_noext );
+
+		$id = $this->_make_attachment( $upload );
+		$uploads = wp_upload_dir();
+
+		// Check that the intermediate thumbnail exists.
+		$thumb = image_get_intermediate_size( $id, 'thumbnail' );
+		$this->assertSame( $basename_noext . '-150x150.jpg', $thumb['file'] );
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $thumb['path'] ) );
+
+		// Check that the intermediate medium doesn't exist.
+		$medium = image_get_intermediate_size( $id, 'medium' );
+		$this->assertEmpty( $medium );
+
+		$meta = wp_get_attachment_metadata( $id );
+		$original = $meta['file'];
+		$this->assertSame( $basename_noext . '.jpg', wp_basename( $original ) );
+		$this->assertTrue( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $original ) );
+
+		// now delete the attachment and make sure all files are gone
+		wp_delete_attachment( $id );
+
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $thumb['path'] ) );
+		$this->assertFalse( is_file( $uploads['basedir'] . DIRECTORY_SEPARATOR . $original ) );
+
+		// Cleanup.
+		setlocale( LC_CTYPE, $prev_ctype_locale );
+		update_option( 'medium_size_w', $prev_opt_medium_size_w );
+		update_option( 'medium_size_h', $prev_opt_medium_size_h );
+	}
+
+	/**
 	 * GUID should never be empty
 	 * @ticket 18310
 	 * @ticket 21963
