diff options
author | Ryo Nakamura <upa@haeena.net> | 2022-10-16 22:12:03 +0900 |
---|---|---|
committer | Ryo Nakamura <upa@haeena.net> | 2022-10-16 22:12:03 +0900 |
commit | def9cfeba74357ca98fc25469d4285139ecd42ce (patch) | |
tree | 077c5de04728a1342392d37d95c3c0a7fbe455a3 /src/file.c | |
parent | 579629dea21dbbb26cc9c42c4f1f1022c7270144 (diff) |
implement fill_file
Diffstat (limited to 'src/file.c')
-rw-r--r-- | src/file.c | 281 |
1 files changed, 281 insertions, 0 deletions
diff --git a/src/file.c b/src/file.c new file mode 100644 index 0000000..88614ec --- /dev/null +++ b/src/file.c @@ -0,0 +1,281 @@ +#include <stdlib.h> +#include <stdbool.h> +#include <sys/stat.h> +#include <dirent.h> +#include <limits.h> + +#include <ssh.h> +#include <util.h> +#include <file.h> + +bool file_has_hostname(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 false; + } 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 */ +int file_is_directory(char *path, sftp_session sftp) +{ + 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) { + pr_err("file %s: %s\n", p, + ssh_get_error(sftp_ssh(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) { + pr_err("file %s: %s\n", path, strerrno()); + ret = -1; + } else if ((statbuf.st_mode & S_IFMT) == S_IFDIR) + ret = 1; + } + + return ret; +} + +static struct file *file_allocate(char *path, size_t size, bool remote) +{ + struct file *f; + + f = malloc(sizeof(*f)); + if (!f) { + pr_err("%s\n", strerrno()); + return NULL; + } + memset(f, 0, sizeof(*f)); + + f->path = strdup(path); + if (!f->path) { + pr_err("%s\n", strerrno()); + free(f); + return NULL; + } + + f->size = size; + f->remote = remote; + + return f; +} + +static bool 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; +} + + +static int file_fill_local_recursive(char *path, struct list_head *head) +{ + 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; + } + + 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 (stat(path, &statbuf) < 0) { + pr_err("file %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_allocate(path, statbuf.st_size, false); + if (!f) { + pr_err("%s\n", strerrno()); + return -1; + } + list_add(&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) { + if (file_should_skip(attr->name)) + continue; + + 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); + } + + 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_allocate(path, attr->size, true); + if (!f) { + pr_err("%s\n", strerrno()); + return -1; + } + list_add(&f->list, head); + sftp_attributes_free(attr); + } + } + + return 0; +} + +int file_fill(sftp_session sftp, struct list_head *head, char **src_array, int count) +{ + char *src, *path; + int ret, n; + + for (n = 0; n < count; 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, head); + else + ret = file_fill_local_recursive(path, head); + if (ret < 0) + return -1; + } + + return 0; +} + + +#ifdef DEBUG +void file_dump(struct list_head *head) +{ + struct file *f; + + list_for_each_entry(f, head, list) { + pr_debug("%s %s %lu-byte\n", f->path, + f->remote ? "(remote)" : "(local)", f->size); + } +} +#endif |