diff --git a/Makefile.am b/Makefile.am index 63de8b7..de9862c 100644 --- a/Makefile.am +++ b/Makefile.am @@ -12,6 +12,7 @@ install-exec-local: $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfrm $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfstat $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gftail + $(LN_S) -f gfcli $(DESTDIR)$(bindir)/gfmv rpms: prep QA_RPATHS=17 rpmbuild --define '_topdir $(abs_top_builddir)/build/rpmbuild' \ @@ -25,7 +26,7 @@ prep: dist cp glusterfs-coreutils.spec build/rpmbuild/SPECS uninstall-local: - cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfmv gfrm gfstat gftail + cd $(DESTDIR)$(bindir) && rm -f gfcat gfcp gfls gfmkdir gfmv gfrm gfstat gftail gftouch clean-local: rm -rf build rpmbuild *.rpm diff --git a/man/Makefile.am b/man/Makefile.am index 8f10683..29581f0 100644 --- a/man/Makefile.am +++ b/man/Makefile.am @@ -39,6 +39,11 @@ gftail.1: $(top_builddir)/build/bin/gftail $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ $(top_builddir)/build/bin/$* && mv $@-t $@ +gfmv.1: $(top_builddir)/build/bin/gfmv + $(HELP2MAN) --output=$@-t -I common_seealso.h2m \ + $(top_builddir)/build/bin/$* && mv $@-t $@ + + man1_MANS = gfcat.1 \ gfcli.1 \ gfcp.1 \ @@ -47,8 +52,8 @@ man1_MANS = gfcat.1 \ gfput.1 \ gfrm.1 \ gfstat.1 \ - gftail.1 - + gftail.1 \ + gfmv.1 EXTRA_DIST = common_seealso.h2m $(man1_MANS): $(srcdir)/common_seealso.h2m diff --git a/src/Makefile.am b/src/Makefile.am index f6c44e8..003a1df 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -9,6 +9,8 @@ all-local: $(LN_S) -f gfcli $(top_builddir)/build/bin/gfrm $(LN_S) -f gfcli $(top_builddir)/build/bin/gfstat $(LN_S) -f gfcli $(top_builddir)/build/bin/gftail + $(LN_S) -f gfcli $(top_builddir)/build/bin/gfmv + bin_PROGRAMS = $(top_builddir)/build/bin/gfcli \ $(top_builddir)/build/bin/gfput @@ -24,7 +26,8 @@ EXTRA_DIST = glfs-cat.h \ glfs-stat.h \ glfs-stat-util.h \ glfs-tail.h \ - glfs-util.h + glfs-util.h \ + glfs-mv.h __top_builddir__build_bin_gfcli_SOURCES = glfs-cli.c \ glfs-cli-commands.c \ @@ -37,7 +40,8 @@ __top_builddir__build_bin_gfcli_SOURCES = glfs-cli.c \ glfs-stat.c \ glfs-stat-util.c \ glfs-tail.c \ - glfs-util.c + glfs-util.c \ + glfs-mv.c __top_builddir__build_bin_gfcli_CFLAGS = $(GLFS_CFLAGS) __top_builddir__build_bin_gfcli_LDADD = $(LDADD) $(GLFS_LIBS) -lreadline diff --git a/src/glfs-cli.c b/src/glfs-cli.c index caac351..d36a5f2 100644 --- a/src/glfs-cli.c +++ b/src/glfs-cli.c @@ -45,6 +45,7 @@ #include "glfs-stat.h" #include "glfs-tail.h" #include "glfs-util.h" +#include "glfs-mv.h" #define AUTHORS "Written by Craig Cabrey." @@ -73,7 +74,8 @@ shell_usage () "* rm\n" "* stat\n" "* tail\n" - "* flock\n"); + "* flock\n" + "* mv\n"); return 0; } @@ -88,12 +90,12 @@ static struct cmd const cmds[] = { .name = "help", .execute = shell_usage }, { .alias = "gfls", .name = "ls", .execute = do_ls }, { .alias = "gfmkdir", .name = "mkdir", .execute = do_mkdir }, - { .alias = "gfmv", .name = "mv", .execute = not_implemented }, { .name = "quit", .execute = handle_quit }, { .alias = "gfrm", .name = "rm", .execute = do_rm }, { .alias = "gfstat", .name = "stat", .execute = do_stat }, { .alias = "gftail", .name = "tail", .execute = do_tail }, - { .name = "flock", .execute = do_flock } + { .name = "flock", .execute = do_flock }, + { .alias = "gfmv", .name = "mv", .execute = do_mv } }; static const struct cmd* diff --git a/src/glfs-mv.c b/src/glfs-mv.c new file mode 100644 index 0000000..f6a4dfb --- /dev/null +++ b/src/glfs-mv.c @@ -0,0 +1,1000 @@ +/** + * A utility to copy a file to or from a remote Gluster volume locally or to or + * from another remote Gluster volume. + * + * Copyright (C) 2017 Red Hat + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#include + +#include "glfs-mv.h" +#include "glfs-util.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AUTHORS "Written by Mohamed Insaf K." +#define BUFFER_SIZE 1024*1024 + +/** + * Represents the various transfer modes supported by gfmv. + * In this context, ESTABLISHED refers to the case where a + * connection has already been established in the CLI and we + * are getting a connection passed to us via cli_context. + */ +enum transfer_mode { + ESTABLISHED_TO_ESTABLISHED, + ESTABLISHED_TO_LOCAL, + ESTABLISHED_TO_REMOTE, + LOCAL_TO_ESTABLISHED, + LOCAL_TO_REMOTE, + REMOTE_TO_ESTABLISHED, + REMOTE_TO_LOCAL, + REMOTE_TO_REMOTE +}; + +/** + * Used to store the state of the program, including user supplied options. + * + * gluster_dest: Struct of the parsed destination url supplied by the user. + * gluster_source: Struct of the parsed source url supplied by the user. + * dest: Raw destination string supplied by the user. + * source: Raw source string supplied by the user. + * debug: Whether to log additional debug information. + * mode: The detected transfer mode (deduced from the supplied source and dest). + */ +struct state { + struct gluster_url *gluster_dest; + struct gluster_url *gluster_source; + struct xlator_option *xlator_options; + char *dest; + char *source; + bool debug; + enum transfer_mode mode; +}; + +static struct state *state; +static struct option const long_options[] = +{ + {"debug", no_argument, NULL, 'd'}, + {"help", no_argument, NULL, 'x'}, + {"port", required_argument, NULL, 'p'}, + {"version", no_argument, NULL, 'v'}, + {"xlator-option", required_argument, NULL, 'o'}, + {NULL, no_argument, NULL, 0} +}; + +/** + * Parses a file:// url into a string with just the path. + */ +static char* +parse_file_url (char *file_url) +{ + char *file_path = NULL; + + // file_url should be minimum of 8 characters: file:/// + if (strlen (file_url) <= 7) { + goto out; + } + + // length of file:// is 7 characters + if (strncmp (file_url, "file://", 7) == 0) { + file_path = file_url + 7; + } + +out: + return file_path; +} + +/** + * Prints usage information. + */ +static void +usage () +{ + printf ("Usage: %s [OPTION]... SOURCE DEST\n" + "Move SOURCE to DEST; one of local to remote, remote to local, or remote to remote.\n\n" + " -o, --xlator-option=OPTION specify a translator option for the\n" + " connection. Multiple options are supported\n" + " and take the form xlator.key=value.\n" + " In the case of both the source and the\n" + " destination being Gluster URLs, the options\n" + " will be applied to both connections.\n" + " -p, --port=PORT specify the port on which to connect\n" + " --help display this help and exit\n" + " --version output version information and exit\n\n" + "Examples:\n" + " gfmv ./file glfs://localhost/groot/remote_file\n" + " Moves the local file 'file' to the destination file 'remote_file'\n" + " on the remote Gluster volume of groot on the host localhost.\n" + " gfmv glfs://localhost/groot/remote_file ./file\n" + " Moves the file 'remote_file' on the remote Gluster gluster\n" + " volume of groot on the host localhost to the local file 'file'.\n" + " gfmv glfs://localhost/groot/remote_file glfs://remote_host/groot/file\n" + " Moves the file 'remote_file' on the remote Gluster gluster\n" + " volume of groot on the host localhost to a second remote Gluster\n" + " volume of groot on the host remote_host to the file 'file'.\n" + " gfcli (localhost/groot)> mv /example file://example\n" + " Copy the file example relative to the root of the connected\n" + " Gluster volume to a local file called example.\n" + " gfcli (localhost/groot)> mv file://example glfs://host/volume/example\n" + " Copy the local file example to a remote Gluster volume on the\n" + " host 'host'.\n", + program_invocation_name); +} + +/** + * Parses command line flags into a global application state. + */ +static int +parse_options (int argc, char *argv[], bool has_connection) +{ + uint16_t port = GLUSTER_DEFAULT_PORT; + int ret = -1; + int opt = 0; + int option_index = 0; + struct xlator_option *option; + + // Reset getopt as other utilities may have called it already. + optind = 0; + while (true) { + opt = getopt_long (argc, argv, "o:p:", long_options, + &option_index); + + if (opt == -1) { + break; + } + + switch (opt) { + case 'd': + state->debug = true; + break; + case 'o': + option = parse_xlator_option (optarg); + if (option == NULL) { + error (0, errno, "%s", optarg); + goto err; + } + + if (append_xlator_option (&state->xlator_options, option) == -1) { + error (0, errno, "append_xlator_option: %s", optarg); + goto err; + } + + break; + case 'p': + port = strtoport (optarg); + if (port == 0) { + goto out; + } + + break; + case 'v': + printf ("%s (%s) %s\n%s\n%s\n%s\n", + program_invocation_name, + PACKAGE_NAME, + PACKAGE_VERSION, + COPYRIGHT, + LICENSE, + AUTHORS); + ret = -2; + goto out; + case 'x': + usage (); + ret = -2; + goto out; + default: + goto err; + } + } + + if ((argc - option_index) < 3) { + error (0, 0, "missing operand"); + goto err; + } else { + /* + * The following block of code parses the source and destination + * arguments. Depending on what the user has indicated, the + * transfer mode of the application will differ. There are a few + * possibilities: + * + * -> Source and destination are identical: show an invalid error + * -> Local source and remote destination + * -> Remote source and local destination + * -> Remote source and remote destination + * -> Local source and local destination: show an invalid error + * + * The transfer mode is decided based upon parsing outcomes. For + * example, if gluster_parse_url () fails on the second to last + * argument, we assume that we will be doing a LOCAL_TO_REMOTE + * transfer. Then, if gluster_parse_url () succeeds on the last + * argument, then we are definitely in that mode. If it fails, + * that must mean the user is attempting to execute a local to + * local transfer, which this application does not support. + */ + + if (strcmp (argv[argc - 1], argv[argc - 2]) == 0) { + error (0, EINVAL, "source and destination are the same"); + goto err; + } + + /* + * If we have a connection passed to us from the shell, then + * there are a few differences. One is that local paths must be + * prefixed with file://. Otherwise, there is no way to + * distinguish between a local path and a path relative to the + * volume of the open connection. However, there is still the + * chance that there will be a remote source or destination, so + * we will still attempt to parse either parameter into a + * gluster_url. + * + * -> Local (file://) source and remote (current conn) destination + * -> Local (file://) source and remote (different conn) destination + * -> Remote (current conn) source and local (file://) destination + * -> Remote (different conn) source and local (file://) destination + * -> Remote (current conn) source and remote (different conn) destination + * -> Remote (different conn) source and remote (current conn) destination + * -> Remote (current conn) source and remote (different conn) destination + * -> Remote (different conn) source and remote (different conn) destination + */ + + if (has_connection) { + char *file_path; + + // Part 1: Parse the source string and guess our destination. + // We'll assume that we'll be interacting with the connected + // volume, so start with that. + ret = gluster_parse_url (argv[argc - 2], &(state->gluster_source)); + if (ret == -1) { + file_path = parse_file_url (argv[argc - 2]); + if (file_path == NULL) { + state->source = strdup (argv[argc - 2]); + if (state->source == NULL) { + error (0, errno, "strdup"); + goto out; + } + + // Parsed source is neither a valid glfs:// + // url or a valid file:// url, so assuming + // a path relative to the connected volume. + state->mode = ESTABLISHED_TO_REMOTE; + } else { + state->source = strdup (file_path); + if (state->source == NULL) { + error (0, errno, "strdup"); + goto out; + } + + // Parsed source is a valid file:// url. + state->mode = LOCAL_TO_ESTABLISHED; + } + } else { + state->source = strdup (argv[argc - 2]); + if (state->source == NULL) { + error (0, errno, "strdup"); + goto out; + } + + // Parsed source is a valid glfs:// url. + state->mode = REMOTE_TO_ESTABLISHED; + state->gluster_source->port = port; + } + + // Part 2: Fix our destination based on our source and + // the result of parsing the destination string. + ret = gluster_parse_url (argv[argc - 1], &(state->gluster_dest)); + if (ret == -1) { + file_path = parse_file_url (argv[argc - 1]); + if (file_path == NULL) { + state->dest = strdup (argv[argc - 1]); + if (state->dest == NULL) { + error (0, errno, "strdup"); + goto out; + } + + // If the source argument is a path relative + // to the root of the connected volume, and the + // destination argument is neither a glfs:// url + // or a file:// url, then we must be mv'ing + // to and from the connected volume. + if (state->mode == ESTABLISHED_TO_REMOTE) { + state->mode = ESTABLISHED_TO_ESTABLISHED; + } + + ret = 0; + goto out; + } else { + state->dest = strdup (file_path); + if (state->dest == NULL) { + error (0, errno, "strdup"); + goto out; + } + + switch (state->mode) { + // If the source argument is glfs:// url + // and the destination argument is a valid + // file:// url, then we must be copying from + // a different remote volume to the local + // system. + case REMOTE_TO_ESTABLISHED: + state->mode = REMOTE_TO_LOCAL; + ret = 0; + goto out; + // If the source argument is a path relative + // to the connected volume, and the destination + // argument is a valid file:// url, then we must + // be copying from the connected volume to the + // local system. + case ESTABLISHED_TO_REMOTE: + state->mode = ESTABLISHED_TO_LOCAL; + ret = 0; + goto out; + default: + error (0, 0, "unknown transfer mode"); + goto out; + } + } + } else { + state->dest = strdup (argv[argc - 1]); + if (state->source == NULL) { + error (0, errno, "strdup"); + goto out; + } + + state->gluster_dest->port = port; + + switch (state->mode) { + // Source arugment is a path relative to + // connected the volume, and the destination + // argument is a valid glfs:// url. + case ESTABLISHED_TO_REMOTE: + ret = 0; + goto out; + // If the source argument is a valid glfs:// url and the + // destination argument is a valid glfs:// url, then the + // copy action is from the remote volume to another remote + // volume. + case REMOTE_TO_ESTABLISHED: + state->mode = REMOTE_TO_REMOTE; + ret = 0; + goto out; + // If the source argument is a valid file:// url and the + // destination argument is a valid glfs:// url, then the + // copy action is from the local system to a remote + // volume. + case LOCAL_TO_ESTABLISHED: + state->mode = LOCAL_TO_REMOTE; + ret = 0; + goto out; + default: + error (0, 0, "unknown transfer mode"); + goto out; + } + } + } + + free (state->dest); + free (state->source); + + state->source = strdup (argv[argc - 2]); + if (state->source == NULL) { + error (0, errno, "strdup"); + goto out; + } + + state->dest = strdup (argv[argc - 1]); + if (state->dest == NULL) { + error (0, errno, "strdup"); + goto out; + } + + ret = gluster_parse_url (argv[argc - 2], &(state->gluster_source)); + if (ret == -1) { + state->mode = LOCAL_TO_REMOTE; + } else { + state->mode = REMOTE_TO_LOCAL; + state->gluster_source->port = port; + } + + ret = gluster_parse_url (argv[argc - 1], &(state->gluster_dest)); + if (ret == -1) { + if (state->mode == LOCAL_TO_REMOTE) { + error (0, EINVAL, "local source and destination"); + goto err; + } + + state->mode = REMOTE_TO_LOCAL; + } else { + if (state->mode == REMOTE_TO_LOCAL) { + state->mode = REMOTE_TO_REMOTE; + } + + state->gluster_dest->port = port; + } + } + + ret = 0; + goto out; + +err: + error (0, 0, "Try --help for more information."); +out: + return ret; +} + +/** + * Initializes the global application state. + */ +static struct state* +init_state () +{ + struct state *state = malloc (sizeof (*state)); + + if (state == NULL) { + goto out; + } + + state->debug = false; + state->dest = NULL; + state->gluster_dest = NULL; + state->gluster_source = NULL; + state->source = NULL; + state->xlator_options = NULL; + +out: + return state; +} + +/** + * Helper function that will complete the destination path, given the full local + * path, the user supplied destination path, and an initialized stat struct for + * the supplied destination path. + * + * For example, presume the following is executed: + * + * $ glfs-mv /tmp/some_data glfs://some_host/some_volume + * + * In this case, the source path is '/tmp/some_data' and the destination path is + * '/' (the default case when the user has not supplied an explicit path in a + * Gluster URL). We execute a stat () on '/' in the context of the Gluster + * connection, which indicates that the path is a directory. Since a file + * cannot overwrite a directory, we complete the path of the destination: + * '/some_data'. This behavior is much in the same vain as the GNU coreutils mv + * utility. + * + * It is the responsibility of the caller to free the return value. + */ +static char * +complete_path (const char *source_path, const char *dest_path, const struct stat *statbuf) +{ + char *full_path; + + if (statbuf && S_ISDIR (statbuf->st_mode)) { + char *fmt = "%s/%s"; + size_t fmt_length = 2; + size_t dest_path_length = strlen (dest_path); + + if (dest_path[dest_path_length - 1] == '/') { + fmt_length = 1; + fmt = "%s%s"; + } + + char *base_file = basename (source_path); + size_t length = dest_path_length + strlen (base_file) + fmt_length; + + full_path = malloc (length); + if (full_path == NULL) { + error (0, errno, "complete_path"); + goto out; + } + + snprintf (full_path, length, fmt, dest_path, base_file); + } else { + full_path = strdup (dest_path); + } + +out: + return full_path; +} + +/** + * Perform a LOCAL_TO_REMOTE transfer, given the local source and remote + * destination, and an active connection to the remote destination. + */ +static int +local_to_remote (const char *local_path, const char *remote_path, glfs_t *fs) +{ + int ret = -1; + int fd; + glfs_fd_t *remote_fd = NULL; + struct stat statbuf; + char *full_path = NULL; + + fd = open (local_path, O_RDONLY); + if (fd == -1) { + error (0, errno, "%s", local_path); + goto out; + } + + ret = glfs_lstat (fs, remote_path, &statbuf); + + if (ret == -1) { + full_path = complete_path (local_path, remote_path, NULL); + } else { + full_path = complete_path (local_path, remote_path, &statbuf); + } + + if (full_path == NULL) { + ret = -1; + goto out; + } + + remote_fd = glfs_creat (fs, full_path, O_RDWR, get_default_file_mode_perm ()); + if (remote_fd == NULL) { + error (0, errno, "failed to create %s", full_path); + ret = -1; + goto out; + } + + ret = gluster_lock (remote_fd, F_WRLCK, false); + if (ret == -1) { + error (0, errno, "failed to lock %s", full_path); + goto out; + } + + ret = glfs_ftruncate (remote_fd, 0); + if (ret == -1) { + error (0, errno, "failed to truncate %s", full_path); + goto out; + } + + if (gluster_write (fd, remote_fd) == 0) { + ret = -1; + error (0, errno, "failed to transfer %s", local_path); + } + + //TODO error check + //Remote transmission, so after completing the copying, delete the source file. + unlink (local_path); + + /// +out: + free (full_path); + + if (fd != -1) { + close (fd); + } + + if (remote_fd) { + glfs_close (remote_fd); + } + + return ret; +} + +/** + * Perform a REMOTE_TO_LOCAL transfer, given the remote path and local + * destination, and an active connection to the remote source. + */ +static int +remote_to_local (const char *remote_path, const char *local_path, glfs_t *fs) +{ + int ret = -1; + int local_fd = -1; + glfs_fd_t *remote_fd = NULL; + struct stat statbuf; + char *full_path; + + ret = stat (local_path, &statbuf); + if (ret == -1) { + full_path = complete_path (remote_path, local_path, NULL); + } else { + full_path = complete_path (remote_path, local_path, &statbuf); + } + + if (full_path == NULL) { + ret = -1; + goto out; + } + + remote_fd = glfs_open (fs, remote_path, O_RDONLY); + if (remote_fd == NULL) { + error (0, errno, "%s", remote_path); + goto out; + } + + local_fd = open (full_path, O_CREAT | O_WRONLY, get_default_file_mode_perm ()); + if (local_fd == -1) { + error (0, errno, "%s", full_path); + goto out; + } + + ret = gluster_lock (remote_fd, F_WRLCK, false); + if (ret == -1) { + error (0, errno, "failed to lock %s", remote_path); + goto out; + } + + if ((ret = gluster_read (remote_fd, local_fd)) == -1) { + error (0, errno, "write error"); + } + + //TODO error checking + glfs_unlink(fs, remote_path); + //After copying delete remote file +out: + free (full_path); + + if (local_fd != -1) { + close (local_fd); + } + + if (remote_fd) { + glfs_close (remote_fd); + } + + return ret; +} + +/** + * Perform a REMOTE_TO_REMOTE transfer, given both source and destination remote + * paths and active connections to both the source and destination. + */ +static int +remote_to_remote (const char *source_path, const char *dest_path, glfs_t *source_fs, glfs_t *dest_fs) +{ + int ret = -1; + size_t num_read = 0; + size_t num_written = 0; + glfs_fd_t *source_fd = NULL; + glfs_fd_t *dest_fd = NULL; + struct stat statbuf; + char buf[BUFFER_SIZE]; + char *full_path; + + if(source_fs == dest_fs) + { + ret = glfs_rename(source_fs, source_path, dest_path); + goto out; + } + ret = glfs_lstat (dest_fs, dest_path, &statbuf); + + if (ret == -1) { + full_path = complete_path (source_path, dest_path, NULL); + } else { + full_path = complete_path (source_path, dest_path, &statbuf); + } + + if (full_path == NULL) { + ret = -1; + goto out; + } + + source_fd = glfs_open (source_fs, source_path, O_RDONLY); + if (source_fd == NULL) { + error (0, errno, "%s", source_path); + goto out; + } + + dest_fd = glfs_creat (dest_fs, full_path, O_CREAT | O_WRONLY, get_default_file_mode_perm ()); + if (dest_fd == NULL) { + error (0, errno, "%s", full_path); + goto out; + } + + bool done = false; + while (true) { + num_read = glfs_read (source_fd, buf, BUFFER_SIZE, 0); + if (num_read == -1) { + ret = -1; + goto out; + } + + if (num_read == 0) { + done = true; + goto out; + } + + for (num_written = 0; num_written < num_read;) { + ret = glfs_write (dest_fd, + &buf[num_written], + num_read - num_written, 0); + if (ret == -1) { + error (0, errno, "write error"); + goto out; + } + + num_written += ret; + } + } + + //glfs_rename(source_fs, source_path, dest_path); +out: + if(done) + glfs_unlink(source_fs, source_path); + free (full_path); + + if (source_fd) { + glfs_close (source_fd); + } + + if (dest_fd) { + glfs_close (dest_fd); + } + + return ret; +} + +static int +mv_without_context () +{ + glfs_t *dest_fs = NULL; + glfs_t *source_fs = NULL; + int ret = -1; + + switch (state->mode) { + case LOCAL_TO_REMOTE: + ret = gluster_getfs (&dest_fs, state->gluster_dest); + if (ret == -1) { + error (0, errno, "%s", state->dest); + goto out; + } + + ret = apply_xlator_options (dest_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + ret = local_to_remote (state->source, + state->gluster_dest->path, + dest_fs); + if (ret == -1) { + goto out; + } + + break; + case REMOTE_TO_LOCAL: + ret = gluster_getfs (&source_fs, state->gluster_source); + if (ret == -1) { + error (0, errno, "%s", state->source); + goto out; + } + + ret = apply_xlator_options (source_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + ret = remote_to_local (state->gluster_source->path, + state->dest, + source_fs); + if (ret == -1) { + goto out; + } + + break; + case REMOTE_TO_REMOTE: + ret = gluster_getfs (&dest_fs, state->gluster_dest); + if (ret == -1) { + error (0, errno, "%s", state->dest); + goto out; + } + + ret = apply_xlator_options (dest_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + /** + * If the host and volume of the source and destination + * are the same, then simply use the same connection to + * make the transfer. + */ + if (strcmp (state->gluster_source->host, state->gluster_dest->host) == 0 + && strcmp (state->gluster_source->volume, state->gluster_dest->volume) == 0) { + source_fs = dest_fs; + } else { + ret = gluster_getfs (&source_fs, state->gluster_source); + if (ret == -1) { + error (0, errno, "%s", state->source); + goto out; + } + + ret = apply_xlator_options (source_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + } + + ret = remote_to_remote (state->gluster_source->path, + state->gluster_dest->path, + source_fs, + dest_fs); + if (ret == -1) { + goto out; + } + + if (dest_fs == source_fs) { + glfs_fini (dest_fs); + dest_fs = source_fs = NULL; + } + + break; + default: + error (0, errno, "unknown error"); + } + +out: + if (dest_fs) { + glfs_fini (dest_fs); + } + + if (source_fs) { + glfs_fini (source_fs); + } + + return ret; +} + +static int +mv_with_context (glfs_t *fs) +{ + glfs_t *dest_fs = NULL; + glfs_t *source_fs = NULL; + int ret = -1; + + switch (state->mode) { + case ESTABLISHED_TO_ESTABLISHED: + ret = remote_to_remote (state->source, + state->dest, + fs, + fs); + break; + case ESTABLISHED_TO_LOCAL: + ret = remote_to_local (state->source, state->dest, fs); + break; + case ESTABLISHED_TO_REMOTE: + ret = gluster_getfs (&dest_fs, state->gluster_dest); + if (ret == -1) { + error (0, errno, "%s", state->dest); + goto out; + } + + ret = apply_xlator_options (dest_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + ret = remote_to_remote (state->source, state->gluster_dest->path, fs, dest_fs); + + break; + case LOCAL_TO_ESTABLISHED: + ret = local_to_remote (state->source, state->dest, fs); + + break; + case REMOTE_TO_ESTABLISHED: + ret = gluster_getfs (&source_fs, state->gluster_source); + if (ret == -1) { + error (0, errno, "%s", state->source); + goto out; + } + + ret = apply_xlator_options (source_fs, &state->xlator_options); + if (ret == -1) { + error (0, errno, "failed to apply translator options"); + goto out; + } + + ret = remote_to_remote (state->gluster_source->path, + state->dest, + source_fs, + fs); + + break; + // Fall through to mv_without_context () for the normal + // transfer functions. + case LOCAL_TO_REMOTE: + case REMOTE_TO_LOCAL: + case REMOTE_TO_REMOTE: + ret = mv_without_context (); + break; + default: + error (0, 0, "unknown error"); + } + +out: + if (dest_fs) { + glfs_fini (dest_fs); + } + + if (source_fs) { + glfs_fini (source_fs); + } + + return ret; +} + +/** + * Main entry point into application (called from glfs-cli.c) + */ +int +do_mv (struct cli_context *ctx) +{ + int argc = ctx->argc; + char **argv = ctx->argv; + int ret = -1; + + state = init_state (); + if (state == NULL) { + error (0, errno, "failed to initialize state"); + goto out; + } + + if (ctx->fs) { + ret = parse_options (argc, argv, true); + if (ret != 0) { + goto out; + } + + ret = mv_with_context (ctx->fs); + } else { + ret = parse_options (argc, argv, false); + switch (ret) { + case -2: + // Fall through + ret = 0; + case -1: + goto out; + } + + ret = mv_without_context (); + } + +out: + if (state) { + if (state->gluster_dest) { + gluster_url_free (state->gluster_dest); + } + + if (state->gluster_source) { + gluster_url_free (state->gluster_source); + } + + free (state->dest); + free (state->source); + } + + free (state); + + return ret; +} diff --git a/src/glfs-mv.h b/src/glfs-mv.h new file mode 100644 index 0000000..1ced705 --- /dev/null +++ b/src/glfs-mv.h @@ -0,0 +1,27 @@ +/** + * Copyright (C) 2015 Facebook Inc. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + */ + +#ifndef GLFS_MV_H +#define GLFS_MV_H + +#include "glfs-cli.h" + +int +do_mv (struct cli_context *ctx); + +#endif /* GLFS_MV_H */