summaryrefslogtreecommitdiff
path: root/src/fileops.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/fileops.c')
-rw-r--r--src/fileops.c310
1 files changed, 310 insertions, 0 deletions
diff --git a/src/fileops.c b/src/fileops.c
new file mode 100644
index 0000000..7f6ffe4
--- /dev/null
+++ b/src/fileops.c
@@ -0,0 +1,310 @@
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <dirent.h>
+
+#include <fileops.h>
+#include <ssh.h>
+#include <message.h>
+
+
+sftp_session __thread tls_sftp;
+/* tls_sftp is used *_wrapped() functions */
+
+void set_tls_sftp_session(sftp_session sftp)
+{
+ tls_sftp = sftp;
+}
+
+static void sftp_err_to_errno(sftp_session sftp)
+{
+ int sftperr = sftp_get_error(sftp);
+
+ switch (sftperr){
+ case SSH_FX_OK:
+ case SSH_FX_EOF:
+ errno = 0;
+ break;
+ case SSH_FX_NO_SUCH_FILE:
+ case SSH_FX_NO_SUCH_PATH:
+ errno = ENOENT;
+ break;
+ case SSH_FX_PERMISSION_DENIED:
+ errno = EACCES;
+ break;
+ case SSH_FX_FAILURE:
+ errno = EINVAL;
+ case SSH_FX_BAD_MESSAGE:
+ errno = EBADMSG;
+ case SSH_FX_NO_CONNECTION:
+ errno = ENOTCONN;
+ break;
+ case SSH_FX_CONNECTION_LOST:
+ errno = ENETRESET;
+ break;
+ case SSH_FX_OP_UNSUPPORTED:
+ errno = EOPNOTSUPP;
+ break;
+ case SSH_FX_INVALID_HANDLE:
+ errno = EBADF;
+ break;
+ case SSH_FX_FILE_ALREADY_EXISTS:
+ errno = EEXIST;
+ break;
+ case SSH_FX_WRITE_PROTECT:
+ errno = EPERM;
+ break;
+ case SSH_FX_NO_MEDIA:
+ errno = ENODEV;
+ break;
+ default:
+ mpr_warn(stderr, "unkown SSH_FX response %d", sftperr);
+ }
+}
+
+
+MDIR *mscp_opendir(const char *path, sftp_session sftp)
+{
+ MDIR *md;
+
+ if (!(md = malloc(sizeof(*md))))
+ return NULL;
+ memset(md, 0, sizeof(*md));
+
+ if (tls_sftp) {
+ md->remote = sftp_opendir(tls_sftp, path);
+ sftp_err_to_errno(sftp);
+ if (!md->remote) {
+ goto free_out;
+ }
+ } else {
+ md->local = opendir(path);
+ if (!md->local) {
+ goto free_out;
+ }
+ }
+
+ return md;
+
+free_out:
+ free(md);
+ return NULL;
+}
+
+MDIR *mscp_opendir_wrapped(const char *path)
+{
+ return mscp_opendir(path, tls_sftp);
+}
+
+int mscp_closedir(MDIR *md)
+{
+ int ret;
+ if (md->remote) {
+ ret = sftp_closedir(md->remote);
+ if (ret < 0)
+ sftp_err_to_errno(md->remote->sftp);
+ } else
+ ret = closedir(md->local);
+
+ free(md);
+ return ret;
+}
+
+
+struct dirent __thread tls_dirent;
+/* tls_dirent contains dirent converted from sftp_attributes returned
+ * from sftp_readdir(). This trick is derived from openssh's
+ * fudge_readdir() */
+
+struct dirent *mscp_readdir(MDIR *md)
+{
+ sftp_attributes attr;
+ struct dirent *ret = NULL;
+ static int inum = 1;
+
+ if (md->remote) {
+ attr = sftp_readdir(md->remote->sftp, md->remote);
+ if (!attr) {
+ sftp_err_to_errno(md->remote->sftp);
+ return NULL;
+ }
+
+ memset(&tls_dirent, 0, sizeof(tls_dirent));
+ strlcpy(tls_dirent.d_name, attr->name, sizeof(tls_dirent.d_name));
+ tls_dirent.d_ino = inum++;
+ if (!inum)
+ inum = 1;
+ ret = &tls_dirent;
+ sftp_attributes_free(attr);
+ } else
+ ret = readdir(md->local);
+
+ return ret;
+}
+
+int mscp_mkdir(const char *path, mode_t mode, sftp_session sftp)
+{
+ int ret;
+
+ if (sftp) {
+ ret = sftp_mkdir(sftp, path, mode);
+ fprintf(stderr, "after sftp_mkdir(%s), sftp_get_error is %d\n",
+ path, sftp_get_error(sftp));
+ sftp_err_to_errno(sftp);
+ } else
+ ret = mkdir(path, mode);
+
+ if (ret < 0 && errno == EEXIST) {
+ ret = 0;
+ }
+
+ return ret;
+}
+
+
+static void sftp_attr_to_stat(sftp_attributes attr, struct stat *st)
+{
+ memset(st, 0, sizeof(*st));
+ st->st_size = attr->size;
+ st->st_uid = attr->uid;
+ st->st_gid = attr->gid;
+ st->st_mode = attr->permissions;
+
+ switch (attr->type) {
+ case SSH_FILEXFER_TYPE_REGULAR:
+ st->st_mode |= S_IFREG;
+ break;
+ case SSH_FILEXFER_TYPE_DIRECTORY:
+ st->st_mode |= S_IFDIR;
+ break;
+ case SSH_FILEXFER_TYPE_SYMLINK:
+ st->st_mode |= S_IFLNK;
+ break;
+ case SSH_FILEXFER_TYPE_SPECIAL:
+ st->st_mode |= S_IFCHR; /* or block? */
+ break;
+ case SSH_FILEXFER_TYPE_UNKNOWN:
+ st->st_mode |= S_IFIFO; /* really? */
+ break;
+ default:
+ mpr_warn(stderr, "unkown SSH_FILEXFER_TYPE %d", attr->type);
+ }
+
+ /* ToDo: convert atime, ctime, and mtime */
+}
+
+
+int mscp_stat(const char *path, struct stat *st, sftp_session sftp)
+{
+ sftp_attributes attr;
+ int ret = 0;
+
+ if (sftp) {
+ attr = sftp_stat(sftp, path);
+ sftp_err_to_errno(sftp);
+ if (!attr)
+ return -1;
+
+ sftp_attr_to_stat(attr, st);
+ sftp_attributes_free(attr);
+ ret = 0;
+ } else
+ ret = stat(path, st);
+
+ return ret;
+}
+
+int mscp_stat_wrapped(const char *path, struct stat *st)
+{
+ return mscp_stat(path, st, tls_sftp);
+}
+
+int mscp_lstat(const char *path, struct stat *st, sftp_session sftp)
+{
+ sftp_attributes attr;
+ int ret = 0;
+
+ if (sftp) {
+ attr = sftp_lstat(sftp, path);
+ sftp_err_to_errno(sftp);
+ if (!attr)
+ return -1;
+
+ sftp_attr_to_stat(attr, st);
+ sftp_attributes_free(attr);
+ ret = 0;
+ } else
+ ret = lstat(path, st);
+
+ return ret;
+}
+
+int mscp_lstat_wrapped(const char *path, struct stat *st)
+{
+ return mscp_lstat(path, st, tls_sftp);
+}
+
+
+mf *mscp_open(const char *path, int flags, mode_t mode, sftp_session sftp)
+{
+ mf *f;
+
+ f = malloc(sizeof(*f));
+ if (!f)
+ return NULL;
+ memset(f, 0, sizeof(*f));
+
+ if (sftp) {
+ f->remote = sftp_open(sftp, path, flags, mode);
+ if (!f->remote) {
+ sftp_err_to_errno(sftp);
+ goto free_out;
+ }
+ } else {
+ f->local = open(path, flags, mode);
+ if (f->local < 0)
+ goto free_out;
+ }
+
+ return f;
+
+free_out:
+ free(f);
+ return NULL;
+}
+
+void mscp_close(mf *f)
+{
+ if (f->remote)
+ sftp_close(f->remote);
+ if (f->local > 0)
+ close(f->local);
+ free(f);
+}
+
+int mscp_lseek(mf *f, size_t off)
+{
+ int ret;
+
+ if (f->remote) {
+ ret = sftp_seek64(f->remote, off);
+ sftp_err_to_errno(f->remote->sftp);
+ } else
+ ret = lseek(f->local, off, SEEK_SET);
+
+ return ret;
+}
+
+int mscp_chmod(const char *path, mode_t mode, sftp_session sftp)
+{
+ int ret;
+
+ if (sftp) {
+ ret = sftp_chmod(sftp, path, mode);
+ sftp_err_to_errno(sftp);
+ } else
+ ret = chmod(path, mode);
+
+ return ret;
+}