/* libarchive plugin for Tux Commander * version 0.1.6, designed for libarchive 2.7.1 * Copyright (C) 2008 Tomas Bzatek * Check for updates on tuxcmd.sourceforge.net * * Uses libarchive library * Copyright (c) 2003-2007 Tim Kientzle * * * 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 "vfs_types.h" #include "vfsutils.h" #include "strutils.h" #include "treepathutils.h" #include "treepath_vfs.h" #include #include #ifndef MODULE_SHARED # include #endif #define MODULE_VERSION "0.1.6" #define MODULE_BUILD_DATE "2009-11-15" #define DEFAULT_BLOCK_SIZE 65536 /******************************************************************************************************/ /** Auxiliary classes */ /************** ****************/ // Declaration of the global plugin object struct TVFSGlobs { TVFSLogFunc log_func; char *curr_dir; char *archive_path; unsigned long block_size; struct PathTree *files; struct VfsFilelistData *vfs_filelist; u_int64_t total_size; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; TVFSProgressCallback callback_progress; void *callback_data; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// // Basic initialization functions struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; globs = (struct TVFSGlobs *) malloc (sizeof (struct TVFSGlobs)); memset (globs, 0, sizeof (struct TVFSGlobs)); globs->block_size = DEFAULT_BLOCK_SIZE; globs->callback_data = NULL; globs->callback_ask_question = NULL; globs->callback_ask_password = NULL; globs->callback_progress = NULL; globs->log_func = log_func; if (globs->log_func != NULL) globs->log_func("libarchive plugin: VFSInit"); 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) { if (globs->log_func != NULL) globs->log_func("libarchive plugin: VFSDestroy"); free (globs); } 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_LIBRARY_VERSION, shared_module); module_info->Copyright = g_strdup ("Plugin Copyright (C) 2008-2009 Tomáš Bžatek\nlibarchive sources Copyright (c) 2003-2007 Tim Kientzle"); return module_info; } char * VFSGetArchiveExts () { #ifndef MODULE_SHARED #ifdef HAVE_BZLIB_H #define FORMAT_BZLIB "tar.bz2;tbz2;" #else #define FORMAT_BZLIB #endif #ifdef HAVE_ZLIB_H #define FORMAT_ZLIB "tar.gz;tgz;deb;" #else #define FORMAT_ZLIB #endif #if HAVE_LZMA_H && HAVE_LIBLZMA #define FORMAT_LZMA "tar.lzma;tar.xz;" #elif HAVE_LZMADEC_H && HAVE_LIBLZMADEC #define FORMAT_LZMA "tar.lzma;" #else #define FORMAT_LZMA #endif return g_strdup ("tar;cpio;iso;a;" FORMAT_ZLIB FORMAT_BZLIB FORMAT_LZMA); #else /* Dynamic linking; we're unable to determine supported formats, let's suppose distribution vendors always compile full set */ return g_strdup ("tar;tar.gz;tar.bz2;tgz;tbz2;cpio;iso;a;deb;tar.lzma;tar.xz"); #endif } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static TVFSResult libarchive_open(struct archive **a, const char *filename, size_t block_size) { int r; *a = archive_read_new(); // Register supported formats archive_read_support_compression_all(*a); archive_read_support_format_all(*a); r = archive_read_open_file(*a, filename, block_size); if (r) { fprintf(stderr, "(EE) libarchive_open: error occured when opening archive: %s\n", archive_error_string(*a)); return cVFS_Failed; } return cVFS_OK; } TVFSResult VFSOpenArchive(struct TVFSGlobs *globs, char *sName) { globs->files = filelist_tree_new(); globs->vfs_filelist = vfs_filelist_new(globs->files); globs->curr_dir = NULL; globs->archive_path = strdup(sName); globs->total_size = 0; fprintf(stderr, "(--) VFSOpenArchive: trying to open archive '%s'...\n", globs->archive_path); TVFSResult Result; struct archive *a; struct archive_entry *entry; int r; Result = libarchive_open(&a, globs->archive_path, globs->block_size); if (Result == cVFS_OK) { for (;;) { entry = NULL; r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) { break; } else if (r == ARCHIVE_WARN) { log("(WW) VFSOpenArchive: file '%s' - libarchive warning: '%s'\n", archive_entry_pathname(entry), archive_error_string(a)); } else if (r != ARCHIVE_OK) { fprintf(stderr, "(EE) VFSOpenArchive: error occured while reading archive: '%s'\n", archive_error_string(a)); Result = cVFS_Failed; break; } log("found file: %s, mode = %x\n", archive_entry_pathname(entry), archive_entry_mode(entry)); // Create a TVFSItem entry and fill all info struct TVFSItem *item = (struct TVFSItem*)malloc(sizeof(struct TVFSItem)); memset(item, 0, sizeof(struct TVFSItem)); item->iSize = (int64_t)archive_entry_size(entry); item->iPackedSize = -1; /* no support */ globs->total_size += item->iSize; mode_t mode = archive_entry_mode(entry); item->iMode = archive_entry_mode(entry); if (S_ISREG(mode)) item->ItemType = vRegular; else if (S_ISDIR(mode)) item->ItemType = vDirectory; else if (S_ISCHR(mode)) item->ItemType = vChardev; else if (S_ISBLK(mode)) item->ItemType = vBlockdev; else if (S_ISFIFO(mode)) item->ItemType = vFifo; else if (S_ISLNK(mode)) item->ItemType = vSymlink; else if (S_ISSOCK(mode)) item->ItemType = vSock; if (item->ItemType == vSymlink) item->sLinkTo = strdup(archive_entry_symlink(entry)); item->iUID = geteuid(); item->iGID = getegid(); item->m_time = archive_entry_mtime(entry); item->c_time = archive_entry_ctime(entry); item->a_time = archive_entry_atime(entry); char *s; 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)); } // g_print("file = '%s', wide = '%ls'\n", archive_entry_pathname(entry), archive_entry_pathname_w(entry)); // Add item to the global list and continue with next file filelist_tree_add_item(globs->files, s, archive_entry_pathname(entry), item, 0); g_free (s); } archive_read_close(a); } archive_read_finish(a); fprintf(stderr, "(II) VFSOpenArchive: done. \n"); /* FIXME: temporarily disabled */ // filelist_tree_resolve_symlinks(globs->files); printf("\n\nList of items:\n"); filelist_tree_print(globs->files); return Result; } TVFSResult VFSClose(struct TVFSGlobs *globs) { if (globs) { fprintf(stderr, "(II) VFSClose: Freeing objects...\n"); if (globs->vfs_filelist) vfs_filelist_free(globs->vfs_filelist); if (globs->files) filelist_tree_free(globs->files); if (globs->archive_path) free(globs->archive_path); if (globs->curr_dir) free(globs->curr_dir); } return cVFS_OK; } char * VFSGetPath (struct TVFSGlobs *globs) { return include_trailing_path_sep (globs->curr_dir); } u_int64_t VFSGetFileSystemFree(struct TVFSGlobs *globs, char *APath) { return 0; } u_int64_t VFSGetFileSystemSize(struct TVFSGlobs *globs, char *APath) { return globs->total_size; } /******************************************************************************************************/ TVFSResult VFSChangeDir(struct TVFSGlobs *globs, char *NewPath) { if (NewPath == NULL) { printf("(EE) VFSChangeDir: NewPath is NULL!\n"); return cVFS_Failed; } globs->curr_dir = vfs_filelist_change_dir(globs->vfs_filelist, NewPath); if (globs->curr_dir) return cVFS_OK; else return cVFS_Failed; } int VFSGetPasswordRequired(struct TVFSGlobs *globs) { return FALSE; } /******************************************************************************************************/ TVFSResult VFSListFirst(struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { if (sDir == NULL) { printf("(EE) VFSListFirst: sDir is NULL!\n"); return cVFS_Failed; } printf ("(--) VFSListFirst: Going to list all items in '%s'\n", sDir); return vfs_filelist_list_first(globs->vfs_filelist, sDir, Item); } TVFSResult VFSListNext(struct TVFSGlobs *globs, char *sDir, struct TVFSItem *Item) { return vfs_filelist_list_next(globs->vfs_filelist, sDir, Item); } TVFSResult VFSListClose(struct TVFSGlobs *globs) { return vfs_filelist_list_close(globs->vfs_filelist); } /******************************************************************************************************/ long VFSFileExists(struct TVFSGlobs *globs, const char *FileName, const long Use_lstat) { if (! globs) return FALSE; return vfs_filelist_file_exists(globs->vfs_filelist, FileName, Use_lstat); } TVFSResult VFSFileInfo(struct TVFSGlobs *globs, char *AFileName, struct TVFSItem *Item) { printf("(--) VFSFileInfo: requested info for object '%s'\n", AFileName); if (!globs) return cVFS_Failed; return vfs_filelist_file_info(globs->vfs_filelist, AFileName, Item); } /******************************************************************************************************/ /** Recursive tree size counting */ /************** ****************/ u_int64_t VFSGetDirSize(struct TVFSGlobs *globs, char *APath) { if (! globs) return 0; return vfs_filelist_get_dir_size(globs->vfs_filelist, APath); } void VFSBreakGetDirSize(struct TVFSGlobs *globs) { printf("(WW) VFSBreakGetDirSize: calling break\n"); if (globs) vfs_filelist_get_dir_size_break(globs->vfs_filelist); } /******************************************************************************************************/ /** Methods modifying the archive */ /************** ****************/ 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, const uint Mode) { printf("(WW) VFSChmod: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChown(struct TVFSGlobs *globs, const char *FileName, const uint UID, const uint GID) { printf("(WW) VFSChown: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChangeTimes(struct TVFSGlobs *globs, char *APath, long mtime, long atime) { printf("(WW) VFSChangeTimes: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// 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; } void VFSSetBlockSize(struct TVFSGlobs *globs, int Value) { globs->block_size = Value; } int VFSIsOnSameFS(struct TVFSGlobs *globs, const char *Path1, const char *Path2) { printf("(WW) VFSIsOnSameFS: Not supported in libarchive plugin.\n"); return TRUE; } int VFSTwoSameFiles(struct TVFSGlobs *globs, const char *Path1, const char *Path2) { printf("(WW) VFSTwoSameFiles: Not supported in libarchive, comparing by paths.\n"); 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 TVFSResult my_archive_read_data_into_fd(struct TVFSGlobs *globs, struct archive *a, struct archive_entry *entry, const char *sDstName, size_t max_block, int Append) { 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; u_int64_t file_size; gboolean cancel = FALSE; printf("(II) my_archive_read_data_into_fd: extracting to '%s', Append = %d\n", 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) { fprintf(stderr, "(EE) my_archive_read_data_into_fd: error occured while extracting data: %s\n", strerror(errno)); return cVFS_Failed; } total_written = 0; output_offset = 0; file_size = (u_int64_t)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) { fprintf(stderr, "(EE) my_archive_read_data_into_fd: error occured while extracting data: %s\n", strerror(errno)); return cVFS_Failed; } output_offset += bytes_written; total_written += bytes_written; p += bytes_written; size -= bytes_written; log(" (II) my_archive_read_data_into_fd: bytes_written = %zd, total_written = %zd\n", bytes_written, total_written); if (globs->callback_progress) { if (! globs->callback_progress(total_written, file_size, globs->callback_data)) { cancel = TRUE; break; } // usleep(100000); } } } if ((close(fd)) || ((r != ARCHIVE_OK) && (r != ARCHIVE_EOF))) { fprintf(stderr, "(EE) my_archive_read_data_into_fd: error closing extracted file: %s\n", strerror(errno)); return cVFS_WriteErr; } if (cancel) { if (unlink(sDstName)) fprintf(stderr, "(EE) my_archive_read_data_into_fd: error unlinking cancelled extraction: %s\n", strerror(errno)); return cVFS_Cancelled; } printf("(II) my_archive_read_data_into_fd: done.\n"); return cVFS_OK; } TVFSResult VFSCopyToLocal(struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, int Append) { if ((sSrcName == NULL) || (sDstName == NULL) || (strlen(sSrcName) < 1) || (strlen(sDstName) < 1)) { printf("(EE) VFSCopyToLocal: The value of 'sSrcName' or 'sDstName' is NULL or empty\n"); return cVFS_Failed; } printf("(II) VFSCopyToLocal: copying file '%s' out to '%s'\n", sSrcName, sDstName); struct PathTree *node = filelist_tree_find_node_by_path(globs->files, sSrcName); if (! node) { fprintf(stderr, "(EE) VFSCopyToLocal: cannot find file '%s'\n", sSrcName); return cVFS_ReadErr; } const char *src = node->original_pathstr; if (! src) { fprintf(stderr, "(WW) VFSCopyToLocal: cannot determine original filename\n"); src = sSrcName; } printf("(II) VFSCopyToLocal: new src path: '%s'\n", src); TVFSResult Result; struct archive *a; struct archive_entry *entry; int r; gboolean found = FALSE; Result = libarchive_open(&a, globs->archive_path, globs->block_size); if (Result == cVFS_OK) { for (;;) { entry = NULL; r = archive_read_next_header(a, &entry); if (r == ARCHIVE_EOF) { break; } else if (r == ARCHIVE_WARN) { log("(WW) VFSOpen: file '%s' - libarchive warning: '%s'\n", archive_entry_pathname(entry), archive_error_string(a)); } else if (r != ARCHIVE_OK) { fprintf(stderr, "(EE) VFSCopyToLocal: error occured while reading archive: '%s'\n", archive_error_string(a)); Result = cVFS_Failed; break; } // printf ("found file: %s, mode = %x\n", archive_entry_pathname(entry), archive_entry_mode(entry)); const char *ssrc = src; const char *asrc = archive_entry_pathname(entry); if (IS_DIR_SEP(*ssrc)) ssrc++; if (IS_DIR_SEP(*asrc)) asrc++; // printf ("strcmp: '%s' vs. '%s'\n", ssrc, asrc); if (strcmp(ssrc, asrc) == 0) { // printf("--> found file, extracting\n"); fprintf(stderr, "(II) VFSCopyToLocal: extract_file_path(sDstName) = '%s', extract_file_name(sDstName) = '%s' \n", extract_file_path(sDstName), extract_file_name(sDstName)); found = TRUE; Result = my_archive_read_data_into_fd(globs, a, entry, sDstName, globs->block_size, Append); break; } } archive_read_close(a); } archive_read_finish(a); if ((! found) && Result == cVFS_OK) { fprintf(stderr, "(EE) VFSCopyToLocal: file not found in archive.\n"); Result = cVFS_ReadErr; } fprintf(stderr, "(II) VFSCopyToLocal: finished. \n"); return Result; } TVFSResult VFSCopyFromLocal(struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, int Append) { printf("(WW) VFSCopyFromLocal: Not supported in libarchive plugin.\n"); return cVFS_Not_Supported; } /********** * TODO: * * - UTF-8, FName/FDisplayName and absolute/relative paths revision needed! * - archive testing (needs new VFS API) * - write support * - support creating new archives (needs new VFS API) * ***/