summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/file.c245
-rw-r--r--src/file.h5
-rw-r--r--src/main.c36
-rw-r--r--src/ssh.h1
4 files changed, 140 insertions, 147 deletions
diff --git a/src/file.c b/src/file.c
index 3c7d81e..89babd0 100644
--- a/src/file.c
+++ b/src/file.c
@@ -3,6 +3,7 @@
#include <sys/stat.h>
#include <dirent.h>
#include <fcntl.h>
+#include <libgen.h>
#include <ssh.h>
#include <util.h>
@@ -68,7 +69,7 @@ static char *file_find_path(char *path)
}
/* return 1 when path is directory, 0 is not directory, and -1 on error */
-int file_is_directory(char *path, sftp_session sftp)
+static int file_is_directory(char *path, sftp_session sftp, bool print_error)
{
int ret = 0;
@@ -79,8 +80,9 @@ int file_is_directory(char *path, sftp_session sftp)
char *p = *remote_path == '\0' ? "." : remote_path;
attr = sftp_stat(sftp, p);
if (!attr) {
- pr_err("%s: %s\n", p,
- ssh_get_error(sftp_ssh(sftp)));
+ if (print_error)
+ pr_err("sftp_stat %s: %s\n",
+ path, sftp_get_ssh_error(sftp));
ret = -1;
} else if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY)
ret = 1;
@@ -88,9 +90,10 @@ int file_is_directory(char *path, sftp_session sftp)
} else {
struct stat statbuf;
if (stat(path, &statbuf) < 0) {
- pr_err("%s: %s\n", path, strerrno());
+ if (print_error)
+ pr_err("stat %s: %s\n", path, strerrno());
ret = -1;
- } else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
+ } else if (S_ISDIR(statbuf.st_mode))
ret = 1;
}
@@ -146,6 +149,7 @@ static struct file *file_alloc(char *path, size_t size, bool remote)
strncpy(f->path, path, PATH_MAX - 1);
f->size = size;
f->remote = remote;
+ f->dst_remote = !remote;
lock_init(&f->lock);
return f;
@@ -162,161 +166,160 @@ static bool file_should_skip(char *path)
}
-static int file_fill_local_recursive(char *path, struct list_head *head)
+/* return -1 when error, 0 when should skip, and 1 when should be copied */
+static int check_file_tobe_copied(char *path, sftp_session sftp, size_t *size)
{
- char child[PATH_MAX];
struct stat statbuf;
- struct dirent *de;
- DIR *dir;
- int ret;
-
- ret = file_is_directory(path, NULL);
- if (ret < 0)
- return -1;
-
- if (ret == 1) {
- if ((dir = opendir(path)) == NULL) {
- pr_err("opend to open dir %s: %s\n", path, strerrno());
- return -1;
- }
+ sftp_attributes attr;
+ int ret = 0;
- while ((de = readdir(dir)) != NULL) {
- if (file_should_skip(de->d_name))
- continue;
- snprintf(child, sizeof(child), "%s/%s", path, de->d_name);
- ret = file_fill_local_recursive(child, head);
- if (ret < 0)
- return ret;
- }
- } else {
- /* path is file */
+ if (!sftp) {
+ /* local */
if (stat(path, &statbuf) < 0) {
- pr_err("file %s: %s\n", path, strerrno());
+ pr_err("failed to get stat for %s: %s\n", path, strerrno());
return -1;
}
-
- if ((statbuf.st_mode & S_IFMT) == S_IFREG ||
- (statbuf.st_mode & S_IFMT) == S_IFLNK) {
- struct file *f = file_alloc(path, statbuf.st_size, false);
- if (!f) {
- pr_err("%s\n", strerrno());
- return -1;
- }
- list_add_tail(&f->list, head);
+ if (S_ISREG(statbuf.st_mode)) {
+ *size = statbuf.st_size;
+ return 1;
}
+ return 0;
}
- return 0;
+ /* remote */
+ attr = sftp_stat(sftp, path);
+ if (!attr) {
+ pr_err("failed to get stat for %s: %s\n",
+ path, ssh_get_error(sftp_ssh(sftp)));
+ return -1;
+ }
+ if (attr->type == SSH_FILEXFER_TYPE_REGULAR ||
+ attr->type == SSH_FILEXFER_TYPE_SYMLINK) {
+ *size = attr->size;
+ ret = 1;
+ }
+
+ sftp_attributes_free(attr);
+
+ return ret;
}
-static int file_fill_remote_recursive(char *path, sftp_session sftp,
- struct list_head *head)
+static int file_fill_recursive(struct list_head *file_list,
+ bool dst_is_remote, sftp_session sftp, char *src_path,
+ char *rel_path, char *dst_path, bool dst_should_dir)
{
- char child[PATH_MAX];
- sftp_attributes attr;
- sftp_dir dir;
+ char next_src_path[PATH_MAX], next_rel_path[PATH_MAX];
+ struct file *f;
+ size_t size;
int ret;
- ret = file_is_directory(path, sftp);
+ ret = file_is_directory(src_path, dst_is_remote ? NULL : sftp, true);
if (ret < 0)
return -1;
- if (ret == 1) {
- dir = sftp_opendir(sftp, path);
- if (!dir) {
- pr_err("failed to open dir %s: %s\n", path,
- ssh_get_error(sftp_ssh(sftp)));
+ if (ret == 0) {
+ /* src_path is file */
+ ret = check_file_tobe_copied(src_path, dst_is_remote ? NULL : sftp,
+ &size);
+ if (ret <= 0)
+ return ret; /* error or skip */
+
+ if ((f = file_alloc(src_path, size, !dst_is_remote)) == NULL) {
+ pr_err("%s\n", strerrno());
return -1;
}
- while ((attr = sftp_readdir(sftp, dir)) != NULL) {
- if (file_should_skip(attr->name))
- continue;
+ if (dst_should_dir)
+ snprintf(f->dst_path, PATH_MAX, "%s/%s%s",
+ dst_path, rel_path, basename(src_path));
+ else
+ snprintf(f->dst_path, PATH_MAX, "%s%s", rel_path, dst_path);
- snprintf(child, sizeof(child), "%s/%s", path, attr->name);
- ret = file_fill_remote_recursive(child, sftp, head);
- if (ret < 0)
- return ret;
- sftp_attributes_free(attr);
- }
+ list_add_tail(&f->list, file_list);
+ pprint2("file %s %s -> %s %s\n",
+ f->path, dst_is_remote ? "(local)" : "(remote)",
+ f->dst_path, dst_is_remote ? "(remote)" : "(local)");
- if (!sftp_dir_eof(dir)) {
- pr_err("can't list directory %s: %s\n", path,
- ssh_get_error(sftp_ssh(sftp)));
- return -1;
- }
+ return 0;
+ }
- if (sftp_closedir(dir) != SSH_OK) {
- pr_err("can't close directory %s: %s\n", path,
- ssh_get_error(sftp_ssh(sftp)));
+ /* src_path is directory */
+ if (dst_is_remote) {
+ /* src_path is local directory */
+ struct dirent *de;
+ DIR *dir;
+ if ((dir = opendir(src_path)) == NULL) {
+ pr_err("opendir '%s': %s\n", src_path, strerrno());
return -1;
}
+ while ((de = readdir(dir)) != NULL) {
+ if (file_should_skip(de->d_name))
+ continue;
+ snprintf(next_src_path, sizeof(next_src_path),
+ "%s/%s", src_path, de->d_name);
+ snprintf(next_rel_path, sizeof(next_rel_path),
+ "%s%s/", rel_path, basename(src_path));
+ ret = file_fill_recursive(file_list, dst_is_remote, sftp,
+ next_src_path, next_rel_path,
+ dst_path, dst_should_dir);
+ if (ret < 0)
+ return ret;
+ }
} else {
- /* path is file */
- attr = sftp_stat(sftp, path);
- if (!attr) {
- pr_err("failed to get stat for %s: %s\n",
- path, ssh_get_error(sftp_ssh(sftp)));
+ /* src_path is remote directory */
+ sftp_attributes attr;
+ sftp_dir dir;
+ if ((dir = sftp_opendir(sftp, src_path)) == NULL) {
+ pr_err("sftp_opendir: '%s': %s\n", src_path,
+ sftp_get_ssh_error(sftp));
return -1;
}
-
- /* skip special and unknown files */
- if (attr->type == SSH_FILEXFER_TYPE_REGULAR ||
- attr->type == SSH_FILEXFER_TYPE_SYMLINK) {
- struct file *f = file_alloc(path, attr->size, true);
- if (!f) {
- pr_err("%s\n", strerrno());
- return -1;
- }
- list_add(&f->list, head);
- sftp_attributes_free(attr);
+ while ((attr = sftp_readdir(sftp, dir)) != NULL) {
+ if (file_should_skip(attr->name))
+ continue;
+ snprintf(next_src_path, sizeof(next_src_path),
+ "%s/%s", src_path, attr->name);
+ snprintf(next_rel_path, sizeof(next_rel_path),
+ "%s%s/", rel_path, basename(src_path));
+ ret = file_fill_recursive(file_list, dst_is_remote, sftp,
+ next_src_path, next_rel_path,
+ dst_path, dst_should_dir);
+ if (ret < 0)
+ return ret;
}
}
return 0;
}
-int file_fill(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt)
+int file_fill(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt,
+ char *dst)
{
- char *src, *path;
- int ret, n;
-
- for (n = 0; n < cnt; n++) {
- src = *(src_array + n);
- path = file_find_path(src);
- path = *path == '\0' ? "." : path;
- if (file_has_hostname(src))
- ret = file_fill_remote_recursive(path, sftp, file_list);
- else
- ret = file_fill_local_recursive(path, file_list);
- if (ret < 0)
- return -1;
- }
+ bool dst_is_remote, dst_is_dir, dst_should_dir;
+ char *dst_path, *src_path;
+ int n, ret;
- return 0;
-}
+ dst_path = file_find_path(dst);
+ dst_path = *dst_path == '\0' ? "." : dst_path;
+ dst_is_remote = file_find_hostname(dst) ? true : false;
-int file_fill_dst(char *target, struct list_head *file_list)
-{
- bool dst_remote = file_find_hostname(target) ? true : false;
- char *dst_path = file_find_path(target);
- int pref_len, path_len;
- struct file *f;
+ if (file_is_directory(dst_path, dst_is_remote ? sftp : NULL, false) > 0)
+ dst_is_dir = true;
+ else
+ dst_is_dir = false;
- dst_path = *dst_path == '\0' ? "." : dst_path;
+ for (n = 0; n < cnt; n++) {
+ src_path = file_find_path(src_array[n]);
- list_for_each_entry(f, file_list, list) {
- f->dst_remote = dst_remote;
- pref_len = strlen(dst_path);
- path_len = strlen(f->path);
- if (pref_len + path_len + 2 > PATH_MAX) { /* +2 is '/' and '\0' */
- pr_err("too long path: %s/%s\n", dst_path, f->path);
- return -1;
- }
- memcpy(f->dst_path, dst_path, pref_len);
- f->dst_path[pref_len] = '/';
- memcpy(f->dst_path + pref_len + 1, f->path, path_len);
- f->dst_path[pref_len + 1 + path_len] = '\0';
+ if (file_is_directory(src_path, dst_is_remote ? NULL : sftp, false) > 0)
+ dst_should_dir = true;
+ else
+ dst_should_dir = false;
+ ret = file_fill_recursive(file_list, dst_is_remote, sftp,
+ src_path, "",
+ dst_path, dst_is_dir | dst_should_dir);
+ if (ret < 0)
+ return ret;
}
return 0;
diff --git a/src/file.h b/src/file.h
index a598ce8..e257bcd 100644
--- a/src/file.h
+++ b/src/file.h
@@ -60,10 +60,9 @@ struct chunk {
char *file_find_hostname(char *path);
bool file_has_hostname(char *path);
-int file_is_directory(char *path, sftp_session sftp);
-int file_fill(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt);
-int file_fill_dst(char *target, struct list_head *file_list);
+int file_fill(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt,
+ char *dst);
int chunk_fill(struct list_head *file_list, struct list_head *chunk_list,
int nr_conn, int min_chunk_sz, int max_chunk_sz);
diff --git a/src/main.c b/src/main.c
index 13db75f..1fbf462 100644
--- a/src/main.c
+++ b/src/main.c
@@ -67,10 +67,10 @@ void stop_copy_threads(int sig)
void usage(bool print_help) {
printf("sscp: super scp, copy files over multiple ssh connections\n"
"\n"
- "Usage: sscp [Cvqdh] [-n nr_conns] [-s min_chunk_sz] [-S max_chunk_sz]\n"
+ "Usage: sscp [CvqDdh] [-n nr_conns] [-s min_chunk_sz] [-S max_chunk_sz]\n"
" [-b sftp_buf_sz] [-B io_buf_sz]\n"
" [-l login_name] [-p port] [-i identity_file]\n"
- " [-c cipher_spec] []source ... target_directory\n"
+ " [-c cipher_spec] source ... target\n"
"\n");
if (!print_help)
@@ -85,6 +85,7 @@ void usage(bool print_help) {
" qemu/block/ssh.c. need investigation...\n"
" -v increment verbose output level\n"
" -q disable output\n"
+ " -D dry run\n"
"\n"
" -l LOGIN_NAME login name\n"
" -p PORT port number\n"
@@ -94,12 +95,6 @@ void usage(bool print_help) {
" -d increment ssh debug output level\n"
" -h print this help\n"
"\n");
-
- printf(" Note:\n"
- " Not similar to scp and rsync, target in sscp must be directory\n"
- " (at present). This means that sscp cannot change file names.\n"
- " sscp copies file(s) into a directory.\n"
- "\n");
}
char *find_hostname(int ind, int argc, char **argv)
@@ -152,6 +147,7 @@ int main(int argc, char **argv)
int min_chunk_sz = DEFAULT_MIN_CHUNK_SZ;
int max_chunk_sz = 0;
int verbose = 1;
+ bool dryrun = false;
int ret = 0, n;
char ch;
@@ -166,7 +162,7 @@ int main(int argc, char **argv)
nr_threads = (int)(nr_cpus() / 2);
nr_threads = nr_threads == 0 ? 1 : nr_threads;
- while ((ch = getopt(argc, argv, "n:s:S:b:B:vql:p:i:c:Cdh")) != -1) {
+ while ((ch = getopt(argc, argv, "n:s:S:b:B:vqDl:p:i:c:Cdh")) != -1) {
switch (ch) {
case 'n':
nr_threads = atoi(optarg);
@@ -225,6 +221,9 @@ int main(int argc, char **argv)
case 'q':
verbose = -1;
break;
+ case 'D':
+ dryrun = true;
+ break;
case 'l':
opts.login_name = optarg;
break;
@@ -280,22 +279,10 @@ int main(int argc, char **argv)
return 1;
sscp.opts = &opts; /* save ssh-able ssh_opts */
- /* check target is directory */
- ret = file_is_directory(sscp.target,
- file_find_hostname(sscp.target) ? sscp.ctrl : NULL);
- if (ret < 0)
- goto out;
- if (ret == 0) {
- pr_err("target must be directory\n");
- goto out;
- }
/* fill file list */
- ret = file_fill(sscp.ctrl, &sscp.file_list, &argv[optind], argc - optind - 1);
- if (ret < 0)
- goto out;
-
- ret = file_fill_dst(sscp.target, &sscp.file_list);
+ ret = file_fill(sscp.ctrl, &sscp.file_list, &argv[optind], argc - optind - 1,
+ sscp.target);
if (ret < 0)
goto out;
@@ -313,6 +300,9 @@ int main(int argc, char **argv)
chunk_dump(&sscp.chunk_list);
#endif
+ if (dryrun)
+ return 0;
+
/* register SIGINT to stop thrads */
if (signal(SIGINT, stop_copy_threads) == SIG_ERR) {
pr_err("cannot set signal: %s\n", strerrno());
diff --git a/src/ssh.h b/src/ssh.h
index fcee29d..84e3d9e 100644
--- a/src/ssh.h
+++ b/src/ssh.h
@@ -23,6 +23,7 @@ sftp_session ssh_make_sftp_session(char *sshdst, struct ssh_opts *opts);
void ssh_sftp_close(sftp_session sftp);
#define sftp_ssh(sftp) (sftp)->session
+#define sftp_get_ssh_error(sftp) ssh_get_error(sftp_ssh(sftp))
/* wrapping multiple sftp_read|write */
int sftp_write2(sftp_file sf, const void *buf, size_t len, size_t sftp_buf_sz);