[FileBackend] Added 'recursive' flag to directory clean() function.
authorAaron <aschulz@wikimedia.org>
Thu, 26 Apr 2012 18:40:47 +0000 (11:40 -0700)
committerAaron <aschulz@wikimedia.org>
Thu, 26 Apr 2012 19:41:33 +0000 (12:41 -0700)
Change-Id: I8fadd25c05840e2324bf8433dc0a4846daf8e2c3

includes/filerepo/backend/FSFileBackend.php
includes/filerepo/backend/FileBackend.php
includes/filerepo/backend/FileBackendStore.php
includes/filerepo/backend/SwiftFileBackend.php
tests/phpunit/includes/filerepo/FileBackendTest.php

index df822fd..9839fa1 100644 (file)
@@ -534,6 +534,14 @@ class FSFileBackend extends FileBackendStore {
                return $tmpFile;
        }
 
+       /**
+        * @see FileBackendStore::directoriesAreVirtual()
+        * @return bool
+        */
+       protected function directoriesAreVirtual() {
+               return false;
+       }
+
        /**
         * Chmod a file, suppressing the warnings
         *
index f3b879a..52baf1e 100644 (file)
@@ -383,7 +383,8 @@ abstract class FileBackend {
         * is that of an empty container, in which case it should be deleted.
         *
         * $params include:
-        *     dir : storage directory
+        *     dir       : storage directory
+        *     recursive : recursively delete empty subdirectories first (@since 1.20)
         *
         * @param $params Array
         * @return Status
index f7e4529..d81cd08 100644 (file)
@@ -371,6 +371,17 @@ abstract class FileBackendStore extends FileBackend {
                wfProfileIn( __METHOD__ . '-' . $this->name );
                $status = Status::newGood();
 
+               // Recursive: first delete all empty subdirs recursively
+               if ( !empty( $params['recursive'] ) && !$this->directoriesAreVirtual() ) {
+                       $subDirsRel = $this->getTopDirectoryList( array( 'dir' => $params['dir'] ) );
+                       if ( $subDirsRel !== null ) { // no errors
+                               foreach ( $subDirsRel as $subDirRel ) {
+                                       $subDir = $params['dir'] . "/{$subDirRel}"; // full path
+                                       $status->merge( $this->doClean( array( 'dir' => $subDir ) + $params ) );
+                               }
+                       }
+               }
+
                list( $fullCont, $dir, $shard ) = $this->resolveStoragePath( $params['dir'] );
                if ( $dir === null ) {
                        $status->fatal( 'backend-fail-invalidpath', $params['dir'] );
@@ -891,6 +902,15 @@ abstract class FileBackendStore extends FileBackend {
         */
        protected function doClearCache( array $paths = null ) {}
 
+       /**
+        * Is this a key/value store where directories are just virtual?
+        * Virtual directories exists in so much as files exists that are
+        * prefixed with the directory path followed by a forward slash.
+        *
+        * @return bool
+        */
+       abstract protected function directoriesAreVirtual();
+
        /**
         * Move a cache entry to the top (such as when accessed)
         *
index af5d693..c8cb62f 100644 (file)
@@ -770,6 +770,14 @@ class SwiftFileBackend extends FileBackendStore {
                return $tmpFile;
        }
 
+       /**
+        * @see FileBackendStore::directoriesAreVirtual()
+        * @return bool
+        */
+       protected function directoriesAreVirtual() {
+               return true;
+       }
+
        /**
         * Get headers to send to Swift when reading a file based
         * on a FileBackend params array, e.g. that of getLocalCopy().
index 612c368..af88bc8 100644 (file)
@@ -1022,6 +1022,58 @@ class FileBackendTest extends MediaWikiTestCase {
                }
        }
 
+       public function testRecursiveClean() {
+               $this->backend = $this->singleBackend;
+               $this->doTestRecursiveClean();
+               $this->tearDownFiles();
+
+               $this->backend = $this->multiBackend;
+               $this->doTestRecursiveClean();
+               $this->tearDownFiles();
+       }
+
+       function doTestRecursiveClean() {
+               $backendName = $this->backendClass();
+
+               $base = $this->baseStorePath();
+               $dirs = array(
+                       "$base/unittest-cont1/a",
+                       "$base/unittest-cont1/a/b",
+                       "$base/unittest-cont1/a/b/c",
+                       "$base/unittest-cont1/a/b/c/d0",
+                       "$base/unittest-cont1/a/b/c/d1",
+                       "$base/unittest-cont1/a/b/c/d2",
+                       "$base/unittest-cont1/a/b/c/d0/1",
+                       "$base/unittest-cont1/a/b/c/d0/2",
+                       "$base/unittest-cont1/a/b/c/d1/3",
+                       "$base/unittest-cont1/a/b/c/d1/4",
+                       "$base/unittest-cont1/a/b/c/d2/5",
+                       "$base/unittest-cont1/a/b/c/d2/6"
+               );
+               foreach ( $dirs as $dir ) {
+                       $status = $this->prepare( array( 'dir' => $dir ) );
+                       $this->assertEquals( array(), $status->errors,
+                               "Preparing dir $dir succeeded without warnings ($backendName)." );
+               }
+
+               if ( $this->backend instanceof FSFileBackend ) {
+                       foreach ( $dirs as $dir ) {
+                               $this->assertEquals( true, $this->backend->directoryExists( array( 'dir' => $dir ) ),
+                                       "Dir $dir exists ($backendName)." );
+                       }
+               }
+
+               $status = $this->backend->clean(
+                       array( 'dir' => "$base/unittest-cont1", 'recursive' => 1 ) );
+               $this->assertEquals( array(), $status->errors,
+                       "Recursive cleaning of dir $dir succeeded without warnings ($backendName)." );
+
+               foreach ( $dirs as $dir ) {
+                       $this->assertEquals( false, $this->backend->directoryExists( array( 'dir' => $dir ) ),
+                               "Dir $dir no longer exists ($backendName)." );
+               }
+       }
+
        // @TODO: testSecure
 
        public function testDoOperations() {