mirror of
https://github.com/nextcloud/desktop.git
synced 2024-11-27 09:30:13 +03:00
973 lines
23 KiB
C
973 lines
23 KiB
C
/*
|
|
* libcsync -- a library to sync a directory with another
|
|
*
|
|
* Copyright (c) 2008 by Andreas Schneider <mail@cynapses.org>
|
|
*
|
|
* This program is free software = NULL, you can redistribute it and/or
|
|
* modify it under the terms of the GNU General Public License
|
|
* as published by the Free Software Foundation = NULL, either version 2
|
|
* of the License, or (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY = NULL, without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program = NULL, if not, write to the Free Software Foundation,
|
|
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#ifndef _GNU_SOURCE
|
|
#define _GNU_SOURCE
|
|
#endif
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <unistd.h>
|
|
#include <pwd.h>
|
|
|
|
#include <sys/socket.h>
|
|
#include <netinet/in.h>
|
|
#include <arpa/inet.h>
|
|
|
|
#include <libssh2.h>
|
|
#include <libssh2_sftp.h>
|
|
|
|
#include "c_lib.h"
|
|
#include "vio/csync_vio_module.h"
|
|
#include "vio/csync_vio_file_stat.h"
|
|
|
|
#ifdef NDEBUG
|
|
#define DEBUG_SFTP(x)
|
|
#else
|
|
#define DEBUG_SFTP(x) printf x
|
|
#endif
|
|
|
|
int connected;
|
|
|
|
int sock = 0;
|
|
LIBSSH2_SESSION *ssh_session;
|
|
LIBSSH2_SFTP *sftp_session;
|
|
|
|
csync_auth_callback auth_cb;
|
|
|
|
static void _kbd_callback(const char *name, int name_len,
|
|
const char *instruction, int instruction_len, int num_prompts,
|
|
const LIBSSH2_USERAUTH_KBDINT_PROMPT *prompts,
|
|
LIBSSH2_USERAUTH_KBDINT_RESPONSE *responses,
|
|
void **abstract)
|
|
{
|
|
char buf[256] = {0};
|
|
int i = 0;
|
|
|
|
(void) name;
|
|
(void) name_len;
|
|
(void) instruction;
|
|
(void) instruction_len;
|
|
|
|
for (i = 0; i < num_prompts; i++) {
|
|
if ((*auth_cb) (prompts[i].text, buf, 255, (int) prompts[i].echo, 0) == 0) {
|
|
responses[i].text = c_strdup(buf);
|
|
responses[i].length = strlen(buf);
|
|
}
|
|
}
|
|
|
|
(void) prompts;
|
|
(void) abstract;
|
|
}
|
|
|
|
static int _libssh2_sftp_connect(const char *uri) {
|
|
in_addr_t hostaddr = 0;
|
|
struct sockaddr_in sin;
|
|
|
|
char *scheme = NULL;
|
|
char *user = NULL;
|
|
char *passwd = NULL;
|
|
char *host = NULL;
|
|
unsigned int port = 22;
|
|
char *path = NULL;
|
|
|
|
const char *fingerprint = NULL;
|
|
const char *userauthlist = NULL;
|
|
|
|
const char *keyfile1 = "~/.ssh/id_dsa.pub";
|
|
const char *keyfile2 = "~/.ssh/id_dsa";
|
|
|
|
int i = 0;
|
|
int auth = 0;
|
|
int rc = -1;
|
|
|
|
if (connected) {
|
|
return 0;
|
|
}
|
|
|
|
rc = c_parse_uri(uri, &scheme, &user, &passwd, &host, &port, &path);
|
|
if (rc < 0) {
|
|
goto out;
|
|
}
|
|
|
|
if (user == NULL || *user == '\0') {
|
|
const char *usr = NULL;
|
|
struct passwd *p_entry;
|
|
|
|
usr = getenv("USER");
|
|
if (usr != NULL) {
|
|
user = c_strdup(usr);
|
|
} else {
|
|
p_entry = getpwuid(getuid());
|
|
user = c_strdup(p_entry->pw_name);
|
|
}
|
|
|
|
if (user == NULL || *user == '\0') {
|
|
rc = -1;
|
|
goto shutdown;
|
|
}
|
|
}
|
|
|
|
DEBUG_SFTP(("csync_sftp - username: %s\n", user));
|
|
DEBUG_SFTP(("csync_sftp - conntecting to: %s\n", host));
|
|
|
|
/*
|
|
* The application code is responsible for creating the socket
|
|
* and establishing the connection
|
|
*/
|
|
|
|
/* FIXME: allow hostnames */
|
|
hostaddr = inet_addr(host);
|
|
sock = socket(AF_INET, SOCK_STREAM, 0);
|
|
|
|
ZERO_STRUCT(sin);
|
|
sin.sin_family = AF_INET;
|
|
sin.sin_port = htons(port);
|
|
sin.sin_addr.s_addr = hostaddr;
|
|
|
|
rc = connect(sock, (struct sockaddr*)(&sin), sizeof(struct sockaddr_in));
|
|
if (rc < 0) {
|
|
fprintf(stderr, "csync_sftp - failed to connect!\n");
|
|
goto out;
|
|
}
|
|
|
|
/* Create a session instance */
|
|
ssh_session = libssh2_session_init();
|
|
if (ssh_session == NULL) {
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
/* Since we have set non-blocking, tell libssh2 we are blocking */
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
/* ... start it up. This will trade welcome banners, exchange keys,
|
|
* and setup crypto, compression, and MAC layers
|
|
*/
|
|
rc = libssh2_session_startup(ssh_session, sock);
|
|
if (rc < 0) {
|
|
fprintf(stderr, "csync_sftp - failure establishing SSH session: %d\n", rc);
|
|
goto shutdown;
|
|
}
|
|
|
|
/* Since we have not set non-blocking, tell libssh2 we are blocking */
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
/* At this point we havn't yet authenticated. The first thing to do
|
|
* is check the hostkey's fingerprint against our known hosts your app
|
|
* may have it hard coded, may go to a file, may present it to the
|
|
* user, that's your call
|
|
*/
|
|
fingerprint = libssh2_hostkey_hash(ssh_session, LIBSSH2_HOSTKEY_HASH_MD5);
|
|
|
|
/* FIXME: validate the fingerprint */
|
|
DEBUG_SFTP(("csync_sftp - md5 fingerprint: "));
|
|
for (i = 0; i < 16; i++) {
|
|
DEBUG_SFTP(("%02X ", (unsigned char)fingerprint[i]));
|
|
}
|
|
DEBUG_SFTP(("\n"));
|
|
|
|
fingerprint = libssh2_hostkey_hash(ssh_session, LIBSSH2_HOSTKEY_HASH_SHA1);
|
|
DEBUG_SFTP(("csync_sftp - sha1 fingerprint: "));
|
|
for (i = 0; i < 20; i++) {
|
|
DEBUG_SFTP(("%02X ", (unsigned char)fingerprint[i]));
|
|
}
|
|
DEBUG_SFTP(("\n"));
|
|
|
|
/* check what authentication methods are available */
|
|
userauthlist = libssh2_userauth_list(ssh_session, user, strlen(user));
|
|
|
|
DEBUG_SFTP(("csync_sftp - authentication methods: %s\n", userauthlist));
|
|
if (strstr(userauthlist, "password") != NULL) {
|
|
auth |= 1;
|
|
}
|
|
|
|
if (strstr(userauthlist, "keyboard-interactive") != NULL) {
|
|
auth |= 2;
|
|
}
|
|
|
|
if (strstr(userauthlist, "publickey") != NULL) {
|
|
auth |= 4;
|
|
}
|
|
|
|
rc = -1;
|
|
if (passwd && *passwd) {
|
|
if (auth & 1) {
|
|
DEBUG_SFTP(("csync_sftp - authentication method: password\n"));
|
|
rc = libssh2_userauth_password(ssh_session, user, passwd);
|
|
if (rc) {
|
|
fprintf(stderr, "csync_sftp - password authentication failed\n");
|
|
}
|
|
}
|
|
|
|
if (auth & 4) {
|
|
DEBUG_SFTP(("csync_sftp - authentication method: publickey\n"));
|
|
rc = libssh2_userauth_publickey_fromfile(ssh_session, user, keyfile1,
|
|
keyfile2, passwd);
|
|
if (rc) {
|
|
fprintf(stderr, "csync_sftp - publickey authentication failed\n");
|
|
}
|
|
}
|
|
}
|
|
|
|
if (rc < 0 && auth_cb && (auth & 2)) {
|
|
DEBUG_SFTP(("csync_sftp - authentication method: keyboard-interactive\n"));
|
|
rc = libssh2_userauth_keyboard_interactive(ssh_session, user, &_kbd_callback);
|
|
}
|
|
|
|
if (rc < 0) {
|
|
fprintf(stderr, "csync_sftp - authentication failed\n");
|
|
goto shutdown;
|
|
}
|
|
|
|
sftp_session = libssh2_sftp_init(ssh_session);
|
|
if (sftp_session == NULL) {
|
|
rc = -1;
|
|
goto shutdown;
|
|
}
|
|
|
|
DEBUG_SFTP(("csync_sftp - connection established...\n"));
|
|
|
|
connected = 1;
|
|
rc = 0;
|
|
|
|
goto out;
|
|
|
|
shutdown:
|
|
if (sftp_session != NULL) {
|
|
libssh2_sftp_shutdown(sftp_session);
|
|
|
|
sftp_session = NULL;
|
|
}
|
|
|
|
if (ssh_session != NULL) {
|
|
libssh2_session_disconnect(ssh_session, "csync_sftp - shutdown on error");
|
|
libssh2_session_free(ssh_session);
|
|
|
|
ssh_session = NULL;
|
|
}
|
|
|
|
sleep(1);
|
|
close(sock);
|
|
|
|
sock = 0;
|
|
connected = 0;
|
|
out:
|
|
SAFE_FREE(scheme);
|
|
SAFE_FREE(user);
|
|
SAFE_FREE(passwd);
|
|
SAFE_FREE(host);
|
|
SAFE_FREE(path);
|
|
|
|
return rc;
|
|
}
|
|
|
|
/*
|
|
* file functions
|
|
*/
|
|
|
|
static csync_vio_method_handle_t *_open(const char *uri, int access, mode_t mode) {
|
|
csync_vio_method_handle_t *mh = NULL;
|
|
char *path = NULL;
|
|
unsigned long sftp_errno;
|
|
int flags = 0;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if(access & O_RDONLY)
|
|
flags |= LIBSSH2_FXF_READ;
|
|
if(access & O_WRONLY)
|
|
flags |= LIBSSH2_FXF_WRITE;
|
|
if(access & O_RDWR)
|
|
flags |= (LIBSSH2_FXF_WRITE | LIBSSH2_FXF_READ);
|
|
if(access & O_CREAT)
|
|
flags |= LIBSSH2_FXF_CREAT;
|
|
if(access & O_TRUNC)
|
|
flags |= LIBSSH2_FXF_TRUNC;
|
|
if(access & O_EXCL)
|
|
flags |= LIBSSH2_FXF_EXCL;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
mh = (csync_vio_method_handle_t *) libssh2_sftp_open(sftp_session, path, flags, mode);
|
|
if (mh == NULL) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_FILE_ALREADY_EXISTS:
|
|
errno = EEXIST;
|
|
break;
|
|
case LIBSSH2_FX_NO_SUCH_FILE:
|
|
errno = ENOENT;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
case LIBSSH2_FX_OP_UNSUPPORTED:
|
|
errno = EINVAL;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(path);
|
|
return mh;
|
|
}
|
|
|
|
static csync_vio_method_handle_t *_creat(const char *uri, mode_t mode) {
|
|
unsigned long sftp_errno = 0;
|
|
csync_vio_method_handle_t *mh = NULL;
|
|
char *path = NULL;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
mh = (csync_vio_method_handle_t *) libssh2_sftp_open(sftp_session, path,
|
|
LIBSSH2_FXF_CREAT|LIBSSH2_FXF_WRITE|LIBSSH2_FXF_TRUNC, mode);
|
|
if (mh == NULL) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_FILE_ALREADY_EXISTS:
|
|
errno = EEXIST;
|
|
break;
|
|
case LIBSSH2_FX_NO_SUCH_FILE:
|
|
errno = ENOENT;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
case LIBSSH2_FX_OP_UNSUPPORTED:
|
|
errno = EINVAL;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(path);
|
|
return mh;
|
|
}
|
|
|
|
static int _close(csync_vio_method_handle_t *fhandle) {
|
|
unsigned long sftp_errno = 0;
|
|
int rc = -1;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_close(fhandle);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EBADF;
|
|
break;
|
|
case LIBSSH2_FX_NO_SPACE_ON_FILESYSTEM:
|
|
errno = ENOSPC;
|
|
break;
|
|
case LIBSSH2_FX_QUOTA_EXCEEDED:
|
|
errno = EDQUOT;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t _read(csync_vio_method_handle_t *fhandle, void *buf, size_t count) {
|
|
unsigned long sftp_errno = 0;
|
|
int rc = -1;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_read(fhandle, buf, count);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EBADF;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t _write(csync_vio_method_handle_t *fhandle, const void *buf, size_t count) {
|
|
unsigned long sftp_errno = 0;
|
|
int rc = -1;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_write(fhandle, buf, count);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EBADF;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static off_t _lseek(csync_vio_method_handle_t *fhandle, off_t offset, int whence) {
|
|
/* FIXME: really implement seek for lseek? */
|
|
(void) whence;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
libssh2_sftp_seek(fhandle, offset);
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* directory functions
|
|
*/
|
|
|
|
static csync_vio_method_handle_t *_opendir(const char *uri) {
|
|
unsigned long sftp_errno = 0;
|
|
csync_vio_method_handle_t *mh = NULL;
|
|
char *path = NULL;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
mh = (csync_vio_method_handle_t *) libssh2_sftp_opendir(sftp_session, path);
|
|
if (mh == NULL) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_NO_SUCH_FILE:
|
|
errno = ENOENT;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(path);
|
|
return mh;
|
|
}
|
|
|
|
static int _closedir(csync_vio_method_handle_t *dhandle) {
|
|
unsigned long sftp_errno = 0;
|
|
int rc = -1;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_closedir(dhandle);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EBADF;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
return rc;
|
|
}
|
|
|
|
static csync_vio_file_stat_t *_readdir(csync_vio_method_handle_t *dhandle) {
|
|
int rc = 0;
|
|
unsigned long sftp_errno = 0;
|
|
char file[512];
|
|
LIBSSH2_SFTP_ATTRIBUTES dirent;
|
|
csync_vio_file_stat_t *fs = NULL;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_readdir(dhandle, file, 512, &dirent);
|
|
if (rc <= 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_NO_SUCH_FILE:
|
|
errno = ENOENT;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
case LIBSSH2_FX_OK:
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
if (! (dirent.flags & LIBSSH2_SFTP_ATTR_PERMISSIONS)) {
|
|
return NULL;
|
|
}
|
|
|
|
fs = c_malloc(sizeof(csync_vio_file_stat_t));
|
|
if (fs == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
fs->name = c_strdup(file);
|
|
fs->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
|
|
|
switch (dirent.permissions & S_IFMT) {
|
|
case S_IFSOCK:
|
|
case S_IFBLK:
|
|
case S_IFCHR:
|
|
case S_IFIFO:
|
|
case S_IFLNK:
|
|
#if 0
|
|
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
fs->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
|
|
#endif
|
|
break;
|
|
case S_IFREG:
|
|
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
fs->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
|
break;
|
|
case S_IFDIR:
|
|
fs->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
fs->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return fs;
|
|
}
|
|
|
|
static int _mkdir(const char *uri, mode_t mode) {
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
unsigned long sftp_errno = 0;
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
rc = libssh2_sftp_mkdir(sftp_session, path, mode);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_FAILURE:
|
|
/*
|
|
* mkdir always returns a failure, even if the path already exists.
|
|
* To be POSIX conform and to be able to map it to EEXIST a stat
|
|
* call should be issued here
|
|
*/
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
if (libssh2_sftp_lstat(sftp_session, path, &attrs) == 0) {
|
|
errno = EEXIST;
|
|
}
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
static int _rmdir(const char *uri) {
|
|
unsigned long sftp_errno = 0;
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_rmdir(sftp_session, path);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_DIR_NOT_EMPTY:
|
|
errno = ENOTEMPTY;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
case LIBSSH2_FX_WRITE_PROTECT:
|
|
errno = EACCES;
|
|
break;
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EINVAL;
|
|
break;
|
|
case LIBSSH2_FX_LINK_LOOP:
|
|
errno = ELOOP;
|
|
break;
|
|
case LIBSSH2_FX_NOT_A_DIRECTORY:
|
|
errno = ENOTDIR;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
}
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
static int _stat(const char *uri, csync_vio_file_stat_t *buf) {
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
char *path = NULL;
|
|
unsigned long sftp_errno = 0;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_lstat(sftp_session, path, &attrs);
|
|
if (rc < 0) {
|
|
sftp_errno = libssh2_sftp_last_error(sftp_session);
|
|
switch (sftp_errno) {
|
|
case LIBSSH2_FX_NO_SUCH_FILE:
|
|
case LIBSSH2_FX_NO_SUCH_PATH:
|
|
errno = ENOENT;
|
|
break;
|
|
case LIBSSH2_FX_PERMISSION_DENIED:
|
|
errno = EACCES;
|
|
break;
|
|
case LIBSSH2_FX_INVALID_HANDLE:
|
|
errno = EBADF;
|
|
break;
|
|
case LIBSSH2_FX_NOT_A_DIRECTORY:
|
|
errno = ENOTDIR;
|
|
break;
|
|
case LIBSSH2_FX_LINK_LOOP:
|
|
errno = ELOOP;
|
|
break;
|
|
default:
|
|
DEBUG_SFTP(("%s:%d - handle sftp_error: %lu\n", __FILE__, __LINE__, sftp_errno));
|
|
break;
|
|
}
|
|
goto out;
|
|
}
|
|
|
|
buf->name = c_basename(path);
|
|
if (buf->name == NULL) {
|
|
csync_vio_file_stat_destroy(buf);
|
|
goto out;
|
|
}
|
|
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
|
|
|
switch (attrs.permissions & S_IFMT) {
|
|
case S_IFSOCK:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_SOCKET;
|
|
case S_IFBLK:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE;
|
|
case S_IFCHR:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE;
|
|
case S_IFIFO:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_FIFO;
|
|
case S_IFLNK:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
|
|
break;
|
|
case S_IFREG:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
|
break;
|
|
case S_IFDIR:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
|
break;
|
|
default:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
|
|
break;
|
|
}
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
|
|
buf->mode = attrs.permissions;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_PERMISSIONS;
|
|
|
|
if (buf->type == CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK) {
|
|
/* FIXME: handle symlink */
|
|
buf->flags = CSYNC_VIO_FILE_FLAGS_SYMLINK;
|
|
} else {
|
|
buf->flags = CSYNC_VIO_FILE_FLAGS_NONE;
|
|
}
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_FLAGS;
|
|
|
|
buf->uid = attrs.uid;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_UID;
|
|
|
|
buf->uid = attrs.gid;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_GID;
|
|
|
|
buf->size = attrs.filesize;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
|
|
|
buf->atime = attrs.atime;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
|
|
|
|
buf->mtime = attrs.mtime;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
|
|
|
|
rc = 0;
|
|
out:
|
|
SAFE_FREE(path);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int _rename(const char *olduri, const char *newuri) {
|
|
char *oldpath = NULL;
|
|
char *newpath = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(olduri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(olduri, NULL, NULL, NULL, NULL, NULL, &oldpath) < 0) {
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
if (c_parse_uri(newuri, NULL, NULL, NULL, NULL, NULL, &newpath) < 0) {
|
|
rc = -1;
|
|
goto out;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_rename(sftp_session, oldpath, newpath);
|
|
|
|
out:
|
|
SAFE_FREE(oldpath);
|
|
SAFE_FREE(newpath);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static int _unlink(const char *uri) {
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_unlink(sftp_session, path);
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
static int _chmod(const char *uri, mode_t mode) {
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
ZERO_STRUCT(attrs);
|
|
attrs.permissions = mode;
|
|
attrs.flags |= LIBSSH2_SFTP_ATTR_PERMISSIONS;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_setstat(sftp_session, path, &attrs);
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
static int _chown(const char *uri, uid_t owner, gid_t group) {
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
ZERO_STRUCT(attrs);
|
|
attrs.uid = owner;
|
|
attrs.gid = group;
|
|
attrs.flags |= LIBSSH2_SFTP_ATTR_UIDGID;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_setstat(sftp_session, path, &attrs);
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
static int _utimes(const char *uri, const struct timeval *times) {
|
|
LIBSSH2_SFTP_ATTRIBUTES attrs;
|
|
char *path = NULL;
|
|
int rc = -1;
|
|
|
|
if (_libssh2_sftp_connect(uri) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (c_parse_uri(uri, NULL, NULL, NULL, NULL, NULL, &path) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
ZERO_STRUCT(attrs);
|
|
|
|
attrs.atime = times[0].tv_sec;
|
|
attrs.mtime = times[1].tv_sec;
|
|
|
|
attrs.flags |= LIBSSH2_SFTP_ATTR_ACMODTIME;
|
|
|
|
libssh2_session_set_blocking(ssh_session, 1);
|
|
|
|
rc = libssh2_sftp_setstat(sftp_session, path, &attrs);
|
|
|
|
SAFE_FREE(path);
|
|
return rc;
|
|
}
|
|
|
|
csync_vio_method_t _method = {
|
|
.method_table_size = sizeof(csync_vio_method_t),
|
|
.open = _open,
|
|
.creat = _creat,
|
|
.close = _close,
|
|
.read = _read,
|
|
.write = _write,
|
|
.lseek = _lseek,
|
|
.opendir = _opendir,
|
|
.closedir = _closedir,
|
|
.readdir = _readdir,
|
|
.mkdir = _mkdir,
|
|
.rmdir = _rmdir,
|
|
.stat = _stat,
|
|
.rename = _rename,
|
|
.unlink = _unlink,
|
|
.chmod = _chmod,
|
|
.chown = _chown,
|
|
.utimes = _utimes
|
|
};
|
|
|
|
csync_vio_method_t *vio_module_init(const char *method_name, const char *args, csync_auth_callback cb) {
|
|
DEBUG_SFTP(("csync_sftp - method_name: %s\n", method_name));
|
|
DEBUG_SFTP(("csync_sftp - args: %s\n", args));
|
|
|
|
(void) method_name;
|
|
(void) args;
|
|
|
|
auth_cb = cb;
|
|
|
|
return &_method;
|
|
}
|
|
|
|
void vio_module_shutdown(csync_vio_method_t *method) {
|
|
(void) method;
|
|
|
|
if (connected) {
|
|
if (sftp_session != NULL) {
|
|
libssh2_sftp_shutdown(sftp_session);
|
|
}
|
|
|
|
if (ssh_session != NULL) {
|
|
libssh2_session_disconnect(ssh_session, "Normal Shutdown, Thank you for playing");
|
|
libssh2_session_free(ssh_session);
|
|
|
|
ssh_session = NULL;
|
|
}
|
|
|
|
sleep(1);
|
|
close(sock);
|
|
}
|
|
}
|
|
|
|
/* vim: set ts=8 sw=2 et cindent: */
|