mirror of
https://github.com/upa/mscp.git
synced 2026-02-04 11:34:44 +08:00
tiny fix on comment
This commit is contained in:
903
src/file.c
903
src/file.c
@@ -1,903 +0,0 @@
|
||||
#include <stdlib.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/stat.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <libgen.h>
|
||||
|
||||
#include <ssh.h>
|
||||
#include <util.h>
|
||||
#include <file.h>
|
||||
#include <pprint.h>
|
||||
#include <platform.h>
|
||||
|
||||
bool file_has_hostname(char *path)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strchr(path, ':');
|
||||
if (p) {
|
||||
if (p == path || ((p > path) && *(p - 1) == '\\'))
|
||||
return false; /* first byte is colon or escaped colon, skip */
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
char *file_find_hostname(char *path)
|
||||
{
|
||||
char *dup, *p;
|
||||
|
||||
dup = strdup(path);
|
||||
if (!dup) {
|
||||
pr_err("%s", strerrno());
|
||||
return NULL;
|
||||
}
|
||||
|
||||
p = strchr(dup, ':');
|
||||
if (p) {
|
||||
if (p == dup || ((p > dup) && *(p - 1) == '\\')) {
|
||||
/* first byte is colon or escaped colon, skip */
|
||||
free(dup);
|
||||
} else {
|
||||
/* handle this as remote hostname (with username) */
|
||||
*p = '\0';
|
||||
return dup;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static char *file_find_path(char *path)
|
||||
{
|
||||
char *p;
|
||||
|
||||
p = strchr(path, ':');
|
||||
if (p) {
|
||||
if (p == path || ((p > path) && *(p - 1) == '\\')) {
|
||||
/* first byte is colon or escaped colon, skip */
|
||||
return path;
|
||||
} else {
|
||||
return p + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/* return 1 when path is directory, 0 is not directory, and -1 on error */
|
||||
static int file_is_directory(char *path, sftp_session sftp, bool print_error)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (sftp) {
|
||||
char *remote_path = file_find_path(path);
|
||||
sftp_attributes attr;
|
||||
|
||||
char *p = *remote_path == '\0' ? "." : remote_path;
|
||||
attr = sftp_stat(sftp, p);
|
||||
if (!attr) {
|
||||
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;
|
||||
sftp_attributes_free(attr);
|
||||
} else {
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) < 0) {
|
||||
if (print_error)
|
||||
pr_err("stat %s: %s\n", path, strerrno());
|
||||
ret = -1;
|
||||
} else if (S_ISDIR(statbuf.st_mode))
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* return 1 when directory exists, 0 not exists, and -1 on error */
|
||||
int file_directory_exists(char *path, sftp_session sftp)
|
||||
{
|
||||
int ret = 0;
|
||||
|
||||
if (sftp) {
|
||||
sftp_attributes attr;
|
||||
attr = sftp_stat(sftp, path);
|
||||
if (!attr) {
|
||||
if (sftp_get_error(sftp) == SSH_FX_NO_SUCH_PATH ||
|
||||
sftp_get_error(sftp) == SSH_FX_NO_SUCH_FILE)
|
||||
ret = 0;
|
||||
else {
|
||||
pr_err("%s: %s\n", path, sftp_get_ssh_error(sftp));
|
||||
ret = -1;
|
||||
}
|
||||
} else if (attr->type == SSH_FILEXFER_TYPE_DIRECTORY)
|
||||
ret = 1;
|
||||
sftp_attributes_free(attr);
|
||||
} else {
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) < 0) {
|
||||
if (errno == ENOENT)
|
||||
ret = 0;
|
||||
else {
|
||||
pr_err("%s: %s\n", path, strerrno());
|
||||
ret = -1;
|
||||
}
|
||||
} else if ((statbuf.st_mode & S_IFMT) == S_IFDIR)
|
||||
ret = 1;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static struct file *file_alloc(char *src_path, size_t size, bool src_is_remote)
|
||||
{
|
||||
struct file *f;
|
||||
|
||||
f = malloc(sizeof(*f));
|
||||
if (!f) {
|
||||
pr_err("%s\n", strerrno());
|
||||
return NULL;
|
||||
}
|
||||
memset(f, 0, sizeof(*f));
|
||||
|
||||
strncpy(f->src_path, src_path, PATH_MAX - 1);
|
||||
f->size = size;
|
||||
f->src_is_remote = src_is_remote;
|
||||
f->dst_is_remote = !src_is_remote;
|
||||
lock_init(&f->lock);
|
||||
|
||||
return f;
|
||||
}
|
||||
|
||||
static bool check_file_should_skip(char *path)
|
||||
{
|
||||
int len = strlen(path);
|
||||
if ((len == 1 && strncmp(path, ".", 1) == 0) ||
|
||||
(len == 2 && strncmp(path, "..", 2) == 0)) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/* 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)
|
||||
{
|
||||
struct stat statbuf;
|
||||
sftp_attributes attr;
|
||||
int ret = 0;
|
||||
|
||||
if (!sftp) {
|
||||
/* local */
|
||||
if (stat(path, &statbuf) < 0) {
|
||||
pr_err("stat %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("sftp_stat %s: %s\n", path, sftp_get_ssh_error(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 check_pathlen(const char *src, const char *dst)
|
||||
{
|
||||
if ((strlen(src) + strlen(dst) + 1) > PATH_MAX) {
|
||||
pr_err("too long path: %s/%s\n", src, dst);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
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, bool replace_dir_name)
|
||||
{
|
||||
char next_src_path[PATH_MAX], next_rel_path[PATH_MAX];
|
||||
struct file *f;
|
||||
size_t size;
|
||||
int ret;
|
||||
|
||||
ret = file_is_directory(src_path, dst_is_remote ? NULL : sftp, true);
|
||||
if (ret < 0)
|
||||
return -1;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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 %luB\n",
|
||||
f->src_path, dst_is_remote ? "(local)" : "(remote)",
|
||||
f->dst_path, dst_is_remote ? "(remote)" : "(local)",
|
||||
f->size);
|
||||
|
||||
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) {
|
||||
if (check_file_should_skip(de->d_name))
|
||||
continue;
|
||||
if (check_pathlen(src_path, de->d_name) < 0 ||
|
||||
check_pathlen(rel_path, basename(src_path)) < 0)
|
||||
return -1;
|
||||
|
||||
snprintf(next_src_path, sizeof(next_src_path),
|
||||
"%s/%s", src_path, de->d_name);
|
||||
if (replace_dir_name)
|
||||
memset(next_rel_path, 0, sizeof(next_rel_path));
|
||||
else
|
||||
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, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
} else {
|
||||
/* 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;
|
||||
}
|
||||
while ((attr = sftp_readdir(sftp, dir)) != NULL) {
|
||||
if (check_file_should_skip(attr->name))
|
||||
continue;
|
||||
if (check_pathlen(src_path, attr->name) < 0 ||
|
||||
check_pathlen(rel_path, basename(src_path)) < 0)
|
||||
return -1;
|
||||
|
||||
snprintf(next_src_path, sizeof(next_src_path),
|
||||
"%s/%s", src_path, attr->name);
|
||||
if (replace_dir_name)
|
||||
memset(next_rel_path, 0, sizeof(next_rel_path));
|
||||
else
|
||||
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, false);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int file_fill(sftp_session sftp, struct list_head *file_list, char **src_array, int cnt,
|
||||
char *dst)
|
||||
{
|
||||
bool dst_is_remote, dst_is_dir, dst_dir_no_exist, dst_should_dir, dst_must_dir;
|
||||
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;
|
||||
dst_must_dir = cnt > 1 ? true : false;
|
||||
|
||||
if (file_is_directory(dst_path, dst_is_remote ? sftp : NULL, false) > 0)
|
||||
dst_is_dir = true;
|
||||
else
|
||||
dst_is_dir = false;
|
||||
dst_dir_no_exist = !dst_is_dir;
|
||||
|
||||
for (n = 0; n < cnt; n++) {
|
||||
src_path = file_find_path(src_array[n]);
|
||||
|
||||
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_should_dir | dst_must_dir | dst_is_dir,
|
||||
dst_dir_no_exist);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* based on
|
||||
* https://stackoverflow.com/questions/2336242/recursive-mkdir-system-call-on-unix */
|
||||
static int file_dst_prepare(struct file *f, sftp_session sftp)
|
||||
{
|
||||
/* XXX: should reflect the permission of the original directory? */
|
||||
mode_t mode = S_IRWXU | S_IRWXG | S_IRWXO;
|
||||
char path[PATH_MAX];
|
||||
char *p;
|
||||
int ret;
|
||||
|
||||
strncpy(path, f->dst_path, sizeof(path));
|
||||
|
||||
pr_debug("prepare for %s\n", path);
|
||||
|
||||
/* mkdir -p */
|
||||
for (p = strchr(path + 1, '/'); p; p = strchr(p + 1, '/')) {
|
||||
*p = '\0';
|
||||
|
||||
ret = file_directory_exists(path, sftp);
|
||||
pr_debug("check %s ret=%d\n", path, ret);
|
||||
if (ret < -1)
|
||||
return -1;
|
||||
if (ret == 1)
|
||||
goto next;
|
||||
|
||||
pr_debug("mkdir %s\n", path);
|
||||
|
||||
if (sftp) {
|
||||
ret = sftp_mkdir(sftp, path, mode);
|
||||
if (ret < 0 &&
|
||||
sftp_get_error(sftp) != SSH_FX_FILE_ALREADY_EXISTS) {
|
||||
pr_err("failed to create %s: %s\n",
|
||||
path, sftp_get_ssh_error(sftp));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (mkdir(path, mode) == -1 && errno != EEXIST) {
|
||||
pr_err("failed to create %s: %s\n",
|
||||
path, strerrno());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
next:
|
||||
*p = '/';
|
||||
}
|
||||
|
||||
/* open file with O_TRUNC to set file size 0 */
|
||||
mode = O_WRONLY|O_CREAT|O_TRUNC;
|
||||
if (sftp) {
|
||||
sftp_file sf;
|
||||
if ((sf = sftp_open(sftp, f->dst_path, mode, S_IRUSR|S_IWUSR)) == NULL) {
|
||||
pr_err("sftp_open: %s\n", sftp_get_ssh_error(sftp));
|
||||
return -1;
|
||||
}
|
||||
if (sftp_close(sf) < 0) {
|
||||
pr_err("sftp_close: %s\n", sftp_get_ssh_error(sftp));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
int fd;
|
||||
if ((fd = open(f->dst_path, mode, S_IRUSR|S_IWUSR)) < 0) {
|
||||
pr_err("open: %s\n", strerrno());
|
||||
return -1;
|
||||
}
|
||||
if (close(fd) < 0) {
|
||||
pr_err("close: %s\n", strerrno());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void file_dump(struct list_head *file_list)
|
||||
{
|
||||
struct file *f;
|
||||
|
||||
list_for_each_entry(f, file_list, list) {
|
||||
pr_debug("%s %s -> %s %s %lu-byte\n",
|
||||
f->src_path, strloc(f->src_is_remote),
|
||||
f->dst_path, strloc(f->dst_is_remote),
|
||||
f->size);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
static void *chunk_alloc(struct file *f)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
c = malloc(sizeof(*c));
|
||||
if (!c) {
|
||||
pr_err("%s\n", strerrno());
|
||||
return NULL;
|
||||
}
|
||||
memset(c, 0, sizeof(*c));
|
||||
|
||||
c->f = f;
|
||||
c->off = 0;
|
||||
c->len = 0;
|
||||
refcnt_inc(&f->refcnt);
|
||||
return c;
|
||||
}
|
||||
|
||||
static int get_page_mask(void)
|
||||
{
|
||||
int n;
|
||||
long page_sz = sysconf(_SC_PAGESIZE);
|
||||
size_t page_mask = 0;
|
||||
|
||||
for (n = 0; page_sz > 0; page_sz >>= 1, n++) {
|
||||
page_mask <<= 1;
|
||||
page_mask |= 1;
|
||||
}
|
||||
|
||||
return page_mask >> 1;
|
||||
}
|
||||
|
||||
int chunk_fill(struct list_head *file_list, struct list_head *chunk_list,
|
||||
int nr_conn, int min_chunk_sz, int max_chunk_sz)
|
||||
{
|
||||
struct chunk *c;
|
||||
struct file *f;
|
||||
size_t page_mask;
|
||||
size_t chunk_sz;
|
||||
size_t size;
|
||||
|
||||
page_mask = get_page_mask();
|
||||
|
||||
list_for_each_entry(f, file_list, list) {
|
||||
if (f->size <= min_chunk_sz)
|
||||
chunk_sz = f->size;
|
||||
else if (max_chunk_sz)
|
||||
chunk_sz = max_chunk_sz;
|
||||
else {
|
||||
chunk_sz = (f->size - (f->size % nr_conn)) / nr_conn;
|
||||
chunk_sz &= ~page_mask; /* align with page_sz */
|
||||
if (chunk_sz <= min_chunk_sz)
|
||||
chunk_sz = min_chunk_sz;
|
||||
}
|
||||
|
||||
pr_debug("%s chunk_sz %lu-byte\n", f->src_path, chunk_sz);
|
||||
|
||||
/* for (size = f->size; size > 0;) does not create a
|
||||
* file (chunk) when file size is 0. This do {} while
|
||||
* (size > 0) creates just open/close a 0-byte file.
|
||||
*/
|
||||
size = f->size;
|
||||
do {
|
||||
c = chunk_alloc(f);
|
||||
if (!c)
|
||||
return -1;
|
||||
c->off = f->size - size;
|
||||
c->len = size < chunk_sz ? size : chunk_sz;
|
||||
size -= c->len;
|
||||
list_add_tail(&c->list, chunk_list);
|
||||
pprint4("chunk %s 0x%010lx-0x%010lx %luB\n",
|
||||
c->f->src_path, c->off, c->off + c->len, c->len);
|
||||
} while (size > 0);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef DEBUG
|
||||
void chunk_dump(struct list_head *chunk_list)
|
||||
{
|
||||
struct chunk *c;
|
||||
|
||||
list_for_each_entry(c, chunk_list, list) {
|
||||
pr_debug("%s %s 0x%010lx-0x%010lx %lu-byte\n",
|
||||
c->f->src_path, strloc(c->f->src_is_remote),
|
||||
c->off, c->off + c->len, c->len);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
struct chunk *chunk_acquire(struct list_head *chunk_list)
|
||||
{
|
||||
/* under the lock for chunk_list */
|
||||
|
||||
struct list_head *first = chunk_list->next;
|
||||
struct chunk *c = NULL;
|
||||
|
||||
if (list_empty(chunk_list))
|
||||
return NULL; /* list is empty */
|
||||
|
||||
c = list_entry(first, struct chunk, list);
|
||||
list_del(first);
|
||||
return c;
|
||||
}
|
||||
|
||||
int chunk_prepare(struct chunk *c, sftp_session sftp)
|
||||
{
|
||||
struct file *f = c->f;
|
||||
int ret = 0;
|
||||
|
||||
LOCK_ACQUIRE_THREAD(&f->lock);
|
||||
if (f->state == FILE_STATE_INIT) {
|
||||
if (file_dst_prepare(f, f->dst_is_remote ? sftp : NULL) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
f->state = FILE_STATE_OPENED;
|
||||
pprint2("copy start: %s\n", f->src_path);
|
||||
}
|
||||
|
||||
out:
|
||||
LOCK_RELEASE_THREAD();
|
||||
return ret;
|
||||
}
|
||||
|
||||
static mode_t file_get_mode(const char *path, sftp_session sftp)
|
||||
{
|
||||
mode_t mode;
|
||||
|
||||
if (sftp) {
|
||||
sftp_attributes attr = sftp_stat(sftp, path);
|
||||
if (!attr) {
|
||||
pr_err("sftp_stat %s: %s\n", path, sftp_get_ssh_error(sftp));
|
||||
return -1;
|
||||
}
|
||||
mode = attr->permissions;
|
||||
sftp_attributes_free(attr);
|
||||
} else {
|
||||
struct stat statbuf;
|
||||
if (stat(path, &statbuf) < 0) {
|
||||
pr_err("stat %s: %s\n", path, strerrno());
|
||||
return -1;
|
||||
}
|
||||
mode = statbuf.st_mode & (S_IRWXU|S_IRWXG|S_IRWXO);
|
||||
}
|
||||
return mode;
|
||||
}
|
||||
|
||||
static int file_set_mode(const char *path, mode_t mode, sftp_session sftp)
|
||||
{
|
||||
if (sftp) {
|
||||
if (sftp_chmod(sftp, path, mode) < 0) {
|
||||
pr_err("sftp_chmod %s: %s\n", path, sftp_get_ssh_error(sftp));
|
||||
return -1;
|
||||
}
|
||||
} else {
|
||||
if (chmod(path, mode) < 0) {
|
||||
pr_err("chmod %s: %s\n", path, strerrno());
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chunk_open_local(const char *path, int flags, mode_t mode, size_t off)
|
||||
{
|
||||
int fd;
|
||||
|
||||
fd = open(path, flags, mode);
|
||||
if (fd < 0) {
|
||||
pr_err("open failed for %s: %s\n", path, strerrno());
|
||||
return -1;
|
||||
}
|
||||
if (lseek(fd, off, SEEK_SET) < 0) {
|
||||
pr_err("seek error for %s: %s\n", path, strerrno());
|
||||
close(fd);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return fd;
|
||||
}
|
||||
|
||||
static sftp_file chunk_open_remote(const char *path, int flags, mode_t mode, size_t off,
|
||||
sftp_session sftp)
|
||||
{
|
||||
sftp_file sf;
|
||||
|
||||
sf = sftp_open(sftp, path, flags, mode);
|
||||
|
||||
if (!sf) {
|
||||
pr_err("sftp_open %s: %s\n", path, sftp_get_ssh_error(sftp));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (sftp_seek64(sf, off) < 0) {
|
||||
pr_err("sftp_seek64 %s: %s\n", path, sftp_get_ssh_error(sftp));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return sf;
|
||||
}
|
||||
|
||||
static ssize_t read_to_buf(void *ptr, size_t len, void *userdata)
|
||||
{
|
||||
int fd = *((int *)userdata);
|
||||
return read(fd, ptr, len);
|
||||
}
|
||||
|
||||
static int chunk_copy_local_to_remote_async(struct chunk *c, int fd, sftp_file sf,
|
||||
int nr_ahead, int buf_sz, size_t *counter)
|
||||
{
|
||||
ssize_t read_bytes, remaind, thrown;
|
||||
int idx, ret;
|
||||
struct {
|
||||
uint32_t id;
|
||||
ssize_t len;
|
||||
} reqs[nr_ahead];
|
||||
|
||||
if (c->len == 0)
|
||||
return 0;
|
||||
|
||||
remaind = thrown = c->len;
|
||||
for (idx = 0; idx < nr_ahead && thrown > 0; idx++) {
|
||||
reqs[idx].len = min(thrown, buf_sz);
|
||||
reqs[idx].len = sftp_async_write(sf, read_to_buf, reqs[idx].len, &fd,
|
||||
&reqs[idx].id);
|
||||
if (reqs[idx].len < 0) {
|
||||
pr_err("sftp_async_write: %d or %s\n",
|
||||
sftp_get_error(sf->sftp), strerrno());
|
||||
return -1;
|
||||
}
|
||||
thrown -= reqs[idx].len;
|
||||
}
|
||||
|
||||
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
||||
ret = sftp_async_write_end(sf, reqs[idx].id, 1);
|
||||
if (ret != SSH_OK) {
|
||||
pr_err("sftp_async_write_end: %d\n", sftp_get_error(sf->sftp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
*counter += reqs[idx].len;
|
||||
remaind -= reqs[idx].len;
|
||||
|
||||
if (remaind <= 0)
|
||||
break;
|
||||
|
||||
if (thrown <= 0)
|
||||
continue;
|
||||
|
||||
reqs[idx].len = min(thrown, buf_sz);
|
||||
reqs[idx].len = sftp_async_write(sf, read_to_buf, reqs[idx].len, &fd,
|
||||
&reqs[idx].id);
|
||||
if (reqs[idx].len < 0) {
|
||||
pr_err("sftp_async_write: %d or %s\n",
|
||||
sftp_get_error(sf->sftp), strerrno());
|
||||
return -1;
|
||||
}
|
||||
thrown -= reqs[idx].len;
|
||||
}
|
||||
|
||||
if (remaind < 0) {
|
||||
pr_err("invalid remaind bytes %ld. last async_write_end bytes %lu.",
|
||||
remaind, reqs[idx].len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static int chunk_copy_remote_to_local_async(struct chunk *c, int fd, sftp_file sf,
|
||||
int nr_ahead, int buf_sz, size_t *counter)
|
||||
{
|
||||
ssize_t read_bytes, write_bytes, remaind, thrown;
|
||||
char buf[buf_sz];
|
||||
int idx;
|
||||
struct {
|
||||
int id;
|
||||
ssize_t len;
|
||||
} reqs[nr_ahead];
|
||||
|
||||
if (c->len == 0)
|
||||
return 0;
|
||||
|
||||
remaind = thrown = c->len;
|
||||
|
||||
for (idx = 0; idx < nr_ahead && thrown > 0; idx++) {
|
||||
reqs[idx].len = min(thrown, sizeof(buf));
|
||||
reqs[idx].id = sftp_async_read_begin(sf, reqs[idx].len);
|
||||
if (reqs[idx].id < 0) {
|
||||
pr_err("sftp_async_read_begin: %d\n",
|
||||
sftp_get_error(sf->sftp));
|
||||
return -1;
|
||||
}
|
||||
thrown -= reqs[idx].len;
|
||||
}
|
||||
|
||||
for (idx = 0; remaind > 0; idx = (idx + 1) % nr_ahead) {
|
||||
read_bytes = sftp_async_read(sf, buf, reqs[idx].len, reqs[idx].id);
|
||||
if (read_bytes == SSH_ERROR) {
|
||||
pr_err("sftp_async_read: %d\n", sftp_get_error(sf->sftp));
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (thrown > 0) {
|
||||
reqs[idx].len = min(thrown, sizeof(buf));
|
||||
reqs[idx].id = sftp_async_read_begin(sf, reqs[idx].len);
|
||||
thrown -= reqs[idx].len;
|
||||
}
|
||||
|
||||
write_bytes = write(fd, buf, read_bytes);
|
||||
if (write_bytes < 0) {
|
||||
pr_err("write: %s\n", strerrno());
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (write_bytes < read_bytes) {
|
||||
pr_err("failed to write full bytes\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
*counter += write_bytes;
|
||||
remaind -= read_bytes;
|
||||
}
|
||||
|
||||
if (remaind < 0) {
|
||||
pr_err("invalid remaind bytes %ld. last async_read bytes %ld. "
|
||||
"last write bytes %ld\n",
|
||||
remaind, read_bytes, write_bytes);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int chunk_copy_local_to_remote(struct chunk *c, sftp_session sftp,
|
||||
int nr_ahead, int buf_sz, size_t *counter)
|
||||
{
|
||||
struct file *f = c->f;
|
||||
sftp_file sf = NULL;
|
||||
mode_t mode;
|
||||
int ret = 0;
|
||||
int fd = 0;
|
||||
int flags;
|
||||
|
||||
flags = O_RDONLY;
|
||||
mode = S_IRUSR;
|
||||
if ((fd = chunk_open_local(f->src_path, flags, mode, c->off)) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = O_WRONLY;
|
||||
mode = S_IRUSR|S_IWUSR;
|
||||
if (!(sf = chunk_open_remote(f->dst_path, flags, mode, c->off, sftp))) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = chunk_copy_local_to_remote_async(c, fd, sf, nr_ahead, buf_sz, counter);
|
||||
if (ret < 0)
|
||||
goto out;
|
||||
out:
|
||||
if (fd > 0)
|
||||
close(fd);
|
||||
if (sf)
|
||||
sftp_close(sf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int chunk_copy_remote_to_local(struct chunk *c, sftp_session sftp,
|
||||
int nr_ahead, int buf_sz, size_t *counter)
|
||||
{
|
||||
struct file *f = c->f;
|
||||
sftp_file sf = NULL;
|
||||
mode_t mode;
|
||||
int flags;
|
||||
int fd = 0;
|
||||
int ret = 0;
|
||||
|
||||
flags = O_WRONLY;
|
||||
mode = S_IRUSR|S_IWUSR;
|
||||
if ((fd = chunk_open_local(f->dst_path, flags, mode, c->off)) < 0) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
flags = O_RDONLY;
|
||||
mode = S_IRUSR;
|
||||
if (!(sf = chunk_open_remote(f->src_path, flags, mode, c->off, sftp))) {
|
||||
ret = -1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ret = chunk_copy_remote_to_local_async(c, fd, sf, nr_ahead, buf_sz, counter);
|
||||
if (ret< 0)
|
||||
goto out;
|
||||
|
||||
out:
|
||||
if (fd > 0)
|
||||
close(fd);
|
||||
if (sf)
|
||||
sftp_close(sf);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int file_cleanup(struct file *f, sftp_session sftp)
|
||||
{
|
||||
sftp_session s, d;
|
||||
mode_t mode;
|
||||
|
||||
if (f->dst_is_remote) {
|
||||
s = NULL;
|
||||
d = sftp;
|
||||
} else {
|
||||
s = sftp;
|
||||
d = NULL;
|
||||
}
|
||||
|
||||
if ((mode = file_get_mode(f->src_path, s)) < 0)
|
||||
return -1;
|
||||
if (file_set_mode(f->dst_path, mode, d) < 0)
|
||||
return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int chunk_copy(struct chunk *c, sftp_session sftp, int nr_ahead, int buf_sz,
|
||||
size_t *counter)
|
||||
{
|
||||
struct file *f = c->f;
|
||||
int ret = 0;
|
||||
|
||||
pprint4("copy start: chunk %s 0x%010lx-0x%010lx %luB\n",
|
||||
c->f->src_path, c->off, c->off + c->len, c->len);
|
||||
|
||||
if (f->dst_is_remote)
|
||||
ret = chunk_copy_local_to_remote(c, sftp, nr_ahead, buf_sz, counter);
|
||||
else
|
||||
ret = chunk_copy_remote_to_local(c, sftp, nr_ahead, buf_sz, counter);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
pprint4("copy done: chunk %s 0x%010lx-0x%010lx %luB\n",
|
||||
c->f->src_path, c->off, c->off + c->len, c->len);
|
||||
|
||||
if (refcnt_dec(&f->refcnt) == 0) {
|
||||
f->state = FILE_STATE_DONE;
|
||||
pprint2("copy done: %s\n", f->src_path);
|
||||
ret = file_cleanup(f, sftp);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
85
src/file.h
85
src/file.h
@@ -1,85 +0,0 @@
|
||||
#ifndef _FILE_H_
|
||||
#define _FILE_H_
|
||||
|
||||
#include <limits.h>
|
||||
#include <pthread.h>
|
||||
#include "libssh/libssh.h"
|
||||
#include "libssh/sftp.h"
|
||||
|
||||
#include <list.h>
|
||||
#include <atomic.h>
|
||||
|
||||
struct file {
|
||||
struct list_head list; /* mscp->file_list */
|
||||
|
||||
char src_path[PATH_MAX]; /* copy source path */
|
||||
bool src_is_remote; /* source is remote */
|
||||
size_t size; /* size of this file */
|
||||
|
||||
char dst_path[PATH_MAX]; /* copy destination path */
|
||||
bool dst_is_remote; /* destination is remote */
|
||||
|
||||
int state; /* destination file state */
|
||||
lock lock; /* mutex to protect state */
|
||||
refcnt refcnt; /* chunks referencing this file */
|
||||
};
|
||||
#define FILE_STATE_INIT 0
|
||||
#define FILE_STATE_OPENED 1
|
||||
#define FILE_STATE_DONE 2
|
||||
|
||||
#define strloc(is_remote) is_remote ? "(remote)" : "(local)"
|
||||
|
||||
/* Allocating chunk increments refcnt of the associating file.
|
||||
* Multiple threads copying files follows:
|
||||
*
|
||||
* acquire a chunk (inside a global lock)
|
||||
*
|
||||
* if the file state of the chunk is INIT:
|
||||
* acquire the file lock
|
||||
* * if file state is INIT:
|
||||
* create directory if necessary
|
||||
* open file with O_TRUNC and close.
|
||||
* set file state OPENED.
|
||||
* // only the first thread in the lock open the destination file
|
||||
* release the file lock
|
||||
* endif
|
||||
*
|
||||
* copy the chunk to the destination.
|
||||
* decrement the refcnt of the file.
|
||||
*
|
||||
* if refcnt == 0:
|
||||
* all chunks are copied.
|
||||
* set the file state DONE, print something useful output.
|
||||
* endif
|
||||
*/
|
||||
|
||||
struct chunk {
|
||||
struct list_head list; /* mscp->chunk_list */
|
||||
struct file *f;
|
||||
size_t off; /* offset of this chunk on the file f */
|
||||
size_t len; /* length of this chunk */
|
||||
size_t done; /* copied bytes for this chunk by a thread */
|
||||
};
|
||||
|
||||
char *file_find_hostname(char *path);
|
||||
bool file_has_hostname(char *path);
|
||||
|
||||
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);
|
||||
|
||||
struct chunk *chunk_acquire(struct list_head *chunk_list);
|
||||
int chunk_prepare(struct chunk *c, sftp_session sftp);
|
||||
int chunk_copy(struct chunk *c, sftp_session sftp, int nr_ahead, int buf_sz,
|
||||
size_t *counter);
|
||||
|
||||
|
||||
#ifdef DEBUG
|
||||
void file_dump(struct list_head *file_list);
|
||||
void chunk_dump(struct list_head *chunk_list);
|
||||
#endif
|
||||
|
||||
|
||||
#endif /* _FILE_H_ */
|
||||
@@ -92,7 +92,7 @@ struct target {
|
||||
|
||||
struct target *validate_targets(char **arg, int len)
|
||||
{
|
||||
/* arg is array of source ... remote.
|
||||
/* arg is array of source ... destination.
|
||||
* There are two cases:
|
||||
*
|
||||
* 1. remote:path remote:path ... path, remote to local copy
|
||||
|
||||
Reference in New Issue
Block a user