mirror of
https://github.com/uroni/urbackup_backend.git
synced 2025-10-26 11:36:50 +00:00
417 lines
13 KiB
C++
417 lines
13 KiB
C++
/*************************************************************************
|
|
* UrBackup - Client/Server backup system
|
|
* Copyright (C) 2011-2015 Martin Raiber
|
|
* MiniZ parts by Rich Geldreich
|
|
*
|
|
* This program is free software: you can redistribute it and/or modify
|
|
* it under the terms of the GNU Affero General Public License as published by
|
|
* the Free Software Foundation, either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU Affero General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Affero General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*
|
|
*
|
|
**************************************************************************/
|
|
|
|
#include "action_header.h"
|
|
#include "../../urbackupcommon/os_functions.h"
|
|
#include "../../Interface/File.h"
|
|
#include "backups.h"
|
|
|
|
#define MINIZ_NO_ZLIB_COMPATIBLE_NAMES
|
|
#include "../../common/miniz.c"
|
|
|
|
namespace
|
|
{
|
|
|
|
int my_stat(const wchar_t *pFilename, struct MZ_FILE_STAT_STRUCT* statbuf)
|
|
{
|
|
#if defined(_MSC_VER) || defined(__MINGW64__)
|
|
return _wstat(pFilename, statbuf);
|
|
#else
|
|
return MZ_FILE_STAT(Server->ConvertToUTF8(pFilename).c_str(), statbuf);
|
|
#endif
|
|
}
|
|
|
|
mz_bool my_mz_zip_get_file_modified_time(const wchar_t *pFilename, mz_uint16 *pDOS_time, mz_uint16 *pDOS_date)
|
|
{
|
|
#ifdef MINIZ_NO_TIME
|
|
(void)pFilename; *pDOS_date = *pDOS_time = 0;
|
|
#else
|
|
struct MZ_FILE_STAT_STRUCT file_stat;
|
|
// On Linux with x86 glibc, this call will fail on large files (>= 0x80000000 bytes) unless you compiled with _LARGEFILE64_SOURCE. Argh.
|
|
if (my_stat(pFilename, &file_stat) != 0)
|
|
return MZ_FALSE;
|
|
mz_zip_time_to_dos_time(file_stat.st_mtime, pDOS_time, pDOS_date);
|
|
#endif // #ifdef MINIZ_NO_TIME
|
|
return MZ_TRUE;
|
|
}
|
|
|
|
|
|
mz_bool my_mz_zip_writer_add_file(mz_zip_archive *pZip, const char *pArchive_name, const wchar_t *pSrc_filename, const void *pComment, mz_uint16 comment_size, mz_uint level_and_flags,
|
|
time_t* last_modified)
|
|
{
|
|
mz_uint16 gen_flags = 1<<3 | 1<<11;
|
|
mz_uint uncomp_crc32 = MZ_CRC32_INIT, level, num_alignment_padding_bytes;
|
|
mz_uint16 method = 0, dos_time = 0, dos_date = 0, ext_attributes = 0;
|
|
mz_uint64 local_dir_header_ofs = pZip->m_archive_size, cur_archive_file_ofs = pZip->m_archive_size, uncomp_size = 0, comp_size = 0;
|
|
size_t archive_name_size;
|
|
mz_uint8 local_dir_header[MZ_ZIP_LOCAL_DIR_HEADER_SIZE];
|
|
IFile *pSrc_file = NULL;
|
|
|
|
if ((int)level_and_flags < 0)
|
|
level_and_flags = MZ_DEFAULT_LEVEL;
|
|
level = level_and_flags & 0xF;
|
|
|
|
if ((!pZip) || (!pZip->m_pState) || (pZip->m_zip_mode != MZ_ZIP_MODE_WRITING) || (!pArchive_name) || ((comment_size) && (!pComment)) || (level > MZ_UBER_COMPRESSION))
|
|
return MZ_FALSE;
|
|
if (level_and_flags & MZ_ZIP_FLAG_COMPRESSED_DATA)
|
|
return MZ_FALSE;
|
|
if (!mz_zip_writer_validate_archive_name(pArchive_name))
|
|
return MZ_FALSE;
|
|
|
|
archive_name_size = strlen(pArchive_name);
|
|
if (archive_name_size > 0xFFFF)
|
|
return MZ_FALSE;
|
|
|
|
num_alignment_padding_bytes = mz_zip_writer_compute_padding_needed_for_file_alignment(pZip);
|
|
|
|
// no zip64 support yet
|
|
if ((pZip->m_total_files == 0xFFFF) || ((pZip->m_archive_size + num_alignment_padding_bytes + MZ_ZIP_LOCAL_DIR_HEADER_SIZE + MZ_ZIP_CENTRAL_DIR_HEADER_SIZE + comment_size + archive_name_size) > 0xFFFFFFFF))
|
|
return MZ_FALSE;
|
|
|
|
if(last_modified!=NULL)
|
|
{
|
|
mz_zip_time_to_dos_time(*last_modified, &dos_time, &dos_date);
|
|
}
|
|
else
|
|
{
|
|
if (!my_mz_zip_get_file_modified_time(pSrc_filename, &dos_time, &dos_date))
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
|
|
pSrc_file = Server->openFile(os_file_prefix(pSrc_filename));
|
|
if (!pSrc_file)
|
|
return MZ_FALSE;
|
|
|
|
uncomp_size = pSrc_file->Size();
|
|
|
|
if (uncomp_size > 0xFFFFFFFF)
|
|
{
|
|
// No zip64 support yet
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
if (!mz_zip_writer_write_zeros(pZip, cur_archive_file_ofs, num_alignment_padding_bytes))
|
|
{
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
local_dir_header_ofs += num_alignment_padding_bytes;
|
|
|
|
if(uncomp_size && level)
|
|
{
|
|
method=MZ_DEFLATED;
|
|
}
|
|
|
|
if (!mz_zip_writer_create_local_dir_header(pZip, local_dir_header, (mz_uint16)archive_name_size, 0, 0, 0, 0, method, gen_flags, dos_time, dos_date))
|
|
return MZ_FALSE;
|
|
|
|
if (pZip->m_pWrite(pZip->m_pIO_opaque, local_dir_header_ofs, local_dir_header, sizeof(local_dir_header)) != sizeof(local_dir_header))
|
|
return MZ_FALSE;
|
|
|
|
if (pZip->m_file_offset_alignment) { MZ_ASSERT((local_dir_header_ofs & (pZip->m_file_offset_alignment - 1)) == 0); }
|
|
cur_archive_file_ofs += num_alignment_padding_bytes + sizeof(local_dir_header);
|
|
|
|
MZ_CLEAR_OBJ(local_dir_header);
|
|
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pArchive_name, archive_name_size) != archive_name_size)
|
|
{
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
cur_archive_file_ofs += archive_name_size;
|
|
|
|
if (uncomp_size)
|
|
{
|
|
mz_uint64 uncomp_remaining = uncomp_size;
|
|
void *pRead_buf = pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, MZ_ZIP_MAX_IO_BUF_SIZE);
|
|
if (!pRead_buf)
|
|
{
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
if (!level)
|
|
{
|
|
while (uncomp_remaining)
|
|
{
|
|
mz_uint n = (mz_uint)MZ_MIN(MZ_ZIP_MAX_IO_BUF_SIZE, uncomp_remaining);
|
|
if ((pSrc_file->Read((char*)pRead_buf, n) != n) || (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, pRead_buf, n) != n))
|
|
{
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, n);
|
|
uncomp_remaining -= n;
|
|
cur_archive_file_ofs += n;
|
|
}
|
|
comp_size = uncomp_size;
|
|
}
|
|
else
|
|
{
|
|
mz_bool result = MZ_FALSE;
|
|
mz_zip_writer_add_state state;
|
|
tdefl_compressor *pComp = (tdefl_compressor *)pZip->m_pAlloc(pZip->m_pAlloc_opaque, 1, sizeof(tdefl_compressor));
|
|
if (!pComp)
|
|
{
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
state.m_pZip = pZip;
|
|
state.m_cur_archive_file_ofs = cur_archive_file_ofs;
|
|
state.m_comp_size = 0;
|
|
|
|
if (tdefl_init(pComp, mz_zip_writer_add_put_buf_callback, &state, tdefl_create_comp_flags_from_zip_params(level, -15, MZ_DEFAULT_STRATEGY)) != TDEFL_STATUS_OKAY)
|
|
{
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
for ( ; ; )
|
|
{
|
|
size_t in_buf_size = (mz_uint32)MZ_MIN(uncomp_remaining, MZ_ZIP_MAX_IO_BUF_SIZE);
|
|
tdefl_status status;
|
|
|
|
if (pSrc_file->Read((char*)pRead_buf, (_u32)in_buf_size) != in_buf_size)
|
|
break;
|
|
|
|
uncomp_crc32 = (mz_uint32)mz_crc32(uncomp_crc32, (const mz_uint8 *)pRead_buf, in_buf_size);
|
|
uncomp_remaining -= in_buf_size;
|
|
|
|
status = tdefl_compress_buffer(pComp, pRead_buf, in_buf_size, uncomp_remaining ? TDEFL_NO_FLUSH : TDEFL_FINISH);
|
|
if (status == TDEFL_STATUS_DONE)
|
|
{
|
|
result = MZ_TRUE;
|
|
break;
|
|
}
|
|
else if (status != TDEFL_STATUS_OKAY)
|
|
break;
|
|
}
|
|
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pComp);
|
|
|
|
if (!result)
|
|
{
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
|
|
Server->destroy(pSrc_file);
|
|
return MZ_FALSE;
|
|
}
|
|
|
|
comp_size = state.m_comp_size;
|
|
cur_archive_file_ofs = state.m_cur_archive_file_ofs;
|
|
|
|
method = MZ_DEFLATED;
|
|
}
|
|
|
|
pZip->m_pFree(pZip->m_pAlloc_opaque, pRead_buf);
|
|
}
|
|
|
|
Server->destroy(pSrc_file); pSrc_file = NULL;
|
|
|
|
// no zip64 support yet
|
|
if ((comp_size > 0xFFFFFFFF) || (cur_archive_file_ofs > 0xFFFFFFFF))
|
|
return MZ_FALSE;
|
|
|
|
mz_uint8 local_dir_footer[16];
|
|
|
|
MZ_WRITE_LE32(local_dir_footer + 0, 0x08074b50);
|
|
MZ_WRITE_LE32(local_dir_footer + 4, uncomp_crc32);
|
|
MZ_WRITE_LE32(local_dir_footer + 8, comp_size);
|
|
MZ_WRITE_LE32(local_dir_footer + 12, uncomp_size);
|
|
|
|
if (pZip->m_pWrite(pZip->m_pIO_opaque, cur_archive_file_ofs, local_dir_footer, sizeof(local_dir_footer)) != sizeof(local_dir_footer))
|
|
return MZ_FALSE;
|
|
|
|
cur_archive_file_ofs+=sizeof(local_dir_footer);
|
|
|
|
if (!mz_zip_writer_add_to_central_dir(pZip, pArchive_name, (mz_uint16)archive_name_size, NULL, 0, pComment, comment_size,
|
|
uncomp_size, comp_size, uncomp_crc32, method, gen_flags, dos_time, dos_date, local_dir_header_ofs, ext_attributes))
|
|
return MZ_FALSE;
|
|
|
|
pZip->m_total_files++;
|
|
pZip->m_archive_size = cur_archive_file_ofs;
|
|
|
|
return MZ_TRUE;
|
|
}
|
|
|
|
struct MiniZFileInfo
|
|
{
|
|
uint64 file_offset;
|
|
THREAD_ID tid;
|
|
};
|
|
|
|
size_t my_mz_write_func(void *pOpaque, mz_uint64 file_ofs, const void *pBuf, size_t n)
|
|
{
|
|
MiniZFileInfo* fileInfo = reinterpret_cast<MiniZFileInfo*>(pOpaque);
|
|
if(fileInfo->file_offset!=file_ofs)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
fileInfo->file_offset=file_ofs+n;
|
|
|
|
bool b=Server->WriteRaw(fileInfo->tid, reinterpret_cast<const char*>(pBuf), n, false);
|
|
return b?n:0;
|
|
}
|
|
|
|
bool miniz_init(mz_zip_archive *pZip, MiniZFileInfo* fileInfo)
|
|
{
|
|
pZip->m_pWrite = my_mz_write_func;
|
|
pZip->m_pIO_opaque = fileInfo;
|
|
|
|
if (!mz_zip_writer_init(pZip, 0))
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
bool add_dir(mz_zip_archive& zip_archive, const std::wstring& archivefoldername, const std::wstring& foldername, const std::wstring& hashfoldername, const std::wstring& filter,
|
|
bool token_authentication, const std::vector<backupaccess::SToken> &backup_tokens, const std::vector<std::string> &tokens, bool skip_special)
|
|
{
|
|
bool has_error=false;
|
|
const std::vector<SFile> files = getFiles(foldername, &has_error);
|
|
|
|
if(has_error)
|
|
return false;
|
|
|
|
for(size_t i=0;i<files.size();++i)
|
|
{
|
|
const SFile& file=files[i];
|
|
|
|
if(skip_special
|
|
&& (file.name==L".hashes" || file.name==L"user_views") )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
std::wstring archivename = archivefoldername + (archivefoldername.empty()?L"":L"/") + file.name;
|
|
std::wstring metadataname = hashfoldername + os_file_sep() + escape_metadata_fn(file.name);
|
|
std::wstring filename = foldername + os_file_sep() + file.name;
|
|
|
|
if(!filter.empty() && archivename!=filter)
|
|
continue;
|
|
|
|
if(file.isdir)
|
|
{
|
|
metadataname+=os_file_sep()+metadata_dir_fn;
|
|
}
|
|
|
|
bool has_metadata = false;
|
|
|
|
FileMetadata metadata;
|
|
if(token_authentication &&
|
|
( !read_metadata(metadataname, metadata) ||
|
|
!backupaccess::checkFileToken(backup_tokens, tokens, metadata) ) )
|
|
{
|
|
continue;
|
|
}
|
|
else if(!token_authentication)
|
|
{
|
|
has_metadata = read_metadata(metadataname, metadata);
|
|
}
|
|
else
|
|
{
|
|
has_metadata = true;
|
|
}
|
|
|
|
time_t* last_modified=NULL;
|
|
time_t last_modified_wt;
|
|
if(has_metadata)
|
|
{
|
|
#ifdef _WIN32
|
|
last_modified_wt=static_cast<time_t>(metadata.last_modified);
|
|
#else
|
|
last_modified_wt=static_cast<time_t>(metadata.last_modified);
|
|
#endif
|
|
last_modified=&last_modified_wt;
|
|
}
|
|
|
|
mz_bool rc;
|
|
if(file.isdir)
|
|
{
|
|
rc = mz_zip_writer_add_mem_ex(&zip_archive, Server->ConvertToUTF8(archivename + L"/").c_str(), NULL, 0, NULL, 0, MZ_DEFAULT_LEVEL,
|
|
0, 0, 1<<11, last_modified);
|
|
}
|
|
else
|
|
{
|
|
rc = my_mz_zip_writer_add_file(&zip_archive, Server->ConvertToUTF8(archivename).c_str(), filename.c_str(), NULL, 0, MZ_DEFAULT_LEVEL,
|
|
last_modified);
|
|
}
|
|
|
|
if(rc==MZ_FALSE)
|
|
{
|
|
Server->Log(L"Error while adding file \""+filename+L"\" to ZIP file. RC="+convert((int)rc), LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(file.isdir)
|
|
{
|
|
add_dir(zip_archive, archivename, filename, hashfoldername + os_file_sep() + escape_metadata_fn(file.name), filter,
|
|
token_authentication, backup_tokens, tokens, false);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
}
|
|
|
|
bool create_zip_to_output(const std::wstring& foldername, const std::wstring& hashfoldername, const std::wstring& filter, bool token_authentication,
|
|
const std::vector<backupaccess::SToken> &backup_tokens, const std::vector<std::string> &tokens, bool skip_hashes)
|
|
{
|
|
mz_zip_archive zip_archive;
|
|
memset(&zip_archive, 0, sizeof(zip_archive));
|
|
|
|
MiniZFileInfo file_info = {};
|
|
|
|
file_info.tid=Server->getThreadID();
|
|
|
|
if(!miniz_init(&zip_archive, &file_info))
|
|
{
|
|
Server->Log("Error while initializing ZIP archive", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(!add_dir(zip_archive, L"", foldername, hashfoldername, filter, token_authentication, backup_tokens, tokens, skip_hashes))
|
|
{
|
|
Server->Log("Error while adding files and folders to ZIP archive", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(!mz_zip_writer_finalize_archive(&zip_archive))
|
|
{
|
|
Server->Log("Error while finalizing ZIP archive", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
if(!mz_zip_writer_end(&zip_archive))
|
|
{
|
|
Server->Log("Error while ending ZIP archive writer", LL_ERROR);
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
} |