mirror of
https://github.com/upa/mscp.git
synced 2026-02-16 10:54:47 +08:00
fix path handling
This commit is contained in:
253
src/file.c
253
src/file.c
@@ -3,6 +3,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <dirent.h>
|
#include <dirent.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
|
#include <libgen.h>
|
||||||
|
|
||||||
#include <ssh.h>
|
#include <ssh.h>
|
||||||
#include <util.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 */
|
/* 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;
|
int ret = 0;
|
||||||
|
|
||||||
@@ -79,8 +80,9 @@ int file_is_directory(char *path, sftp_session sftp)
|
|||||||
char *p = *remote_path == '\0' ? "." : remote_path;
|
char *p = *remote_path == '\0' ? "." : remote_path;
|
||||||
attr = sftp_stat(sftp, p);
|
attr = sftp_stat(sftp, p);
|
||||||
if (!attr) {
|
if (!attr) {
|
||||||
pr_err("%s: %s\n", p,
|
if (print_error)
|
||||||
ssh_get_error(sftp_ssh(sftp)));
|
pr_err("sftp_stat %s: %s\n",
|
||||||
|
path, sftp_get_ssh_error(sftp));
|
||||||
ret = -1;
|
ret = -1;
|
||||||
} else if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY)
|
} else if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY)
|
||||||
ret = 1;
|
ret = 1;
|
||||||
@@ -88,9 +90,10 @@ int file_is_directory(char *path, sftp_session sftp)
|
|||||||
} else {
|
} else {
|
||||||
struct stat statbuf;
|
struct stat statbuf;
|
||||||
if (stat(path, &statbuf) < 0) {
|
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;
|
ret = -1;
|
||||||
} else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
|
} else if (S_ISDIR(statbuf.st_mode))
|
||||||
ret = 1;
|
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);
|
strncpy(f->path, path, PATH_MAX - 1);
|
||||||
f->size = size;
|
f->size = size;
|
||||||
f->remote = remote;
|
f->remote = remote;
|
||||||
|
f->dst_remote = !remote;
|
||||||
lock_init(&f->lock);
|
lock_init(&f->lock);
|
||||||
|
|
||||||
return f;
|
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 stat statbuf;
|
||||||
struct dirent *de;
|
sftp_attributes attr;
|
||||||
DIR *dir;
|
int ret = 0;
|
||||||
|
|
||||||
|
if (!sftp) {
|
||||||
|
/* local */
|
||||||
|
if (stat(path, &statbuf) < 0) {
|
||||||
|
pr_err("failed to get stat for %s: %s\n", path, strerrno());
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (S_ISREG(statbuf.st_mode)) {
|
||||||
|
*size = statbuf.st_size;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
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_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 next_src_path[PATH_MAX], next_rel_path[PATH_MAX];
|
||||||
|
struct file *f;
|
||||||
|
size_t size;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ret = file_is_directory(path, NULL);
|
ret = file_is_directory(src_path, dst_is_remote ? NULL : sftp, true);
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
if (ret == 1) {
|
if (ret == 0) {
|
||||||
if ((dir = opendir(path)) == NULL) {
|
/* src_path is file */
|
||||||
pr_err("opend to open dir %s: %s\n", path, strerrno());
|
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;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
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)");
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 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) {
|
while ((de = readdir(dir)) != NULL) {
|
||||||
if (file_should_skip(de->d_name))
|
if (file_should_skip(de->d_name))
|
||||||
continue;
|
continue;
|
||||||
snprintf(child, sizeof(child), "%s/%s", path, de->d_name);
|
snprintf(next_src_path, sizeof(next_src_path),
|
||||||
ret = file_fill_local_recursive(child, head);
|
"%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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* path is file */
|
/* src_path is remote directory */
|
||||||
if (stat(path, &statbuf) < 0) {
|
sftp_attributes attr;
|
||||||
pr_err("file %s: %s\n", path, strerrno());
|
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;
|
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);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int file_fill_remote_recursive(char *path, sftp_session sftp,
|
|
||||||
struct list_head *head)
|
|
||||||
{
|
|
||||||
char child[PATH_MAX];
|
|
||||||
sftp_attributes attr;
|
|
||||||
sftp_dir dir;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
ret = file_is_directory(path, sftp);
|
|
||||||
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)));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
while ((attr = sftp_readdir(sftp, dir)) != NULL) {
|
while ((attr = sftp_readdir(sftp, dir)) != NULL) {
|
||||||
if (file_should_skip(attr->name))
|
if (file_should_skip(attr->name))
|
||||||
continue;
|
continue;
|
||||||
|
snprintf(next_src_path, sizeof(next_src_path),
|
||||||
snprintf(child, sizeof(child), "%s/%s", path, attr->name);
|
"%s/%s", src_path, attr->name);
|
||||||
ret = file_fill_remote_recursive(child, sftp, head);
|
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)
|
if (ret < 0)
|
||||||
return ret;
|
return ret;
|
||||||
sftp_attributes_free(attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!sftp_dir_eof(dir)) {
|
|
||||||
pr_err("can't list directory %s: %s\n", path,
|
|
||||||
ssh_get_error(sftp_ssh(sftp)));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (sftp_closedir(dir) != SSH_OK) {
|
|
||||||
pr_err("can't close directory %s: %s\n", path,
|
|
||||||
ssh_get_error(sftp_ssh(sftp)));
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
} 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)));
|
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
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;
|
bool dst_is_remote, dst_is_dir, dst_should_dir;
|
||||||
int ret, n;
|
char *dst_path, *src_path;
|
||||||
|
int n, ret;
|
||||||
|
|
||||||
|
dst_path = file_find_path(dst);
|
||||||
|
dst_path = *dst_path == '\0' ? "." : dst_path;
|
||||||
|
dst_is_remote = file_find_hostname(dst) ? true : false;
|
||||||
|
|
||||||
|
if (file_is_directory(dst_path, dst_is_remote ? sftp : NULL, false) > 0)
|
||||||
|
dst_is_dir = true;
|
||||||
|
else
|
||||||
|
dst_is_dir = false;
|
||||||
|
|
||||||
for (n = 0; n < cnt; n++) {
|
for (n = 0; n < cnt; n++) {
|
||||||
src = *(src_array + n);
|
src_path = file_find_path(src_array[n]);
|
||||||
path = file_find_path(src);
|
|
||||||
path = *path == '\0' ? "." : path;
|
if (file_is_directory(src_path, dst_is_remote ? NULL : sftp, false) > 0)
|
||||||
if (file_has_hostname(src))
|
dst_should_dir = true;
|
||||||
ret = file_fill_remote_recursive(path, sftp, file_list);
|
|
||||||
else
|
else
|
||||||
ret = file_fill_local_recursive(path, file_list);
|
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)
|
if (ret < 0)
|
||||||
return -1;
|
return ret;
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
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;
|
|
||||||
|
|
||||||
dst_path = *dst_path == '\0' ? "." : dst_path;
|
|
||||||
|
|
||||||
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';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
|
|||||||
@@ -60,10 +60,9 @@ struct chunk {
|
|||||||
|
|
||||||
char *file_find_hostname(char *path);
|
char *file_find_hostname(char *path);
|
||||||
bool file_has_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(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt,
|
||||||
int file_fill_dst(char *target, struct list_head *file_list);
|
char *dst);
|
||||||
|
|
||||||
int chunk_fill(struct list_head *file_list, struct list_head *chunk_list,
|
int chunk_fill(struct list_head *file_list, struct list_head *chunk_list,
|
||||||
int nr_conn, int min_chunk_sz, int max_chunk_sz);
|
int nr_conn, int min_chunk_sz, int max_chunk_sz);
|
||||||
|
|||||||
36
src/main.c
36
src/main.c
@@ -67,10 +67,10 @@ void stop_copy_threads(int sig)
|
|||||||
void usage(bool print_help) {
|
void usage(bool print_help) {
|
||||||
printf("sscp: super scp, copy files over multiple ssh connections\n"
|
printf("sscp: super scp, copy files over multiple ssh connections\n"
|
||||||
"\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"
|
" [-b sftp_buf_sz] [-B io_buf_sz]\n"
|
||||||
" [-l login_name] [-p port] [-i identity_file]\n"
|
" [-l login_name] [-p port] [-i identity_file]\n"
|
||||||
" [-c cipher_spec] []source ... target_directory\n"
|
" [-c cipher_spec] source ... target\n"
|
||||||
"\n");
|
"\n");
|
||||||
|
|
||||||
if (!print_help)
|
if (!print_help)
|
||||||
@@ -85,6 +85,7 @@ void usage(bool print_help) {
|
|||||||
" qemu/block/ssh.c. need investigation...\n"
|
" qemu/block/ssh.c. need investigation...\n"
|
||||||
" -v increment verbose output level\n"
|
" -v increment verbose output level\n"
|
||||||
" -q disable output\n"
|
" -q disable output\n"
|
||||||
|
" -D dry run\n"
|
||||||
"\n"
|
"\n"
|
||||||
" -l LOGIN_NAME login name\n"
|
" -l LOGIN_NAME login name\n"
|
||||||
" -p PORT port number\n"
|
" -p PORT port number\n"
|
||||||
@@ -94,12 +95,6 @@ void usage(bool print_help) {
|
|||||||
" -d increment ssh debug output level\n"
|
" -d increment ssh debug output level\n"
|
||||||
" -h print this help\n"
|
" -h print this help\n"
|
||||||
"\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)
|
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 min_chunk_sz = DEFAULT_MIN_CHUNK_SZ;
|
||||||
int max_chunk_sz = 0;
|
int max_chunk_sz = 0;
|
||||||
int verbose = 1;
|
int verbose = 1;
|
||||||
|
bool dryrun = false;
|
||||||
int ret = 0, n;
|
int ret = 0, n;
|
||||||
char ch;
|
char ch;
|
||||||
|
|
||||||
@@ -166,7 +162,7 @@ int main(int argc, char **argv)
|
|||||||
nr_threads = (int)(nr_cpus() / 2);
|
nr_threads = (int)(nr_cpus() / 2);
|
||||||
nr_threads = nr_threads == 0 ? 1 : nr_threads;
|
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) {
|
switch (ch) {
|
||||||
case 'n':
|
case 'n':
|
||||||
nr_threads = atoi(optarg);
|
nr_threads = atoi(optarg);
|
||||||
@@ -225,6 +221,9 @@ int main(int argc, char **argv)
|
|||||||
case 'q':
|
case 'q':
|
||||||
verbose = -1;
|
verbose = -1;
|
||||||
break;
|
break;
|
||||||
|
case 'D':
|
||||||
|
dryrun = true;
|
||||||
|
break;
|
||||||
case 'l':
|
case 'l':
|
||||||
opts.login_name = optarg;
|
opts.login_name = optarg;
|
||||||
break;
|
break;
|
||||||
@@ -280,22 +279,10 @@ int main(int argc, char **argv)
|
|||||||
return 1;
|
return 1;
|
||||||
sscp.opts = &opts; /* save ssh-able ssh_opts */
|
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 */
|
/* fill file list */
|
||||||
ret = file_fill(sscp.ctrl, &sscp.file_list, &argv[optind], argc - optind - 1);
|
ret = file_fill(sscp.ctrl, &sscp.file_list, &argv[optind], argc - optind - 1,
|
||||||
if (ret < 0)
|
sscp.target);
|
||||||
goto out;
|
|
||||||
|
|
||||||
ret = file_fill_dst(sscp.target, &sscp.file_list);
|
|
||||||
if (ret < 0)
|
if (ret < 0)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
@@ -313,6 +300,9 @@ int main(int argc, char **argv)
|
|||||||
chunk_dump(&sscp.chunk_list);
|
chunk_dump(&sscp.chunk_list);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
if (dryrun)
|
||||||
|
return 0;
|
||||||
|
|
||||||
/* register SIGINT to stop thrads */
|
/* register SIGINT to stop thrads */
|
||||||
if (signal(SIGINT, stop_copy_threads) == SIG_ERR) {
|
if (signal(SIGINT, stop_copy_threads) == SIG_ERR) {
|
||||||
pr_err("cannot set signal: %s\n", strerrno());
|
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);
|
void ssh_sftp_close(sftp_session sftp);
|
||||||
|
|
||||||
#define sftp_ssh(sftp) (sftp)->session
|
#define sftp_ssh(sftp) (sftp)->session
|
||||||
|
#define sftp_get_ssh_error(sftp) ssh_get_error(sftp_ssh(sftp))
|
||||||
|
|
||||||
/* wrapping multiple sftp_read|write */
|
/* wrapping multiple sftp_read|write */
|
||||||
int sftp_write2(sftp_file sf, const void *buf, size_t len, size_t sftp_buf_sz);
|
int sftp_write2(sftp_file sf, const void *buf, size_t len, size_t sftp_buf_sz);
|
||||||
|
|||||||
Reference in New Issue
Block a user