EXPORTED int backup_verify(struct backup *backup, unsigned level, int verbose, FILE *out) { struct backup_chunk_list *chunk_list = NULL; struct gzuncat *gzuc = NULL; int r = 0; /* don't double-verify last checksum when verifying all */ if ((level & BACKUP_VERIFY_ALL_CHECKSUMS)) level &= ~BACKUP_VERIFY_LAST_CHECKSUM; /* don't double-verify message links when verifying message guids */ if ((level & BACKUP_VERIFY_MESSAGE_GUIDS)) level &= ~BACKUP_VERIFY_MESSAGE_LINKS; chunk_list = backup_get_chunks(backup); if (!chunk_list || !chunk_list->count) goto done; gzuc = gzuc_new(backup->fd); if (!gzuc) { r = -1; goto done; } if (!r && (level & BACKUP_VERIFY_LAST_CHECKSUM)) r = verify_chunk_checksums(backup, chunk_list->tail, gzuc, verbose, out); if (!r && level > BACKUP_VERIFY_LAST_CHECKSUM) { struct backup_chunk *chunk = chunk_list->head; while (!r && chunk) { if (!r && (level & BACKUP_VERIFY_ALL_CHECKSUMS)) r = verify_chunk_checksums(backup, chunk, gzuc, verbose, out); if (!r && (level & BACKUP_VERIFY_MESSAGES)) r = verify_chunk_messages(backup, chunk, gzuc, level, verbose, out); if (!r && (level & BACKUP_VERIFY_MAILBOX_LINKS)) r = verify_chunk_mailbox_links(backup, chunk, gzuc, verbose, out); chunk = chunk->next; } } done: if (gzuc) gzuc_free(&gzuc); if (chunk_list) backup_chunk_list_free(&chunk_list); return r; }
static int cmd_list_chunks(struct backup *backup, const struct cyrbu_cmd_options *options) { struct backup_chunk_list *chunk_list = NULL; struct backup_chunk *chunk; (void) options; chunk_list = backup_get_chunks(backup); if (!chunk_list) return -1; // FIXME dedup this with lcb_printinfo.c:detail_full() fprintf(stdout, " id offset\tlength\tratio%%\tstart time end time\n"); for (chunk = chunk_list->head; chunk; chunk = chunk->next) { char ts_start[32] = "[unknown]"; char ts_end[32] = "[unknown]"; double ratio; strftime(ts_start, sizeof(ts_start), "%F %T", localtime(&chunk->ts_start)); strftime(ts_end, sizeof(ts_end), "%F %T", localtime(&chunk->ts_end)); if (chunk->next) { ratio = 100.0 * (chunk->next->offset - chunk->offset) / chunk->length; } else { // FIXME need to stat the underlying file to see disk size of last chunk ratio = 0.0; } fprintf(stdout, "%7d " OFF_T_FMT "\t" SIZE_T_FMT "\t%6.1f\t%s %s\n", chunk->id, chunk->offset, chunk->length, ratio, ts_start, ts_end); } backup_chunk_list_free(&chunk_list); return 0; }
static int detail_full(struct backup *backup, const char *userid, FILE *out) { struct backup_chunk_list *all_chunks = NULL; struct backup_chunk *chunk = NULL; struct stat data_stat_buf, index_stat_buf; char data_timestamp[32] = "[unknown]"; char index_timestamp[32] = "[unknown]"; int r = 0; r = fstat(backup->fd, &data_stat_buf); if (r) { fprintf(stderr, "fstat %s: %s\n", backup->data_fname, strerror(errno)); data_stat_buf.st_size = -1; } r = stat(backup->index_fname, &index_stat_buf); if (r) { fprintf(stderr, "stat %s: %s\n", backup->index_fname, strerror(errno)); index_stat_buf.st_size = -1; } strftime(data_timestamp, sizeof(data_timestamp), "%F %T", localtime(&data_stat_buf.st_mtime)); strftime(index_timestamp, sizeof(index_timestamp), "%F %T", localtime(&index_stat_buf.st_mtime)); fprintf(out, "userid: %s\n", userid); fprintf(out, " data: %s\n", backup->data_fname); fprintf(out, " " OFF_T_FMT "\tmodified: %s\n", data_stat_buf.st_size, data_timestamp); fprintf(out, " index: %s\n", backup->index_fname); fprintf(out, " " OFF_T_FMT "\tmodified: %s\n", index_stat_buf.st_size, index_timestamp); all_chunks = backup_get_chunks(backup); if (all_chunks) { double total_length = 0.0; fprintf(out, "chunks: " SIZE_T_FMT "\n", all_chunks->count); fprintf(out, " id offset\tlength\tratio%%\tstart time end time\n"); for (chunk = all_chunks->head; chunk; chunk = chunk->next) { char ts_start[32] = "[unknown]"; char ts_end[32] = "[unknown]"; double ratio; strftime(ts_start, sizeof(ts_start), "%F %T", localtime(&chunk->ts_start)); strftime(ts_end, sizeof(ts_end), "%F %T", localtime(&chunk->ts_end)); if (chunk->next) { ratio = 100.0 * (chunk->next->offset - chunk->offset) / chunk->length; } else { ratio = 100.0 * (data_stat_buf.st_size - chunk->offset) / chunk->length; } total_length += chunk->length; fprintf(out, "%7d " OFF_T_FMT "\t" SIZE_T_FMT "\t%6.1f\t%s %s\n", chunk->id, chunk->offset, chunk->length, ratio, ts_start, ts_end); } fprintf(out, "overall compression: %.1f%%\n", 100.0 * data_stat_buf.st_size / total_length); backup_chunk_list_free(&all_chunks); } fprintf(out, "\n"); return r; }
/* returns: * 0 on success * 1 if compact was not needed * negative on error */ EXPORTED int backup_compact(const char *name, enum backup_open_nonblock nonblock, int force, int verbose, FILE *out) { struct backup *original = NULL; struct backup *compact = NULL; struct backup_chunk_list *all_chunks = NULL; struct backup_chunk_list *keep_chunks = NULL; struct backup_chunk *chunk = NULL; struct sync_msgid_list *keep_message_guids = NULL; struct gzuncat *gzuc = NULL; struct protstream *in = NULL; time_t since, chunk_start_time, ts; int r; compact_readconfig(); r = compact_open(name, &original, &compact, nonblock); if (r) return r; /* calculate current time after obtaining locks, in case of a wait */ const time_t now = time(NULL); const int retention_days = config_getint(IMAPOPT_BACKUP_RETENTION_DAYS); if (retention_days > 0) { since = now - (retention_days * 24 * 60 * 60); } else { /* zero or negative retention days means "keep forever" */ since = -1; } all_chunks = backup_get_chunks(original); if (!all_chunks) goto error; keep_chunks = backup_get_live_chunks(original, since); if (!keep_chunks) goto error; if (!force && !compact_required(all_chunks, keep_chunks)) { /* nothing to do */ backup_chunk_list_free(&all_chunks); backup_chunk_list_free(&keep_chunks); backup_unlink(&compact); backup_close(&original); return 1; } if (verbose) { fprintf(out, "keeping " SIZE_T_FMT " chunks:\n", keep_chunks->count); for (chunk = keep_chunks->head; chunk; chunk = chunk->next) { fprintf(out, " %d", chunk->id); } fprintf(out, "\n"); } gzuc = gzuc_new(original->fd); if (!gzuc) goto error; chunk_start_time = -1; ts = 0; struct buf cmd = BUF_INITIALIZER; for (chunk = keep_chunks->head; chunk; chunk = chunk->next) { keep_message_guids = sync_msgid_list_create(0); r = backup_message_foreach(original, chunk->id, &since, _keep_message_guids_cb, keep_message_guids); if (r) goto error; gzuc_member_start_from(gzuc, chunk->offset); in = prot_readcb(_prot_fill_cb, gzuc); while (1) { struct dlist *dl = NULL; int c = parse_backup_line(in, &ts, &cmd, &dl); if (c == EOF) { const char *error = prot_error(in); if (error && 0 != strcmp(error, PROT_EOF_STRING)) { syslog(LOG_ERR, "IOERROR: %s: error reading chunk at offset %jd, byte %i: %s\n", name, chunk->offset, prot_bytes_in(in), error); if (out) fprintf(out, "error reading chunk at offset %jd, byte %i: %s\n", chunk->offset, prot_bytes_in(in), error); r = IMAP_IOERROR; goto error; } break; } if (chunk_start_time == -1) { r = backup_append_start(compact, &ts, BACKUP_APPEND_NOFLUSH); if (r) goto error; chunk_start_time = ts; } // XXX if this line is worth keeping if (want_append(original, chunk->id, dl, keep_message_guids)) { // FIXME if message is removed due to unneeded chunk, // subsequent mailbox lines for it will fail here // so we need to be able to tell which lines apply to messages we don't want anymore r = backup_append(compact, dl, &ts, BACKUP_APPEND_NOFLUSH); if (r) goto error; } dlist_unlink_files(dl); dlist_free(&dl); // if this line put us over compact_maxsize if (want_split(chunk, &compact->append_state->wrote)) { r = backup_append_end(compact, &ts); chunk_start_time = -1; if (verbose) { fprintf(out, "splitting chunk %d\n", chunk->id); } } } // if we're due to start a new chunk if (compact->append_state && compact->append_state->mode) { if (!want_combine(compact->append_state->wrote, chunk->next)) { r = backup_append_end(compact, &ts); chunk_start_time = -1; } else if (verbose) { fprintf(out, "combining chunks %d and %d\n", chunk->id, chunk->next->id); } } prot_free(in); in = NULL; gzuc_member_end(gzuc, NULL); sync_msgid_list_free(&keep_message_guids); } buf_free(&cmd); if (compact->append_state && compact->append_state->mode) backup_append_end(compact, &ts); gzuc_free(&gzuc); backup_chunk_list_free(&keep_chunks); /* if we get here okay, then the compact succeeded */ r = compact_closerename(&original, &compact, now); if (r) goto error; return 0; error: if (in) prot_free(in); if (gzuc) gzuc_free(&gzuc); if (keep_message_guids) sync_msgid_list_free(&keep_message_guids); if (all_chunks) backup_chunk_list_free(&all_chunks); if (keep_chunks) backup_chunk_list_free(&keep_chunks); if (compact) backup_unlink(&compact); if (original) backup_close(&original); return r ? r : -1; }