/* ZIP plugin for Tux Commander * version 0.8, designed for ZipArchive v4.6.9 * Copyright (C) 2004-2024 Tomas Bzatek * Check for updates on tuxcmd.sourceforge.net * * Uses ZipArchive library * Copyright (C) 2000 - 2022 Artpol Software - Tadeusz Dracz * http://www.artpol-software.com/ZipArchive/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include #include #include #include #include "tuxcmd-vfs.h" #include "strutils.h" #include "vfsutils.h" #include "logutils.h" #include "filelist.h" #include "filelist-vfs-intf.h" #include "tuxcmd-error.h" #include "ZipArchive.h" #include "ZipPlatform.h" #include "ZipCallback.h" #define VERSION "0.8" #define BUILD_DATE "2024-10-25" #define DEFAULT_BLOCK_SIZE 65536 using namespace std; extern "C" { /******************************************************************************************************/ /** Utilities */ /************** ****************/ static void zip_error_to_gerror (CZipException e, GError **error) { gint code; switch (e.m_iCause) { case CZipException::noError: // No error. code = G_IO_ERROR_UNKNOWN; break; case CZipException::genericError: // An unknown error. case CZipException::badCrc: // Crc is mismatched. case CZipException::internalError: // An internal error. case CZipException::badPassword: // An incorrect password set for the file being decrypted. case CZipException::dirWithSize: // The directory with a non-zero size found while testing. case CZipException::streamEnd: // Zlib library error. case CZipException::needDict: // Zlib library error. case CZipException::errNo: // Zlib library error. case CZipException::streamError: // Zlib library error. case CZipException::dataError: // Zlib library error. case CZipException::memError: // Zlib library or CZipMemFile error. case CZipException::bufError: // Zlib library error. code = G_IO_ERROR_FAILED; break; case CZipException::badZipFile: // Damaged or not a zip file. code = G_IO_ERROR_NOT_MOUNTABLE_FILE; break; case CZipException::aborted: // The disk change callback method returned false. case CZipException::abortedAction: // The action callback method returned false. case CZipException::abortedSafely: // The action callback method returned false, but the data is not corrupted. code = G_IO_ERROR_CANCELLED; break; case CZipException::nonRemovable: // The device selected for the spanned archive is not removable. case CZipException::tooManyVolumes: // The limit of the maximum volumes reached. case CZipException::tooManyFiles: // The limit of the maximum files in an archive reached. case CZipException::tooLongData: // The filename, the comment or the local or central extra field of the file added to the archive is too long. case CZipException::tooBigSize: // The file size is too large to be supported. code = G_IO_ERROR_NOT_SUPPORTED; break; case CZipException::notRemoved: // Error while removing a file case CZipException::notRenamed: // Error while renaming a file (under Windows call GetLastError() to find out more). code = G_IO_ERROR_NOT_EMPTY; break; case CZipException::cdirNotFound: // The central directory was not found in the archive (or you were trying to open not the last disk of a segmented archive). code = G_IO_ERROR_NOT_FOUND; break; // case CZipException::cdir64NotFound: return cVFS_ReadErr; // The Zip64 central directory signature was not found in the archive where expected. // case CZipException::noBBZInZip64: return cVFS_ReadErr; // The number of bytes before a zip archive must be zero in the Zip64 format. // case CZipException::badAesAuthCode: return cVFS_ReadErr; // Mismatched authentication code in WinZip AEC decrypted data. case CZipException::platfNotSupp: // Cannot create a file for the specified platform. case CZipException::noZip64: // The Zip64 format has not been enabled for the library, but is required to open the archive. case CZipException::noAES: // WinZip AES encryption has not been enabled for the library, but is required to decompress the archive. code = G_IO_ERROR_NOT_SUPPORTED; break; case CZipException::noCallback: // There is no spanned archive callback object set. case CZipException::outOfBounds: // The collection is empty and the bounds do not exist. case CZipException::versionError: // Zlib library error. code = G_IO_ERROR_INVALID_ARGUMENT; break; // case CZipException::mutexError: return cVFS_ReadErr; // Locking or unlocking resources access was unsuccessful. // case CZipException::bzSequenceError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzParamError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzMemError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzDataError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzDataErrorMagic: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzIoError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzUnexpectedEof: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzOutbuffFull: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzConfigError: return cVFS_ReadErr; // Bzlib library error. // case CZipException::bzInternalError: return cVFS_ReadErr; // Internal Bzlib library error. /* 1 - 199 reserved for errno (from STL) values - used only in non-MFC versions */ case 1 ... 199: code = g_io_error_from_errno (e.m_iCause); break; default: code = G_IO_ERROR_FAILED; break; } g_set_error_literal (error, G_IO_ERROR, code, (LPCTSTR)e.GetErrorDescription()); } /******************************************************************************************************/ /** Auxiliary classes */ /************** ****************/ struct ZIP_API CVFSZipActionCallback; struct TVFSGlobs { TVFSLogFunc log_func; char *curr_dir; char *archive_path; gboolean need_password; CZipArchive *zip; CVFSZipActionCallback *extract_callback; gboolean archive_opened; guint32 block_size; gboolean archive_modified; struct PathTree *files; struct VfsFilelistData *vfs_filelist; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; TVFSProgressCallback callback_progress; void *callback_data; int open_file_mode; gboolean after_exception; }; // Define the progress class and the class methods struct ZIP_API CVFSZipActionCallback : public CZipActionCallback { struct TVFSGlobs *globs; virtual bool Callback(ZIP_SIZE_TYPE uProgress) { bool ret = true; log_info ("extract_callback: position = %" G_GUINT32_FORMAT "; m_uTotalToProcess = %" G_GUINT32_FORMAT "; m_uProcessed = %" G_GUINT32_FORMAT, uProgress, m_uTotalToProcess, m_uProcessed); try { if (globs && globs->callback_progress) ret = globs->callback_progress (m_uProcessed, NULL, globs->callback_data); } catch (...) { log_error ("extract_callback: Fatal error occured when calling pCallBackProgress\n"); } return ret; } CVFSZipActionCallback() { m_uTotalToProcess = 0; m_uProcessed = 0; globs = NULL; } }; /*********************************************************************************************************************** * Internal tree functions ********/ static void build_global_filelist (struct TVFSGlobs *globs) { unsigned long int iCount; unsigned long int i; CZipFileHeader *fh; char *s; /* Ensure the filelist is freed */ if (globs->vfs_filelist) vfs_filelist_free (globs->vfs_filelist); if (globs->files) filelist_tree_free (globs->files); globs->files = filelist_tree_new (); globs->vfs_filelist = vfs_filelist_new (globs->files); iCount = globs->zip->GetCount (); /* list files in the archive */ for (i = 0; i < iCount; i++) { fh = globs->zip->GetFileInfo (i); if (fh != NULL && _log_level >= LOG_INFO) g_print (" [%lu] '%s', IsDir: %i, Size: %" G_GUINT32_FORMAT ", SystemAttr = 0x%" G_GINT32_MODIFIER "X, OriginalAttr = 0x%" G_GINT32_MODIFIER "X, encrypted = %d\n", i, (LPCTSTR)fh->GetFileName(), fh->IsDirectory(), fh->m_uUncomprSize, fh->GetSystemAttr(), fh->GetOriginalAttributes(), fh->IsEncrypted()); } for (i = 0; i < iCount; i++) { fh = globs->zip->GetFileInfo (i); if (fh != NULL) { struct TVFSItem *item; /* Create a TVFSItem entry and fill all info */ item = (struct TVFSItem *) g_malloc0 (sizeof (struct TVFSItem)); item->iSize = (guint64) fh->m_uUncomprSize; item->iPackedSize = (guint64) fh->m_uComprSize; item->inode_no = (guint64) (i + 1); if (fh->IsDirectory ()) item->ItemType = vDirectory; else item->ItemType = vRegular; item->iMode = fh->GetSystemAttr (); item->iUID = geteuid (); item->iGID = getegid (); item->m_time = (__time_t) fh->GetModificationTime (); item->c_time = (__time_t) fh->GetCreationTime (); item->a_time = (__time_t) fh->GetLastAccessTime (); if (fh->IsEncrypted ()) globs->need_password = TRUE; /* Add item to the global list and continue with next file */ s = g_filename_display_name ((LPCTSTR) fh->GetFileName ()); filelist_tree_add_item (globs->files, s, item, (LPCTSTR) fh->GetFileName (), i + 1); g_free (s); } } if (globs->need_password) log_notice ("Password present."); filelist_tree_print (globs->files); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Basic initialization functions struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; log_init (); globs = (struct TVFSGlobs *) g_malloc0 (sizeof (struct TVFSGlobs)); globs->block_size = DEFAULT_BLOCK_SIZE; globs->log_func = log_func; globs->open_file_mode = -1; return globs; } void VFSSetCallbacks (struct TVFSGlobs *globs, TVFSAskQuestionCallback ask_question_callback, TVFSAskPasswordCallback ask_password_callback, TVFSProgressCallback progress_func, void *data) { globs->callback_ask_question = ask_question_callback; globs->callback_ask_password = ask_password_callback; globs->callback_progress = progress_func; globs->callback_data = data; } void VFSFree (struct TVFSGlobs *globs) { g_free (globs->curr_dir); g_free (globs->archive_path); vfs_filelist_free (globs->vfs_filelist); filelist_tree_free (globs->files); g_free (globs); } int VFSVersion () { return cVFSVersion; } struct TVFSInfo * VFSGetInfo () { struct TVFSInfo *module_info; module_info = (TVFSInfo*) g_malloc0 (sizeof (struct TVFSInfo)); module_info->ID = g_strdup ("zip_plugin"); module_info->Name = g_strdup ("ZIP plugin"); module_info->About = g_strdup_printf ("version %s, build date: %s\nusing ZipArchive library v%s\n", VERSION, BUILD_DATE, CZipArchive::m_gszVersion); module_info->Copyright = g_strdup_printf ("Plugin Copyright (C) 2004-2024 Tomáš Bžatek\n%s", CZipArchive::m_gszCopyright); return module_info; } guint32 VFSGetCapabilities () { return VFS_CAP_CAN_CREATE_ARCHIVES; } char * VFSGetArchiveExts () { return g_strdup ("zip"); } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ gboolean VFSOpenArchive (struct TVFSGlobs *globs, const char *sName, GError **error) { int iCount; log_notice ("VFSOpenArchive: opening archive '%s':", sName); globs->zip = new CZipArchive; try { try { if (! globs->zip->Open (sName, CZipArchive::zipOpen, 0)) { log_error ("VFSOpenArchive: error opening zip archive"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error opening zip archive."); return FALSE; } } catch (...) { log_notice ("VFSOpenArchive: error opening readwrite zip, trying readonly..."); try { /* try to open in read only mode (required if there's no write access on the media) */ if (! globs->zip->Open (sName, CZipArchive::zipOpenReadOnly, 0)) { log_error ("VFSOpenArchive: error opening readonly zip archive"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error opening readonly zip archive."); return FALSE; } } catch (CZipException& e) { log_error ("VFSOpenArchive: error opening readonly zip archive"); zip_error_to_gerror (e, error); return FALSE; } } iCount = globs->zip->GetCount (false); log_info ("VFSOpenArchive: %i records found, %i files.", iCount, globs->zip->GetCount (true)); if (iCount < 1) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "No files found in the archive."); return FALSE; } /* build global file list */ build_global_filelist (globs); /* set progress callback */ globs->extract_callback = new CVFSZipActionCallback; globs->extract_callback->globs = globs; globs->zip->SetCallback (globs->extract_callback, CZipActionCallback::cbExtract); globs->zip->SetCallback (globs->extract_callback, CZipActionCallback::cbAdd); /* set automatic flushing of changes to disk */ globs->zip->SetAutoFinalize (true); } catch (CZipException& e) { log_error ("VFSOpenArchive: Error while processing archive %s: %s", sName, (LPCTSTR)e.GetErrorDescription()); if (! e.m_szFileName.IsEmpty()) log_notice ("VFSOpenArchive: Filename in error object: %s", (LPCTSTR)e.m_szFileName); globs->zip->Close (true); zip_error_to_gerror (e, error); return FALSE; } catch (...) { log_error ("VFSOpenArchive: Unknown error while processing archive %s", sName); globs->zip->Close (true); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Unknown error while processing zip archive."); return FALSE; } log_info ("VFSOpenArchive: done."); globs->archive_path = g_strdup (sName); globs->archive_modified = FALSE; return TRUE; } gboolean VFSClose (struct TVFSGlobs *globs, GError **error) { if (globs) { /* flush and close the archive... */ log_notice ("VFSClose: Closing the archive..."); try { if (globs->archive_modified) globs->zip->FlushBuffers (); /* In case of inconsistency, try using afWriteDir value. * (use when an exception was thrown, the Close method writes the * central directory structure to the archive, so that the archive should be usable.) */ globs->zip->Close (CZipArchive::afNoException, false); } catch (CZipException& e) { log_error ("VFSClose: Error while closing archive: %s", (LPCTSTR)e.GetErrorDescription()); zip_error_to_gerror (e, error); return FALSE; } /* free the ZIP objects... */ log_debug ("VFSClose: Freeing ZipArchive objects..."); try { delete globs->extract_callback; delete globs->zip; } catch (...) { log_error ("VFSClose: Error freeing ZipArchive objects"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error freeing ZipArchive objects."); return FALSE; } /* free the filelist */ log_debug ("VFSClose: Freeing filelist..."); vfs_filelist_free (globs->vfs_filelist); globs->vfs_filelist = NULL; filelist_tree_free (globs->files); globs->files = NULL; /* free the rest... */ g_free (globs->archive_path); globs->archive_path = NULL; g_free (globs->curr_dir); globs->curr_dir = NULL; } return TRUE; } char * VFSGetPath (struct TVFSGlobs *globs) { return g_strdup (globs->curr_dir); } void VFSGetFileSystemInfo (struct TVFSGlobs *globs, const char *APath, gint64 *FSSize, gint64 *FSFree, char **FSLabel) { if (FSSize) *FSSize = globs->zip->GetOccupiedSpace (); if (FSFree) *FSFree = 0; if (FSLabel) *FSLabel = NULL; } /******************************************************************************************************/ gboolean VFSChangeDir (struct TVFSGlobs *globs, const char *NewPath, GError **error) { char *s; s = vfs_filelist_change_dir (globs->vfs_filelist, NewPath, error); if (s) { globs->curr_dir = s; return TRUE; } else return FALSE; } gboolean VFSGetPasswordRequired (struct TVFSGlobs *globs) { if (globs) return globs->need_password; return FALSE; } void VFSResetPassword (struct TVFSGlobs *globs) { if (globs) globs->zip->SetPassword (NULL); } /******************************************************************************************************/ struct TVFSItem * VFSListFirst (struct TVFSGlobs *globs, const char *sDir, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { log_debug ("VFSListFirst: Going to list all items in '%s'", sDir); return vfs_filelist_list_first (globs->vfs_filelist, sDir, FollowSymlinks, AddFullPath, error); } struct TVFSItem * VFSListNext (struct TVFSGlobs *globs, GError **error) { return vfs_filelist_list_next (globs->vfs_filelist, error); } gboolean VFSListClose (struct TVFSGlobs *globs, GError **error) { return vfs_filelist_list_close (globs->vfs_filelist, error); } /******************************************************************************************************/ struct TVFSItem * VFSFileInfo (struct TVFSGlobs *globs, const char *AFileName, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { log_debug ("VFSFileInfo: requested info for object '%s'", AFileName); return vfs_filelist_file_info (globs->vfs_filelist, AFileName, FollowSymlinks, AddFullPath, error); } /******************************************************************************************************/ /** Recursive tree size counting */ /************** ****************/ guint64 VFSGetDirSize (struct TVFSGlobs *globs, const char *APath) { if (! globs) return 0; return vfs_filelist_get_dir_size (globs->vfs_filelist, APath); } void VFSBreakGetDirSize (struct TVFSGlobs *globs) { log_debug ("VFSBreakGetDirSize: calling break"); if (globs) vfs_filelist_get_dir_size_break (globs->vfs_filelist); } /******************************************************************************************************/ /** Methods modifying the archive */ /************** ****************/ gboolean VFSMkDir (struct TVFSGlobs *globs, const char *sDirName, GError **error) { CZipFileHeader header; char *s; bool bRet; if (sDirName == NULL || strlen (sDirName) < 1) { log_error ("VFSMkDir: The value of 'sDirName' is NULL or empty"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "The value of 'sDirName' is NULL or empty."); return FALSE; } if (strcmp (sDirName, "/") == 0) { log_error ("VFSMkDir: Invalid value '%s' (duplicate root entry?)", sDirName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Invalid value '%s' (duplicate root entry)", sDirName); return FALSE; } log_notice ("VFSMkDir: Creating new directory '%s'", sDirName); try { try { header.SetSystemAttr (ZipPlatform::GetDefaultDirAttributes()); s = exclude_leading_path_sep (sDirName); header.SetFileName (s); g_free (s); header.SetCreationTime (time (NULL)); header.SetModificationTime (time (NULL)); bRet = globs->zip->OpenNewFile (header, CZipCompressor::levelStore, NULL); globs->zip->CloseNewFile (); if (! bRet) { log_error ("VFSMkDir: Error creating new directory '%s'", sDirName); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error creating new directory."); return FALSE; } globs->archive_modified = TRUE; build_global_filelist (globs); return TRUE; } catch (CZipException& e) { globs->zip->CloseNewFile (true); log_error ("VFSMkDir: Error creating new directory '%s': [%d] %s, archive closed = %d", sDirName, e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed ()); zip_error_to_gerror (e, error); return FALSE; } } catch (...) { log_error ("VFSMkDir: Error creating new directory '%s'", sDirName); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error creating new directory."); return FALSE; } } gboolean VFSRemove (struct TVFSGlobs *globs, const char *APath, GError **error) { char *AFile, *AFile1, *AFile2, *AFile3; long int file_no; log_notice ("VFSRemove: Removing file '%s'...", APath); AFile = exclude_trailing_path_sep (APath); file_no = filelist_find_original_index_by_path (globs->files, AFile) - 1; g_free (AFile); if (file_no < 0) { log_error ("VFSRemove: can't find the file specified: '%s'", APath); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Can't find the file specified."); return FALSE; } try { try { if (! globs->zip->RemoveFile (file_no)) { log_error ("VFSRemove: Delete file '%s' failed.", APath); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Delete file '%s' failed.", APath); return FALSE; } build_global_filelist (globs); globs->archive_modified = TRUE; log_debug ("VFSRemove OK."); /* If we delete last file from a directory, we should make an empty one. * Some archives store pathnames only with filenames, no separate records for directories. **/ AFile1 = exclude_trailing_path_sep (APath); AFile2 = g_path_get_dirname (AFile1); AFile3 = exclude_trailing_path_sep (AFile2); if (strlen (AFile3) > 0 && g_strcmp0 (AFile3, "/") != 0) { log_debug ("VFSRemove: AFile1: '%s', AFile2: '%s', AFile3: '%s'", AFile1, AFile2, AFile3); file_no = filelist_find_original_index_by_path (globs->files, AFile2) - 1; log_debug ("VFSRemove: deleted: '%s', parent: '%s', file_no = %ld", APath, AFile3, file_no); if (file_no < 0) { log_notice ("VFSRemove: sparse ZIP archive detected, adding an empty directory: '%s'", AFile3); VFSMkDir (globs, AFile3, NULL); } } g_free (AFile1); g_free (AFile2); g_free (AFile3); return TRUE; } catch (CZipException& e) { log_error ("VFSRemove: Delete file '%s' failed: [%d] %s, archive closed = %d", APath, e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed ()); zip_error_to_gerror (e, error); return FALSE; } } catch (...) { log_error ("VFSRemove: Delete file '%s' failed.", APath); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Delete file '%s' failed.", APath); return FALSE; } } gboolean VFSRename (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, GError **error) { char *AFile; char *ADestFile; char *s; long int file_no; CZipFileHeader *fh; log_notice ("VFSRename: Renaming/moving file '%s' to '%s'...", sSrcName, sDstName); AFile = exclude_trailing_path_sep (sSrcName); s = exclude_trailing_path_sep (sDstName); ADestFile = exclude_leading_path_sep (s); g_free (s); file_no = filelist_find_original_index_by_path (globs->files, AFile) - 1; g_free (AFile); if (file_no < 0) { log_error ("VFSRename: can't find the file specified: '%s'", sSrcName); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Can't find the file specified."); return FALSE; } try { try { fh = globs->zip->GetFileInfo (file_no); if (fh == NULL) { log_error ("VFSRename: can't find the file specified: '%s'", sSrcName); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Can't find the file specified."); return FALSE; } if (! fh->SetFileName (ADestFile)) { log_error ("VFSRename: Rename/move file '%s' failed.", sSrcName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Rename/move file '%s' failed.", sSrcName); return FALSE; } g_free (ADestFile); build_global_filelist (globs); globs->archive_modified = TRUE; return TRUE; } catch (CZipException& e) { log_error ("VFSRename: Rename/move file '%s' failed: [%d] %s, archive closed = %d.", sSrcName, e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed ()); zip_error_to_gerror (e, error); return FALSE; } } catch (...) { log_error ("VFSRename: Rename/move file failed."); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Rename/move file failed."); return FALSE; } } gboolean VFSMakeSymLink (struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo, GError **error) { log_error ("VFSMakeSymLink: Symbolic links not supported in ZIP archives."); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Symbolic links not supported in ZIP archives."); return FALSE; } gboolean VFSChmod (struct TVFSGlobs *globs, const char *FileName, guint32 Mode, GError **error) { char *AFile; long int file_no; CZipFileHeader *header; log_notice ("VFSChmod: Changing permissions of '%s' to %u...", FileName, Mode); AFile = exclude_trailing_path_sep (FileName); file_no = filelist_find_original_index_by_path (globs->files, AFile) - 1; g_free (AFile); if (file_no < 0) { log_error ("VFSChmod: can't find the file specified: '%s'", FileName); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Can't find the file specified."); return FALSE; } try { try { /* set system compatibility first */ if (! globs->zip->SetSystemCompatibility (ZipCompatibility::zcUnix)) { log_warn ("VFSChmod: Unable to set system compatibility of the ZIP archive to Unix"); } /* change the header data */ globs->zip->ReadLocalHeader (file_no); header = globs->zip->GetFileInfo (file_no); if (! header) { log_error ("VFSChmod: Permissions modification of the file '%s' failed: NULL returned by GetFileInfo()", FileName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Permissions modification of the file '%s' failed: NULL returned by GetFileInfo()", FileName); return FALSE; } /* Need to change only 0xF000FFFF mask. * The 0xF_______ bits represents file/directory type, the 0x____FFFF represents ZIP attributes and 0x_FFF____ represents unix permissions. */ log_debug ("VFSChmod: Current permissions: 0x%" G_GINT32_MODIFIER "X, stripped: 0x%" G_GINT32_MODIFIER "X, setting to: 0x%X, modified: 0x%" G_GINT32_MODIFIER "X", header->GetSystemAttr(), header->GetSystemAttr() & 0xFFFFF000, Mode & 0xFFF, (header->GetSystemAttr() & 0xFFFFF000) + (Mode & 0xFFF)); header->SetSystemAttr ((header->GetSystemAttr() & 0xFFFFF000) + (Mode & 0xFFF)); // globs->zip->SetFileHeaderAttr (*header, (header->GetSystemAttr() & 0xFFFFF000) + (Mode & 0xFFF)); /* write local header information */ globs->zip->OverwriteLocalHeader (file_no); #if 0 // Re-encrypt the file if (header->IsEncrypted()) { printf("(II) VFSChmod: Re-encrypting the file...\n"); if (! globs->zip->EncryptFile(file_no)) printf("(EE) VFSChmod: Unable to encrypt the file\n"); } #endif globs->zip->RemoveCentralDirectoryFromArchive (); globs->zip->FlushBuffers (); log_debug ("VFSChmod OK."); build_global_filelist (globs); globs->archive_modified = TRUE; return TRUE; } catch (CZipException& e) { globs->zip->CloseNewFile (true); log_error ("VFSChmod: permissions modification of the file '%s' failed: [%d] %s, archive closed = %d.", FileName, e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); zip_error_to_gerror (e, error); return FALSE; } } catch (...) { log_error ("VFSChmod: permissions modification of the file '%s' failed.", FileName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Permissions modification of the file '%s' failed.", FileName); return FALSE; } } gboolean VFSChown (struct TVFSGlobs *globs, const char *FileName, guint32 UID, guint32 GID, GError **error) { log_error ("VFSChown: Owner changing is not supported in ZIP archives."); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Owner changing is not supported in ZIP archives."); return FALSE; } gboolean VFSChangeTimes (struct TVFSGlobs *globs, const char *APath, guint32 mtime, guint32 atime, GError **error) { char *AFile; long int file_no; CZipFileHeader *header; log_notice ("VFSChangeTimes: Changing date/times of the file '%s'...", APath); AFile = exclude_trailing_path_sep (APath); file_no = filelist_find_original_index_by_path (globs->files, AFile) - 1; g_free (AFile); if (file_no < 0) { log_error ("VFSChangeTimes: can't find the file specified: '%s'", APath); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "Can't find the file specified."); return FALSE; } try { try { /* read the local header information */ globs->zip->ReadLocalHeader (file_no); header = globs->zip->GetFileInfo (file_no); if (! header) { log_error ("VFSChangeTimes: DateTime modification of the file '%s' failed: NULL returned by GetFileInfo()", APath); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "DateTime modification of the file '%s' failed: NULL returned by GetFileInfo()", APath); return FALSE; } /* change the header data */ header->SetModificationTime (mtime); header->SetLastAccessTime (atime); /* // Re-encrypt the file if (header->IsEncrypted()) { printf("(II) VFSChangeTimes: Re-encrypting the file...\n"); if (! globs->zip->EncryptFile(file_no)) printf("(EE) VFSChangeTimes: Unable to encrypt the file\n"); } */ /* write local header information */ globs->zip->OverwriteLocalHeader (file_no); globs->zip->RemoveCentralDirectoryFromArchive (); log_debug ("VFSChangeTimes OK."); build_global_filelist (globs); globs->archive_modified = TRUE; return TRUE; } catch (CZipException& e) { globs->zip->CloseNewFile (true); log_error ("VFSChangeTimes: DateTime modification of the file '%s' failed: [%d] %s, archive closed = %d.", APath, e.m_iCause, (LPCTSTR)e.GetErrorDescription (), globs->zip->IsClosed ()); zip_error_to_gerror (e, error); return FALSE; } } catch (...) { log_error ("VFSChangeTimes: DateTime modification of the file '%s' failed.", APath); g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, "DateTime modification of the file '%s' failed.", APath); return FALSE; } } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// void VFSSetBlockSize (struct TVFSGlobs *globs, guint32 Value) { if (globs) globs->block_size = Value; } gboolean VFSIsOnSameFS (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { log_debug ("VFSIsOnSameFS: Not supported in ZIP archives."); return TRUE; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { log_debug ("VFSTwoSameFiles: Not supported in ZIP archives, comparing by paths."); return compare_two_same_files (Path1, Path2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// gboolean VFSStartCopyOperation (struct TVFSGlobs *globs, GError **error) { g_return_val_if_fail (globs != NULL, FALSE); log_debug ("VFSStartCopyOperation: doing nothing for the moment."); return TRUE; } gboolean VFSStopCopyOperation (struct TVFSGlobs *globs, GError **error) { g_return_val_if_fail (globs != NULL, FALSE); if (globs->archive_modified) { log_debug ("VFSStopCopyOperation: archive modified, rebuilding the tree."); globs->zip->FlushBuffers (); build_global_filelist (globs); } else { log_debug ("VFSStartCopyOperation: doing nothing for the moment."); } return TRUE; } /* returns TRUE if password has been set and to try again */ static gboolean handle_password_prompt (struct TVFSGlobs *globs, const gchar *log_prefix, GError **error) { gchar *passwd = NULL; gboolean res; if (globs->callback_ask_password) { res = globs->callback_ask_password ("The archive is encrypted and requires a password", NULL, NULL, NULL, (TVFSAskPasswordFlags)(VFS_ASK_PASSWORD_NEED_PASSWORD | VFS_ASK_PASSWORD_ARCHIVE_MODE), NULL, &passwd, NULL, NULL, NULL, globs->callback_data); if (res && passwd) { log_debug ("%s: setting password to '%s'", log_prefix, passwd); globs->zip->SetPassword (passwd); g_free (passwd); return TRUE; } else { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CANCELLED, "Operation has been cancelled."); return FALSE; } } g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_SOURCE_READ, "An incorrect password set for the file being decrypted."); return FALSE; } /* Known issues: * - crashes when no space left on NFS mounts, probably unhandled exception in further ZipArchive code (repro: Gentoo, Ubuntu) * **/ gboolean VFSCopyToLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { gboolean try_again; long int file_no; char *s; char *dest_path; char *dest_filename; if (sSrcName == NULL || sDstName == NULL || strlen (sSrcName) < 1 || strlen (sDstName) < 1) { log_error ("VFSCopyToLocal: The value of 'sSrcName' or 'sDstName' is NULL or empty"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "The value of 'sSrcName' or 'sDstName' is NULL or empty."); return FALSE; } log_notice ("VFSCopyToLocal: Copying file '%s' out to '%s'", sSrcName, sDstName); file_no = filelist_find_original_index_by_path (globs->files, sSrcName) - 1; if (file_no < 0) { log_error ("VFSCopyToLocal: can't find source file '%s'", sSrcName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "cannot find file '%s'", sSrcName); return FALSE; } s = exclude_trailing_path_sep (sDstName); dest_path = g_path_get_dirname (s); dest_filename = g_path_get_basename (s); g_free (s); /* Perform the extract */ try { do { try { try_again = FALSE; if (! globs->zip->ExtractFile (file_no, dest_path, false, dest_filename, ZipPlatform::fomRegular, globs->block_size)) { globs->zip->CloseFile (NULL, true); log_error ("VFSCopyToLocal: Error while copying out, archive closed = %d.", globs->zip->IsClosed ()); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error while copying out."); return FALSE; } log_debug ("VFSCopyToLocal: copy OK, archive closed = %d.", globs->zip->IsClosed ()); } catch (CZipException& e) { log_error ("VFSCopyToLocal: Error while copying out: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); globs->zip->CloseFile (NULL, true); switch (e.m_iCause) { case CZipException::badPassword: if (! handle_password_prompt (globs, "VFSCopyToLocal", error)) return FALSE; try_again = TRUE; break; default: zip_error_to_gerror (e, error); return FALSE; } } } while (try_again); } catch (...) { log_error ("VFSCopyToLocal: Fatal error while copying out..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Fatal error while copying out."); globs->zip->CloseFile (NULL, true); return FALSE; } g_free (dest_path); g_free (dest_filename); return TRUE; } /* Known issues: * - archive corruption when no space left on device * - encrypted files are unreadable after copy in **/ gboolean VFSCopyFromLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { gboolean try_again; char *s; if (sSrcName == NULL || sDstName == NULL || strlen (sSrcName) < 1 || strlen (sDstName) < 1) { log_error ("VFSCopyFromLocal: The value of 'sSrcName' or 'sDstName' is NULL or empty"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "The value of 'sSrcName' or 'sDstName' is NULL or empty."); return FALSE; } log_notice ("VFSCopyFromLocal: Copying file '%s' in to '%s'", sSrcName, sDstName); try { do { try { try_again = FALSE; s = exclude_leading_path_sep (sDstName); if (! globs->zip->AddNewFile (sSrcName, s, CZipCompressor::levelDefault, CZipArchive::zipsmSafeSmart, globs->block_size)) { globs->zip->CloseNewFile (true); globs->zip->CloseFile (NULL, true); log_error ("VFSCopyFromLocal: Error while copying in, archive closed = %d.", globs->zip->IsClosed ()); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Error while copying in."); return FALSE; } globs->zip->CloseNewFile (); log_debug ("VFSCopyFromLocal: copy OK, archive closed = %d.", globs->zip->IsClosed ()); globs->archive_modified = TRUE; /* // Encrypt the file if archive contains any encrypted files if (globs->need_password) { unsigned long int file_no = filelist_find_index_by_path(globs->files, s) - 1; if (file_no < 0) { printf("(EE) VFSCopyFromLocal: unable to find index for newly written file '%s'\n", sSrcName); return cVFS_WriteErr; } printf("(II) VFSCopyFromLocal: Encrypting the newly written file...\n"); if (! globs->zip->EncryptFile(file_no)) printf("(EE) VFSCopyFromLocal: Unable to encrypt the newly written file\n"); } */ g_free (s); } catch (CZipException& e) { log_error ("VFSCopyFromLocal: Error while copying in: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); globs->zip->CloseNewFile (true); globs->zip->CloseFile (NULL, true); switch (e.m_iCause) { case CZipException::badPassword: if (! handle_password_prompt (globs, "VFSCopyFromLocal", error)) return FALSE; try_again = TRUE; break; default: zip_error_to_gerror (e, error); return FALSE; } } } while (try_again); } catch (...) { log_error ("VFSCopyFromLocal: Fatal error while copying in..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Fatal error while copying in."); globs->zip->CloseNewFile (true); globs->zip->CloseFile (NULL, true); return FALSE; } return TRUE; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// TVFSFileDes VFSOpenFile (struct TVFSGlobs *globs, const char *APath, int Mode, GError **error) { gboolean try_again; long int file_no; GError *local_error = NULL; switch (Mode) { case cVFS_OpenRead: case cVFS_OpenWrite: /* OK, break */ break; case cVFS_OpenAppend: g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Appending files in the archive is not supported"); return NULL; default: g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Invalid file open mode %d", Mode); return NULL; } globs->open_file_mode = Mode; log_notice ("VFSOpenFile: Opening file '%s'", APath); try { do { try { try_again = FALSE; globs->after_exception = FALSE; /* Open for read */ if (Mode == cVFS_OpenRead) { file_no = filelist_find_original_index_by_path (globs->files, APath) - 1; if (file_no < 0) { log_error ("VFSOpenFile: can't find file '%s'", APath); g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Cannot find file '%s'", APath); return NULL; } if (! globs->zip->OpenFile (file_no)) { log_error ("VFSOpenFile: Error opening file, archive closed = %d.", globs->zip->IsClosed ()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Error opening file."); return NULL; } } else /* Open for write */ if (Mode == cVFS_OpenWrite) { CZipFileHeader header; gchar *s; s = exclude_leading_path_sep (APath); header.SetFileName (s); header.SetSystemAttr (ZipPlatform::GetDefaultAttributes()); g_free (s); /* FIXME: Encryption? */ if (! globs->zip->OpenNewFile (header, CZipCompressor::levelDefault, NULL)) { log_error ("VFSOpenFile: Error opening new file, archive closed = %d.", globs->zip->IsClosed ()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Error opening new file."); return NULL; } globs->archive_modified = TRUE; file_no = 1; /* bogus, unused */ } else g_warn_if_reached (); } catch (CZipException& e) { log_error ("VFSOpenFile: Error opening file: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); globs->zip->CloseFile (NULL, true); switch (e.m_iCause) { case CZipException::badPassword: if (! handle_password_prompt (globs, "VFSOpenFile", &local_error)) { if (g_error_matches (local_error, TUXCMD_ERROR, TUXCMD_ERROR_CANCELLED)) g_propagate_error (error, local_error); else { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, local_error->message); g_error_free (local_error); } globs->after_exception = TRUE; return NULL; } try_again = TRUE; break; default: zip_error_to_gerror (e, &local_error); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, local_error->message); g_error_free (local_error); globs->after_exception = TRUE; return NULL; } } } while (try_again); } catch (...) { log_error ("VFSCopyToLocal: Fatal error while opening file..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Fatal error while opening file."); globs->zip->CloseFile (NULL, true); globs->after_exception = TRUE; return NULL; } return (TVFSFileDes)(file_no + 1); /* FIXME: what to use as a filedescriptor? */ } gboolean VFSCloseFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, GError **error) { GError *local_error = NULL; int r; gboolean b; try { try { if (globs->open_file_mode == cVFS_OpenWrite || globs->open_file_mode == cVFS_OpenAppend) { b = globs->zip->CloseNewFile (globs->after_exception); globs->open_file_mode = -1; globs->after_exception = FALSE; if (globs->archive_modified) { log_debug ("VFSCloseFile: archive modified, rebuilding the tree."); globs->zip->FlushBuffers (); build_global_filelist (globs); } if (! b) { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "Error closing new file."); return FALSE; } } else { r = globs->zip->CloseFile (NULL, globs->after_exception); globs->open_file_mode = -1; globs->after_exception = FALSE; if (r != 1) { /* Returns one of the following values: * 1 : The operation was successful. * 0 : There is no file opened. * -1 : Some bytes were left to uncompress - probably due to a bad password or a corrupted archive. * -2 : Setting extracted file date and attributes was not successful. */ log_error ("VFSCloseFile: Error closing file, archive closed = %d.", globs->zip->IsClosed ()); switch (r) { case 0: g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "There is no file opened."); break; case -1: g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "Some bytes were left to uncompress - probably due to a bad password or a corrupted archive."); break; case -2: g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "Setting extracted file date and attributes was not successful."); break; default: g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "Error closing file."); } return FALSE; } } } catch (CZipException& e) { log_error ("VFSCloseFile: Error closing file: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); zip_error_to_gerror (e, &local_error); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, local_error->message); g_error_free (local_error); return FALSE; } } catch (...) { log_error ("VFSCloseFile: Fatal error while closing file..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CLOSE_FILE, "Fatal error while closing file."); return FALSE; } return TRUE; } gint64 VFSReadFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, guint64 ABlockSize, GError **error) { gboolean try_again; GError *local_error = NULL; try { do { try { DWORD r; try_again = FALSE; /* no file handle, only a global archive state (no multithreading) */ r = globs->zip->ReadFile (Buffer, ABlockSize); /* returns 0 on EOF */ /* no error state available */ return r; } catch (CZipException& e) { log_error ("VFSReadFile: Error reading file: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); switch (e.m_iCause) { case CZipException::badPassword: if (! handle_password_prompt (globs, "VFSReadFile", &local_error)) { if (g_error_matches (local_error, TUXCMD_ERROR, TUXCMD_ERROR_CANCELLED)) g_propagate_error (error, local_error); else { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, local_error->message); g_error_free (local_error); } globs->zip->CloseFile (NULL, true); globs->after_exception = TRUE; return -1; } try_again = TRUE; break; default: zip_error_to_gerror (e, &local_error); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, local_error->message); g_error_free (local_error); globs->zip->CloseFile (NULL, true); globs->after_exception = TRUE; return -1; } } } while (try_again); } catch (...) { log_error ("VFSCopyToLocal: Fatal error while opening file..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_OPEN_FILE, "Fatal error while opening file."); globs->zip->CloseFile (NULL, true); globs->after_exception = TRUE; return -1; } g_warn_if_reached (); return -1; } gint64 VFSWriteFile (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, gpointer Buffer, guint64 BytesCount, GError **error) { gboolean try_again; GError *local_error = NULL; try { do { try { try_again = FALSE; if (! globs->zip->WriteNewFile (Buffer, BytesCount)) { log_error ("VFSWriteFile: Error writing new file data, archive closed = %d.", globs->zip->IsClosed ()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_WRITE_FILE, "Error writing file data."); return -1; } globs->archive_modified = TRUE; return BytesCount; /* can't seem to have control on how many bytes got actually written */ } catch (CZipException& e) { log_error ("VFSWriteFile: Error writing file: [%d] %s, archive closed = %d.", e.m_iCause, (LPCTSTR)e.GetErrorDescription(), globs->zip->IsClosed()); switch (e.m_iCause) { case CZipException::badPassword: if (! handle_password_prompt (globs, "VFSWriteFile", &local_error)) { if (g_error_matches (local_error, TUXCMD_ERROR, TUXCMD_ERROR_CANCELLED)) g_propagate_error (error, local_error); else { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_WRITE_FILE, local_error->message); g_error_free (local_error); } globs->zip->CloseNewFile (true); globs->after_exception = TRUE; return -1; } try_again = TRUE; break; default: zip_error_to_gerror (e, &local_error); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_WRITE_FILE, local_error->message); g_error_free (local_error); globs->zip->CloseNewFile (true); globs->after_exception = TRUE; return -1; } } } while (try_again); } catch (...) { log_error ("VFSWriteFile: Fatal error while writing new file data..., archive closed = %d.", globs->zip->IsClosed()); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_WRITE_FILE, "Fatal error while writing new file data."); globs->zip->CloseNewFile (true); globs->after_exception = TRUE; return -1; } g_warn_if_reached (); return -1; } guint64 VFSFileSeek (struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, guint64 AbsoluteOffset, GError **error) { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_SEEK, "Seeking in compressed data is not supported"); return 0; } /////////////////////////////// // end of extern "C" } /*** * Todo: * * - UTF-8, FName/FDisplayName and absolute/relative paths revision needed! * (check http://www.artpol-software.com/ZipArchive/KB/0610051525.aspx ) * - implement an "opened" flag - is it really needed? + add tests to all functions * - readonly checking IsReadOnly() + add tests to all functions modifying the archive * - after VFS API update implement archive testing, compression level setting, retrieving compression info for each file and archive, readonly flags, begin-action, end-action (no refresh, faster operations) ... * - moving files from one directory to another within the archive * - do something like start/stop operation for write, so that chmod/chown/utime calls can find newly packed file. * */