mirror of
https://github.com/nextcloud/desktop.git
synced 2024-12-11 10:14:59 +03:00
d80da2a088
All contributors have agreed to change the license of libcsync to LGPL.
513 lines
12 KiB
C
513 lines
12 KiB
C
/*
|
|
* libcsync -- a library to sync a directory with another
|
|
*
|
|
* Copyright (c) 2008-2013 by Andreas Schneider <asn@cryptomilk.org>
|
|
*
|
|
* This library is free software; you can redistribute it and/or
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
* License as published by the Free Software Foundation; either
|
|
* version 2.1 of the License, or (at your option) any later version.
|
|
*
|
|
* This library is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
* License along with this library; if not, write to the Free Software
|
|
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <stdio.h>
|
|
#include <libsmbclient.h>
|
|
|
|
#include "c_lib.h"
|
|
#include "c_private.h"
|
|
|
|
#include "vio/csync_vio_module.h"
|
|
#include "vio/csync_vio_file_stat.h"
|
|
|
|
#ifdef NDEBUG
|
|
#define DEBUG_SMB(x)
|
|
#else
|
|
#define DEBUG_SMB(x) printf x
|
|
#endif
|
|
|
|
SMBCCTX *smb_context = NULL;
|
|
csync_auth_callback _authcb = NULL;
|
|
void *_userdata;
|
|
|
|
/*
|
|
* Authentication callback for libsmbclient
|
|
*/
|
|
static void get_auth_data_with_context_fn(SMBCCTX *smb_ctx,
|
|
const char *srv,
|
|
const char *shr,
|
|
char *wg, int wglen,
|
|
char *un, int unlen,
|
|
char *pw, int pwlen)
|
|
{
|
|
static int try_krb5 = 1;
|
|
char *h;
|
|
|
|
(void) smb_ctx;
|
|
(void) shr;
|
|
(void) wg;
|
|
(void) wglen;
|
|
|
|
DEBUG_SMB(("csync_smb - user=%s, workgroup=%s, server=%s, share=%s\n",
|
|
un, wg, srv, shr));
|
|
|
|
/* Don't authenticate for workgroup listing */
|
|
if (srv == NULL || srv[0] == '\0') {
|
|
DEBUG_SMB(("csync_smb - emtpy server name"));
|
|
return;
|
|
}
|
|
|
|
/* Try kerberos authentication if available */
|
|
if (try_krb5 && getenv("KRB5CCNAME")) {
|
|
try_krb5 = 0;
|
|
|
|
return;
|
|
}
|
|
|
|
/* check for an existing user */
|
|
h = smbc_getUser(smb_ctx);
|
|
if (h != NULL) {
|
|
/* The username is known from the url. */
|
|
DEBUG_SMB(("csync_smb - have username from url: %s\n", h));
|
|
if (snprintf(un, unlen, "%s", h) < 0) {
|
|
/* Even if that fials, go on. */
|
|
}
|
|
} else {
|
|
if (_authcb != NULL) {
|
|
DEBUG_SMB(("csync_smb - execute authentication callback\n"));
|
|
(*_authcb) ("Username:", un, unlen, 1, 0, smbc_getOptionUserData(smb_ctx));
|
|
}
|
|
}
|
|
|
|
/* Always ask for the password */
|
|
if (_authcb != NULL) {
|
|
/* Call the passwort prompt */
|
|
(*_authcb) ("Password:", pw, pwlen, 0, 0, smbc_getOptionUserData(smb_ctx));
|
|
}
|
|
|
|
DEBUG_SMB(("csync_smb - user=%s, workgroup=%s, server=%s, share=%s\n",
|
|
un, wg, srv, shr));
|
|
|
|
try_krb5 = 1;
|
|
|
|
return;
|
|
}
|
|
|
|
typedef struct smb_fhandle_s {
|
|
int fd;
|
|
} smb_fhandle_t;
|
|
|
|
|
|
/*
|
|
* file functions
|
|
*/
|
|
|
|
static csync_vio_method_handle_t *_open(const char *durl, int flags, mode_t mode) {
|
|
smb_fhandle_t *handle = NULL;
|
|
int fd = -1;
|
|
|
|
if ((fd = smbc_open(durl, flags, mode)) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
handle = c_malloc(sizeof(smb_fhandle_t));
|
|
if (handle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
handle->fd = fd;
|
|
return (csync_vio_method_handle_t *) handle;
|
|
}
|
|
|
|
static csync_vio_method_handle_t *_creat(const char *durl, mode_t mode) {
|
|
smb_fhandle_t *handle = NULL;
|
|
int fd = -1;
|
|
|
|
if ((fd = smbc_creat(durl, mode)) < 0) {
|
|
return NULL;
|
|
}
|
|
|
|
handle = c_malloc(sizeof(smb_fhandle_t));
|
|
if (handle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
handle->fd = fd;
|
|
return (csync_vio_method_handle_t *) handle;
|
|
}
|
|
|
|
static int _close(csync_vio_method_handle_t *fhandle) {
|
|
int rc = -1;
|
|
smb_fhandle_t *handle = NULL;
|
|
|
|
if (fhandle == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
handle = (smb_fhandle_t *) fhandle;
|
|
|
|
rc = smbc_close(handle->fd);
|
|
|
|
SAFE_FREE(handle);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static ssize_t _read(csync_vio_method_handle_t *fhandle, void *buf, size_t count) {
|
|
smb_fhandle_t *handle = NULL;
|
|
|
|
if (fhandle == NULL) {
|
|
errno = EBADF;
|
|
return (ssize_t) -1;
|
|
}
|
|
|
|
handle = (smb_fhandle_t *) fhandle;
|
|
|
|
return smbc_read(handle->fd, buf, count);
|
|
}
|
|
|
|
static ssize_t _write(csync_vio_method_handle_t *fhandle, const void *buf, size_t count) {
|
|
smb_fhandle_t *handle = NULL;
|
|
|
|
if (fhandle == NULL) {
|
|
errno = EBADF;
|
|
return (ssize_t) -1;
|
|
}
|
|
|
|
handle = (smb_fhandle_t *) fhandle;
|
|
|
|
return smbc_write(handle->fd, (char *) buf, count);
|
|
}
|
|
|
|
static off_t _lseek(csync_vio_method_handle_t *fhandle, off_t offset, int whence) {
|
|
smb_fhandle_t *handle = NULL;
|
|
|
|
if (fhandle == NULL) {
|
|
errno = EBADF;
|
|
return (off_t) -1;
|
|
}
|
|
|
|
handle = (smb_fhandle_t *) fhandle;
|
|
|
|
return smbc_lseek(handle->fd, offset, whence);
|
|
}
|
|
|
|
/*
|
|
* directory functions
|
|
*/
|
|
|
|
typedef struct smb_dhandle_s {
|
|
int dh;
|
|
char *path;
|
|
} smb_dhandle_t;
|
|
|
|
static csync_vio_method_handle_t *_opendir(const char *name) {
|
|
smb_dhandle_t *handle = NULL;
|
|
|
|
handle = c_malloc(sizeof(smb_dhandle_t));
|
|
if (handle == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
handle->dh = smbc_opendir(name);
|
|
if (handle->dh < 0) {
|
|
SAFE_FREE(handle);
|
|
return NULL;
|
|
}
|
|
handle->path = c_strdup(name);
|
|
|
|
return (csync_vio_method_handle_t *) handle;
|
|
}
|
|
|
|
static int _closedir(csync_vio_method_handle_t *dhandle) {
|
|
smb_dhandle_t *handle = NULL;
|
|
int rc = -1;
|
|
|
|
if (dhandle == NULL) {
|
|
errno = EBADF;
|
|
return -1;
|
|
}
|
|
|
|
handle = (smb_dhandle_t *) dhandle;
|
|
|
|
rc = smbc_closedir(handle->dh);
|
|
|
|
SAFE_FREE(handle->path);
|
|
SAFE_FREE(handle);
|
|
|
|
return rc;
|
|
}
|
|
|
|
static csync_vio_file_stat_t *_readdir(csync_vio_method_handle_t *dhandle) {
|
|
struct smbc_dirent *dirent = NULL;
|
|
smb_dhandle_t *handle = NULL;
|
|
csync_vio_file_stat_t *file_stat = NULL;
|
|
|
|
handle = (smb_dhandle_t *) dhandle;
|
|
|
|
errno = 0;
|
|
dirent = smbc_readdir(handle->dh);
|
|
if (dirent == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
file_stat = c_malloc(sizeof(csync_vio_file_stat_t));
|
|
if (file_stat == NULL) {
|
|
return NULL;
|
|
}
|
|
|
|
file_stat->name = c_strndup(dirent->name, dirent->namelen);
|
|
file_stat->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
|
|
|
switch (dirent->smbc_type) {
|
|
case SMBC_FILE_SHARE:
|
|
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
|
break;
|
|
case SMBC_WORKGROUP:
|
|
case SMBC_SERVER:
|
|
case SMBC_COMMS_SHARE:
|
|
case SMBC_IPC_SHARE:
|
|
break;
|
|
case SMBC_DIR:
|
|
case SMBC_FILE:
|
|
file_stat->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
if (dirent->smbc_type == SMBC_DIR) {
|
|
file_stat->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
|
} else {
|
|
file_stat->type = CSYNC_VIO_FILE_TYPE_REGULAR;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return file_stat;
|
|
}
|
|
|
|
static int _mkdir(const char *uri, mode_t mode) {
|
|
return smbc_mkdir(uri, mode);
|
|
}
|
|
|
|
static int _rmdir(const char *uri) {
|
|
return smbc_rmdir(uri);
|
|
}
|
|
|
|
static int _stat(const char *uri, csync_vio_file_stat_t *buf) {
|
|
csync_stat_t sb;
|
|
|
|
if (smbc_stat(uri, &sb) < 0) {
|
|
return -1;
|
|
}
|
|
|
|
buf->name = c_basename(uri);
|
|
if (buf->name == NULL) {
|
|
csync_vio_file_stat_destroy(buf);
|
|
return -1;
|
|
}
|
|
buf->fields = CSYNC_VIO_FILE_STAT_FIELDS_NONE;
|
|
|
|
switch(sb.st_mode & S_IFMT) {
|
|
case S_IFBLK:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_BLOCK_DEVICE;
|
|
break;
|
|
case S_IFCHR:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_CHARACTER_DEVICE;
|
|
break;
|
|
case S_IFDIR:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_DIRECTORY;
|
|
break;
|
|
case S_IFIFO:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_FIFO;
|
|
break;
|
|
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_IFSOCK:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_SYMBOLIC_LINK;
|
|
break;
|
|
default:
|
|
buf->type = CSYNC_VIO_FILE_TYPE_UNKNOWN;
|
|
break;
|
|
}
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_TYPE;
|
|
|
|
buf->mode = sb.st_mode;
|
|
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->device = sb.st_dev;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_DEVICE;
|
|
|
|
buf->inode = sb.st_ino;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_INODE;
|
|
|
|
buf->nlink = sb.st_nlink;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_LINK_COUNT;
|
|
|
|
buf->uid = sb.st_uid;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_UID;
|
|
|
|
buf->gid = sb.st_gid;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_GID;
|
|
|
|
buf->size = sb.st_size;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_SIZE;
|
|
|
|
buf->blksize = sb.st_blksize;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_SIZE;
|
|
|
|
buf->blkcount = sb.st_blocks;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_BLOCK_COUNT;
|
|
|
|
buf->atime = sb.st_atime;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_ATIME;
|
|
|
|
buf->mtime = sb.st_mtime;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_MTIME;
|
|
|
|
buf->ctime = sb.st_ctime;
|
|
buf->fields |= CSYNC_VIO_FILE_STAT_FIELDS_CTIME;
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _rename(const char *olduri, const char *newuri) {
|
|
return smbc_rename(olduri, newuri);
|
|
}
|
|
|
|
static int _unlink(const char *uri) {
|
|
return smbc_unlink(uri);
|
|
}
|
|
|
|
static int _chmod(const char *uri, mode_t mode) {
|
|
return smbc_chmod(uri, mode);
|
|
}
|
|
|
|
static int _chown(const char *uri, uid_t owner, gid_t group) {
|
|
(void) uri;
|
|
(void) owner;
|
|
(void) group;
|
|
|
|
/* FIXME: implement smbc_setxattr() */
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int _utimes(const char *uri, const struct timeval *times) {
|
|
return smbc_utimes(uri, (struct timeval *) times);
|
|
}
|
|
|
|
static struct csync_vio_capabilities_s _smb_capabilities = {
|
|
.atomar_copy_support = false
|
|
};
|
|
|
|
static struct csync_vio_capabilities_s *_smb_get_capabilities(void)
|
|
{
|
|
return &_smb_capabilities;
|
|
}
|
|
|
|
csync_vio_method_t _method = {
|
|
.method_table_size = sizeof(csync_vio_method_t),
|
|
.get_capabilities = _smb_get_capabilities,
|
|
.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, void *userdata) {
|
|
smb_context = smbc_new_context();
|
|
|
|
DEBUG_SMB(("csync_smb - method_name: %s\n", method_name));
|
|
DEBUG_SMB(("csync_smb - args: %s\n", args));
|
|
(void) method_name;
|
|
(void) args;
|
|
(void) cb;
|
|
|
|
if (smb_context == NULL) {
|
|
fprintf(stderr, "csync_smb - failed to create new smbc context\n");
|
|
return NULL;
|
|
}
|
|
|
|
if (cb != NULL) {
|
|
_authcb = cb;
|
|
}
|
|
|
|
/* set debug level and authentication function callback */
|
|
smbc_setDebug(smb_context, 0);
|
|
smbc_setOptionUserData(smb_context, userdata);
|
|
smbc_setFunctionAuthDataWithContext(smb_context, get_auth_data_with_context_fn);
|
|
|
|
/* Kerberos support */
|
|
smbc_setOptionUseKerberos(smb_context, 1);
|
|
smbc_setOptionFallbackAfterKerberos(smb_context, 1);
|
|
|
|
DEBUG_SMB(("csync_smb - use kerberos = %d\n",
|
|
smbc_getOptionUseKerberos(smb_context)));
|
|
DEBUG_SMB(("csync_smb - use fallback after kerberos = %d\n",
|
|
smbc_getOptionFallbackAfterKerberos(smb_context)));
|
|
|
|
if (smbc_init_context(smb_context) == NULL) {
|
|
fprintf(stderr, "csync_smb - failed to initialize the smbc context");
|
|
smbc_free_context(smb_context, 0);
|
|
smb_context = NULL;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
DEBUG_SMB(("csync_smb - KRB5CCNAME = %s\n", getenv("KRB5CCNAME") != NULL ?
|
|
getenv("KRB5CCNAME") : "not set"));
|
|
|
|
smbc_set_context(smb_context);
|
|
|
|
return &_method;
|
|
}
|
|
|
|
void vio_module_shutdown(csync_vio_method_t *method) {
|
|
(void) method;
|
|
|
|
if (smb_context != NULL) {
|
|
/*
|
|
* If we have a context, all connections and files will be closed even
|
|
* if they are busy.
|
|
*/
|
|
smbc_free_context(smb_context, 1);
|
|
smb_context = NULL;
|
|
}
|
|
}
|
|
|
|
/* vim: set ts=8 sw=2 et cindent: */
|