diff options
-rw-r--r-- | src/file.c | 245 | ||||
-rw-r--r-- | src/file.h | 5 | ||||
-rw-r--r-- | src/main.c | 36 | ||||
-rw-r--r-- | src/ssh.h | 1 |
4 files changed, 140 insertions, 147 deletions
@@ -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; @@ -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); @@ -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()); @@ -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); |