setup_paths leaks memory: realpath is already set in mx_open_mailbox() restore_paths is unneeded. mx_fastclose_mailbox() will free stuff, and nothing is looking at the path once we are closing or aborting.
Make a copy of the hooks. Otherwise 'unhook *' will leave dangling pointers. Add compress_info freeing inside mx_fastclose_mailbox(). Only free inside compress.c when we want to prevent close() from doing anything. close_mailbox() didn't preserve ctx->path on error. execute_command() didn't return an error if the mutt_system() command failed. mx_open_mailbox_append() should check mutt_comp_can_append() only for the case that the mailbox doesn't exist. When it exists, mx_get_magic() has already looked at the file contents before checking for matching open_hooks. In open_append_mailbox() if no append hook is defined, it should't call ci->open() if the mailbox doesn't exist. It should act just like append and create a temporary file. check_mailbox() needs more work. For now, at least have it properly close the mailbox on error.
# HG changeset patch # User Kevin McCarthy <ke...@8t8.us> # Date 1478734908 28800 # Wed Nov 09 15:41:48 2016 -0800 # Node ID 1cc878cc71bf90d5358230075b05029b7c839d77 # Parent 326fac112b39ddfde430e80a6ee58fed74c0b9e0 Compress: fix several logic and memory bugs. setup_paths leaks memory: realpath is already set in mx_open_mailbox() restore_paths is unneeded. mx_fastclose_mailbox() will free stuff, and nothing is looking at the path once we are closing or aborting. Make a copy of the hooks. Otherwise 'unhook *' will leave dangling pointers. Add compress_info freeing inside mx_fastclose_mailbox(). Only free inside compress.c when we want to prevent close() from doing anything. close_mailbox() didn't preserve ctx->path on error. execute_command() didn't return an error if the mutt_system() command failed. mx_open_mailbox_append() should check mutt_comp_can_append() only for the case that the mailbox doesn't exist. When it exists, mx_get_magic() has already looked at the file contents before checking for matching open_hooks. In open_append_mailbox() if no append hook is defined, it should't call ci->open() if the mailbox doesn't exist. It should act just like append and create a temporary file. check_mailbox() needs more work. For now, at least have it properly close the mailbox on error. diff --git a/compress.c b/compress.c --- a/compress.c +++ b/compress.c @@ -112,60 +112,35 @@ /** * setup_paths - Set the mailbox paths * @ctx: Mailbox to modify * * Save the compressed filename in ctx->realpath. * Create a temporary filename and put its name in ctx->path. * * Note: The temporary file is NOT created. - * Note: ctx->path will be freed by restore_path() */ static void setup_paths (CONTEXT *ctx) { if (!ctx) return; char tmppath[_POSIX_PATH_MAX]; /* Setup the right paths */ + FREE(&ctx->realpath); ctx->realpath = ctx->path; /* We will uncompress to /tmp */ mutt_mktemp (tmppath, sizeof (tmppath)); ctx->path = safe_strdup (tmppath); } /** - * restore_path - Put back the original mailbox name - * @ctx: Mailbox to modify - * - * When we use a compressed mailbox, we change the CONTEXT to refer to the - * uncompressed file. We store the original name in ctx->realpath. - * ctx->path = "/tmp/mailbox" - * ctx->realpath = "mailbox.gz" - * - * When we have finished with a compressed mailbox, we put back the original - * name. - * ctx->path = "mailbox.gz" - * ctx->realpath = NULL - */ -static void -restore_path (CONTEXT *ctx) -{ - if (!ctx) - return; - - FREE(&ctx->path); - ctx->path = ctx->realpath; - ctx->realpath = NULL; -} - -/** * get_size - Get the size of a file * @path: File to measure * * Returns: * number: Size in bytes * 0: On error */ static int @@ -232,18 +207,16 @@ } /** * set_compress_info - Find the compress hooks for a mailbox * @ctx: Mailbox to examine * * When a mailbox is opened, we check if there are any matching hooks. * - * Note: Caller must free the COMPRESS_INFO when done. - * * Returns: * COMPRESS_INFO: Hook info for the mailbox's path * NULL: On error */ static COMPRESS_INFO * set_compress_info (CONTEXT *ctx) { if (!ctx || !ctx->path) @@ -258,24 +231,44 @@ return NULL; const char *c = find_hook (MUTT_CLOSEHOOK, ctx->path); const char *a = find_hook (MUTT_APPENDHOOK, ctx->path); COMPRESS_INFO *ci = safe_calloc (1, sizeof (COMPRESS_INFO)); ctx->compress_info = ci; - ci->open = o; - ci->close = c; - ci->append = a; + ci->open = safe_strdup (o); + ci->close = safe_strdup (c); + ci->append = safe_strdup (a); return ci; } /** + * mutt_free_compress_info - Frees the compress info members and structure. + * @ctx: Mailbox to free compress_info for. + */ +void +mutt_free_compress_info (CONTEXT *ctx) +{ + COMPRESS_INFO *ci; + + if (!ctx || !ctx->compress_info) + return; + + ci = ctx->compress_info; + FREE (&ci->open); + FREE (&ci->close); + FREE (&ci->append); + + FREE (&ctx->compress_info); +} + +/** * cb_format_str - Expand the filenames in the command string * @dest: Buffer in which to save string * @destlen: Buffer length * @col: Starting column, UNUSED * @cols: Number of screen columns, UNUSED * @op: printf-like operator, e.g. 't' * @src: printf-like format string * @fmt: Field formatting string, UNUSED @@ -350,63 +343,67 @@ * * Returns: * 1: Success * 0: Failure */ static int execute_command (CONTEXT *ctx, const char *command, int create_file, const char *progress) { + FILE *fp; + int rc = 0; + int unlock = 0; + char sys_cmd[HUGE_STRING]; + if (!ctx || !command || !progress) return 0; if (!ctx->quiet) mutt_message (progress, ctx->realpath); - FILE *fp; if (create_file) fp = fopen (ctx->realpath, "a"); else fp = fopen (ctx->realpath, "r"); - if (!fp) { mutt_perror (ctx->realpath); return 0; } mutt_block_signals(); /* If we're creating the file, lock it exclusively */ if (!lock_mailbox (ctx, fp, create_file)) { - safe_fclose (&fp); - mutt_unblock_signals(); mutt_error (_("Unable to lock mailbox!")); - return 0; + goto cleanup; } + unlock = 1; endwin(); fflush (stdout); - char sys_cmd[HUGE_STRING]; - expand_command_str (ctx, command, sys_cmd, sizeof (sys_cmd)); - int rc = mutt_system (sys_cmd); - if (rc != 0) + if (mutt_system (sys_cmd) != 0) { mutt_any_key_to_continue (NULL); mutt_error (_("Error executing: %s\n"), sys_cmd); + goto cleanup; } - unlock_mailbox (ctx, fp); + rc = 1; + +cleanup: + if (unlock) + unlock_mailbox (ctx, fp); mutt_unblock_signals(); safe_fclose (&fp); - return 1; + return rc; } /** * open_read - Open a compressed mailbox for reading * @ctx: Mailbox to open * * Decompress the mailbox and set up the paths and hooks needed. * @@ -456,17 +453,17 @@ goto or_fail; } return 1; or_fail: /* remove the partial uncompressed file */ remove (ctx->path); - restore_path (ctx); + mutt_free_compress_info (ctx); return 0; } /** * open_mailbox - Open a compressed mailbox * @ctx: Mailbox to open * @@ -517,69 +514,71 @@ /* If this succeeds, we know there's an open-hook */ COMPRESS_INFO *ci = set_compress_info (ctx); if (!ci) return -1; /* To append we need an append-hook or a close-hook */ if (!ci->append && !ci->close) { - FREE(&ctx->compress_info); mutt_error (_("Cannot append without an append-hook or close-hook : %s"), ctx->path); - return -1; + goto oa_fail1; } ctx->magic = DefaultMagic; /* We can only deal with mbox and mmdf mailboxes */ if ((ctx->magic != MUTT_MBOX) && (ctx->magic != MUTT_MMDF)) - return -1; + goto oa_fail1; setup_paths (ctx); ctx->mx_ops = &mx_comp_ops; ci->child_ops = mx_get_ops (ctx->magic); if (!ci->child_ops) { mutt_error (_("Can't find mailbox ops for mailbox type %d"), ctx->magic); - return -1; + goto oa_fail2; } - if (ci->append) + if (ci->append || (access (ctx->realpath, F_OK) != 0)) { /* Create an empty temporary file */ ctx->fp = safe_fopen (ctx->path, "w"); if (!ctx->fp) { mutt_perror (ctx->path); - goto oa_fail; + goto oa_fail2; } } else { /* Open the existing mailbox */ int rc = execute_command (ctx, ci->open, 0, _("Decompressing %s")); if (rc == 0) { mutt_error (_("Compress command failed: %s"), ci->open); - goto oa_fail; + goto oa_fail2; } ctx->fp = safe_fopen (ctx->path, "a"); if (!ctx->fp) { mutt_perror (ctx->path); - goto oa_fail; + goto oa_fail2; } } return 0; -oa_fail: +oa_fail2: /* remove the partial uncompressed file */ remove (ctx->path); - restore_path (ctx); +oa_fail1: + /* Free the compress_info to prevent close from trying to recompress */ + mutt_free_compress_info (ctx); + return -1; } /** * close_mailbox - Close a compressed mailbox * @ctx: Mailbox to close * * If the mailbox has been changed then re-compress the tmp file. @@ -609,18 +608,16 @@ { remove (ctx->realpath); } else { remove (ctx->path); } - restore_path (ctx); - FREE(&ctx->compress_info); return 0; } const char *append; const char *msg; /* The file exists and we can append */ if ((access (ctx->realpath, F_OK) == 0) && ci->append) @@ -635,20 +632,18 @@ } int rc = execute_command (ctx, append, 1, msg); if (rc == 0) { mutt_any_key_to_continue (NULL); mutt_error (_(" %s: Error compressing mailbox! Uncompressed one kept!\n"), ctx->path); } - - remove (ctx->path); - restore_path (ctx); - FREE(&ctx->compress_info); + else + remove (ctx->path); return 0; } /** * check_mailbox - Check for changes in the compressed file * @ctx: Mailbox * @@ -673,24 +668,27 @@ COMPRESS_INFO *ci = ctx->compress_info; if (!ci) return -1; int size = get_size (ctx->realpath); if (size == ci->size) return 0; + /* TODO: this is a copout. We should reopen the compressed mailbox + * and call mutt_reopen_mailbox. */ if (ctx->changed) { - FREE(&ctx->compress_info); - restore_path (ctx); + mutt_free_compress_info (ctx); + mx_fastclose_mailbox (ctx); mutt_error (_("Mailbox was corrupted!")); return -1; } + /* TODO: this block leaks memory. this is doing it all wrong */ close_mailbox (ctx); const char *path = ctx->path; ctx->path = NULL; mx_open_mailbox (path, 0, ctx); FREE(&path); diff --git a/compress.h b/compress.h --- a/compress.h +++ b/compress.h @@ -14,16 +14,18 @@ * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef _COMPRESS_H_ #define _COMPRESS_H_ +void mutt_free_compress_info (CONTEXT *ctx); + int mutt_comp_can_append (CONTEXT *ctx); int mutt_comp_can_read (const char *path); int mutt_comp_sync (CONTEXT *ctx); int mutt_comp_valid_command (const char *cmd); extern struct mx_ops mx_comp_ops; #endif /* _COMPRESS_H_ */ diff --git a/mx.c b/mx.c --- a/mx.c +++ b/mx.c @@ -503,34 +503,34 @@ } if (ctx->magic < 0) { if (stat (ctx->path, &sb) == -1) { if (errno == ENOENT) { - ctx->magic = DefaultMagic; +#ifdef USE_COMPRESSED + if (mutt_comp_can_append (ctx)) + ctx->magic = MUTT_COMPRESSED; + else +#endif + ctx->magic = DefaultMagic; flags |= MUTT_APPENDNEW; } else { mutt_perror (ctx->path); return -1; } } else return -1; } -#ifdef USE_COMPRESSED - if (mutt_comp_can_append (ctx)) - ctx->mx_ops = &mx_comp_ops; - else -#endif ctx->mx_ops = mx_get_ops (ctx->magic); if (!ctx->mx_ops || !ctx->mx_ops->open_append) return -1; return ctx->mx_ops->open_append (ctx, flags); } /* @@ -652,16 +652,20 @@ /* never announce that a mailbox we've just left has new mail. #3290 * XXX: really belongs in mx_close_mailbox, but this is a nice hook point */ if (!ctx->peekonly) mutt_buffy_setnotified(ctx->path); if (ctx->mx_ops) ctx->mx_ops->close (ctx); +#ifdef USE_COMPRESSED + mutt_free_compress_info (ctx); +#endif /* USE_COMPRESSED */ + if (ctx->subj_hash) hash_destroy (&ctx->subj_hash, NULL); if (ctx->id_hash) hash_destroy (&ctx->id_hash, NULL); mutt_clear_threads (ctx); for (i = 0; i < ctx->msgcount; i++) mutt_free_header (&ctx->hdrs[i]); FREE (&ctx->hdrs);