Make WordPress Core

Opened 11 years ago

Closed 10 years ago

#29257 closed defect (bug) (fixed)

media_sideload_image() calls rename() on temp file, which can break streams

Reported by: joehoyle's profile joehoyle Owned by: dd32's profile dd32
Milestone: 4.4 Priority: normal
Severity: normal Version: 3.9.2
Component: Media Keywords: has-patch
Focuses: Cc:

Description

I'm not sure where I would put the "blame" on the following issue, but this is the problem as I see it:

  1. media_sideload_image() downloads an URL to a temp file on filesystem
  2. media_handle_sideload() is called with the file array which calls _wp_handle_upload()
  3. _wp_handle_upload() will attempt to rename() the file to the correct uploads dir form the temp file

That should all be fine, unless one is using a custom stream wrapper for their uploads dir (S3, App Engine etc). It's not possible to rename() across streams, from http://php.net/manual/en/function.rename.php:

Note:
The old name. The wrapper used in oldname must match the wrapper used in newname.

A couple of solutions I see:

  1. Use copy() / unlink() combo in the case of different stream wrappers
  2. Make get_temp_dir() filterable so plugins offering custom stream wrappers can replace the temp dir with a directory on the custom stream. This might have some unintended affects though, such as plugin updates etc being downloaded to the custom stream wrapper that may (or may not) be intended for uploads only.

Attachments (1)

29257.diff (648 bytes) - added by mattheu 11 years ago.
Use copy & unlink

Download all attachments as: .zip

Change History (8)

@mattheu
11 years ago

Use copy & unlink

#1 @mattheu
11 years ago

  • Keywords has-patch added

Added a patch that uses copy & unlink instead of rename. This solution seems less likely to cause issues.

#2 @mattheu
11 years ago

A bit more context - the issue arose when using this: https://github.com/humanmade/S3-Uploads

#3 @joehoyle
11 years ago

Patch still applies cleanly.

Discussed briefly with johnbillion, potential issue: server doesn't have room to copy the file so fails. However, in the case of move_uploaded_file being called - then double the image uploaded file size is required as move_uploaded_file does a copy operation, not a rename; any checks that make use of this function will apply to both and I don't think should be applied to this small-fix path.

#4 @chriscct7
10 years ago

  • Milestone changed from Awaiting Review to 4.4
  • Owner set to chriscct7
  • Status changed from new to reviewing

How likely though is it that the server will not have enough room to duplicate an image?
I think that is an almost non-existent occurrence.

#5 @wonderboymusic
10 years ago

  • Owner changed from chriscct7 to dd32

#6 @dd32
10 years ago

This is pretty interesting, in that PHP internally determines when it can't use a standard rename (Say, when it's a different disk volume) and fakes it using copy() instead. Why it doesn't do this with streams I'm not sure.. (I can guess..)

#7 @dd32
10 years ago

  • Resolution set to fixed
  • Status changed from reviewing to closed

In 35579:

Media: Allow media_sideload_image() to work when the upload directory is a PHP Stream. Using copy() instead of rename() allows the function to work across different stream sources.
Props mattheu.
Fixes #29257

Note: See TracTickets for help on using tickets.