summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuy Harris <guy@alum.mit.edu>2012-06-05 06:52:18 +0000
committerGuy Harris <guy@alum.mit.edu>2012-06-05 06:52:18 +0000
commit9fe3d4b4f356084e8631a972e0217f02e344ff3e (patch)
treedb60aec8b9e377d0ec0b77768b5c2b872d87cb64
parent1d27ce5d9962520e0d37f9a424f02b3448934207 (diff)
downloadwireshark-9fe3d4b4f356084e8631a972e0217f02e344ff3e.tar.gz
If we do a Save or Save As with a move, don't reread the capture file,
just tweak the elements in the capture_file structure as necessary and poke the UI to update stuff such as the windows title. If we do a Save or Save As with a copy, don't reread the capture file, just close the old wtap, open a wtap for the copy, and tweak the elements in the capture_file structure as necessary and poke the UI to update stuff such as the windows title. Otherwise, don't do a full read-and-dissect pass on the capture file, just close the old wtap, open a wtap for the new file, tweak the elements in the capture_file structure as necessary and poke the UI to update stuff such as the windows title, and rescan the file to update the packet offsets (and cause Wiretap to regenerate, for a gzipped file, the information needed to support fast random access to the gzipped file). This should speed up Save and Save As a bit, as well as removing some glitches in the UI (e.g., you won't see the packet list disappear and reappear). svn path=/trunk/; revision=43101
-rw-r--r--file.c334
-rw-r--r--file.h3
-rw-r--r--ui/gtk/main.c30
-rw-r--r--ui/gtk/main_statusbar.c8
4 files changed, 336 insertions, 39 deletions
diff --git a/file.c b/file.c
index 076574ad9b..033761516d 100644
--- a/file.c
+++ b/file.c
@@ -3820,13 +3820,245 @@ cf_can_save_as(capture_file *cf)
return FALSE;
}
+/*
+ * Quick scan to find packet offsets.
+ */
+static cf_read_status_t
+rescan_file(capture_file *cf, const char *fname, gboolean is_tempfile, int *err)
+{
+ gchar *err_info;
+ gchar *name_ptr;
+ const char *errmsg;
+ char errmsg_errno[1024+1];
+ gint64 data_offset;
+ gint64 file_pos;
+ progdlg_t *volatile progbar = NULL;
+ gboolean stop_flag;
+ volatile gint64 size;
+ volatile float progbar_val;
+ GTimeVal start_time;
+ gchar status_str[100];
+ volatile gint64 progbar_nextstep;
+ volatile gint64 progbar_quantum;
+ guint32 framenum;
+ frame_data *fdata;
+ volatile int count = 0;
+#ifdef HAVE_LIBPCAP
+ volatile int displayed_once = 0;
+#endif
+
+ /* Close the old handle. */
+ wtap_close(cf->wth);
+
+ /* Open the new file. */
+ cf->wth = wtap_open_offline(fname, err, &err_info, TRUE);
+ if (cf->wth == NULL) {
+ cf_open_failure_alert_box(fname, *err, err_info, FALSE, 0);
+ return CF_READ_ERROR;
+ }
+
+ /* We're scanning a file whose contents should be the same as what
+ we had before, so we don't discard dissection state etc.. */
+ cf->f_datalen = 0;
+
+ /* Set the file name because we need it to set the follow stream filter.
+ XXX - is that still true? We need it for other reasons, though,
+ in any case. */
+ cf->filename = g_strdup(fname);
+
+ /* Indicate whether it's a permanent or temporary file. */
+ cf->is_tempfile = is_tempfile;
+
+ /* No user changes yet. */
+ cf->unsaved_changes = FALSE;
+
+ cf->cd_t = wtap_file_type(cf->wth);
+
+ cf->snap = wtap_snapshot_length(cf->wth);
+ if (cf->snap == 0) {
+ /* Snapshot length not known. */
+ cf->has_snap = FALSE;
+ cf->snap = WTAP_MAX_PACKET_SIZE;
+ } else
+ cf->has_snap = TRUE;
+
+ name_ptr = g_filename_display_basename(cf->filename);
+
+ cf_callback_invoke(cf_cb_file_rescan_started, cf);
+
+ /* Record whether the file is compressed. */
+ cf->iscompressed = wtap_iscompressed(cf->wth);
+
+ /* Find the size of the file. */
+ size = wtap_file_size(cf->wth, NULL);
+
+ /* Update the progress bar when it gets to this value. */
+ progbar_nextstep = 0;
+ /* When we reach the value that triggers a progress bar update,
+ bump that value by this amount. */
+ if (size >= 0){
+ progbar_quantum = size/N_PROGBAR_UPDATES;
+ if (progbar_quantum < MIN_QUANTUM)
+ progbar_quantum = MIN_QUANTUM;
+ }else
+ progbar_quantum = 0;
+ /* Progress so far. */
+ progbar_val = 0.0f;
+
+ stop_flag = FALSE;
+ g_get_current_time(&start_time);
+
+ framenum = 0;
+ while ((wtap_read(cf->wth, err, &err_info, &data_offset))) {
+ framenum++;
+ fdata = frame_data_sequence_find(cf->frames, framenum);
+ fdata->file_off = data_offset;
+ if (size >= 0) {
+ count++;
+ file_pos = wtap_read_so_far(cf->wth);
+
+ /* Create the progress bar if necessary.
+ * Check whether it should be created or not every MIN_NUMBER_OF_PACKET
+ */
+ if ((progbar == NULL) && !(count % MIN_NUMBER_OF_PACKET)){
+ progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
+ progbar = delayed_create_progress_dlg("Rescanning", name_ptr,
+ TRUE, &stop_flag, &start_time, progbar_val);
+ }
+
+ /* Update the progress bar, but do it only N_PROGBAR_UPDATES times;
+ when we update it, we have to run the GTK+ main loop to get it
+ to repaint what's pending, and doing so may involve an "ioctl()"
+ to see if there's any pending input from an X server, and doing
+ that for every packet can be costly, especially on a big file. */
+ if (file_pos >= progbar_nextstep) {
+ if (progbar != NULL) {
+ progbar_val = calc_progbar_val(cf, size, file_pos, status_str, sizeof(status_str));
+ /* update the packet bar content on the first run or frequently on very large files */
+#ifdef HAVE_LIBPCAP
+ if (progbar_quantum > 500000 || displayed_once == 0) {
+ if ((auto_scroll_live || displayed_once == 0 || cf->displayed_count < 1000) && cf->count != 0) {
+ displayed_once = 1;
+ packets_bar_update();
+ }
+ }
+#endif /* HAVE_LIBPCAP */
+ update_progress_dlg(progbar, progbar_val, status_str);
+ }
+ progbar_nextstep += progbar_quantum;
+ }
+ }
+
+ if (stop_flag) {
+ /* Well, the user decided to abort the rescan. Sadly, as this
+ isn't a reread, recovering is difficult, so we'll just
+ close the current capture. */
+ break;
+ }
+ }
+
+ /* Free the display name */
+ g_free(name_ptr);
+
+ /* We're done reading the file; destroy the progress bar if it was created. */
+ if (progbar != NULL)
+ destroy_progress_dlg(progbar);
+
+ /* We're done reading sequentially through the file. */
+ cf->state = FILE_READ_DONE;
+
+ /* Close the sequential I/O side, to free up memory it requires. */
+ wtap_sequential_close(cf->wth);
+
+ /* compute the time it took to load the file */
+ compute_elapsed(&start_time);
+
+ /* Set the file encapsulation type now; we don't know what it is until
+ we've looked at all the packets, as we don't know until then whether
+ there's more than one type (and thus whether it's
+ WTAP_ENCAP_PER_PACKET). */
+ cf->lnk_t = wtap_file_encap(cf->wth);
+
+ cf_callback_invoke(cf_cb_file_rescan_finished, cf);
+
+ if (stop_flag) {
+ /* Our caller will give up at this point. */
+ return CF_READ_ABORTED;
+ }
+
+ if (*err != 0) {
+ /* Put up a message box noting that the read failed somewhere along
+ the line. Don't throw out the stuff we managed to read, though,
+ if any. */
+ switch (*err) {
+
+ case WTAP_ERR_UNSUPPORTED:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The capture file contains record data that TShark doesn't support.\n(%s)",
+ err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
+
+ case WTAP_ERR_UNSUPPORTED_ENCAP:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The capture file has a packet with a network type that Wireshark doesn't support.\n(%s)",
+ err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
+
+ case WTAP_ERR_CANT_READ:
+ errmsg = "An attempt to read from the capture file failed for"
+ " some unknown reason.";
+ break;
+
+ case WTAP_ERR_SHORT_READ:
+ errmsg = "The capture file appears to have been cut short"
+ " in the middle of a packet.";
+ break;
+
+ case WTAP_ERR_BAD_FILE:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The capture file appears to be damaged or corrupt.\n(%s)",
+ err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
+
+ case WTAP_ERR_DECOMPRESS:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "The compressed capture file appears to be damaged or corrupt.\n"
+ "(%s)", err_info);
+ g_free(err_info);
+ errmsg = errmsg_errno;
+ break;
+
+ default:
+ g_snprintf(errmsg_errno, sizeof(errmsg_errno),
+ "An error occurred while reading the"
+ " capture file: %s.", wtap_strerror(*err));
+ errmsg = errmsg_errno;
+ break;
+ }
+ simple_dialog(ESD_TYPE_ERROR, ESD_BTN_OK, "%s", errmsg);
+ return CF_READ_ERROR;
+ } else
+ return CF_READ_OK;
+}
+
cf_write_status_t
cf_save_packets(capture_file *cf, const char *fname, guint save_format,
gboolean compressed, gboolean dont_reopen)
{
gchar *fname_new = NULL;
int err;
- gboolean do_copy;
+ gchar *err_info;
+ enum {
+ SAVE_WITH_MOVE,
+ SAVE_WITH_COPY,
+ SAVE_WITH_WTAP
+ } how_to_save;
wtap_dumper *pdh;
save_callback_args_t callback_args;
@@ -3864,12 +4096,12 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
#ifndef _WIN32
if (ws_rename(cf->filename, fname) == 0) {
/* That succeeded - there's no need to copy the source file. */
- do_copy = FALSE;
+ how_to_save = SAVE_WITH_MOVE;
} else {
if (errno == EXDEV) {
/* They're on different file systems, so we have to copy the
file. */
- do_copy = TRUE;
+ how_to_save = SAVE_WITH_COPY;
} else {
/* The rename failed, but not because they're on different
file systems - put up an error message. (Or should we
@@ -3884,15 +4116,15 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
}
}
#else
- do_copy = TRUE;
+ how_to_save = SAVE_WITH_COPY;
#endif
} else {
/* It's a permanent file, so we should copy it, and not remove the
original. */
- do_copy = TRUE;
+ how_to_save = SAVE_WITH_COPY;
}
- if (do_copy) {
+ if (how_to_save == SAVE_WITH_COPY) {
/* Copy the file, if we haven't moved it. If we're overwriting
an existing file, we do it with a "safe save", by writing
to a new file and, if the write succeeds, renaming the
@@ -3980,6 +4212,8 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
cf_close_failure_alert_box(fname, err);
goto fail;
}
+
+ how_to_save = SAVE_WITH_WTAP;
}
if (fname_new != NULL) {
@@ -4013,41 +4247,63 @@ cf_save_packets(capture_file *cf, const char *fname, guint save_format,
cf->unsaved_changes = FALSE;
if (!dont_reopen) {
- /* Open and read the file we saved to.
-
- XXX - this is somewhat of a waste; we already have the
- packets, all this gets us is updated file type information
- (which we could just stuff into "cf"), and having the new
- file be the one we have opened and from which we're reading
- the data, and it means we have to spend time opening and
- reading the file, which could be a significant amount of
- time if the file is large.
-
- If the capture-file-writing code were to return the
- seek offset of each packet it writes, we could save that
- in the frame_data structure for the frame, and just open
- the file without reading it again. */
-
- if ((cf_open(cf, fname, FALSE, &err)) == CF_OK) {
- /* XXX - report errors if this fails?
- What should we return if it fails or is aborted? */
-
- switch (cf_read(cf, TRUE)) {
-
- case CF_READ_OK:
- case CF_READ_ERROR:
- /* Just because we got an error, that doesn't mean we were unable
- to read any of the file; we handle what we could get from the
- file. */
- break;
+ switch (how_to_save) {
+
+ case SAVE_WITH_MOVE:
+ /* We just moved the file, so the wtap structure refers to the
+ new file, and all the information other than the filename
+ and the "is temporary" status applies to the new file; just
+ update that. */
+ g_free(cf->filename);
+ cf->filename = g_strdup(fname);
+ cf->is_tempfile = FALSE;
+ cf_callback_invoke(cf_cb_file_fast_save_finished, cf);
+ break;
- case CF_READ_ABORTED:
- /* The user bailed out of re-reading the capture file; the
- capture file has been closed - just return (without
- changing any menu settings; "cf_close()" set them
- correctly for the "no capture file open" state). */
- break;
+ case SAVE_WITH_COPY:
+ /* We just copied the file, s all the information other than
+ the wtap structure, the filename, and the "is temporary"
+ status applies to the new file; just update that. */
+ wtap_close(cf->wth);
+ cf->wth = wtap_open_offline(fname, &err, &err_info, TRUE);
+ if (cf->wth == NULL) {
+ cf_open_failure_alert_box(fname, err, err_info, FALSE, 0);
+ cf_close(cf);
+ } else {
+ g_free(cf->filename);
+ cf->filename = g_strdup(fname);
+ cf->is_tempfile = FALSE;
+ }
+ cf_callback_invoke(cf_cb_file_fast_save_finished, cf);
+ break;
+
+ case SAVE_WITH_WTAP:
+ /* Open and read the file we saved to.
+
+ XXX - this is somewhat of a waste; we already have the
+ packets, all this gets us is updated file type information
+ (which we could just stuff into "cf"), and having the new
+ file be the one we have opened and from which we're reading
+ the data, and it means we have to spend time opening and
+ reading the file, which could be a significant amount of
+ time if the file is large.
+
+ If the capture-file-writing code were to return the
+ seek offset of each packet it writes, we could save that
+ in the frame_data structure for the frame, and just open
+ the file without reading it again...
+
+ ...as long as, for gzipped files, the process of writing
+ out the file *also* generates the information needed to
+ support fast random access to the compressed file. */
+ if (rescan_file(cf, fname, FALSE, &err) != CF_READ_OK) {
+ /* The rescan failed; just close the file. Either
+ a dialog was popped up for the failure, so the
+ user knows what happened, or they stopped the
+ rescan, in which case they know what happened. */
+ cf_close(cf);
}
+ break;
}
}
return CF_WRITE_OK;
diff --git a/file.h b/file.h
index b40a642d91..08c146a845 100644
--- a/file.h
+++ b/file.h
@@ -72,6 +72,9 @@ typedef enum {
cf_cb_file_read_finished,
cf_cb_file_reload_started,
cf_cb_file_reload_finished,
+ cf_cb_file_rescan_started,
+ cf_cb_file_rescan_finished,
+ cf_cb_file_fast_save_finished,
cf_cb_packet_selected,
cf_cb_packet_unselected,
cf_cb_field_unselected,
diff --git a/ui/gtk/main.c b/ui/gtk/main.c
index 2c38529ab1..80dd886fb7 100644
--- a/ui/gtk/main.c
+++ b/ui/gtk/main.c
@@ -1485,6 +1485,25 @@ main_cf_cb_file_read_finished(capture_file *cf)
set_menus_for_captured_packets(TRUE);
}
+static void
+main_cf_cb_file_rescan_finished(capture_file *cf)
+{
+ gchar *dir_path;
+
+ if (!cf->is_tempfile && cf->filename) {
+ /* Add this filename to the list of recent files in the "Recent Files" submenu */
+ add_menu_recent_capture_file(cf->filename);
+
+ /* Remember folder for next Open dialog and save it in recent */
+ dir_path = get_dirname(g_strdup(cf->filename));
+ set_last_open_dir(dir_path);
+ g_free(dir_path);
+ }
+
+ /* Update the appropriate parts of the main window. */
+ main_update_for_unsaved_changes(cf);
+}
+
#ifdef HAVE_LIBPCAP
static GList *icon_list_create(
const char **icon16_xpm,
@@ -1747,6 +1766,17 @@ main_cf_callback(gint event, gpointer data, gpointer user_data _U_)
g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Reload finished");
main_cf_cb_file_read_finished(data);
break;
+ case(cf_cb_file_rescan_started):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Rescan started");
+ break;
+ case(cf_cb_file_rescan_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Rescan finished");
+ main_cf_cb_file_rescan_finished(data);
+ break;
+ case(cf_cb_file_fast_save_finished):
+ g_log(LOG_DOMAIN_MAIN, G_LOG_LEVEL_DEBUG, "Callback: Fast save finished");
+ main_cf_cb_file_rescan_finished(data);
+ break;
case(cf_cb_packet_selected):
main_cf_cb_packet_selected(data);
break;
diff --git a/ui/gtk/main_statusbar.c b/ui/gtk/main_statusbar.c
index 1d3ffbc6e9..324d03f110 100644
--- a/ui/gtk/main_statusbar.c
+++ b/ui/gtk/main_statusbar.c
@@ -1002,6 +1002,14 @@ statusbar_cf_callback(gint event, gpointer data, gpointer user_data _U_)
case(cf_cb_file_reload_finished):
statusbar_cf_file_read_finished_cb(data);
break;
+ case(cf_cb_file_rescan_started):
+ statusbar_cf_file_read_started_cb(data, "Rescanning");
+ break;
+ case(cf_cb_file_rescan_finished):
+ statusbar_cf_file_read_finished_cb(data);
+ break;
+ case(cf_cb_file_fast_save_finished):
+ break;
case(cf_cb_packet_selected):
break;
case(cf_cb_packet_unselected):