2024-02-28 15:45:30 +01:00
// Copyright (C) 2024, The Duplicati Team
// https://duplicati.com, hello@duplicati.com
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
2017-10-07 13:59:46 -07:00
using System ;
using System.IO ;
using Duplicati.Library.Utility ;
using System.Collections.Generic ;
namespace Duplicati.Library.Main.Operation
{
public class RestoreHandlerMetadataStorage : IDisposable
{
2018-03-12 14:07:11 +01:00
/// <summary>
/// The tag used for logging
/// </summary>
private static readonly string LOGTAG = Logging . Log . LogTagFromType < RestoreHandlerMetadataStorage >();
2017-10-07 13:59:46 -07:00
private TempFile m_temp ;
private FileStream m_stream ;
private long m_entries ;
private long m_filepos ;
public RestoreHandlerMetadataStorage ()
{
m_temp = new TempFile ();
m_stream = File . Open ( m_temp , FileMode . Truncate , FileAccess . ReadWrite , FileShare . None );
}
public void Add ( string path , Stream data )
{
var datalen = data . Length ;
if ( datalen > Int32 . MaxValue )
2017-10-08 09:05:40 -07:00
throw new ArgumentOutOfRangeException ( nameof ( datalen ), "Metadata is larger than int32" );
2017-10-07 13:59:46 -07:00
var pathbytes = System . Text . Encoding . UTF8 . GetBytes ( path );
var pathlen = BitConverter . GetBytes ( pathbytes . LongLength );
var entrylen = BitConverter . GetBytes ( datalen );
var totalsize = pathbytes . Length + pathlen . Length + entrylen . Length + datalen ;
m_stream . Position = m_filepos ;
m_stream . Write ( pathlen , 0 , pathlen . Length );
m_stream . Write ( pathbytes , 0 , pathbytes . Length );
m_stream . Write ( entrylen , 0 , entrylen . Length );
data . CopyTo ( m_stream );
if ( m_stream . Position != m_filepos + totalsize )
throw new Exception ( "Bad file write!" );
m_filepos += totalsize ;
m_entries ++;
}
2018-05-30 17:42:59 -07:00
private void CheckedRead ( byte [] buffer , int bytesToRead )
2017-10-07 13:59:46 -07:00
{
2018-05-30 17:42:59 -07:00
if ( Duplicati . Library . Utility . Utility . ForceStreamRead ( m_stream , buffer , bytesToRead ) != bytesToRead )
2017-10-07 13:59:46 -07:00
{
throw new Exception ( "Bad file read" );
2018-05-30 17:42:59 -07:00
}
2017-10-07 13:59:46 -07:00
}
public IEnumerable < KeyValuePair < string , Stream >> Records
{
get
{
long pos = 0 ;
var bf = BitConverter . GetBytes ( 0L );
var buf = new byte [ 8 * 1024 ];
2018-03-12 14:07:11 +01:00
Logging . Log . WriteProfilingMessage ( LOGTAG , "MetadataStorageSize" , "The metadata storage file has {0} entries and takes up {1}" , m_entries , Library . Utility . Utility . FormatSizeString ( m_stream . Length ));
2017-10-07 13:59:46 -07:00
2018-03-12 14:07:11 +01:00
using ( new Logging . Timer ( LOGTAG , "ReadMetadata" , "Read metadata from file" ))
2017-10-07 13:59:46 -07:00
for ( var e = 0L ; e < m_entries ; e ++)
{
m_stream . Position = pos ;
2018-05-30 17:42:59 -07:00
CheckedRead ( bf , bf . Length );
2017-10-07 13:59:46 -07:00
var stringlen = BitConverter . ToInt64 ( bf , 0 );
var strbuf = stringlen > buf . Length ? new byte [ stringlen ] : buf ;
2018-05-30 17:42:59 -07:00
CheckedRead ( strbuf , ( int ) stringlen );
2017-10-07 13:59:46 -07:00
var path = System . Text . Encoding . UTF8 . GetString ( strbuf , 0 , ( int ) stringlen );
2018-05-30 17:42:59 -07:00
CheckedRead ( bf , bf . Length );
2017-10-07 13:59:46 -07:00
var datalen = BitConverter . ToInt64 ( bf , 0 );
if ( datalen > Int32 . MaxValue )
2017-10-08 09:05:40 -07:00
throw new ArgumentOutOfRangeException ( nameof ( datalen ), "Metadata is larger than int32" );
2017-10-07 13:59:46 -07:00
var databuf = datalen > buf . Length ? new byte [ datalen ] : buf ;
2018-05-30 17:42:59 -07:00
CheckedRead ( databuf , ( int ) datalen );
2017-10-07 13:59:46 -07:00
pos += datalen + stringlen + bf . Length + bf . Length ;
yield return new KeyValuePair < string , Stream >( path , new MemoryStream ( databuf , 0 , ( int ) datalen ));
}
}
}
#region IDisposable implementation
public void Dispose ()
{
if ( m_stream != null )
try { m_stream . Dispose (); }
2018-03-12 14:07:11 +01:00
catch ( Exception ex ) { Logging . Log . WriteWarningMessage ( LOGTAG , "DisposeError" , ex , "Failed to dispose stream" ); }
2017-10-07 13:59:46 -07:00
finally { m_stream = null ; }
if ( m_temp != null )
try { m_temp . Dispose (); }
2018-03-12 14:07:11 +01:00
catch ( Exception ex ) { Logging . Log . WriteWarningMessage ( LOGTAG , "DisposeError" , ex , "Failed to temp file stream" ); }
2017-10-07 13:59:46 -07:00
finally { m_temp = null ; }
}
#endregion
}
}