12a9e2e59SSteve Sistare /* 22a9e2e59SSteve Sistare * Copyright (c) 2021-2023 Oracle and/or its affiliates. 32a9e2e59SSteve Sistare * 42a9e2e59SSteve Sistare * This work is licensed under the terms of the GNU GPL, version 2 or later. 52a9e2e59SSteve Sistare * See the COPYING file in the top-level directory. 62a9e2e59SSteve Sistare */ 72a9e2e59SSteve Sistare 82a9e2e59SSteve Sistare #include "qemu/osdep.h" 9d117ed06SFabiano Rosas #include "exec/ramblock.h" 10385f510dSSteve Sistare #include "qemu/cutils.h" 11385f510dSSteve Sistare #include "qapi/error.h" 122a9e2e59SSteve Sistare #include "channel.h" 132a9e2e59SSteve Sistare #include "file.h" 142a9e2e59SSteve Sistare #include "migration.h" 15b7b03eb6SFabiano Rosas #include "multifd.h" 162a9e2e59SSteve Sistare #include "io/channel-file.h" 172a9e2e59SSteve Sistare #include "io/channel-util.h" 182a9e2e59SSteve Sistare #include "trace.h" 192a9e2e59SSteve Sistare 20385f510dSSteve Sistare #define OFFSET_OPTION ",offset=" 21385f510dSSteve Sistare 22b7b03eb6SFabiano Rosas static struct FileOutgoingArgs { 23b7b03eb6SFabiano Rosas char *fname; 24b7b03eb6SFabiano Rosas } outgoing_args; 25b7b03eb6SFabiano Rosas 26385f510dSSteve Sistare /* Remove the offset option from @filespec and return it in @offsetp. */ 27385f510dSSteve Sistare 2872a8192eSHet Gala int file_parse_offset(char *filespec, uint64_t *offsetp, Error **errp) 29385f510dSSteve Sistare { 30385f510dSSteve Sistare char *option = strstr(filespec, OFFSET_OPTION); 31385f510dSSteve Sistare int ret; 32385f510dSSteve Sistare 33385f510dSSteve Sistare if (option) { 34385f510dSSteve Sistare *option = 0; 35385f510dSSteve Sistare option += sizeof(OFFSET_OPTION) - 1; 36385f510dSSteve Sistare ret = qemu_strtosz(option, NULL, offsetp); 37385f510dSSteve Sistare if (ret) { 38385f510dSSteve Sistare error_setg_errno(errp, -ret, "file URI has bad offset %s", option); 39385f510dSSteve Sistare return -1; 40385f510dSSteve Sistare } 41385f510dSSteve Sistare } 42385f510dSSteve Sistare return 0; 43385f510dSSteve Sistare } 44385f510dSSteve Sistare 45b7b03eb6SFabiano Rosas void file_cleanup_outgoing_migration(void) 46b7b03eb6SFabiano Rosas { 47b7b03eb6SFabiano Rosas g_free(outgoing_args.fname); 48b7b03eb6SFabiano Rosas outgoing_args.fname = NULL; 49b7b03eb6SFabiano Rosas } 50b7b03eb6SFabiano Rosas 51b7b03eb6SFabiano Rosas bool file_send_channel_create(gpointer opaque, Error **errp) 52b7b03eb6SFabiano Rosas { 53b7b03eb6SFabiano Rosas QIOChannelFile *ioc; 54b7b03eb6SFabiano Rosas int flags = O_WRONLY; 55b7b03eb6SFabiano Rosas bool ret = true; 56b7b03eb6SFabiano Rosas 57b7b03eb6SFabiano Rosas ioc = qio_channel_file_new_path(outgoing_args.fname, flags, 0, errp); 58b7b03eb6SFabiano Rosas if (!ioc) { 59b7b03eb6SFabiano Rosas ret = false; 60b7b03eb6SFabiano Rosas goto out; 61b7b03eb6SFabiano Rosas } 62b7b03eb6SFabiano Rosas 63b7b03eb6SFabiano Rosas multifd_channel_connect(opaque, QIO_CHANNEL(ioc)); 64b7b03eb6SFabiano Rosas 65b7b03eb6SFabiano Rosas out: 66b7b03eb6SFabiano Rosas /* 67b7b03eb6SFabiano Rosas * File channel creation is synchronous. However posting this 68b7b03eb6SFabiano Rosas * semaphore here is simpler than adding a special case. 69b7b03eb6SFabiano Rosas */ 70b7b03eb6SFabiano Rosas multifd_send_channel_created(); 71b7b03eb6SFabiano Rosas 72b7b03eb6SFabiano Rosas return ret; 73b7b03eb6SFabiano Rosas } 74b7b03eb6SFabiano Rosas 7502afba63SFabiano Rosas void file_start_outgoing_migration(MigrationState *s, 7602afba63SFabiano Rosas FileMigrationArgs *file_args, Error **errp) 772a9e2e59SSteve Sistare { 782a9e2e59SSteve Sistare g_autoptr(QIOChannelFile) fioc = NULL; 7902afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 8002afba63SFabiano Rosas uint64_t offset = file_args->offset; 812a9e2e59SSteve Sistare QIOChannel *ioc; 822a9e2e59SSteve Sistare 832a9e2e59SSteve Sistare trace_migration_file_outgoing(filename); 842a9e2e59SSteve Sistare 852a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_CREAT | O_WRONLY | O_TRUNC, 862a9e2e59SSteve Sistare 0600, errp); 872a9e2e59SSteve Sistare if (!fioc) { 882a9e2e59SSteve Sistare return; 892a9e2e59SSteve Sistare } 902a9e2e59SSteve Sistare 91b7b03eb6SFabiano Rosas outgoing_args.fname = g_strdup(filename); 92b7b03eb6SFabiano Rosas 932a9e2e59SSteve Sistare ioc = QIO_CHANNEL(fioc); 94385f510dSSteve Sistare if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 95385f510dSSteve Sistare return; 96385f510dSSteve Sistare } 972a9e2e59SSteve Sistare qio_channel_set_name(ioc, "migration-file-outgoing"); 982a9e2e59SSteve Sistare migration_channel_connect(s, ioc, NULL, NULL); 992a9e2e59SSteve Sistare } 1002a9e2e59SSteve Sistare 1012a9e2e59SSteve Sistare static gboolean file_accept_incoming_migration(QIOChannel *ioc, 1022a9e2e59SSteve Sistare GIOCondition condition, 1032a9e2e59SSteve Sistare gpointer opaque) 1042a9e2e59SSteve Sistare { 1052a9e2e59SSteve Sistare migration_channel_process_incoming(ioc); 1062a9e2e59SSteve Sistare object_unref(OBJECT(ioc)); 1072a9e2e59SSteve Sistare return G_SOURCE_REMOVE; 1082a9e2e59SSteve Sistare } 1092a9e2e59SSteve Sistare 11002afba63SFabiano Rosas void file_start_incoming_migration(FileMigrationArgs *file_args, Error **errp) 1112a9e2e59SSteve Sistare { 11202afba63SFabiano Rosas g_autofree char *filename = g_strdup(file_args->filename); 1132a9e2e59SSteve Sistare QIOChannelFile *fioc = NULL; 11402afba63SFabiano Rosas uint64_t offset = file_args->offset; 1152a9e2e59SSteve Sistare QIOChannel *ioc; 1162a9e2e59SSteve Sistare 1172a9e2e59SSteve Sistare trace_migration_file_incoming(filename); 1182a9e2e59SSteve Sistare 1192a9e2e59SSteve Sistare fioc = qio_channel_file_new_path(filename, O_RDONLY, 0, errp); 1202a9e2e59SSteve Sistare if (!fioc) { 1212a9e2e59SSteve Sistare return; 1222a9e2e59SSteve Sistare } 1232a9e2e59SSteve Sistare 1242a9e2e59SSteve Sistare ioc = QIO_CHANNEL(fioc); 125385f510dSSteve Sistare if (offset && qio_channel_io_seek(ioc, offset, SEEK_SET, errp) < 0) { 126385f510dSSteve Sistare return; 127385f510dSSteve Sistare } 1282a9e2e59SSteve Sistare qio_channel_set_name(QIO_CHANNEL(ioc), "migration-file-incoming"); 1292a9e2e59SSteve Sistare qio_channel_add_watch_full(ioc, G_IO_IN, 1302a9e2e59SSteve Sistare file_accept_incoming_migration, 1312a9e2e59SSteve Sistare NULL, NULL, 1322a9e2e59SSteve Sistare g_main_context_get_thread_default()); 1332a9e2e59SSteve Sistare } 134