/* libarchive plugin for Tux Commander * version 0.3.1, designed for libarchive v3.6.1 * Copyright (C) 2008-2024 Tomas Bzatek * Check for updates on tuxcmd.sourceforge.net * * Uses libarchive library * Copyright (c) 2003-2010 Tim Kientzle * https://libarchive.org/ * * * 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 "tuxcmd-vfs.h" #include "vfsutils.h" #include "strutils.h" #include "logutils.h" #include "filelist.h" #include "filelist-vfs-intf.h" #include "tuxcmd-error.h" #include #include #if ARCHIVE_VERSION_NUMBER < 3000002 #error "libarchive 3.0.2 or greater is required to compile this module." #endif #define MODULE_VERSION "0.3.2" #define MODULE_BUILD_DATE "2024-10-23" #define DEFAULT_BLOCK_SIZE 65536 /******************************************************************************************************/ /** Auxiliary classes */ /************** ****************/ /* declaration of the global plugin object */ struct TVFSGlobs { TVFSLogFunc log_func; char *curr_dir; char *archive_path; guint32 block_size; struct PathTree *files; struct VfsFilelistData *vfs_filelist; guint64 total_size; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; TVFSProgressCallback callback_progress; void *callback_data; /* copy operation private */ struct archive *op_archive; }; /******************************************************************************************************/ /** Basic initialization functions */ /************** ****************/ struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; log_init (); globs = g_malloc0 (sizeof (struct TVFSGlobs)); globs->block_size = DEFAULT_BLOCK_SIZE; globs->log_func = log_func; return globs; } void VFSFree (struct TVFSGlobs *globs) { vfs_filelist_free (globs->vfs_filelist); filelist_tree_free (globs->files); g_free (globs->curr_dir); g_free (globs->archive_path); g_free (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; } int VFSVersion() { return cVFSVersion; } struct TVFSInfo * VFSGetInfo() { struct TVFSInfo *module_info = g_malloc0 (sizeof (struct TVFSInfo)); #ifdef MODULE_SHARED const char *shared_module = ", dynamically linked"; #else const char *shared_module = ", statically linked"; #endif module_info->ID = g_strdup ("libarchive_plugin"); module_info->Name = g_strdup ("libarchive plugin"); module_info->About = g_strdup_printf ("version %s, build date: %s\nusing %s%s\n", MODULE_VERSION, MODULE_BUILD_DATE, archive_version_string (), shared_module); module_info->Copyright = g_strdup ("Plugin Copyright (C) 2008-2024 Tomáš Bžatek\nlibarchive sources Copyright (c) 2003-2010 Tim Kientzle"); return module_info; } guint32 VFSGetCapabilities () { return VFS_CAP_CAN_CREATE_ARCHIVES | VFS_CAP_ARCHIVE_STREAMING; } char * VFSGetArchiveExts () { /* Make sure these end with a semicolon */ #define BASIC_FORMATS "tar;cpio;iso;a;shar;" #define BZIP_FORMATS "tar.bz2;tbz2;tbz;img.bz2;" #define ZLIB_FORMATS "tar.gz;tgz;tar.z;iso.z;cpio.gz;deb;img.gz;" #define XZ_FORMATS "tar.lzma;tlz;tar.xz;txz;img.xz;" #define LZMA_FORMATS "tar.lzma;tlz;" #define LZIP_FORMATS "tar.lz;" #define LRZIP_FORMATS "tar.lrz;" #define LZOP_FORMATS "tar.lzo;tzo;" #define LZ4_FORMATS "tar.lz4;img.lz4;" #define ZSTD_FORMATS "tar.zst;img.zst;" #define RPM_FORMATS "rpm;" #define UU_FORMATS "uu;" #define XAR_FORMATS "xar;" #define CAB_FORMATS "cab;" #define _7Z_FORMATS "7z;" #define WARC_FORMATS "warc;warc.gz;warc.bz2;warc.lzma;warc.xz;warc.lz4;warc.lzo;warc.zst;" char *formats; struct archive *archive; char *s; /* Determine supported compression formats by calling each one. */ /* We ignore external program pass-through */ archive = archive_read_new (); formats = g_strdup (BASIC_FORMATS); if (archive_read_support_filter_bzip2 (archive) == ARCHIVE_OK) { s = g_strconcat (formats, BZIP_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_filter_gzip (archive) == ARCHIVE_OK) { s = g_strconcat (formats, ZLIB_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_filter_xz (archive) == ARCHIVE_OK) { s = g_strconcat (formats, XZ_FORMATS, NULL); g_free (formats); formats = s; } else if (archive_read_support_filter_lzma (archive) == ARCHIVE_OK) { s = g_strconcat (formats, LZMA_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_filter_lzip (archive) == ARCHIVE_OK) { s = g_strconcat (formats, LZIP_FORMATS, NULL); g_free (formats); formats = s; } #if ARCHIVE_VERSION_NUMBER >= 3001000 if (archive_read_support_filter_lrzip (archive) == ARCHIVE_OK) { s = g_strconcat (formats, LRZIP_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_filter_lzop (archive) == ARCHIVE_OK) { s = g_strconcat (formats, LZOP_FORMATS, NULL); g_free (formats); formats = s; } #endif if (archive_read_support_filter_rpm (archive) == ARCHIVE_OK) { s = g_strconcat (formats, RPM_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_filter_uu (archive) == ARCHIVE_OK) { s = g_strconcat (formats, UU_FORMATS, NULL); g_free (formats); formats = s; } #if ARCHIVE_VERSION_NUMBER >= 3002000 if (archive_read_support_filter_lz4 (archive) == ARCHIVE_OK) { s = g_strconcat (formats, LZ4_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_format_warc (archive) == ARCHIVE_OK) { s = g_strconcat (formats, WARC_FORMATS, NULL); g_free (formats); formats = s; } #endif #if ARCHIVE_VERSION_NUMBER >= 3003003 if (archive_read_support_filter_zstd (archive) == ARCHIVE_OK) { s = g_strconcat (formats, ZSTD_FORMATS, NULL); g_free (formats); formats = s; } #endif if (archive_read_support_format_xar (archive) == ARCHIVE_OK) { s = g_strconcat (formats, XAR_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_format_cab (archive) == ARCHIVE_OK) { s = g_strconcat (formats, CAB_FORMATS, NULL); g_free (formats); formats = s; } if (archive_read_support_format_7zip (archive) == ARCHIVE_OK) { s = g_strconcat (formats, _7Z_FORMATS, NULL); g_free (formats); formats = s; } archive_read_free (archive); #ifdef MODULE_SHARED log_debug ("dynamically linked"); #else log_debug ("statically linked"); #endif log_debug ("VFSGetArchiveExts: supported archives = '%s'", formats); return formats; } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static gboolean libarchive_open (struct archive **a, const char *filename, guint32 block_size, GError **error) { int r; *a = archive_read_new (); /* Register supported formats */ archive_read_support_filter_all (*a); archive_read_support_format_all (*a); r = archive_read_open_filename (*a, filename, block_size); if (r) { log_error ("libarchive_open: error occured when opening archive: %s", archive_error_string (*a)); g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (archive_errno (*a)), archive_error_string (*a)); return FALSE; } return TRUE; } gboolean VFSOpenArchive (struct TVFSGlobs *globs, const char *sName, GError **error) { struct archive *a; struct archive_entry *entry; struct TVFSItem *item; int r; char *s; guint64 inode_no; gboolean res; globs->files = filelist_tree_new (); globs->vfs_filelist = vfs_filelist_new (globs->files); globs->curr_dir = NULL; globs->archive_path = g_strdup (sName); globs->total_size = 0; log_notice ("VFSOpenArchive: Opening archive '%s'...", globs->archive_path); res = libarchive_open (&a, globs->archive_path, globs->block_size, error); if (res) { inode_no = 0; for (;;) { entry = NULL; r = archive_read_next_header (a, &entry); if (r == ARCHIVE_EOF) { break; } else if (r == ARCHIVE_WARN) { log_warn ("VFSOpenArchive: file '%s' - libarchive warning: '%s'", archive_entry_pathname (entry), archive_error_string (a)); } else if (r != ARCHIVE_OK) { log_error ("VFSOpenArchive: error occured while reading archive: '%s'", archive_error_string (a)); g_set_error_literal (error, G_IO_ERROR, g_io_error_from_errno (archive_errno (a)), archive_error_string (a)); res = FALSE; break; } log_debug ("found file: %s, mode = %x", archive_entry_pathname (entry), archive_entry_mode (entry)); if (archive_entry_pathname (entry) == NULL) { log_warn ("VFSOpenArchive: found entry with empty pathname, inode_no = %lu", inode_no); continue; } inode_no++; /* Create a TVFSItem entry and fill all info */ item = g_malloc0 (sizeof (struct TVFSItem)); item->iSize = (guint64) archive_entry_size (entry); item->iPackedSize = -1; /* no support */ item->inode_no = inode_no; globs->total_size += item->iSize; item->iMode = archive_entry_mode (entry); item->ItemType = vRegular; /* fallback */ item->IsLink = FALSE; switch (archive_entry_filetype (entry)) { case AE_IFDIR: item->ItemType = vDirectory; break; case AE_IFIFO: item->ItemType = vFifo; break; case AE_IFCHR: item->ItemType = vChardev; break; case AE_IFBLK: item->ItemType = vBlockdev; break; case AE_IFSOCK: item->ItemType = vSock; break; case AE_IFLNK: item->IsLink = TRUE; break; case AE_IFMT: item->ItemType = vOther; break; case AE_IFREG: default: item->ItemType = vRegular; break; } if (item->IsLink) item->sLinkTo = g_strdup (archive_entry_symlink (entry)); /* item->iUID = geteuid(); item->iGID = getegid(); */ item->iUID = archive_entry_uid (entry); item->iGID = archive_entry_gid (entry); item->m_time = archive_entry_mtime (entry); item->c_time = archive_entry_ctime (entry); item->a_time = archive_entry_atime (entry); /* Create valid UTF-8 filename */ if (g_utf8_validate (archive_entry_pathname (entry), -1, NULL)) s = g_strdup (archive_entry_pathname (entry)); else { if (archive_entry_pathname_w (entry)) s = wide_to_utf8 (archive_entry_pathname_w (entry)); else s = g_filename_display_name (archive_entry_pathname (entry)); } /* Add item to the global tree and continue with next file */ filelist_tree_add_item (globs->files, s, item, archive_entry_pathname (entry), inode_no); g_free (s); } archive_read_close (a); } archive_read_free (a); log_debug ("VFSOpenArchive: done."); /* Resolve symlinks */ #ifdef __DEBUG_INTERNAL GTimer *timer = g_timer_new (); #endif filelist_tree_resolve_symlinks (globs->files); #ifdef __DEBUG_INTERNAL log_debug ("filelist_tree_resolve_symlinks: elapsed %d ms", (int) (g_timer_elapsed (timer, NULL) * 1000)); g_timer_destroy (timer); #endif filelist_tree_print (globs->files); return res; } gboolean VFSClose (struct TVFSGlobs *globs, GError **error) { if (globs) { log_debug ("VFSClose: Freeing objects..."); vfs_filelist_free (globs->vfs_filelist); globs->vfs_filelist = NULL; filelist_tree_free (globs->files); globs->files = NULL; 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 include_trailing_path_sep (globs->curr_dir); } void VFSGetFileSystemInfo (struct TVFSGlobs *globs, const char *APath, gint64 *FSSize, gint64 *FSFree, char **FSLabel) { if (FSSize) *FSSize = globs->total_size; 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) { /* no support for encrypted archives in libarchive */ return FALSE; } /******************************************************************************************************/ 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 */ /************** ****************/ #if 0 TVFSResult VFSMkDir (struct TVFSGlobs *globs, const char *sDirName) { printf ("(WW) VFSMkDir: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRemove (struct TVFSGlobs *globs, const char *APath) { printf ("(WW) VFSRemove: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRename (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName) { printf ("(WW) VFSRename: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSMakeSymLink (struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo) { printf ("(WW) VFSMakeSymLink: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChmod (struct TVFSGlobs *globs, const char *FileName, guint32 Mode) { printf ("(WW) VFSChmod: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChown (struct TVFSGlobs *globs, const char *FileName, guint32 UID, guint32 GID) { printf ("(WW) VFSChown: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChangeTimes (struct TVFSGlobs *globs, char *APath, guint32 mtime, guint32 atime) { printf ("(WW) VFSChangeTimes: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } #endif /******************************************************************************************************/ /************** **/ #if 0 TVFSFileDes VFSOpenFile(struct TVFSGlobs *globs, const char *APath, int Mode, int *Error) { *Error = cVFS_Not_Supported; return (TVFSFileDes)0; } TVFSResult VFSCloseFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor) { return cVFS_Not_Supported; } u_int64_t VFSFileSeek(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, u_int64_t AbsoluteOffset, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSReadFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int ABlockSize, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSWriteFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int BytesCount, int *Error) { *Error = cVFS_Not_Supported; return 0; } #endif 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 libarchive plugin."); return TRUE; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { log_debug ("VFSTwoSameFiles: Not supported in libarchive, comparing by paths."); return compare_two_same_files (Path1, Path2); } /******************************************************************************************************/ /************** **/ /** * The following code has been stolen from archive_read_data_into_fd.c (libarchive sources) and modified to allow progress callbacks * Quote: "This implementation minimizes copying of data and is sparse-file aware." **/ static gboolean my_archive_read_data_into_fd (struct TVFSGlobs *globs, struct archive *a, struct archive_entry *entry, const char *sDstName, size_t max_block, gboolean Append, GError **error) { int r; int fd; const void *buff; size_t size, bytes_to_write; ssize_t bytes_written, total_written; off_t offset; off_t output_offset; guint64 file_size; gboolean cancel = FALSE; int saved_errno; log_debug ("my_archive_read_data_into_fd: extracting to '%s', Append = %d", sDstName, Append); if (Append) fd = open (sDstName, O_APPEND | O_WRONLY); else fd = open (sDstName, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd < 0) { saved_errno = errno; log_error ("my_archive_read_data_into_fd: error opening dest file '%s': %s", sDstName, strerror (saved_errno)); g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_TARGET_OPEN, "%s", g_strerror (saved_errno)); return FALSE; } total_written = 0; output_offset = 0; file_size = (guint64) archive_entry_size (entry); while ((r = archive_read_data_block (a, &buff, &size, &offset)) == ARCHIVE_OK) { const char *p = buff; if (offset > output_offset) { lseek (fd, offset - output_offset, SEEK_CUR); output_offset = offset; } while (size > 0) { if (cancel) break; bytes_to_write = size; if (bytes_to_write > max_block) bytes_to_write = max_block; bytes_written = write (fd, p, bytes_to_write); if (bytes_written < 0) { saved_errno = errno; log_error ("my_archive_read_data_into_fd: error occured while writing data: %s", strerror (saved_errno)); g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_TARGET_WRITE, "%s", g_strerror (saved_errno)); close (fd); return FALSE; } output_offset += bytes_written; total_written += bytes_written; p += bytes_written; size -= bytes_written; log_debug (" my_archive_read_data_into_fd: bytes_written = %zd, total_written = %zd", bytes_written, total_written); if (globs->callback_progress) if (! globs->callback_progress (total_written, NULL, globs->callback_data)) { cancel = TRUE; break; } } } /* FIXME: shall we treat ARCHIVE_EOF as an error? */ if (r != ARCHIVE_OK && r != ARCHIVE_EOF) { log_error ("my_archive_read_data_into_fd: error reading archive: %s", archive_error_string (a)); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_SOURCE_READ, archive_error_string (a)); close (fd); return FALSE; } if (close (fd)) { saved_errno = errno; log_error ("my_archive_read_data_into_fd: error closing extracted file: %m"); g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_TARGET_CLOSE, "%s", g_strerror (saved_errno)); return FALSE; } if (cancel) { if (unlink (sDstName)) log_error ("my_archive_read_data_into_fd: error unlinking cancelled extraction: %m"); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_CANCELLED, "Operation has been cancelled."); return FALSE; } log_debug ("my_archive_read_data_into_fd: done."); return TRUE; } gboolean VFSStartCopyOperation (struct TVFSGlobs *globs, GError **error) { if (globs->op_archive != NULL) { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_EXCEPTION, "globs->op_archive != NULL"); return FALSE; } log_notice ("VFSStartCopyOperation: opening archive '%s'", globs->archive_path); return libarchive_open (&globs->op_archive, globs->archive_path, globs->block_size, error); } gboolean VFSStopCopyOperation (struct TVFSGlobs *globs, GError **error) { if (globs->op_archive == NULL) { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_EXCEPTION, "globs->op_archive == NULL"); return FALSE; } log_notice ("VFSStopCopyOperation: closing the archive."); archive_read_close (globs->op_archive); archive_read_free (globs->op_archive); globs->op_archive = NULL; return TRUE; } gboolean VFSCopyToLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { struct PathTree *node; const char *src; struct archive_entry *entry; int r; gboolean found; gboolean res; if (globs->op_archive == NULL) { g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_EXCEPTION, "globs->op_archive == NULL"); return FALSE; } 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, TUXCMD_ERROR, TUXCMD_ERROR_EXCEPTION, "The value of 'sSrcName' or 'sDstName' is NULL or empty."); return FALSE; } log_notice ("VFSCopyToLocal: copying file '%s' out to '%s'", sSrcName, sDstName); node = filelist_tree_find_node_by_path (globs->files, sSrcName); if (! node) { log_error ("VFSCopyToLocal: cannot find file '%s'", sSrcName); g_set_error (error, TUXCMD_ERROR, TUXCMD_ERROR_SOURCE_OPEN, "Cannot find file '%s'", sSrcName); return FALSE; } src = node->original_pathstr; if (! src) { log_warn ("VFSCopyToLocal: cannot determine original filename"); src = sSrcName; } log_debug ("VFSCopyToLocal: new src path: '%s'", src); found = FALSE; res = TRUE; for (;;) { entry = NULL; r = archive_read_next_header (globs->op_archive, &entry); if (r == ARCHIVE_EOF) { break; } else if (r == ARCHIVE_WARN) { log_warn ("VFSCopyToLocal: file '%s' - libarchive warning: '%s'", archive_entry_pathname (entry), archive_error_string (globs->op_archive)); } else if (r != ARCHIVE_OK) { log_error ("VFSCopyToLocal: error occured while reading archive: '%s'", archive_error_string (globs->op_archive)); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_SOURCE_READ, archive_error_string (globs->op_archive)); break; } if (g_strcmp0 (src, archive_entry_pathname (entry)) == 0) { found = TRUE; res = my_archive_read_data_into_fd (globs, globs->op_archive, entry, sDstName, globs->block_size, Append, error); break; } } if (! found && res) { log_error ("VFSCopyToLocal: file not found in the archive."); if (error && *error == NULL) g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_SOURCE_OPEN, "File not found in the archive."); res = FALSE; } log_debug ("VFSCopyToLocal: finished."); return res; } gboolean VFSCopyFromLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { log_debug ("VFSCopyFromLocal: Not supported by the libarchive plugin."); g_set_error_literal (error, TUXCMD_ERROR, TUXCMD_ERROR_NOT_SUPPORTED, "Not supported by the libarchive plugin."); return FALSE; } /********** * TODO: * * - archive testing (needs new VFS API) * - write support * - support creating new archives (needs new VFS API) * ***/