#include "config.h"
#include <time.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <sys/uio.h>
#include <sys/mman.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <signal.h>
#include <pthread.h>
#include <glib.h>
#include "list.h"
#include "common.h"
#include "smbitem.h"
#include "auth-libsecret.h"
#include "auth.h"
#include "smb_conn_proto.h"
#include "process.h"
#include "smb_conn.h"
#include "neg_cache.h"
#ifndef MAP_ANONYMOUS
#define MAP_ANONYMOUS MAP_ANON
#endif
#define SMB_CONN_PROCESS_STATE_UNKNOWN -1
#define SMB_CONN_PROCESS_STATE_ALIVE 0
#define SMB_CONN_PROCESS_STATE_DIED 1
struct smb_conn_query_result{
int errno_value;
int process_state;
};
static int smb_conn_max_retry_count = 2;
static int smb_conn_max_passwd_query_count = 10;
static int smb_conn_server_reply_timeout = 60;
static inline int smb_conn_is_neg_cache_candidate(enum smb_conn_cmd query_cmd,
struct smb_conn_query_result *result){
if (result->process_state == SMB_CONN_PROCESS_STATE_DIED)
switch(result->errno_value){
case ETIMEDOUT:
case ECONNREFUSED:
case EHOSTUNREACH:
return 1;
case EINVAL:
switch(query_cmd){
case OPENDIR:
case STAT:
return 1;
default:
break;
}
default:
break;
}
return 0;
}
static inline int smb_conn_query_result_map(struct smb_conn_query_result *result){
if (result->process_state == SMB_CONN_PROCESS_STATE_ALIVE)
return result->errno_value;
return EIO;
}
static inline int smb_conn_query_result_check(enum smb_conn_cmd query_cmd,
struct smb_conn_query_result *result){
if (result->process_state == SMB_CONN_PROCESS_STATE_ALIVE)
return 1;
if (smb_conn_is_neg_cache_candidate(query_cmd, result))
return 1;
return 0;
}
int smb_conn_set_max_retry_count(int count){
if (count < 0) return 0;
DPRINTF(7, "count=%d\n", count);
g_atomic_int_set(&smb_conn_max_retry_count, count);
return 1;
}
static inline int smb_conn_get_max_retry_count(void){
return g_atomic_int_get(&smb_conn_max_retry_count);
}
int smb_conn_set_max_passwd_query_count(int count){
if (count < 3) return 0;
DPRINTF(7, "count=%d\n", count);
g_atomic_int_set(&smb_conn_max_passwd_query_count, count);
return 1;
}
static inline int smb_conn_get_max_passwd_query_count(void){
return g_atomic_int_get(&smb_conn_max_passwd_query_count);
}
int smb_conn_set_server_reply_timeout(int timeout){
if (timeout < 10) return 0;
DPRINTF(7, "timeout=%d\n", timeout);
g_atomic_int_set(&smb_conn_server_reply_timeout, timeout);
return 1;
}
static inline int smb_conn_get_server_reply_timeout(void){
return g_atomic_int_get(&smb_conn_server_reply_timeout);
}
static void smb_conn_connection_close(struct smb_conn_ctx *ctx){
LIST *elem;
struct smb_conn_file *conn_file;
if (ctx->shmem_ptr == NULL) return;
process_kill_by_smb_conn_fd(ctx->conn_fd);
ctx->conn_fd = -1;
elem = first_list_elem(&ctx->smb_conn_file_list);
while(is_valid_list_elem(&ctx->smb_conn_file_list, elem)){
conn_file = list_entry(elem, struct smb_conn_file, entries);
conn_file->access_time = 0;
conn_file->srv_fd = NULL;
elem = elem->next;
}
}
static int smb_conn_up_if_broken(struct smb_conn_ctx *ctx){
if (ctx->conn_fd != -1){
if (process_is_smb_conn_alive(ctx->conn_fd)) return 0;
smb_conn_connection_close(ctx);
}
ctx->conn_fd = process_start_new_smb_conn(ctx->shmem_ptr, ctx->shmem_size);
return (ctx->conn_fd != -1) ? 0 : -1;
}
int smb_conn_ctx_init(struct smb_conn_ctx *ctx, size_t shmem_size){
if ((ssize_t) shmem_size < getpagesize()) return -1;
init_list(&ctx->smb_conn_file_list);
pthread_mutex_init(&ctx->mutex, NULL);
ctx->access_time = time(NULL);
ctx->shmem_size = shmem_size;
ctx->conn_fd = -1;
if ((ctx->shmem_ptr = (char*) mmap(NULL, ctx->shmem_size,
PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_ANONYMOUS, -1, 0)) == MAP_FAILED){
pthread_mutex_destroy(&ctx->mutex);
return -1;
}
return 0;
}
int smb_conn_ctx_destroy(struct smb_conn_ctx *ctx){
int result = -1;
if (ctx->shmem_ptr == NULL) return -1;
pthread_mutex_lock(&ctx->mutex);
if (is_list_empty(&ctx->smb_conn_file_list)){
if (ctx->conn_fd != -1) smb_conn_connection_close(ctx);
munmap(ctx->shmem_ptr, ctx->shmem_size);
ctx->shmem_ptr = NULL;
result = 0;
}
pthread_mutex_unlock(&ctx->mutex);
if (result == 0) pthread_mutex_destroy(&ctx->mutex);
return result;
}
static int smb_conn_send_password_base(struct smb_conn_ctx *ctx, const char *domain,
const char *user, const char *password){
ssize_t bytes;
struct iovec iov[5];
struct smb_conn_query_hdr header;
struct smb_conn_passwd data;
if ((ctx == NULL) || (ctx->conn_fd == -1)) return -1;
iov[0].iov_base = &header;
iov[0].iov_len = sizeof(header);
iov[1].iov_base = &data;
iov[1].iov_len = sizeof(data);
iov[2].iov_base = (char*) domain;
iov[2].iov_len = strlen(domain) + 1;
iov[3].iov_base = (char*) user;
iov[3].iov_len = strlen(user) + 1;
iov[4].iov_base = (char*) password;
iov[4].iov_len = strlen(password) + 1;
header.query_len = iov[0].iov_len + iov[1].iov_len +
iov[2].iov_len + iov[3].iov_len + iov[4].iov_len;
header.query_cmd = PASSWORD;
header.debug_level = common_get_smbnetfs_debug_level();
data.domain_offs = sizeof(data);
data.username_offs = sizeof(data) + iov[2].iov_len;
data.password_offs = sizeof(data) + iov[2].iov_len + iov[3].iov_len;
if (header.query_len <= COMM_BUF_SIZE){
/* send password data */
bytes = writev(ctx->conn_fd, iov, 5);
}else{
bytes = -1;
}
return (bytes == (ssize_t) header.query_len) ? 0 : -1;
}
static int smb_conn_send_password(struct smb_conn_ctx *ctx,
const char *server, const char *share){
#ifdef HAVE_LIBSECRET
struct libsecret_authinfo *libsecret_info;
#endif /* HAVE_LIBSECRET */
struct authinfo *config_file_info;
int config_file_info_suitability;
char workgroup[256];
int ret;
if ((ctx == NULL) || (ctx->conn_fd == -1)) return -1;
memset(workgroup, 0, sizeof(workgroup));
smbitem_get_group(server, workgroup, sizeof(workgroup));
config_file_info_suitability = -1;
config_file_info = auth_get_authinfo(
workgroup, server, share,
&config_file_info_suitability);
if ((config_file_info != NULL) &&
((config_file_info->domain == NULL) ||
(config_file_info->user == NULL) ||
(config_file_info->password == NULL))){
DPRINTF(0, "WARNING!!! Damaged authinfo record\n");
auth_release_authinfo(config_file_info);
config_file_info = NULL;
config_file_info_suitability = -1;
}
#ifdef HAVE_LIBSECRET
libsecret_info = libsecret_get_authinfo(workgroup, server, share);
if ((libsecret_info != NULL) &&
((libsecret_info->domain == NULL) ||
(libsecret_info->user == NULL) ||
(libsecret_info->password == NULL))){
DPRINTF(0, "WARNING!!! Damaged libsecret_info record\n");
libsecret_free_authinfo(libsecret_info);
libsecret_info = NULL;
}
if (libsecret_info != NULL){
if (libsecret_info->suitability >= config_file_info_suitability){
if (config_file_info != NULL)
auth_release_authinfo(config_file_info);
config_file_info = NULL;
config_file_info_suitability = -1;
goto use_libsecret_info;
}
libsecret_free_authinfo(libsecret_info);
libsecret_info = NULL;
}
#endif /* HAVE_LIBSECRET */
if (config_file_info == NULL) return -1;
ret = smb_conn_send_password_base(ctx,
config_file_info->domain,
config_file_info->user,
config_file_info->password);
auth_release_authinfo(config_file_info);
return ret;
#ifdef HAVE_LIBSECRET
use_libsecret_info:
ret = smb_conn_send_password_base(ctx,
libsecret_info->domain,
libsecret_info->user,
libsecret_info->password);
libsecret_free_authinfo(libsecret_info);
return ret;
#endif /* HAVE_LIBSECRET */
}
static int smb_conn_process_query_lowlevel_va(
struct smb_conn_ctx *ctx,
enum smb_conn_cmd query_cmd,
void *query, size_t query_len,
struct smb_conn_query_result *result,
void *reply, size_t reply_len,
va_list ap){
int iov_cnt, retval, count;
ssize_t bytes;
struct iovec iov[4];
struct smb_conn_query_hdr query_header;
if ((ctx == NULL) || (ctx->conn_fd == -1) ||
((query == NULL) || (query_len == 0)) ||
(result == NULL) ||
((reply == NULL) && (reply_len != 0))) return EINVAL;
result->process_state = SMB_CONN_PROCESS_STATE_UNKNOWN;
result->errno_value = EINVAL;
iov_cnt = 2;
query_header.query_cmd = query_cmd;
query_header.debug_level = common_get_smbnetfs_debug_level();
query_header.query_len = sizeof(query_header) + query_len;
iov[0].iov_base = &query_header;
iov[0].iov_len = sizeof(query_header);
iov[1].iov_base = query;
iov[1].iov_len = query_len;
while(1){
const char *str;
str = va_arg(ap, const char *);
if (str == NULL) break;
if (iov_cnt >= (ssize_t) (sizeof(iov) / sizeof(struct iovec))){
va_end(ap);
return EINVAL;
}
iov[iov_cnt].iov_base = (void *) str;
iov[iov_cnt].iov_len = strlen(str) + 1;
query_header.query_len += iov[iov_cnt].iov_len;
iov_cnt++;
}
if (query_header.query_len > COMM_BUF_SIZE) return EIO;
/* send query */
bytes = writev(ctx->conn_fd, iov, iov_cnt);
if (bytes != (ssize_t) query_header.query_len) goto error;
count = 0;
while(1){
fd_set readfds, exceptfds;
struct timeval tv;
struct smb_conn_reply_hdr *reply_hdr;
char buf[COMM_BUF_SIZE];
tv.tv_sec = smb_conn_get_server_reply_timeout();
tv.tv_usec = 0;
FD_ZERO(&readfds);
FD_SET(ctx->conn_fd, &readfds);
FD_ZERO(&exceptfds);
FD_SET(ctx->conn_fd, &exceptfds);
/* wait for reply */
retval = select(ctx->conn_fd + 1, &readfds, NULL, &exceptfds, &tv);
if ((retval <= 0) || FD_ISSET(ctx->conn_fd, &exceptfds)) goto error;
/* read reply */
bytes = read(ctx->conn_fd, buf, COMM_BUF_SIZE);
if (bytes < (ssize_t) sizeof(struct smb_conn_reply_hdr)) goto error;
/* check reply */
reply_hdr = (struct smb_conn_reply_hdr *) buf;
if ((ssize_t) reply_hdr->reply_len != bytes) goto error;
/* is it message? */
if ((reply_hdr->reply_cmd == MESSAGE) ||
(reply_hdr->reply_cmd == DIE_MSG)){
const char *msg;
struct smb_conn_message_req *msg_req;
if ((reply_hdr->reply_cmd == MESSAGE) &&
(reply_hdr->errno_value != 0)) goto error;
if (buf[bytes - 1] != '\0' ) goto error;
bytes -= sizeof(struct smb_conn_reply_hdr);
if (bytes < (ssize_t) sizeof(struct smb_conn_message_req))
goto error;
msg_req = (struct smb_conn_message_req *) (reply_hdr + 1);
if (msg_req->msg_offs != sizeof(struct smb_conn_message_req))
goto error;
bytes -= sizeof(struct smb_conn_message_req);
msg = ((char *) msg_req) + msg_req->msg_offs;
if (bytes != (ssize_t) (strlen(msg) + 1)) goto error;
DEBUG_PRINT(msg_req->debug_level, "%s", msg);
if (reply_hdr->reply_cmd == DIE_MSG){
result->errno_value = reply_hdr->errno_value;
result->process_state = SMB_CONN_PROCESS_STATE_DIED;
smb_conn_connection_close(ctx);
return 0;
}
continue;
}
/* is it password request? */
if (reply_hdr->reply_cmd == PASSWORD){
const char *server, *share;
struct smb_conn_passwd_req *passwd_req;
/* infinite loop? */
if (count > smb_conn_get_max_passwd_query_count()) goto error;
if (reply_hdr->errno_value != 0) goto error;
if (buf[bytes - 1] != '\0' ) goto error;
bytes -= sizeof(struct smb_conn_reply_hdr);
if (bytes < (ssize_t) sizeof(struct smb_conn_passwd_req)) goto error;
passwd_req = (struct smb_conn_passwd_req *) (reply_hdr + 1);
if ((passwd_req->server_offs != sizeof(struct smb_conn_passwd_req)) ||
(passwd_req->share_offs <= passwd_req->server_offs) ||
((ssize_t) passwd_req->share_offs > bytes - 1)) goto error;
bytes -= sizeof(struct smb_conn_passwd_req);
server = ((char *) passwd_req) + passwd_req->server_offs;
share = ((char *) passwd_req) + passwd_req->share_offs;
if (bytes != (ssize_t) (strlen(server) + strlen(share) + 2))
goto error;
/* process password request */
count++;
if (smb_conn_send_password(ctx, server, share) != 0) goto error;
continue;
}
/* it should be our reply */
if (reply_hdr->reply_cmd != query_cmd) goto error;
if (reply_hdr->errno_value != 0) reply_len = 0;
if (bytes != (ssize_t) (sizeof(struct smb_conn_reply_hdr) + reply_len))
goto error;
/* ok, we got a reply, store error/reply and exit */
if (reply_len != 0) memcpy(reply, reply_hdr + 1, reply_len);
result->errno_value = reply_hdr->errno_value;
result->process_state = SMB_CONN_PROCESS_STATE_ALIVE;
return 0;
}
error:
smb_conn_connection_close(ctx);
return EIO;
}
static int smb_conn_process_query_lowlevel(
struct smb_conn_ctx *ctx,
enum smb_conn_cmd query_cmd,
void *query, size_t query_len,
struct smb_conn_query_result *result,
void *reply, size_t reply_len,
...){
va_list ap;
int retval;
va_start(ap, reply_len);
retval = smb_conn_process_query_lowlevel_va(
ctx,
query_cmd,
query, query_len,
result,
reply, reply_len,
ap);
va_end(ap);
return retval;
}
static int smb_conn_process_query(
struct smb_conn_ctx *ctx,
enum smb_conn_cmd query_cmd,
void *query, size_t query_len,
void *reply, size_t reply_len,
...){
va_list ap;
int count, retval;
struct smb_conn_query_result result;
const char *url = NULL;
if ((query_cmd >= OPEN) && (query_cmd <= REMOVEXATTR)){
va_start(ap, reply_len);
url = va_arg(ap, const char *);
va_end(ap);
if (url != NULL){
retval = neg_cache_check(url);
if (retval != 0){
errno = retval;
return retval;
}
}
}
for(count = 0; ; count++){
if (smb_conn_up_if_broken(ctx) != 0) break;
va_start(ap, reply_len);
retval = smb_conn_process_query_lowlevel_va(
ctx,
query_cmd,
query, query_len,
&result,
reply, reply_len,
ap);
va_end(ap);
if ((retval == 0) && smb_conn_query_result_check(query_cmd, &result)){
retval = smb_conn_query_result_map(&result);
if (smb_conn_is_neg_cache_candidate(query_cmd, &result) && (url != NULL))
neg_cache_store(url, retval);
return retval;
}
if (count >= smb_conn_get_max_retry_count()) break;
sleep(2);
}
if (url != NULL) neg_cache_store(url, EIO);
return EIO;
}
static int smb_conn_process_fd_query(
struct smb_conn_ctx *ctx,
enum smb_conn_cmd query_cmd,
struct smb_conn_file *file,
smb_conn_srv_fd *query_fd_ptr,
void *query, size_t query_len,
void *reply, size_t reply_len){
int count, retval;
struct smb_conn_query_result result;
if ((file == NULL) || (file->url == NULL)) return EINVAL;
for(count = 0; ; count++){
if (smb_conn_up_if_broken(ctx) != 0) break;
if (file->srv_fd == NULL){
union{
struct smb_conn_open_query open;
struct smb_conn_url_query opendir;
} fd_query;
size_t fd_len;
struct smb_conn_fd_reply fd_reply;
switch(file->reopen_cmd){
case OPEN:
fd_len = sizeof(fd_query.open);
fd_query.open.url_offs = sizeof(fd_query.open);
fd_query.open.mode = 0664;
fd_query.open.flags = file->reopen_flags & (~(O_CREAT | O_TRUNC));
break;
case OPENDIR:
/* we cant reopen directory with non-zero offset, so return EIO */
if (file->reopen_flags != 0) return EIO;
fd_len = sizeof(fd_query.opendir);
fd_query.opendir.url_offs = sizeof(fd_query.opendir);
break;
default:
return EIO;
}
retval = smb_conn_process_query_lowlevel(
ctx,
file->reopen_cmd,
&fd_query, fd_len,
&result,
&fd_reply, sizeof(fd_reply),
file->url, NULL);
if ((retval != 0) || !smb_conn_query_result_check(file->reopen_cmd, &result)) goto loop_end;
if (smb_conn_query_result_map(&result) != 0)
return smb_conn_query_result_map(&result);
file->srv_fd = fd_reply.srv_fd;
}
*query_fd_ptr = file->srv_fd;
retval = smb_conn_process_query_lowlevel(
ctx,
query_cmd,
query, query_len,
&result,
reply, reply_len,
NULL);
if ((retval == 0) && smb_conn_query_result_check(query_cmd, &result))
return smb_conn_query_result_map(&result);
loop_end:
if (count >= smb_conn_get_max_retry_count()) break;
sleep(2);
}
return EIO;
}
smb_conn_fd smb_conn_open(struct smb_conn_ctx *ctx,
const char *url, int flags, mode_t mode){
int error;
struct smb_conn_file *file;
struct smb_conn_open_query query;
struct smb_conn_fd_reply reply;
file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
if (file == NULL){
errno = ENOMEM;
return NULL;
}
memset(&file->entries, 0, sizeof(LIST));
file->access_time = (time_t) 0;
file->ctx = ctx;
file->url = ((char *) file) + sizeof(struct smb_conn_file);
file->reopen_cmd = OPEN;
file->reopen_flags = flags & ~(O_CREAT | O_EXCL | O_TRUNC);
file->srv_fd = NULL;
strcpy(file->url, url);
query.url_offs = sizeof(struct smb_conn_open_query);
query.mode = mode;
query.flags = flags;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_query(
ctx, OPEN,
&query, sizeof(query),
&reply, sizeof(reply),
url, NULL);
if (error == 0){
file->srv_fd = reply.srv_fd;
add_to_list(&ctx->smb_conn_file_list, &file->entries);
}else{
free(file);
file = NULL;
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return file;
}
smb_conn_fd smb_conn_creat(struct smb_conn_ctx *ctx,
const char *url, mode_t mode){
int error;
struct smb_conn_file *file;
struct smb_conn_url_mode_query query;
struct smb_conn_fd_reply reply;
file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
if (file == NULL){
errno = ENOMEM;
return NULL;
}
memset(&file->entries, 0, sizeof(LIST));
file->access_time = (time_t) 0;
file->ctx = ctx;
file->url = ((char *) file) + sizeof(struct smb_conn_file);
file->reopen_cmd = OPEN;
file->reopen_flags = O_WRONLY;
file->srv_fd = NULL;
strcpy(file->url, url);
query.url_offs = sizeof(struct smb_conn_url_mode_query);
query.mode = mode;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_query(
ctx, CREAT,
&query, sizeof(query),
&reply, sizeof(reply),
url, NULL);
if (error == 0){
file->srv_fd = reply.srv_fd;
add_to_list(&ctx->smb_conn_file_list, &file->entries);
}else{
free(file);
file = NULL;
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return file;
}
ssize_t smb_conn_read(struct smb_conn_ctx *ctx,
smb_conn_fd fd, off_t offset,
void *buf, size_t bufsize){
int error;
struct smb_conn_file *file;
struct smb_conn_rw_query query;
struct smb_conn_buf_reply reply;
if ((fd == NULL) || (bufsize > ctx->shmem_size)){
errno = EINVAL;
return -1;
}
/* query.fd will be substituted in smb_conn_process_fd_query() */
query.offset = offset;
query.bufsize = bufsize;
error = EINVAL;
reply.bufsize = -1;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_fd_query(
ctx, READ, file,
&query.srv_fd,
&query, sizeof(query),
&reply, sizeof(reply));
if ((error == 0) && (reply.bufsize <= (ssize_t) bufsize)){
memcpy(buf, ctx->shmem_ptr, reply.bufsize);
}else{
reply.bufsize = -1;
if (error == 0) error = EIO;
}
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return reply.bufsize;
}
ssize_t smb_conn_write(struct smb_conn_ctx *ctx,
smb_conn_fd fd, off_t offset,
const void *buf, size_t bufsize){
int error;
struct smb_conn_file *file;
struct smb_conn_rw_query query;
struct smb_conn_buf_reply reply;
if ((fd == NULL) || (bufsize > ctx->shmem_size)){
errno = EINVAL;
return -1;
}
/* query.fd will be substituted in smb_conn_process_fd_query() */
query.offset = offset;
query.bufsize = bufsize;
error = EINVAL;
reply.bufsize = -1;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
ctx->access_time = file->access_time = time(NULL);
memcpy(ctx->shmem_ptr, buf, bufsize);
msync(ctx->shmem_ptr, bufsize, MS_SYNC);
error = smb_conn_process_fd_query(
ctx, WRITE, file,
&query.srv_fd,
&query, sizeof(query),
&reply, sizeof(reply));
if ((error != 0) || (reply.bufsize > (ssize_t) bufsize)){
reply.bufsize = -1;
if (error == 0) error = EIO;
}
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return reply.bufsize;
}
int smb_conn_close(struct smb_conn_ctx *ctx, smb_conn_fd fd){
int error;
struct smb_conn_file *file;
if (fd == NULL){
errno = EINVAL;
return -1;
}
error = EINVAL;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
ctx->access_time = time(NULL);
if (file->srv_fd != NULL){
struct smb_conn_query_result result;
struct smb_conn_fd_query query;
query.srv_fd = file->srv_fd;
smb_conn_process_query_lowlevel(
ctx,
CLOSE,
&query, sizeof(query),
&result,
NULL, 0,
NULL);
}
remove_from_list(&ctx->smb_conn_file_list, &file->entries);
free(file);
error = 0;
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return (error != 0) ? -1 : 0;
}
int smb_conn_unlink(struct smb_conn_ctx *ctx, const char *url){
int error;
struct smb_conn_url_query query;
query.url_offs = sizeof(struct smb_conn_url_query);
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, UNLINK,
&query, sizeof(query),
NULL, 0,
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_rename(struct smb_conn_ctx *ctx, const char *old_url, const char *new_url){
int error;
struct smb_conn_rename_query query;
query.old_url_offs = sizeof(struct smb_conn_rename_query);
query.new_url_offs = sizeof(struct smb_conn_rename_query) + strlen(old_url) + 1;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, RENAME,
&query, sizeof(query),
NULL, 0,
old_url, new_url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
smb_conn_fd smb_conn_opendir(struct smb_conn_ctx *ctx,
const char *url){
int error;
struct smb_conn_file *file;
struct smb_conn_url_query query;
struct smb_conn_fd_reply reply;
file = malloc(sizeof(struct smb_conn_file) + strlen(url) + 1);
if (file == NULL){
errno = ENOMEM;
return NULL;
}
memset(&file->entries, 0, sizeof(LIST));
file->access_time = (time_t) 0;
file->ctx = ctx;
file->url = ((char *) file) + sizeof(struct smb_conn_file);
file->reopen_cmd = OPENDIR;
file->reopen_flags = 0;
file->srv_fd = NULL;
strcpy(file->url, url);
query.url_offs = sizeof(struct smb_conn_url_query);
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_query(
ctx, OPENDIR,
&query, sizeof(query),
&reply, sizeof(reply),
url, NULL);
if (error == 0){
add_to_list(&ctx->smb_conn_file_list, &file->entries);
file->srv_fd = reply.srv_fd;
}else{
free(file);
file = NULL;
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return file;
}
int smb_conn_closedir(struct smb_conn_ctx *ctx, smb_conn_fd fd){
int error;
struct smb_conn_file *file;
if (fd == NULL){
errno = EINVAL;
return -1;
}
error = EINVAL;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPENDIR) && (file->ctx == ctx)){
ctx->access_time = time(NULL);
if (file->srv_fd != NULL){
struct smb_conn_query_result result;
struct smb_conn_fd_query query;
query.srv_fd = file->srv_fd;
smb_conn_process_query_lowlevel(
ctx,
CLOSEDIR,
&query, sizeof(query),
&result,
NULL, 0,
NULL);
}
remove_from_list(&ctx->smb_conn_file_list, &file->entries);
free(file);
error = 0;
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return (error != 0) ? -1 : 0;
}
ssize_t smb_conn_readdir(struct smb_conn_ctx *ctx,
smb_conn_fd fd, void *buf, size_t bufsize){
int error;
struct smb_conn_file *file;
struct smb_conn_rw_query query;
struct smb_conn_buf_reply reply;
if ((fd == NULL) || (bufsize > ctx->shmem_size)){
errno = EINVAL;
return -1;
}
/* query.fd will be substituted in smb_conn_process_fd_query() */
query.offset = (off_t) (-1);
query.bufsize = bufsize;
error = EINVAL;
reply.bufsize = -1;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPENDIR) && (file->ctx == ctx)){
ctx->access_time = file->access_time = time(NULL);
/* we cant reopen directory with non-zero offset, so use */
/* file->reopen_flags for indication of zero/non-zero directory offset */
if (file->reopen_flags == 0){
/* =================================== */
/* reading from zero offset, use */
/* function with connection recovery */
/* =================================== */
error = smb_conn_process_fd_query(
ctx,
READDIR,
file,
&query.srv_fd,
&query, sizeof(query),
&reply, sizeof(reply));
}else{
/* =================================== */
/* reading from non-zero offset, */
/* connection recovery is not possible */
/* =================================== */
struct smb_conn_query_result result;
if (file->srv_fd == NULL){
errno = EIO;
return -1;
}
query.srv_fd = file->srv_fd;
error = smb_conn_process_query_lowlevel(
ctx,
READDIR,
&query, sizeof(query),
&result,
&reply, sizeof(reply),
NULL);
if (error == 0) error = smb_conn_query_result_map(&result);
}
if ((error == 0) && (reply.bufsize <= (ssize_t) bufsize) &&
(reply.bufsize % sizeof(struct smb_conn_dirent_rec) == 0)){
memcpy(buf, ctx->shmem_ptr, reply.bufsize);
file->reopen_flags++;
}else{
reply.bufsize = -1;
if (error == 0) error = EIO;
}
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0) errno = error;
return reply.bufsize;
}
int smb_conn_mkdir(struct smb_conn_ctx *ctx, const char *url, mode_t mode){
int error;
struct smb_conn_url_mode_query query;
query.url_offs = sizeof(struct smb_conn_url_mode_query);
query.mode = mode;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, MKDIR,
&query, sizeof(query),
NULL, 0,
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_rmdir(struct smb_conn_ctx *ctx, const char *url){
int error;
struct smb_conn_url_query query;
query.url_offs = sizeof(struct smb_conn_url_query);
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, RMDIR,
&query, sizeof(query),
NULL, 0,
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_stat(struct smb_conn_ctx *ctx, const char *url, struct stat *st){
int error;
struct smb_conn_url_query query;
struct smb_conn_stat_reply reply;
query.url_offs = sizeof(struct smb_conn_url_query);
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, STAT,
&query, sizeof(query),
&reply, sizeof(reply),
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
memcpy(st, &reply.stat, sizeof(struct stat));
return 0;
}
int smb_conn_fstat(struct smb_conn_ctx *ctx,
smb_conn_fd fd, struct stat *st){
int error;
struct smb_conn_file *file;
struct smb_conn_fd_query query;
struct smb_conn_stat_reply reply;
if (fd == NULL){
errno = EINVAL;
return -1;
}
/* query.fd will be substituted in smb_conn_process_fd_query() */
error = EINVAL;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_fd_query(
ctx, FSTAT, file,
&query.srv_fd,
&query, sizeof(query),
&reply, sizeof(reply));
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
memcpy(st, &reply.stat, sizeof(struct stat));
return 0;
}
int smb_conn_ftruncate(struct smb_conn_ctx *ctx,
smb_conn_fd fd, off_t size){
int error;
struct smb_conn_file *file;
struct smb_conn_ftruncate_query query;
if (fd == NULL){
errno = EINVAL;
return -1;
}
/* query.fd will be substituted in smb_conn_process_fd_query() */
query.offset = size;
error = EINVAL;
file = (struct smb_conn_file *) fd;
pthread_mutex_lock(&ctx->mutex);
if ((file->reopen_cmd == OPEN) && (file->ctx == ctx)){
ctx->access_time = file->access_time = time(NULL);
error = smb_conn_process_fd_query(
ctx, FTRUNCATE, file,
&query.srv_fd,
&query, sizeof(query),
NULL, 0);
}
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_chmod(struct smb_conn_ctx *ctx, const char *url, mode_t mode){
int error;
struct smb_conn_url_mode_query query;
query.url_offs = sizeof(struct smb_conn_url_mode_query);
query.mode = mode;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, CHMOD,
&query, sizeof(query),
NULL, 0,
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_utimes(struct smb_conn_ctx *ctx, const char *url, struct timeval *tbuf){
int error;
struct smb_conn_utimes_query query;
query.url_offs = sizeof(struct smb_conn_utimes_query);
memcpy(&query.tbuf, tbuf, 2 * sizeof(struct timeval));
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, UTIMES,
&query, sizeof(query),
NULL, 0,
url, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_setxattr(struct smb_conn_ctx *ctx, const char *url, const char *name,
const void *value, size_t size, int flags){
int error;
struct smb_conn_setxattr_query query;
if (size > ctx->shmem_size){
errno = EINVAL;
return -1;
}
query.url_offs = sizeof(struct smb_conn_setxattr_query);
query.name_offs = sizeof(struct smb_conn_setxattr_query) + strlen(url) + 1;
query.bufsize = size;
query.flags = flags;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
memcpy(ctx->shmem_ptr, value, size);
msync(ctx->shmem_ptr, size, MS_SYNC);
error = smb_conn_process_query(
ctx, SETXATTR,
&query, sizeof(query),
NULL, 0,
url, name, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}
int smb_conn_getxattr(struct smb_conn_ctx *ctx,
const char *url, const char *name,
void *value, size_t size){
int error;
struct smb_conn_getxattr_query query;
struct smb_conn_buf_reply reply;
if (size > ctx->shmem_size){
errno = EINVAL;
return -1;
}
query.url_offs = sizeof(struct smb_conn_getxattr_query);
query.name_offs = sizeof(struct smb_conn_getxattr_query) + strlen(url) + 1;
query.bufsize = size;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, GETXATTR,
&query, sizeof(query),
&reply, sizeof(reply),
url, name, NULL);
if ((error == 0) &&
(reply.bufsize > 0) &&
(reply.bufsize <= (ssize_t) size))
memcpy(value, ctx->shmem_ptr, reply.bufsize);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
if (((size == 0) && (reply.bufsize > (ssize_t) ctx->shmem_size)) ||
((size > 0) && (reply.bufsize > (ssize_t) size))){
errno = EIO;
return -1;
}
return reply.bufsize;
}
int smb_conn_listxattr(struct smb_conn_ctx *ctx,
const char *url,
char *list, size_t size){
int error;
struct smb_conn_listxattr_query query;
struct smb_conn_buf_reply reply;
if (size > ctx->shmem_size){
errno = EINVAL;
return -1;
}
query.url_offs = sizeof(struct smb_conn_listxattr_query);
query.bufsize = size;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, LISTXATTR,
&query, sizeof(query),
&reply, sizeof(reply),
url, NULL);
if ((error == 0) &&
(reply.bufsize > 0) &&
(reply.bufsize <= (ssize_t) size))
memcpy(list, ctx->shmem_ptr, reply.bufsize);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
if (((size == 0) && (reply.bufsize > (ssize_t) ctx->shmem_size)) ||
((size > 0) && (reply.bufsize > (ssize_t) size))){
errno = EIO;
return -1;
}
return reply.bufsize;
}
int smb_conn_removexattr(struct smb_conn_ctx *ctx,
const char *url, const char *name){
int error;
struct smb_conn_removexattr_query query;
query.url_offs = sizeof(struct smb_conn_removexattr_query);
query.name_offs = sizeof(struct smb_conn_removexattr_query) + strlen(url) + 1;
pthread_mutex_lock(&ctx->mutex);
ctx->access_time = time(NULL);
error = smb_conn_process_query(
ctx, GETXATTR,
&query, sizeof(query),
NULL, 0,
url, name, NULL);
pthread_mutex_unlock(&ctx->mutex);
if (error != 0){
errno = error;
return -1;
}
return 0;
}