File "disk.php"
Full Path: /home/pylifeesyu/www/wp-content/plugins/wponlinebackup/include/disk.php
File size: 25.54 KB
MIME-type: text/x-php
Charset: utf-8
<?php
/*
WPOnlineBackup_Disk - Performs the disk operations required to write the backup
Also allows spanning accross multiple files in timeout situations and generates patch files if necessary
*/
class WPOnlineBackup_Disk
{
/*private*/ var $WPOnlineBackup;
/*private*/ var $status;
/*private*/ var $rotate_id;
/*private*/ var $prefix;
/*private*/ var $name;
/*private*/ var $file;
/*private*/ var $offset;
/*private*/ var $size;
/*private*/ var $p_file;
/*private*/ var $p_offset;
/*private*/ var $p_size;
/*private*/ var $p_index;
/*private*/ var $rotation;
/*private*/ var $patches;
/*private*/ var $header = false;
/*private*/ var $rc_header;
/*private*/ var $r_files;
/*private*/ var $rp_files;
/*private*/ var $rc_file;
/*private*/ var $rc_offset;
/*public*/ function WPOnlineBackup_Disk( & $WPOnlineBackup, $rotate_id = 0 )
{
$this->WPOnlineBackup = & $WPOnlineBackup;
$this->rotate_id = $rotate_id;
$this->r_files = $this->rp_files = array();
}
/*public*/ function Get_Header()
{
if ( $this->header === false )
$this->header = <<<HEADER
<?php
/*Rejection header*/
__halt_compiler();
}
HEADER;
return $this->header;
}
/*public*/ function Save()
{
// Ensure all data is flushed
if ( $this->file !== false ) fflush( $this->file );
if ( $this->p_file !== false ) fflush( $this->p_file );
if ( $this->rc_file !== false ) fflush( $this->rc_file );
$state = array(
'status' => $this->status,
'header' => $this->header, // In case we change during update
'rc_header' => $this->rc_header,
'has_header' => $this->has_header,
'rotate_id' => $this->rotate_id,
'prefix' => $this->prefix,
'name' => $this->name,
'offset' => $this->offset,
'size' => $this->size,
'd_offset' => $this->d_offset,
'rotation' => $this->rotation,
'p_offset' => $this->p_offset,
'p_size' => $this->p_size,
'p_index' => $this->p_index,
'p_rindex' => $this->p_rindex,
'patches' => $this->patches,
);
if ( $this->status == 2 ) {
$state['rc_offset'] = $this->rc_offset;
}
return $state;
}
/*public*/ function Load( $state, $rotation = false )
{
// ASSERTION - Status is null and we are initialised
$this->status = $state['status'];
$this->header = $state['header'];
$this->rc_header = $state['rc_header'];
$this->has_header = $state['has_header'];
$this->rotate_id = $state['rotate_id'];
if ( $rotation === false )
$rotate = false;
else
$rotate = $rotation != $this->rotate_id ? true : false;
$this->prefix = $state['prefix'];
$this->name = $state['name'];
$this->offset = $state['offset'];
$this->size = $state['size'];
$this->d_offset = $state['d_offset'];
$this->p_file = false;
$this->p_offset = $state['p_offset'];
$this->p_size = $state['p_size'];
$this->p_index = $state['p_index'];
$this->p_rindex = $state['p_rindex'];
$this->rotation = $state['rotation'];
$this->patches = $state['patches'];
if ( $this->status == 0 && $rotate ) {
// Rotate the files
$this->rotation[$this->rotate_id] = array(
'rotate_id' => $this->rotate_id,
'offset' => $this->offset,
'size' => $this->size,
'has_header' => $this->has_header,
);
$this->offset += $this->size;
$this->size = 0;
$this->patches[$this->rotate_id] = array(
'rotate_id' => $this->rotate_id,
'offset' => $this->p_offset,
'size' => $this->p_size,
);
$this->p_offset += $this->p_size;
$this->p_size = 0;
// Fill in any gaps, to ensure we delete all files in cleanup
$i = $this->rotate_id + 1;
while ( $i < $rotation ) {
$this->rotation[$i] = array(
'rotate_id' => $i,
'offset' => 0,
'size' => 0,
);
$this->patches[$i] = array(
'rotate_id' => $i,
'offset' => 0,
'size' => 0,
);
$i++;
}
// Set the new rotation value
$this->rotate_id = $rotation;
// All rotations have rejection header
$this->has_header = true;
// Attempt to cleanup rotated files
$this->CleanUp_Rotated();
}
if ( $this->status == 0 ) {
if ( $this->rotate_id ) $state['name'] .= '.' . $this->rotate_id;
// Reopen the file
if ( ( $this->file = @fopen( $this->prefix . '/' . $state['name'] . '.php', $rotate ? 'w+b' : 'r+b' ) ) === false )
return OBFW_Exception();
// Attempt to truncate the file to the correct size
@ftruncate( $this->file, ( $this->has_header ? strlen( $this->header ) : 0 ) + $this->size );
if ( $rotate || $this->size == 0 ) {
if ( !$rotate && @fseek( $this->file, 0, SEEK_SET ) != 0 ) {
@fclose( $this->file );
return OBFW_Exception();
}
// Write the Rejection header if we want one
if ( $this->has_header && true !== ( $ret = $this->Write_Rejection_Header( $this->file ) ) ) {
@fclose( $this->file );
return $ret;
}
} else {
// Seek to where we left off
if ( @fseek( $this->file, ( $this->has_header ? strlen( $this->header ) : 0 ) + $this->size, SEEK_SET ) != 0 ) {
@fclose( $this->file );
return OBFW_Exception();
}
}
$this->rc_file = false;
} else if ( $this->status == 2 ) {
$this->rc_offset = $state['rc_offset'];
// Open a file to reconstruct into
if ( ( $this->rc_file = @fopen( $this->prefix . '/' . $state['name'] . '.rc.php', 'r+b' ) ) === false )
return OBFW_Exception();
// Attempt to truncate the file to the correct size
@ftruncate( $this->rc_file, ( $this->rc_header ? strlen( $this->header ) : 0 ) + $this->rc_offset );
if ( $this->rc_offset == 0 ) {
// Seek to where we left off
if ( @fseek( $this->rc_file, 0, SEEK_SET ) != 0 ) {
@fclose( $this->rc_file );
return OBFW_Exception();
}
// Write the Rejection header if we want one in the rc file
if ( $this->rc_header && true !== ( $ret = $this->Write_Rejection_Header( $this->rc_file ) ) ) {
@fclose( $this->rc_file );
return $ret;
}
} else {
// Seek to where we left off
if ( @fseek( $this->rc_file, ( $this->rc_header ? strlen( $this->header ) : 0 ) + $this->rc_offset, SEEK_SET ) != 0 ) {
@fclose( $this->rc_file );
return OBFW_Exception();
}
}
$this->d_offset = $this->rc_offset;
$this->file = false;
} else {
$this->file = false;
$this->rc_file = false;
}
// Clean up deleted data if need be
$this->Delete();
return true;
}
/*public*/ function Initialise( $prefix )
{
$this->prefix = $prefix;
}
/*public*/ function Open( $name, $rjct_header = true )
{
// ASSERTION - Status is null and we are initialised
$this->status = 0;
// Set the rejection header
$this->Get_Header();
// Modify the name to include a random seed so it is hidden from the world
$this->name = $name = $name . '.' . $this->WPOnlineBackup->Random_Seed( $name, 'disk' );
if ( $this->rotate_id )
$name .= '.' . $this->rotate_id;
// Create the file with PHP extension so you cannot download it
if ( ( $this->file = @fopen( $this->prefix . '/' . $name . '.php', 'w+b' ) ) === false )
return OBFW_Exception();
$this->rc_header = $rjct_header;
$this->has_header = $rjct_header;
// Write the Rejection header if we want one on the initial file
if ( $this->has_header && true !== ( $ret = $this->Write_Rejection_Header( $this->file ) ) ) {
@fclose( $this->file );
return $ret;
}
$this->offset = 0;
$this->size = 0;
$this->d_offset = 0;
$this->p_file = false;
$this->p_offset = 0;
$this->p_size = 0;
$this->p_index = array();
$this->p_rindex = array();
$this->rotation = array();
$this->patches = array();
$this->rc_file = false;
return true;
}
/*public*/ function Get_File_Path()
{
$name = $this->name;
if ( $this->rotate_id )
$name .= '.' . $this->rotate_id;
return $this->prefix . '/' . $name . '.php';
}
/*private*/ function CleanUp_Handles()
{
foreach ( $this->r_files as $id => $file ) @fclose( $this->r_files[$id] );
foreach ( $this->rp_files as $id => $file ) @fclose( $this->rp_files[$id] );
if ( $this->p_file !== false ) @fclose( $this->p_file );
if ( $this->file !== false ) @fclose( $this->file );
}
/*public*/ function Close()
{
$this->CleanUp_Handles();
$this->p_file = false;
$this->file = false;
$this->status = 1;
return true;
}
/*public*/ function CleanUp( $wipe = true )
{
if ( $wipe )
$this->Delete( true );
$this->CleanUp_Handles();
if ( $this->rc_file !== false ) {
@fclose( $this->rc_file );
if ( $wipe )
@unlink( $this->prefix . '/' . $this->name . '.rc.php' );
}
}
/*private*/ function Partial_Read( $read, $length, $type, $id )
{
$e = OBFW_Exception();
$name = $this->name;
switch ( $type ) {
case 'patch':
$name .= '.patch';
default:
if ( $id ) $name .= '.' . $id;
break;
}
if ( $e ) $e = 'PHP last error: ' . $e;
else $e = 'PHP has no record of an error.';
return 'Attempt to read from file ' . $this->prefix . '/' . $name . '.php only partially succeeded. Unexpected end of file. Only ' . $read . ' of ' . $length . ' bytes were read. ' . $e;
}
/*private*/ function Partial_Write( $method, $written, $length, $type = '' )
{
$e = OBFW_Exception();
$name = $this->name;
switch ( $type ) {
case 'patch':
$name .= '.patch';
default:
if ( $this->rotate_id ) $name .= '.' . $this->rotate_id;
break;
case 'rc':
$name .= '.rc';
break;
}
if ( $e ) $e = 'PHP last error: ' . $e;
else $e = 'PHP has no record of an error.';
return 'Attempt to ' . $method . ' to file ' . $this->prefix . '/' . $name . '.php only partially succeeded. Only ' . $written . ' of ' . $length . ' bytes were written. (' . $this->size . ' bytes already written.) ' . $e;
}
/*private*/ function _Try_Write( $file, $data, $length, $partial, $partial_type = '' )
{
// It seems when you fwrite lots of times it sometimes begins to write less than what you requested
// Maybe the write buffer is not quite full, but has room for some bytes, and so PHP adds to it and then returns how many fit?
// Then when the buffer is actually full, it waits so it never returns 0 unless something is wrong... Maybe one needs to check the source.
// Just loop until we failed, wrote nothing, or finish
// Interestingly we ARE seeing instances of 0 getting returned
// One cause we have confirmed is the file reached 2GB and PHP is 32-bit with the bodged large file support disabled
// However, we still see it even with arbitrary bytes getting written - so that is why we retry with a pause
// Another interesting case we are seeing is sometimes the file can reach a certain size and fwrite just crashes the script without reason
// This has a completely unknown cause
$todo_length = $length;
$tries = 5;
while ( true ) {
if ( ( $written = @fwrite( $file, $data, $todo_length ) ) === false )
return OBFW_Exception();
// If we wrote nothing, fail
if ( $written == 0 ) {
if ( --$tries <= 0 )
return $this->Partial_Write( $partial, $length - $todo_length, $length, $partial_type );
// Rest a second
sleep(1);
// Try again
continue;
}
$todo_length -= $written;
if ( $todo_length == 0 )
break;
// Trim off what we wrote
$data = substr( $data, $written );
}
return true;
}
/*public*/ function Write( $data, $length = null )
{
// ASSERTION - Status is 0
$rlen = strlen( $data );
if ( is_null( $length ) )
$length = $rlen;
else if ( $rlen < $length )
return 'Attempt to write file ' . $this->prefix . '/' . $name . '.php failed due to insufficient data. Only ' . $rlen . ' of ' . $length . ' bytes were available.';
// Nothing to write?
if ( $length == 0 )
return true;
if ( true !== ( $ret = $this->_Try_Write( $this->file, $data, $length, 'write' ) ) )
return $ret;
$this->size += $length;
return true;
}
/*public*/ function Pos()
{
// Return current position - used for incremental index file.
return $this->offset + $this->size;
}
/*public*/ function Rewrite( $offset, $data, $length = null )
{
// ASSERTION - Status is 0
// ASSERTION - Rewrite always rewrites EXISTING data, and never writes past the current end of the file
$rlen = strlen( $data );
if ( is_null( $length ) )
$length = $rlen;
else if ( $rlen < $length )
return 'Attempt to write file ' . $this->prefix . '/' . $name . '.php failed due to insufficient data. Only ' . $rlen . ' of ' . $length . ' bytes were available.';
if ( $offset >= $this->offset ) {
// Rewriting in the active file
if ( @fseek( $this->file, ( $this->has_header ? strlen( $this->header ) : 0 ) + ( $offset - $this->offset ), SEEK_SET ) != 0 ) return OBFW_Exception();
if ( true !== ( $ret = $this->_Try_Write( $this->file, $data, $length, 'rewrite' ) ) )
return $ret;
if ( @fseek( $this->file, ( $this->has_header ? strlen( $this->header ) : 0 ) + $this->size, SEEK_SET ) != 0 ) return OBFW_Exception();
} else {
// Store in patch files only data that is rewritten in rotated files
$this_length = min( $this->offset - $offset, $length );
// Open the patches file if needed
if ( $this->p_file === false ) {
if ( $this->p_size == 0 ) {
if ( ( $this->p_file = @fopen( $this->prefix . '/' . $this->name . '.patch.' . $this->rotate_id . '.php', 'w+b' ) ) === false ) return OBFW_Exception();
if ( ( $ret = $this->Write_Rejection_Header( $this->p_file, 'patch' ) ) !== true ) return $ret;
} else {
if ( ( $this->p_file = @fopen( $this->prefix . '/' . $this->name . '.patch.' . $this->rotate_id . '.php', 'r+b' ) ) === false ) return OBFW_Exception();
// Seek to where we left off
if ( @fseek( $this->p_file, strlen( $this->header ) + $this->p_size, SEEK_SET ) != 0 ) return OBFW_Exception();
}
}
// Write the patch
if ( true !== ( $ret = $this->_Try_Write( $this->p_file, $data, $this_length, 'rewrite', 'patch' ) ) )
return $ret;
// Index the patch so we can find it when we need it
if ( !array_key_exists( $offset, $this->p_index ) ) {
$this->p_index[$offset] = array();
ksort( $this->p_index );
}
$this->p_index[$offset][$this_length] = array( $this->rotate_id, $this->p_size );
ksort( $this->p_index[$offset] );
if ( !array_key_exists( $this->rotate_id, $this->p_rindex ) ) $this->p_rindex[$this->rotate_id] = 1;
else $this->p_rindex[$this->rotate_id]++;
$this->p_size += $this_length;
// Modify length and offset and recurse if we still have to rewrite some more in the active file
$length -= $this_length;
if ( $length == 0 ) return true;
$offset += $this_length;
return $this->Rewrite( $offset, substr( $data, $this_length ), $length );
}
return true;
}
/*public*/ function Flush( & $buffer, $length )
{
// ASSERTION - Status is 1 (closed) or 2 (reconstructing)
$buffer = '';
$flushed = 0;
foreach ( $this->rotation as $id => $rotate ) {
if ( $rotate['size'] == 0 ) continue;
// Find the file that contains this part of the data
if ( $rotate['offset'] + $rotate['size'] > $this->d_offset + $flushed ) {
if ( !array_key_exists( $id, $this->r_files ) ) {
$name = $this->name;
if ( $id ) $name .= '.' . $id;
if ( ( $this->r_files[$id] = @fopen( $this->prefix . '/' . $name . '.php', 'rb' ) ) === false ) {
$ret = OBFW_Exception();
unset( $this->r_files[$id] );
return $ret;
}
}
$available = $rotate['offset'] + $rotate['size'] - $this->d_offset - $flushed;
// Get the data
if ( ( $to_flush = min( $length - $flushed, $available ) ) != 0 ) {
if ( @fseek( $this->r_files[$id], ( $rotate['has_header'] ? strlen( $this->header ) : 0 ) + $this->d_offset + $flushed - $rotate['offset'], SEEK_SET ) != 0 ) return OBFW_Exception();
if ( ( $extract = @fread( $this->r_files[$id], $to_flush ) ) === false ) return OBFW_Exception();
if ( ( $len = strlen( $extract ) ) != $to_flush ) return $this->Partial_Read( $len, $to_flush, '', $id );
$buffer .= $extract;
$flushed += $to_flush;
}
// If anymore to retrieve from different rotation files, keep looping
if ( $flushed < $length ) {
@fclose( $this->r_files[$id] );
unset( $this->r_files[$id] );
continue;
} else if ( $to_flush == $available ) {
// Also close (closing is optimized for continuous flushes)
@fclose( $this->r_files[$id] );
unset( $this->r_files[$id] );
}
// More data
$ret = $this->Patch( $this->d_offset, $length, $buffer, true );
$this->d_offset += $flushed;
return $ret;
}
}
// Check the current file
if ( $this->offset + $this->size > $this->d_offset + $flushed ) {
// Check the file is available (won't be if first call)
if ( $this->file === false ) {
$name = $this->name;
if ( $this->rotate_id ) $name .= '.' . $this->rotate_id;
if ( ( $this->file = @fopen( $this->prefix . '/' . $name . '.php', 'rb' ) ) === false ) return OBFW_Exception();
}
$available = $this->offset + $this->size - $this->d_offset - $flushed;
if ( ( $to_flush = min( $length - $flushed, $available ) ) != 0 ) {
// Get the data
if ( @fseek( $this->file, ( $this->has_header ? strlen( $this->header ) : 0 ) + $this->d_offset + $flushed - $this->offset, SEEK_SET ) != 0 ) return OBFW_Exception();
if ( ( $extract = @fread( $this->file, $to_flush ) ) === false ) return OBFW_Exception();
if ( ( $len = strlen( $extract ) ) != $to_flush ) return $this->Partial_Read( $len, $to_flush, '', $this->rotate_id );
$buffer .= $extract;
$flushed += $to_flush;
}
// Anymore data?
$ret = $to_flush < $available ? true : false;
if ( $ret === false ) {
// Also close if there is no more data
@fclose( $this->file );
$this->file = false;
}
$ret = $this->Patch( $this->d_offset + $flushed, $to_flush, $buffer, $ret );
$this->d_offset += $flushed;
return $ret;
}
// No more data
return false;
}
/*private*/ function Patch( $offset, $length, & $buffer, $ret )
{
// Don't be concerned about loading p_file and not setting it's offset to the end (meaning Rewrite() will overwrite data)
// Rewrite() should never be called after Flush() (which calls Patch()) is called so it would be a bug if so. Flush() requires the file is closed for changes
// Find applicable patches
foreach ( $this->p_index as $p_offset => $index ) {
foreach ( $index as $p_length => $patch ) {
// If patch already applied, skip
if ( $p_offset + $p_length <= $offset ) continue;
// If patch doesn't apply yet, return as no more apply (patches are in order)
if ( $p_offset >= $offset + $length ) return $ret;
// Patch applies, calculate what needs patching
if ( $p_offset < $offset ) {
$p_start = $offset - $p_offset;
$to_copy = min( $p_length - $p_start, $length );
$to = 0;
} else {
$p_start = 0;
$to_copy = min( $offset + $length - $p_offset, $p_length );
$to = $p_offset - $offset;
}
if ( $to_copy == 0 ) continue;
if ( $this->rotate_id == $patch[0] ) {
if ( $this->p_file === false ) {
if ( ( $this->p_file = @fopen( $this->prefix . '/' . $this->name . '.patch.' . $this->rotate_id . '.php', 'rb' ) ) === false ) return OBFW_Exception();
}
$file = $this->p_file;
} else {
if ( !array_key_exists( $patch[0], $this->rp_files ) ) {
$name = $this->name . '.patch';
if ( $patch[0] ) $name .= '.' . $patch[0];
if ( ( $this->rp_files[ $patch[0] ] = @fopen( $this->prefix . '/' . $name . '.php', 'rb' ) ) === false ) {
$ret = OBFW_Exception();
unset( $this->rp_files[ $patch[0] ] );
return $ret;
}
}
$file = $this->rp_files[ $patch[0] ];
}
// Get the data
if ( @fseek( $file, strlen( $this->header ) + $patch[1] + $p_start, SEEK_SET ) != 0 ) return OBFW_Exception();
if ( ( $extract = @fread( $file, $to_copy ) ) === false ) return OBFW_Exception();
if ( ( $len = strlen( $extract ) ) != $to_copy ) return $this->Partial_Read( $len, $to_copy, 'patch', $patch[0] );
$buffer = substr_replace( $buffer, $extract, $to, $len );
}
}
return $ret;
}
/*public*/ function Delete( $all = false )
{
// First, remove patches we no longer need
foreach ( $this->p_index as $p_offset => $index ) {
foreach ( $index as $p_length => $patch ) {
// If patch still required, keep and leave (patches are kept in order)
if ( !$all && $p_offset + $p_length > $this->d_offset ) break 2;
// Remove patch, and patch file if neccessary
unset( $this->p_index[$p_offset][$p_length] );
$this->p_rindex[ $patch[0] ]--;
if ( $this->p_rindex[ $patch[0] ] == 0 ) {
// Ensure we don't have this file open
if ( array_key_exists( $patch[0], $this->rp_files ) ) {
@fclose( $this->rp_files[ $patch[0] ] );
unset( $this->rp_files[ $patch[0] ] );
}
$name = $this->name . '.patch';
if ( $patch[0] ) $name .= '.' . $patch[0];
@unlink( $this->prefix . '/' . $name . '.php' );
unset( $this->p_rindex[ $patch[0] ] );
}
}
}
// Find actual files that can be deleted
foreach ( $this->rotation as $id => $rotate ) {
// If file is past the deletion offset, we need it and all ones after it
if ( !$all && $rotate['offset'] + $rotate['size'] > $this->d_offset ) break;
// Ensure we don't have this file open
if ( array_key_exists( $id, $this->r_files ) ) {
@fclose( $this->r_files[ $id ] );
unset( $this->r_files[ $id ] );
}
$name = $this->name;
if ( $id ) $name .= '.' . $id;
@unlink( $this->prefix . '/' . $name . '.php' );
unset( $this->rotation[ $id ] );
}
// Delete completely and close if specified to do so
if ( $all ) {
// Ensure we don't have it open
if ( $this->file !== false ) {
@fclose( $this->file );
$this->file = false;
}
$name = $this->name;
if ( $this->rotate_id ) $name .= '.' . $this->rotate_id;
@unlink( $this->prefix . '/' . $name . '.php' );
}
return true;
}
/*public*/ function Start_Reconstruct()
{
// ASSERTION - Haven't already started flushing - we can only Flush OR Reconstruct
// No reconstruction neccessary, just return true and return the result in Do_Reconstruct()
if ( count( $this->rotation ) == 0 ) {
$this->status = 3;
return true;
}
// Open a file to reconstruct into
if ( ( $this->rc_file = @fopen( $this->prefix . '/' . $this->name . '.rc.php', 'w+b' ) ) === false )
return OBFW_Exception();
$this->status = 2;
$this->rc_offset = 0;
// Only write rejection header if we want one on the rc file
if ( $this->rc_header && true !== ( $ret = $this->Write_Rejection_Header( $this->rc_file ) ) ) {
@fclose( $this->rc_file );
return $ret;
}
return true;
}
/*public*/ function Do_Reconstruct()
{
if ( $this->status == 3 ) {
$name = $this->name;
if ( $this->rotate_id ) $name .= '.' . $this->rotate_id;
return array(
'file' => $this->prefix . '/' . $name . '.php',
'offset' => $this->rc_header ? strlen( $this->header ) : 0,
'size' => $this->offset + $this->size,
);
}
// Grab a block of data
$length = $this->WPOnlineBackup->Get_Setting( 'max_block_size' );
if ( !is_bool( $ret = $this->Flush( $data, $length ) ) ) {
@fclose( $this->rc_file );
return $ret;
}
$length = strlen( $data );
// Flush into the reconstruction file
if ( true !== ( $ret = $this->_Try_Write( $this->rc_file, $data, $length, 'write', 'rc' ) ) ) {
@fclose( $this->rc_file );
return $ret;
}
$this->rc_offset += $length;
if ( $ret === false ) {
// No more data
$this->status = 3;
return array(
'file' => $this->prefix . '/' . $this->name . '.rc.php',
'offset' => $this->rc_header ? strlen( $this->header ) : 0,
'size' => $this->offset + $this->size,
);
}
return true;
}
/*public*/ function End_Reconstruct()
{
if ( $this->rc_file !== false ) {
$this->Delete( true );
@fclose( $this->rc_file );
$this->rc_file = false;
}
return true;
}
/*public*/ function Branch( & $branch )
{
// Create a new disk with the same destination folder and rotation ID and return it
$branch = new WPOnlineBackup_Disk( $this->WPOnlineBackup, $this->rotate_id );
$branch->Initialise( $this->prefix );
return true;
}
/*private*/ function Write_Rejection_Header( $file, $type = '' )
{
// This simply turns the entire file into a PHP script
// If anyone tries to access it through HTTP, on 99.99% of WordPress servers PHP is PHP...
// it will parse the file as PHP instead of streaming it to the user
// PHP 5 - __halt_compiler() cleanly aborts the compiler in the first line of the file
// Users accessing the file see a blank page and no data
// PHP 4 - We get a little dirty and force a Syntax Error to prevent PHP scanning the entire file
// Users accessing the file see a Syntax Error or a blank page and no data
// If a server returns the actual code instead of parsing it as PHP - this would already be considered a security issue
$data = $this->header;
$length = strlen( $data );
if ( true !== ( $ret = $this->_Try_Write( $file, $data, $length, 'write header', $type ) ) )
return $ret;
return true;
}
/*private*/ function CleanUp_Rotated()
{
// Loop through rotated files
foreach ( $this->rotation as $id => $rotate ) {
$name = $this->name;
if ( $id ) $name .= '.' . $id;
// If size is 0, just delete the file
if ( $rotate['size'] == 0 ) {
@unlink( $this->prefix . '/' . $name . '.php' );
} else {
// Open the file - ignore error we can get access violations and all kinds of crap if it timed out
if ( ( $file = @fopen( $this->prefix . '/' . $name . '.php', 'r+b' ) ) === false ) continue;
// Attempt to truncate the file to the correct size
@ftruncate( $file, ( $rotate['has_header'] ? strlen( $this->header ) : 0 ) + $rotate['size'] );
// And close
@fclose($file);
}
}
}
}
?>