/* when receive/send is complete */ static void receive_done (gint still_more, gpointer data) { struct _send_info *info = data; const gchar *uid; uid = camel_service_get_uid (info->service); g_return_if_fail (uid != NULL); /* if we've been called to run again - run again */ if (info->type == SEND_SEND && info->state == SEND_ACTIVE && info->again) { EMailSession *session; CamelFolder *local_outbox; session = info->session; local_outbox = e_mail_session_get_local_folder ( session, E_MAIL_LOCAL_FOLDER_OUTBOX); g_return_if_fail (CAMEL_IS_TRANSPORT (info->service)); info->again = 0; mail_send_queue ( info->session, local_outbox, CAMEL_TRANSPORT (info->service), E_FILTER_SOURCE_OUTGOING, info->cancellable, receive_get_folder, info, receive_status, info, send_done, info); return; } //FIXME Set SEND completed here /* if (info->state == SEND_CANCELLED) text = _("Canceled."); else { text = _("Complete."); info->state = SEND_COMPLETE; } */ /* remove/free this active download */ d(printf("%s: freeing info %p\n", G_STRFUNC, info)); if (info->type == SEND_SEND) g_hash_table_steal (info->data->active, SEND_URI_KEY); else g_hash_table_steal (info->data->active, uid); info->data->infos = g_list_remove (info->data->infos, info); if (g_hash_table_size (info->data->active) == 0) { //FIXME: THIS MEANS SEND RECEIVE IS COMPLETED free_send_data (); } free_send_info (info); }
/* when receive/send is complete */ static void receive_done (char *uri, void *data) { struct _send_info *info = data; /* if we've been called to run again - run again */ if (info->type == SEND_SEND && info->state == SEND_ACTIVE && info->again) { info->again = 0; mail_send_queue (mail_component_get_folder(NULL, MAIL_COMPONENT_FOLDER_OUTBOX), info->uri, FILTER_SOURCE_OUTGOING, info->cancel, receive_get_folder, info, receive_status, info, receive_done, info); return; } if (info->progress_bar) { const gchar *text; gtk_progress_bar_set_fraction( GTK_PROGRESS_BAR (info->progress_bar), 1.0); if (info->state == SEND_CANCELLED) text = _("Canceled."); else { text = _("Complete."); info->state = SEND_COMPLETE; } gtk_label_set_text (GTK_LABEL (info->status_label), text); } if (info->cancel_button) gtk_widget_set_sensitive (info->cancel_button, FALSE); /* remove/free this active download */ d(printf("%s: freeing info %p\n", G_STRFUNC, info)); if (info->type == SEND_SEND) g_hash_table_steal(info->data->active, SEND_URI_KEY); else g_hash_table_steal(info->data->active, info->uri); info->data->infos = g_list_remove(info->data->infos, info); if (g_hash_table_size(info->data->active) == 0) { if (info->data->gd) gtk_widget_destroy((GtkWidget *)info->data->gd); free_send_data(); } free_send_info(info); }
int cmd_send(int argc, char **argv) { char *subvol = NULL; int ret; char outname[PATH_MAX]; struct btrfs_send send; u32 i; char *mount_root = NULL; char *snapshot_parent = NULL; u64 root_id = 0; u64 parent_root_id = 0; int full_send = 1; int new_end_cmd_semantic = 0; u64 send_flags = 0; memset(&send, 0, sizeof(send)); send.dump_fd = fileno(stdout); outname[0] = 0; while (1) { enum { GETOPT_VAL_SEND_NO_DATA = 256 }; static const struct option long_options[] = { { "verbose", no_argument, NULL, 'v' }, { "quiet", no_argument, NULL, 'q' }, { "no-data", no_argument, NULL, GETOPT_VAL_SEND_NO_DATA } }; int c = getopt_long(argc, argv, "vqec:f:i:p:", long_options, NULL); if (c < 0) break; switch (c) { case 'v': g_verbose++; break; case 'q': g_verbose = 0; break; case 'e': new_end_cmd_semantic = 1; break; case 'c': subvol = realpath(optarg, NULL); if (!subvol) { ret = -errno; error("realpath %s failed: %s\n", optarg, strerror(-ret)); goto out; } ret = set_root_info(&send, subvol, &root_id); if (ret < 0) goto out; ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("cloned subvolume %s is not read-only", subvol); goto out; } ret = add_clone_source(&send, root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } free(subvol); subvol = NULL; free_send_info(&send); full_send = 0; break; case 'f': if (arg_copy_path(outname, optarg, sizeof(outname))) { error("output file path too long (%zu)", strlen(optarg)); ret = 1; goto out; } break; case 'p': if (snapshot_parent) { error("you cannot have more than one parent (-p)"); ret = 1; goto out; } snapshot_parent = realpath(optarg, NULL); if (!snapshot_parent) { ret = -errno; error("realpath %s failed: %s", optarg, strerror(-ret)); goto out; } ret = is_subvol_ro(&send, snapshot_parent); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("parent subvolume %s is not read-only", snapshot_parent); goto out; } full_send = 0; break; case 'i': error("option -i was removed, use -c instead"); ret = 1; goto out; case GETOPT_VAL_SEND_NO_DATA: send_flags |= BTRFS_SEND_FLAG_NO_FILE_DATA; break; case '?': default: error("send arguments invalid"); ret = 1; goto out; } } if (check_argc_min(argc - optind, 1)) usage(cmd_send_usage); if (outname[0]) { int tmpfd; /* * Try to use an existing file first. Even if send runs as * root, it might not have permissions to create file (eg. on a * NFS) but it should still be able to use a pre-created file. */ tmpfd = open(outname, O_WRONLY | O_TRUNC); if (tmpfd < 0) { if (errno == ENOENT) tmpfd = open(outname, O_CREAT | O_WRONLY | O_TRUNC, 0600); } send.dump_fd = tmpfd; if (send.dump_fd == -1) { ret = -errno; error("cannot create '%s': %s", outname, strerror(-ret)); goto out; } } if (isatty(send.dump_fd)) { error( "not dumping send stream into a terminal, redirect it into a file"); ret = 1; goto out; } /* use first send subvol to determine mount_root */ subvol = realpath(argv[optind], NULL); if (!subvol) { ret = -errno; error("unable to resolve %s", argv[optind]); goto out; } ret = init_root_path(&send, subvol); if (ret < 0) goto out; if (snapshot_parent != NULL) { ret = get_root_id(&send, subvol_strip_mountpoint(send.root_path, snapshot_parent), &parent_root_id); if (ret < 0) { error("could not resolve rootid for %s", snapshot_parent); goto out; } ret = add_clone_source(&send, parent_root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } } for (i = optind; i < argc; i++) { free(subvol); subvol = realpath(argv[i], NULL); if (!subvol) { ret = -errno; error("unable to resolve %s", argv[i]); goto out; } ret = find_mount_root(subvol, &mount_root); if (ret < 0) { error("find_mount_root failed on %s: %s", subvol, strerror(-ret)); goto out; } if (ret > 0) { error("%s does not belong to btrfs mount point", subvol); ret = -EINVAL; goto out; } if (strcmp(send.root_path, mount_root) != 0) { ret = -EINVAL; error("all subvolumes must be from the same filesystem"); goto out; } free(mount_root); ret = is_subvol_ro(&send, subvol); if (ret < 0) goto out; if (!ret) { ret = -EINVAL; error("subvolume %s is not read-only", subvol); goto out; } } if ((send_flags & BTRFS_SEND_FLAG_NO_FILE_DATA) && g_verbose > 1) if (g_verbose > 1) fprintf(stderr, "Mode NO_FILE_DATA enabled\n"); for (i = optind; i < argc; i++) { int is_first_subvol; int is_last_subvol; free(subvol); subvol = argv[i]; if (g_verbose > 0) fprintf(stderr, "At subvol %s\n", subvol); subvol = realpath(subvol, NULL); if (!subvol) { ret = -errno; error("realpath %s failed: %s", argv[i], strerror(-ret)); goto out; } if (!full_send && !snapshot_parent) { ret = set_root_info(&send, subvol, &root_id); if (ret < 0) goto out; ret = find_good_parent(&send, root_id, &parent_root_id); if (ret < 0) { error("parent determination failed for %lld", root_id); goto out; } } if (new_end_cmd_semantic) { /* require new kernel */ is_first_subvol = (i == optind); is_last_subvol = (i == argc - 1); } else { /* be compatible to old and new kernel */ is_first_subvol = 1; is_last_subvol = 1; } ret = do_send(&send, parent_root_id, is_first_subvol, is_last_subvol, subvol, send_flags); if (ret < 0) goto out; if (!full_send && !snapshot_parent) { /* done with this subvol, so add it to the clone sources */ ret = add_clone_source(&send, root_id); if (ret < 0) { error("cannot add clone source: %s", strerror(-ret)); goto out; } free_send_info(&send); } } ret = 0; out: free(subvol); free(snapshot_parent); free(send.clone_sources); free_send_info(&send); return !!ret; }