diff options
author | Ryo Nakamura <upa@haeena.net> | 2023-02-25 22:17:29 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2023-02-25 22:17:29 +0900 |
commit | 1be9b70808ca235cd784d66efa92ecd2ce8c4e86 (patch) | |
tree | 77dd580ffb3749c8bd8aeb45f5603afabb866d4a /src/path.c | |
parent | b4c021c954866aade1ea893b04f307afa7295bd7 (diff) |
start to impliment mscp as a library
this commit starts to refactor file.h|c to path.h|c and
add mscp.c|h. not completed yet.
Diffstat (limited to 'src/path.c')
-rw-r--r-- | src/path.c | 250 |
1 files changed, 250 insertions, 0 deletions
diff --git a/src/path.c b/src/path.c new file mode 100644 index 0000000..2951dfd --- /dev/null +++ b/src/path.c @@ -0,0 +1,250 @@ +#include <string.h> +#include <unistd.h> +#include <dirent.h> +#include <sys/stat.h> +#include <libgen.h> + +#include <ssh.h> +#include <util.h> +#include <list.h> +#include <atomic.h> +#include <path.h> + + + +static int append_path(sftp_session sftp, const char *path, mstat s, + struct list_head *path_list) +{ + struct path *p; + + if (!(p = malloc(sizeof(*p)))) { + pr_err("failed to allocate memory: %s\n", strerrno()); + return -1; + } + + memset(p, 0, sizeof(*p)); + INIT_LIST_HEAD(&p->list); + strncpy(p->path, path, PATH_MAX - 1); + p->size = mstat_size(s); + p->mode = mstat_mode(s); + p->state = FILE_STATE_INIT; + lock_init(&p->lock); + list_add_tail(&p->list, path_list); + + return 0; +} + +static bool check_path_should_skip(const char *path) +{ + int len = strlen(path); + if ((len == 1 && strncmp(path, ".", 1) == 0) || + (len == 2 && strncmp(path, "..", 2) == 0)) { + return true; + } + return false; +} + +static int walk_path_recursive(sftp_session sftp, const char *path, + struct list_head *path_list) +{ + char next_path[PATH_MAX]; + mdirent *e; + mdir *d; + mstat s; + int ret; + + if (mscp_stat(path, &s, sftp) < 0) + return -1; + + if (mstat_is_regular(s)) { + /* this path is regular file. it is to be copied */ + ret = append_path(sftp, path, s, path_list); + mscp_stat_free(s); + return ret; + } + + if (!mstat_is_dir(s)) { + /* not regular file and not directory, skip it. */ + mscp_stat_free(s); + return 0; + } + + mscp_stat_free(s); + + + /* ok, this path is directory. walk it. */ + if (!(d = mscp_opendir(path, sftp))) + return -1; + + for (e = mscp_readdir(d); !mdirent_is_null(e); e = mscp_readdir(d)) { + if (check_path_should_skip(mdirent_name(e))) + continue; + + if (strlen(path) + 1 + strlen(mdirent_name(e)) > PATH_MAX) { + pr_err("too long path: %s/%s\n", path, mdirent_name(e)); + return -1; + } + snprintf(next_path, sizeof(next_path), "%s/%s", path, mdirent_name(e)); + ret = walk_path_recursive(sftp, next_path, path_list); + if (ret < 0) + return ret; + } + + mscp_closedir(d); + + return 0; +} + +int walk_src_path(sftp_session sftp, const char *src_path, struct list_head *path_list) +{ + return walk_path_recursive(sftp, src_path, path_list); +} + +static int src2dst_path(const char *src_path, const char *src_file_path, + const char *dst_path, char *dst_file_path, size_t len, + bool src_path_is_dir, bool dst_path_is_dir) +{ + char copy[PATH_MAX]; + char *prefix; + int offset; + + strncpy(copy, src_path, PATH_MAX - 1); + prefix = dirname(copy); + if (!prefix) { + pr_err("dirname: %s\n", strerrno()); + return -1; + } + if (strlen(prefix) == 1 && prefix[0] == '.') + offset = 0; + else + offset = strlen(prefix) + 1; + + + /* both are file */ + if (!src_path_is_dir && !dst_path_is_dir) + strncpy(dst_file_path, dst_path, len); + + /* src is file, and dst is dir */ + if (!src_path_is_dir && dst_path_is_dir) + snprintf(dst_file_path, len, "%s/%s", dst_path, src_path + offset); + + /* both are directory */ + if (src_path_is_dir && dst_path_is_dir) + snprintf(dst_file_path, len, "%s/%s", dst_path, src_file_path + offset); + + /* dst path does not exist. change dir name to dst_path */ + if (src_path_is_dir && !dst_path_is_dir) + snprintf(dst_file_path, len, "%s/%s", + dst_path, src_file_path + strlen(src_path) + 1); + + return 0; +} + +int resolve_dst_path(sftp_session sftp, const char *src_path, const char *dst_path, + struct list_head *path_list, bool src_is_dir, bool dst_is_dir) +{ + struct path *p; + + list_for_each_entry(p, path_list, list) { + if (src2dst_path(src_path, p->path, dst_path, p->dst_path, PATH_MAX, + src_is_dir, dst_is_dir) < 0) + return -1; + } + + return 0; +} + +void path_dump(struct list_head *path_list) +{ + struct path *p; + + list_for_each_entry(p, path_list, list) { + printf("src: %s %lu-byte\n", p->path, p->size); + printf("dst: %s\n", p->dst_path); + } +} + +/* chunk preparation */ + +static struct chunk *alloc_chunk(struct path *p) +{ + struct chunk *c; + + if (!(c = malloc(sizeof(*c)))) { + pr_err("%s\n", strerrno()); + return NULL; + } + memset(c, 0, sizeof(*c)); + + c->p = p; + c->off = 0; + c->len = 0; + refcnt_inc(&p->refcnt); + return c; +} + +static int get_page_mask(void) +{ + long page_sz = sysconf(_SC_PAGESIZE); + size_t page_mask = 0; + int n; + + for (n = 0; page_sz > 0; page_sz >>= 1, n++) { + page_mask <<= 1; + page_mask |= 1; + } + + return page_mask >> 1; +} + +int prepare_chunk(struct list_head *path_list, struct list_head *chunk_list, + int nr_conn, int min_chunk_sz, int max_chunk_sz) +{ + struct chunk *c; + struct path *p; + size_t page_mask; + size_t chunk_sz; + size_t size; + + page_mask = get_page_mask(); + + list_for_each_entry(p, path_list, list) { + if (p->size <= min_chunk_sz) + chunk_sz = p->size; + else if (max_chunk_sz) + chunk_sz = max_chunk_sz; + else { + chunk_sz = (p->size - (p->size % nr_conn)) / nr_conn; + chunk_sz &= ~page_mask; /* align with page_sz */ + if (chunk_sz <= min_chunk_sz) + chunk_sz = min_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 = p->size; + do { + c = alloc_chunk(p); + if (!c) + return -1; + c->off = p->size - size; + c->len = size < chunk_sz ? size : chunk_sz; + size -= c->len; + list_add_tail(&c->list, chunk_list); + } while (size > 0); + } + + return 0; +} + +void chunk_dump(struct list_head *chunk_list) +{ + struct chunk *c; + + list_for_each_entry(c, chunk_list, list) { + printf("chunk: %s 0x%lx-%lx bytes\n", + c->p->path, c->off, c->off + c->len); + } +} |