MediaWiki  1.23.14
FSFileBackend.php
Go to the documentation of this file.
1 <?php
43  protected $basePath;
44 
46  protected $containerPaths = array();
47 
49  protected $fileMode;
50 
52  protected $fileOwner;
53 
55  protected $currentUser;
56 
58  protected $hadWarningErrors = array();
59 
68  public function __construct( array $config ) {
69  parent::__construct( $config );
70 
71  // Remove any possible trailing slash from directories
72  if ( isset( $config['basePath'] ) ) {
73  $this->basePath = rtrim( $config['basePath'], '/' ); // remove trailing slash
74  } else {
75  $this->basePath = null; // none; containers must have explicit paths
76  }
77 
78  if ( isset( $config['containerPaths'] ) ) {
79  $this->containerPaths = (array)$config['containerPaths'];
80  foreach ( $this->containerPaths as &$path ) {
81  $path = rtrim( $path, '/' ); // remove trailing slash
82  }
83  }
84 
85  $this->fileMode = isset( $config['fileMode'] ) ? $config['fileMode'] : 0644;
86  if ( isset( $config['fileOwner'] ) && function_exists( 'posix_getuid' ) ) {
87  $this->fileOwner = $config['fileOwner'];
88  $info = posix_getpwuid( posix_getuid() );
89  $this->currentUser = $info['name']; // cache this, assuming it doesn't change
90  }
91  }
92 
93  protected function resolveContainerPath( $container, $relStoragePath ) {
94  // Check that container has a root directory
95  if ( isset( $this->containerPaths[$container] ) || isset( $this->basePath ) ) {
96  // Check for sane relative paths (assume the base paths are OK)
97  if ( $this->isLegalRelPath( $relStoragePath ) ) {
98  return $relStoragePath;
99  }
100  }
101 
102  return null;
103  }
104 
111  protected function isLegalRelPath( $path ) {
112  // Check for file names longer than 255 chars
113  if ( preg_match( '![^/]{256}!', $path ) ) { // ext3/NTFS
114  return false;
115  }
116  if ( wfIsWindows() ) { // NTFS
117  return !preg_match( '![:*?"<>|]!', $path );
118  } else {
119  return true;
120  }
121  }
122 
131  protected function containerFSRoot( $shortCont, $fullCont ) {
132  if ( isset( $this->containerPaths[$shortCont] ) ) {
133  return $this->containerPaths[$shortCont];
134  } elseif ( isset( $this->basePath ) ) {
135  return "{$this->basePath}/{$fullCont}";
136  }
137 
138  return null; // no container base path defined
139  }
140 
147  protected function resolveToFSPath( $storagePath ) {
148  list( $fullCont, $relPath ) = $this->resolveStoragePathReal( $storagePath );
149  if ( $relPath === null ) {
150  return null; // invalid
151  }
152  list( , $shortCont, ) = FileBackend::splitStoragePath( $storagePath );
153  $fsPath = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
154  if ( $relPath != '' ) {
155  $fsPath .= "/{$relPath}";
156  }
157 
158  return $fsPath;
159  }
160 
161  public function isPathUsableInternal( $storagePath ) {
162  $fsPath = $this->resolveToFSPath( $storagePath );
163  if ( $fsPath === null ) {
164  return false; // invalid
165  }
166  $parentDir = dirname( $fsPath );
167 
168  if ( file_exists( $fsPath ) ) {
169  $ok = is_file( $fsPath ) && is_writable( $fsPath );
170  } else {
171  $ok = is_dir( $parentDir ) && is_writable( $parentDir );
172  }
173 
174  if ( $this->fileOwner !== null && $this->currentUser !== $this->fileOwner ) {
175  $ok = false;
176  trigger_error( __METHOD__ . ": PHP process owner is not '{$this->fileOwner}'." );
177  }
178 
179  return $ok;
180  }
181 
182  protected function doCreateInternal( array $params ) {
183  $status = Status::newGood();
184 
185  $dest = $this->resolveToFSPath( $params['dst'] );
186  if ( $dest === null ) {
187  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
188 
189  return $status;
190  }
191 
192  if ( !empty( $params['async'] ) ) { // deferred
193  $tempFile = TempFSFile::factory( 'create_', 'tmp' );
194  if ( !$tempFile ) {
195  $status->fatal( 'backend-fail-create', $params['dst'] );
196 
197  return $status;
198  }
199  $this->trapWarnings();
200  $bytes = file_put_contents( $tempFile->getPath(), $params['content'] );
201  $this->untrapWarnings();
202  if ( $bytes === false ) {
203  $status->fatal( 'backend-fail-create', $params['dst'] );
204 
205  return $status;
206  }
207  $cmd = implode( ' ', array(
208  wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
209  wfEscapeShellArg( $this->cleanPathSlashes( $tempFile->getPath() ) ),
210  wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
211  ) );
212  $status->value = new FSFileOpHandle( $this, $params, 'Create', $cmd, $dest );
213  $tempFile->bind( $status->value );
214  } else { // immediate write
215  $this->trapWarnings();
216  $bytes = file_put_contents( $dest, $params['content'] );
217  $this->untrapWarnings();
218  if ( $bytes === false ) {
219  $status->fatal( 'backend-fail-create', $params['dst'] );
220 
221  return $status;
222  }
223  $this->chmod( $dest );
224  }
225 
226  return $status;
227  }
228 
232  protected function getResponseCreate( $errors, Status $status, array $params, $cmd ) {
233  if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
234  $status->fatal( 'backend-fail-create', $params['dst'] );
235  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
236  }
237  }
238 
239  protected function doStoreInternal( array $params ) {
240  $status = Status::newGood();
241 
242  $dest = $this->resolveToFSPath( $params['dst'] );
243  if ( $dest === null ) {
244  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
245 
246  return $status;
247  }
248 
249  if ( !empty( $params['async'] ) ) { // deferred
250  $cmd = implode( ' ', array(
251  wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
252  wfEscapeShellArg( $this->cleanPathSlashes( $params['src'] ) ),
253  wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
254  ) );
255  $status->value = new FSFileOpHandle( $this, $params, 'Store', $cmd, $dest );
256  } else { // immediate write
257  $this->trapWarnings();
258  $ok = copy( $params['src'], $dest );
259  $this->untrapWarnings();
260  // In some cases (at least over NFS), copy() returns true when it fails
261  if ( !$ok || ( filesize( $params['src'] ) !== filesize( $dest ) ) ) {
262  if ( $ok ) { // PHP bug
263  unlink( $dest ); // remove broken file
264  trigger_error( __METHOD__ . ": copy() failed but returned true." );
265  }
266  $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
267 
268  return $status;
269  }
270  $this->chmod( $dest );
271  }
272 
273  return $status;
274  }
275 
279  protected function getResponseStore( $errors, Status $status, array $params, $cmd ) {
280  if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
281  $status->fatal( 'backend-fail-store', $params['src'], $params['dst'] );
282  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
283  }
284  }
285 
286  protected function doCopyInternal( array $params ) {
287  $status = Status::newGood();
288 
289  $source = $this->resolveToFSPath( $params['src'] );
290  if ( $source === null ) {
291  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
292 
293  return $status;
294  }
295 
296  $dest = $this->resolveToFSPath( $params['dst'] );
297  if ( $dest === null ) {
298  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
299 
300  return $status;
301  }
302 
303  if ( !is_file( $source ) ) {
304  if ( empty( $params['ignoreMissingSource'] ) ) {
305  $status->fatal( 'backend-fail-copy', $params['src'] );
306  }
307 
308  return $status; // do nothing; either OK or bad status
309  }
310 
311  if ( !empty( $params['async'] ) ) { // deferred
312  $cmd = implode( ' ', array(
313  wfIsWindows() ? 'COPY /B /Y' : 'cp', // (binary, overwrite)
315  wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
316  ) );
317  $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd, $dest );
318  } else { // immediate write
319  $this->trapWarnings();
320  $ok = ( $source === $dest ) ? true : copy( $source, $dest );
321  $this->untrapWarnings();
322  // In some cases (at least over NFS), copy() returns true when it fails
323  if ( !$ok || ( filesize( $source ) !== filesize( $dest ) ) ) {
324  if ( $ok ) { // PHP bug
325  $this->trapWarnings();
326  unlink( $dest ); // remove broken file
327  $this->untrapWarnings();
328  trigger_error( __METHOD__ . ": copy() failed but returned true." );
329  }
330  $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
331 
332  return $status;
333  }
334  $this->chmod( $dest );
335  }
336 
337  return $status;
338  }
339 
343  protected function getResponseCopy( $errors, Status $status, array $params, $cmd ) {
344  if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
345  $status->fatal( 'backend-fail-copy', $params['src'], $params['dst'] );
346  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
347  }
348  }
349 
350  protected function doMoveInternal( array $params ) {
351  $status = Status::newGood();
352 
353  $source = $this->resolveToFSPath( $params['src'] );
354  if ( $source === null ) {
355  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
356 
357  return $status;
358  }
359 
360  $dest = $this->resolveToFSPath( $params['dst'] );
361  if ( $dest === null ) {
362  $status->fatal( 'backend-fail-invalidpath', $params['dst'] );
363 
364  return $status;
365  }
366 
367  if ( !is_file( $source ) ) {
368  if ( empty( $params['ignoreMissingSource'] ) ) {
369  $status->fatal( 'backend-fail-move', $params['src'] );
370  }
371 
372  return $status; // do nothing; either OK or bad status
373  }
374 
375  if ( !empty( $params['async'] ) ) { // deferred
376  $cmd = implode( ' ', array(
377  wfIsWindows() ? 'MOVE /Y' : 'mv', // (overwrite)
379  wfEscapeShellArg( $this->cleanPathSlashes( $dest ) )
380  ) );
381  $status->value = new FSFileOpHandle( $this, $params, 'Move', $cmd );
382  } else { // immediate write
383  $this->trapWarnings();
384  $ok = ( $source === $dest ) ? true : rename( $source, $dest );
385  $this->untrapWarnings();
386  clearstatcache(); // file no longer at source
387  if ( !$ok ) {
388  $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
389 
390  return $status;
391  }
392  }
393 
394  return $status;
395  }
396 
400  protected function getResponseMove( $errors, Status $status, array $params, $cmd ) {
401  if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
402  $status->fatal( 'backend-fail-move', $params['src'], $params['dst'] );
403  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
404  }
405  }
406 
407  protected function doDeleteInternal( array $params ) {
408  $status = Status::newGood();
409 
410  $source = $this->resolveToFSPath( $params['src'] );
411  if ( $source === null ) {
412  $status->fatal( 'backend-fail-invalidpath', $params['src'] );
413 
414  return $status;
415  }
416 
417  if ( !is_file( $source ) ) {
418  if ( empty( $params['ignoreMissingSource'] ) ) {
419  $status->fatal( 'backend-fail-delete', $params['src'] );
420  }
421 
422  return $status; // do nothing; either OK or bad status
423  }
424 
425  if ( !empty( $params['async'] ) ) { // deferred
426  $cmd = implode( ' ', array(
427  wfIsWindows() ? 'DEL' : 'unlink',
429  ) );
430  $status->value = new FSFileOpHandle( $this, $params, 'Copy', $cmd );
431  } else { // immediate write
432  $this->trapWarnings();
433  $ok = unlink( $source );
434  $this->untrapWarnings();
435  if ( !$ok ) {
436  $status->fatal( 'backend-fail-delete', $params['src'] );
437 
438  return $status;
439  }
440  }
441 
442  return $status;
443  }
444 
448  protected function getResponseDelete( $errors, Status $status, array $params, $cmd ) {
449  if ( $errors !== '' && !( wfIsWindows() && $errors[0] === " " ) ) {
450  $status->fatal( 'backend-fail-delete', $params['src'] );
451  trigger_error( "$cmd\n$errors", E_USER_WARNING ); // command output
452  }
453  }
454 
461  protected function doPrepareInternal( $fullCont, $dirRel, array $params ) {
462  $status = Status::newGood();
463  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
464  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
465  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
466  $existed = is_dir( $dir ); // already there?
467  // Create the directory and its parents as needed...
468  $this->trapWarnings();
469  if ( !wfMkdirParents( $dir ) ) {
470  $status->fatal( 'directorycreateerror', $params['dir'] ); // fails on races
471  } elseif ( !is_writable( $dir ) ) {
472  $status->fatal( 'directoryreadonlyerror', $params['dir'] );
473  } elseif ( !is_readable( $dir ) ) {
474  $status->fatal( 'directorynotreadableerror', $params['dir'] );
475  }
476  $this->untrapWarnings();
477  // Respect any 'noAccess' or 'noListing' flags...
478  if ( is_dir( $dir ) && !$existed ) {
479  $status->merge( $this->doSecureInternal( $fullCont, $dirRel, $params ) );
480  }
481 
482  return $status;
483  }
484 
485  protected function doSecureInternal( $fullCont, $dirRel, array $params ) {
486  $status = Status::newGood();
487  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
488  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
489  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
490  // Seed new directories with a blank index.html, to prevent crawling...
491  if ( !empty( $params['noListing'] ) && !file_exists( "{$dir}/index.html" ) ) {
492  $this->trapWarnings();
493  $bytes = file_put_contents( "{$dir}/index.html", $this->indexHtmlPrivate() );
494  $this->untrapWarnings();
495  if ( $bytes === false ) {
496  $status->fatal( 'backend-fail-create', $params['dir'] . '/index.html' );
497  }
498  }
499  // Add a .htaccess file to the root of the container...
500  if ( !empty( $params['noAccess'] ) && !file_exists( "{$contRoot}/.htaccess" ) ) {
501  $this->trapWarnings();
502  $bytes = file_put_contents( "{$contRoot}/.htaccess", $this->htaccessPrivate() );
503  $this->untrapWarnings();
504  if ( $bytes === false ) {
505  $storeDir = "mwstore://{$this->name}/{$shortCont}";
506  $status->fatal( 'backend-fail-create', "{$storeDir}/.htaccess" );
507  }
508  }
509 
510  return $status;
511  }
512 
513  protected function doPublishInternal( $fullCont, $dirRel, array $params ) {
514  $status = Status::newGood();
515  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
516  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
517  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
518  // Unseed new directories with a blank index.html, to allow crawling...
519  if ( !empty( $params['listing'] ) && is_file( "{$dir}/index.html" ) ) {
520  $exists = ( file_get_contents( "{$dir}/index.html" ) === $this->indexHtmlPrivate() );
521  $this->trapWarnings();
522  if ( $exists && !unlink( "{$dir}/index.html" ) ) { // reverse secure()
523  $status->fatal( 'backend-fail-delete', $params['dir'] . '/index.html' );
524  }
525  $this->untrapWarnings();
526  }
527  // Remove the .htaccess file from the root of the container...
528  if ( !empty( $params['access'] ) && is_file( "{$contRoot}/.htaccess" ) ) {
529  $exists = ( file_get_contents( "{$contRoot}/.htaccess" ) === $this->htaccessPrivate() );
530  $this->trapWarnings();
531  if ( $exists && !unlink( "{$contRoot}/.htaccess" ) ) { // reverse secure()
532  $storeDir = "mwstore://{$this->name}/{$shortCont}";
533  $status->fatal( 'backend-fail-delete', "{$storeDir}/.htaccess" );
534  }
535  $this->untrapWarnings();
536  }
537 
538  return $status;
539  }
540 
541  protected function doCleanInternal( $fullCont, $dirRel, array $params ) {
542  $status = Status::newGood();
543  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
544  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
545  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
546  $this->trapWarnings();
547  if ( is_dir( $dir ) ) {
548  rmdir( $dir ); // remove directory if empty
549  }
550  $this->untrapWarnings();
551 
552  return $status;
553  }
554 
555  protected function doGetFileStat( array $params ) {
556  $source = $this->resolveToFSPath( $params['src'] );
557  if ( $source === null ) {
558  return false; // invalid storage path
559  }
560 
561  $this->trapWarnings(); // don't trust 'false' if there were errors
562  $stat = is_file( $source ) ? stat( $source ) : false; // regular files only
563  $hadError = $this->untrapWarnings();
564 
565  if ( $stat ) {
566  return array(
567  'mtime' => wfTimestamp( TS_MW, $stat['mtime'] ),
568  'size' => $stat['size']
569  );
570  } elseif ( !$hadError ) {
571  return false; // file does not exist
572  } else {
573  return null; // failure
574  }
575  }
576 
580  protected function doClearCache( array $paths = null ) {
581  clearstatcache(); // clear the PHP file stat cache
582  }
583 
584  protected function doDirectoryExists( $fullCont, $dirRel, array $params ) {
585  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
586  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
587  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
588 
589  $this->trapWarnings(); // don't trust 'false' if there were errors
590  $exists = is_dir( $dir );
591  $hadError = $this->untrapWarnings();
592 
593  return $hadError ? null : $exists;
594  }
595 
603  public function getDirectoryListInternal( $fullCont, $dirRel, array $params ) {
604  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
605  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
606  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
607  $exists = is_dir( $dir );
608  if ( !$exists ) {
609  wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
610 
611  return array(); // nothing under this dir
612  } elseif ( !is_readable( $dir ) ) {
613  wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
614 
615  return null; // bad permissions?
616  }
617 
618  return new FSFileBackendDirList( $dir, $params );
619  }
620 
628  public function getFileListInternal( $fullCont, $dirRel, array $params ) {
629  list( , $shortCont, ) = FileBackend::splitStoragePath( $params['dir'] );
630  $contRoot = $this->containerFSRoot( $shortCont, $fullCont ); // must be valid
631  $dir = ( $dirRel != '' ) ? "{$contRoot}/{$dirRel}" : $contRoot;
632  $exists = is_dir( $dir );
633  if ( !$exists ) {
634  wfDebug( __METHOD__ . "() given directory does not exist: '$dir'\n" );
635 
636  return array(); // nothing under this dir
637  } elseif ( !is_readable( $dir ) ) {
638  wfDebug( __METHOD__ . "() given directory is unreadable: '$dir'\n" );
639 
640  return null; // bad permissions?
641  }
642 
643  return new FSFileBackendFileList( $dir, $params );
644  }
645 
646  protected function doGetLocalReferenceMulti( array $params ) {
647  $fsFiles = array(); // (path => FSFile)
648 
649  foreach ( $params['srcs'] as $src ) {
650  $source = $this->resolveToFSPath( $src );
651  if ( $source === null || !is_file( $source ) ) {
652  $fsFiles[$src] = null; // invalid path or file does not exist
653  } else {
654  $fsFiles[$src] = new FSFile( $source );
655  }
656  }
657 
658  return $fsFiles;
659  }
660 
661  protected function doGetLocalCopyMulti( array $params ) {
662  $tmpFiles = array(); // (path => TempFSFile)
663 
664  foreach ( $params['srcs'] as $src ) {
665  $source = $this->resolveToFSPath( $src );
666  if ( $source === null ) {
667  $tmpFiles[$src] = null; // invalid path
668  } else {
669  // Create a new temporary file with the same extension...
671  $tmpFile = TempFSFile::factory( 'localcopy_', $ext );
672  if ( !$tmpFile ) {
673  $tmpFiles[$src] = null;
674  } else {
675  $tmpPath = $tmpFile->getPath();
676  // Copy the source file over the temp file
677  $this->trapWarnings();
678  $ok = copy( $source, $tmpPath );
679  $this->untrapWarnings();
680  if ( !$ok ) {
681  $tmpFiles[$src] = null;
682  } else {
683  $this->chmod( $tmpPath );
684  $tmpFiles[$src] = $tmpFile;
685  }
686  }
687  }
688  }
689 
690  return $tmpFiles;
691  }
692 
693  protected function directoriesAreVirtual() {
694  return false;
695  }
696 
697  protected function doExecuteOpHandlesInternal( array $fileOpHandles ) {
698  $statuses = array();
699 
700  $pipes = array();
701  foreach ( $fileOpHandles as $index => $fileOpHandle ) {
702  $pipes[$index] = popen( "{$fileOpHandle->cmd} 2>&1", 'r' );
703  }
704 
705  $errs = array();
706  foreach ( $pipes as $index => $pipe ) {
707  // Result will be empty on success in *NIX. On Windows,
708  // it may be something like " 1 file(s) [copied|moved].".
709  $errs[$index] = stream_get_contents( $pipe );
710  fclose( $pipe );
711  }
712 
713  foreach ( $fileOpHandles as $index => $fileOpHandle ) {
714  $status = Status::newGood();
715  $function = 'getResponse' . $fileOpHandle->call;
716  $this->$function( $errs[$index], $status, $fileOpHandle->params, $fileOpHandle->cmd );
717  $statuses[$index] = $status;
718  if ( $status->isOK() && $fileOpHandle->chmodPath ) {
719  $this->chmod( $fileOpHandle->chmodPath );
720  }
721  }
722 
723  clearstatcache(); // files changed
724  return $statuses;
725  }
726 
733  protected function chmod( $path ) {
734  $this->trapWarnings();
735  $ok = chmod( $path, $this->fileMode );
736  $this->untrapWarnings();
737 
738  return $ok;
739  }
740 
746  protected function indexHtmlPrivate() {
747  return '';
748  }
749 
755  protected function htaccessPrivate() {
756  return "Deny from all\n";
757  }
758 
765  protected function cleanPathSlashes( $path ) {
766  return wfIsWindows() ? strtr( $path, '/', '\\' ) : $path;
767  }
768 
772  protected function trapWarnings() {
773  $this->hadWarningErrors[] = false; // push to stack
774  set_error_handler( array( $this, 'handleWarning' ), E_WARNING );
775  }
776 
782  protected function untrapWarnings() {
783  restore_error_handler(); // restore previous handler
784  return array_pop( $this->hadWarningErrors ); // pop from stack
785  }
786 
793  public function handleWarning( $errno, $errstr ) {
794  wfDebugLog( 'FSFileBackend', $errstr ); // more detailed error logging
795  $this->hadWarningErrors[count( $this->hadWarningErrors ) - 1] = true;
796 
797  return true; // suppress from PHP handler
798  }
799 }
800 
805  public $cmd; // string; shell command
806  public $chmodPath; // string; file to chmod
807 
815  public function __construct(
817  ) {
818  $this->backend = $backend;
819  $this->params = $params;
820  $this->call = $call;
821  $this->cmd = $cmd;
822  $this->chmodPath = $chmodPath;
823  }
824 }
825 
833 abstract class FSFileBackendList implements Iterator {
835  protected $iter;
836 
838  protected $suffixStart;
839 
841  protected $pos = 0;
842 
844  protected $params = array();
845 
850  public function __construct( $dir, array $params ) {
851  $path = realpath( $dir ); // normalize
852  if ( $path === false ) {
853  $path = $dir;
854  }
855  $this->suffixStart = strlen( $path ) + 1; // size of "path/to/dir/"
856  $this->params = $params;
857 
858  try {
859  $this->iter = $this->initIterator( $path );
860  } catch ( UnexpectedValueException $e ) {
861  $this->iter = null; // bad permissions? deleted?
862  }
863  }
864 
871  protected function initIterator( $dir ) {
872  if ( !empty( $this->params['topOnly'] ) ) { // non-recursive
873  # Get an iterator that will get direct sub-nodes
874  return new DirectoryIterator( $dir );
875  } else { // recursive
876  # Get an iterator that will return leaf nodes (non-directories)
877  # RecursiveDirectoryIterator extends FilesystemIterator.
878  # FilesystemIterator::SKIP_DOTS default is inconsistent in PHP 5.3.x.
879  $flags = FilesystemIterator::CURRENT_AS_SELF | FilesystemIterator::SKIP_DOTS;
880 
881  return new RecursiveIteratorIterator(
882  new RecursiveDirectoryIterator( $dir, $flags ),
883  RecursiveIteratorIterator::CHILD_FIRST // include dirs
884  );
885  }
886  }
887 
892  public function key() {
893  return $this->pos;
894  }
895 
900  public function current() {
901  return $this->getRelPath( $this->iter->current()->getPathname() );
902  }
903 
908  public function next() {
909  try {
910  $this->iter->next();
911  $this->filterViaNext();
912  } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
913  throw new FileBackendError( "File iterator gave UnexpectedValueException." );
914  }
915  ++$this->pos;
916  }
917 
922  public function rewind() {
923  $this->pos = 0;
924  try {
925  $this->iter->rewind();
926  $this->filterViaNext();
927  } catch ( UnexpectedValueException $e ) { // bad permissions? deleted?
928  throw new FileBackendError( "File iterator gave UnexpectedValueException." );
929  }
930  }
931 
936  public function valid() {
937  return $this->iter && $this->iter->valid();
938  }
939 
943  protected function filterViaNext() {
944  }
945 
953  protected function getRelPath( $dir ) {
954  $path = realpath( $dir );
955  if ( $path === false ) {
956  $path = $dir;
957  }
958 
959  return strtr( substr( $path, $this->suffixStart ), '\\', '/' );
960  }
961 }
962 
964  protected function filterViaNext() {
965  while ( $this->iter->valid() ) {
966  if ( $this->iter->current()->isDot() || !$this->iter->current()->isDir() ) {
967  $this->iter->next(); // skip non-directories and dot files
968  } else {
969  break;
970  }
971  }
972  }
973 }
974 
976  protected function filterViaNext() {
977  while ( $this->iter->valid() ) {
978  if ( !$this->iter->current()->isFile() ) {
979  $this->iter->next(); // skip non-files and dot files
980  } else {
981  break;
982  }
983  }
984  }
985 }
FileBackend\splitStoragePath
static splitStoragePath( $storagePath)
Split a storage path into a backend name, a container name, and a relative file path.
Definition: FileBackend.php:1342
FSFileBackendList\initIterator
initIterator( $dir)
Return an appropriate iterator object to wrap.
Definition: FSFileBackend.php:861
FSFileBackend\__construct
__construct(array $config)
Definition: FSFileBackend.php:62
FSFileBackend\doCleanInternal
doCleanInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:535
FSFileOpHandle
Definition: FSFileBackend.php:798
FSFileBackendFileList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:966
FSFileBackend\doStoreInternal
doStoreInternal(array $params)
Definition: FSFileBackend.php:233
php
skin txt MediaWiki includes four core it has been set as the default in MediaWiki since the replacing Monobook it had been been the default skin since before being replaced by Vector largely rewritten in while keeping its appearance Several legacy skins were removed in the as the burden of supporting them became too heavy to bear Those in etc for skin dependent CSS etc for skin dependent JavaScript These can also be customised on a per user by etc This feature has led to a wide variety of user styles becoming that gallery is a good place to ending in php
Definition: skin.txt:62
FSFileBackend\doClearCache
doClearCache(array $paths=null)
Definition: FSFileBackend.php:574
FSFileBackend\getResponseStore
getResponseStore( $errors, Status $status, array $params, $cmd)
Definition: FSFileBackend.php:273
FSFileBackend\doPublishInternal
doPublishInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:507
Status\fatal
fatal( $message)
Add an error and set OK to false, indicating that the operation as a whole was fatal.
Definition: Status.php:146
wfMkdirParents
wfMkdirParents( $dir, $mode=null, $caller=null)
Make directory, and make all parent directories if they don't exist.
Definition: GlobalFunctions.php:2637
FSFileBackend\$currentUser
string $currentUser
OS username running this script *.
Definition: FSFileBackend.php:50
Status\isOK
isOK()
Returns whether the operation completed.
Definition: Status.php:109
Status\merge
merge( $other, $overwriteValue=false)
Merge another status object into this one.
Definition: Status.php:325
FSFileBackend\getResponseCreate
getResponseCreate( $errors, Status $status, array $params, $cmd)
Definition: FSFileBackend.php:226
FSFileBackend\doPrepareInternal
doPrepareInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:455
wfTimestamp
wfTimestamp( $outputtype=TS_UNIX, $ts=0)
Get a timestamp string in one of various formats.
Definition: GlobalFunctions.php:2530
FSFileBackendList\current
current()
Definition: FSFileBackend.php:890
FileBackendError
File backend exception for checked exceptions (e.g.
Definition: FileBackend.php:1500
wfDebugLog
wfDebugLog( $logGroup, $text, $dest='all')
Send a line to a supplementary debug log file, if configured, or main debug log if not.
Definition: GlobalFunctions.php:1087
FileBackend\extensionFromPath
static extensionFromPath( $path, $case='lowercase')
Get the final extension from a storage or FS path.
Definition: FileBackend.php:1401
Status\newGood
static newGood( $value=null)
Factory function for good results.
Definition: Status.php:77
$params
$params
Definition: styleTest.css.php:40
FileBackend\copy
copy(array $params, array $opts=array())
Performs a single copy operation.
Definition: FileBackend.php:444
FSFileBackend\$hadWarningErrors
array $hadWarningErrors
Definition: FSFileBackend.php:52
FSFileBackend\htaccessPrivate
htaccessPrivate()
Return the text of a .htaccess file to make a directory private.
Definition: FSFileBackend.php:749
FSFileBackend\doGetFileStat
doGetFileStat(array $params)
Definition: FSFileBackend.php:549
FSFileBackend\directoriesAreVirtual
directoriesAreVirtual()
Is this a key/value store where directories are just virtual? Virtual directories exists in so much a...
Definition: FSFileBackend.php:687
$flags
it s the revision text itself In either if gzip is the revision text is gzipped $flags
Definition: hooks.txt:2124
FSFileBackend\$fileOwner
string $fileOwner
Required OS username to own files *.
Definition: FSFileBackend.php:48
FSFileBackend\getResponseMove
getResponseMove( $errors, Status $status, array $params, $cmd)
Definition: FSFileBackend.php:394
FSFileBackend\doGetLocalReferenceMulti
doGetLocalReferenceMulti(array $params)
Definition: FSFileBackend.php:640
FSFileBackend\$basePath
string $basePath
Directory holding the container directories *.
Definition: FSFileBackend.php:42
FSFileBackendDirList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:954
Status
Generic operation result class Has warning/error list, boolean status and arbitrary value.
Definition: Status.php:40
FSFileBackend\doDirectoryExists
doDirectoryExists( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:578
FSFileBackend\getDirectoryListInternal
getDirectoryListInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:597
FSFileBackendFileList
Definition: FSFileBackend.php:965
FSFileBackendList\getRelPath
getRelPath( $dir)
Return only the relative path and normalize slashes to FileBackend-style.
Definition: FSFileBackend.php:943
FSFileBackend\containerFSRoot
containerFSRoot( $shortCont, $fullCont)
Given the short (unresolved) and full (resolved) name of a container, return the file system path of ...
Definition: FSFileBackend.php:125
FileBackendStoreOpHandle\$backend
FileBackendStore $backend
Definition: FileBackendStore.php:1814
FSFileBackendDirList
Definition: FSFileBackend.php:953
array
the array() calling protocol came about after MediaWiki 1.4rc1.
List of Api Query prop modules.
FSFileBackend\doSecureInternal
doSecureInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:479
FSFileBackendList
Wrapper around RecursiveDirectoryIterator/DirectoryIterator that catches exception or does any custom...
Definition: FSFileBackend.php:827
FSFileBackend\doExecuteOpHandlesInternal
doExecuteOpHandlesInternal(array $fileOpHandles)
Definition: FSFileBackend.php:691
FSFileBackend\getResponseCopy
getResponseCopy( $errors, Status $status, array $params, $cmd)
Definition: FSFileBackend.php:337
list
deferred txt A few of the database updates required by various functions here can be deferred until after the result page is displayed to the user For updating the view updating the linked to tables after a etc PHP does not yet have any way to tell the server to actually return and disconnect while still running these but it might have such a feature in the future We handle these by creating a deferred update object and putting those objects on a global list
Definition: deferred.txt:11
false
processing should stop and the error should be shown to the user * false
Definition: hooks.txt:188
FSFileBackend\$containerPaths
array $containerPaths
Map of container names to root paths for custom container paths *.
Definition: FSFileBackend.php:44
FSFileBackend\resolveToFSPath
resolveToFSPath( $storagePath)
Get the absolute file system path for a storage path.
Definition: FSFileBackend.php:141
FSFileBackendList\$iter
Iterator $iter
Definition: FSFileBackend.php:828
$ok
$ok
Definition: UtfNormalTest.php:71
FSFileBackend\doDeleteInternal
doDeleteInternal(array $params)
Definition: FSFileBackend.php:401
TS_MW
const TS_MW
MediaWiki concatenated string timestamp (YYYYMMDDHHMMSS)
Definition: GlobalFunctions.php:2478
wfDebug
wfDebug( $text, $dest='all')
Sends a line to the debug log if enabled or, optionally, to a comment in output.
Definition: GlobalFunctions.php:980
FSFileBackend\$fileMode
int $fileMode
File permission mode *.
Definition: FSFileBackend.php:46
TempFSFile\factory
static factory( $prefix, $extension='')
Make a new temporary file on the file system.
Definition: TempFSFile.php:44
FSFileBackendList\valid
valid()
Definition: FSFileBackend.php:926
FSFileBackend\resolveContainerPath
resolveContainerPath( $container, $relStoragePath)
Resolve a relative storage path, checking if it's allowed by the backend.
Definition: FSFileBackend.php:87
FSFileBackendList\$pos
int $pos
Definition: FSFileBackend.php:832
wfIsWindows
wfIsWindows()
Check if the operating system is Windows.
Definition: GlobalFunctions.php:2571
FileBackendStore\resolveStoragePathReal
resolveStoragePathReal( $storagePath)
Like resolveStoragePath() except null values are returned if the container is sharded and the shard c...
Definition: FileBackendStore.php:1418
wfEscapeShellArg
wfEscapeShellArg()
Windows-compatible version of escapeshellarg() Windows doesn't recognise single-quotes in the shell,...
Definition: GlobalFunctions.php:2752
FileBackendStore
Base class for all backends using particular storage medium.
Definition: FileBackendStore.php:38
FileBackendStoreOpHandle
FileBackendStore helper class for performing asynchronous file operations.
Definition: FileBackendStore.php:1812
FSFile
Class representing a non-directory file on the file system.
Definition: FSFile.php:29
FSFileBackendList\next
next()
Definition: FSFileBackend.php:898
FSFileBackend
Class for a file system (FS) based file backend.
Definition: FSFileBackend.php:41
FSFileBackendList\key
key()
Definition: FSFileBackend.php:882
$dir
if(count( $args)==0) $dir
Definition: importImages.php:49
FSFileBackend\indexHtmlPrivate
indexHtmlPrivate()
Return the text of an index.html file to hide directory listings.
Definition: FSFileBackend.php:740
$ext
$ext
Definition: NoLocalSettings.php:34
FSFileBackend\cleanPathSlashes
cleanPathSlashes( $path)
Clean up directory separators for the given OS.
Definition: FSFileBackend.php:759
$path
$path
Definition: NoLocalSettings.php:35
FSFileOpHandle\__construct
__construct(FSFileBackend $backend, array $params, $call, $cmd, $chmodPath=null)
Definition: FSFileBackend.php:809
as
This document is intended to provide useful advice for parties seeking to redistribute MediaWiki to end users It s targeted particularly at maintainers for Linux since it s been observed that distribution packages of MediaWiki often break We ve consistently had to recommend that users seeking support use official tarballs instead of their distribution s and this often solves whatever problem the user is having It would be nice if this could such as
Definition: distributors.txt:9
FSFileBackend\doCreateInternal
doCreateInternal(array $params)
Definition: FSFileBackend.php:176
FSFileBackend\chmod
chmod( $path)
Chmod a file, suppressing the warnings.
Definition: FSFileBackend.php:727
FSFileBackendList\$suffixStart
int $suffixStart
Definition: FSFileBackend.php:830
FileBackendStoreOpHandle\$params
array $params
Definition: FileBackendStore.php:1813
$source
if(PHP_SAPI !='cli') $source
Definition: mwdoc-filter.php:18
FSFileBackend\isLegalRelPath
isLegalRelPath( $path)
Sanity check a relative file system path for validity.
Definition: FSFileBackend.php:105
FSFileBackendList\rewind
rewind()
Definition: FSFileBackend.php:912
FSFileBackend\isPathUsableInternal
isPathUsableInternal( $storagePath)
Check if a file can be created or changed at a given storage path.
Definition: FSFileBackend.php:155
FSFileBackend\handleWarning
handleWarning( $errno, $errstr)
Definition: FSFileBackend.php:787
FSFileBackend\untrapWarnings
untrapWarnings()
Stop listening for E_WARNING errors and return true if any happened.
Definition: FSFileBackend.php:776
FSFileBackend\doGetLocalCopyMulti
doGetLocalCopyMulti(array $params)
Definition: FSFileBackend.php:655
FSFileOpHandle\$cmd
$cmd
Definition: FSFileBackend.php:799
FSFileBackend\doCopyInternal
doCopyInternal(array $params)
Definition: FSFileBackend.php:280
FSFileBackendList\$params
array $params
Definition: FSFileBackend.php:834
FSFileBackend\getFileListInternal
getFileListInternal( $fullCont, $dirRel, array $params)
Definition: FSFileBackend.php:622
FSFileBackend\trapWarnings
trapWarnings()
Listen for E_WARNING errors and track whether any happen.
Definition: FSFileBackend.php:766
FileBackendStoreOpHandle\$call
$call
Definition: FileBackendStore.php:1817
FSFileBackend\getResponseDelete
getResponseDelete( $errors, Status $status, array $params, $cmd)
Definition: FSFileBackend.php:442
FSFileBackendList\__construct
__construct( $dir, array $params)
Definition: FSFileBackend.php:840
FSFileBackend\doMoveInternal
doMoveInternal(array $params)
Definition: FSFileBackend.php:344
$e
div flags Integer display flags(NO_ACTION_LINK, NO_EXTRA_USER_LINKS) 'LogException' returning false will NOT prevent logging $e
Definition: hooks.txt:1632
FSFileOpHandle\$chmodPath
$chmodPath
Definition: FSFileBackend.php:800
FSFileBackendList\filterViaNext
filterViaNext()
Filter out items by advancing to the next ones.
Definition: FSFileBackend.php:933