[wp-trac] [WordPress Trac] #41855: copy_dir fails in coping some files in nested dirs

WordPress Trac noreply at wordpress.org
Mon Sep 11 12:12:47 UTC 2017


#41855: copy_dir fails in coping some files in nested dirs
----------------------------+-----------------------------
 Reporter:  caraffande      |      Owner:
     Type:  defect (bug)    |     Status:  new
 Priority:  normal          |  Milestone:  Awaiting Review
Component:  Filesystem API  |    Version:  4.8.1
 Severity:  normal          |   Keywords:
  Focuses:                  |
----------------------------+-----------------------------
 Tricky, subtle bug!
 Let's suppose we do have a folder structure like this:

 /folder1/folder2/
 /folder1/folder2/file1.txt
 /folder1/folder2/subfolder1/
 /folder1/folder2/subfolder1/file2.txt
 /folder3/

 and that we want to move or copy the entire ''folder2'' to ''folder3'' so
 that the final structure is like this:

 /folder1/folder2/
 /folder1/folder2/file1.txt
 /folder1/folder2/subfolder1/
 /folder1/folder2/subfolder1/file1.txt
 /folder3/folder2/
 /folder3/folder2/file1.txt
 /folder3/folder2/subfolder1/
 /folder3/folder2/subfolder1/file1.txt

 IMPORTANT: Please note that initially the path /folder3/folder2 doesn't
 exist.
 The problem arise based on the order used internally by copy_dir to
 retieve the ''source'' elements.
 Let's see it in detail.

 command:
 {{{
 copy_dir('/folder1/folder2', '/folder3/folder2')
 }}}

 Result:

 First iteration: /folder3/folder2 is created with mkdir.
 Second iteration (if copy_dir retrieves first /folder1/folder2/file1.txt):
 copy(/folder1/folder2/file1.txt, /folder3/folder2/file1.txt) fails because
 /folder3/folder2 doesn't exist!
 Second iteration (if copydir retrieves first /folder1/folder2):
 /folder3/folder2 is created. Now, by the next iteration,
 copy(/folder1/folder2/file1.txt, /folder3/folder2/file1.txt) will not
 fail.

 I've discover this bug almost accidentally using the following (buggy)
 code:
 {{{#!php
 <?php
     function copy_recursive($source, $dest, $merge = true)
     {
         if (is_file($dest) && is_dir($source)) {
             error_log(__("[PPC] - Error: Cannot copy a dir into a
 file."));
             return;
         }

         if (!$merge) delete_recursive($dest);

         if (is_dir($source)) {
             $iterator = new RecursiveIteratorIterator(
                 new RecursiveDirectoryIterator($source,
 RecursiveDirectoryIterator::SKIP_DOTS),
                 RecursiveIteratorIterator::SELF_FIRST
             );

             foreach ($iterator as $file) {
                 if ($file->isDir()) {
 mkdir($dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName(), 0777, true);
                 } else {
                     copy($file,
 $dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
                 }
             }
         } else {
             copy($source, $dest);
         }
     }
 }}}

 I need a copy_dir (or copy_recursive) function for copying my custom
 plugin's data (a complex nested structure of file/folders) to wordpress'
 upload dir. The above code used to fail, apparently randomly, the copy of
 only some files.
 Then I've tried to use wordpress' copy_dir function only to discover, with
 disappointment, that it used to fail exactly the same way.

 The solution, at least for my (buggy) code, is to create the
 ''destination'' folder before any copy command, like this is:

 {{{#!php
 <?php
     function copy_recursive($source, $dest, $merge = true)
     {
         if (is_file($dest) && is_dir($source)) {
             error_log(__("[PPC] - Error: Cannot copy a dir into a
 file."));
             return;
         }

         if (!$merge) delete_recursive($dest);

         if (!file_exists($dest)) mkdir($dest, 0777, true); // <<<---- Bug
 Fix!

         if (is_dir($source)) {
             $iterator = new RecursiveIteratorIterator(
                 new RecursiveDirectoryIterator($source,
 RecursiveDirectoryIterator::SKIP_DOTS),
                 RecursiveIteratorIterator::SELF_FIRST
             );

             foreach ($iterator as $file) {
                 if ($file->isDir()) {
 mkdir($dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName(), 0777, true);
                 } else {
                     copy($file,
 $dest.DIRECTORY_SEPARATOR.$iterator->getSubPathName());
                 }
             }
         } else {
             copy($source, $dest);
         }
     }

 }}}

--
Ticket URL: <https://core.trac.wordpress.org/ticket/41855>
WordPress Trac <https://core.trac.wordpress.org/>
WordPress publishing platform


More information about the wp-trac mailing list