mirror of
https://git.libssh.org/projects/libssh.git
synced 2025-07-29 13:01:13 +03:00
sftpserver: Move duplicate code handling SFTP operations to library
These can be replaced by user-provided functions when needed. Signed-off-by: Jakub Jelen <jjelen@redhat.com> Reviewed-by: Andreas Schneider <asn@cryptomilk.org> Reviewed-by: Anderson Toshiyuki Sasaki <ansasaki@redhat.com>
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@ -20,6 +20,13 @@ if (WITH_SERVER)
|
|||||||
${libssh_HDRS}
|
${libssh_HDRS}
|
||||||
server.h
|
server.h
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if (WITH_SFTP)
|
||||||
|
set(libssh_HDRS
|
||||||
|
${libssh_HDRS}
|
||||||
|
sftpserver.h
|
||||||
|
)
|
||||||
|
endif (WITH_SFTP)
|
||||||
endif (WITH_SERVER)
|
endif (WITH_SERVER)
|
||||||
|
|
||||||
install(
|
install(
|
||||||
|
@ -899,7 +899,7 @@ LIBSSH_API void sftp_server_free(sftp_session sftp);
|
|||||||
*
|
*
|
||||||
* @param data The pointer to the data buffer of channel.
|
* @param data The pointer to the data buffer of channel.
|
||||||
*
|
*
|
||||||
* @return Length of data deocded.
|
* @return Length of data decoded.
|
||||||
*/
|
*/
|
||||||
LIBSSH_API int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data);
|
LIBSSH_API int sftp_decode_channel_data_to_packet(sftp_session sftp, void *data);
|
||||||
|
|
||||||
@ -927,7 +927,7 @@ LIBSSH_API int sftp_process_init_packet(sftp_client_message client_msg);
|
|||||||
* @brief Handle the statvfs request, return information the mounted file system.
|
* @brief Handle the statvfs request, return information the mounted file system.
|
||||||
*
|
*
|
||||||
* @param msg The sftp client message.
|
* @param msg The sftp client message.
|
||||||
*
|
*
|
||||||
* @param st The statvfs state of target file.
|
* @param st The statvfs state of target file.
|
||||||
*
|
*
|
||||||
* @return 0 on success, < 0 on error with ssh and sftp error set.
|
* @return 0 on success, < 0 on error with ssh and sftp error set.
|
||||||
|
73
include/libssh/sftpserver.h
Normal file
73
include/libssh/sftpserver.h
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of the SSH Library
|
||||||
|
*
|
||||||
|
* Copyright (c) 2022 Zeyu Sheng <shengzeyu19_98@163.com>
|
||||||
|
* Copyright (c) 2023 Red Hat, Inc.
|
||||||
|
*
|
||||||
|
* Authors: Jakub Jelen <jjelen@redhat.com>
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef SFTP_SERVER_H
|
||||||
|
#define SFTP_SERVER_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <stdint.h>
|
||||||
|
/**
|
||||||
|
* @defgroup libssh_sftp_server The libssh SFTP server API
|
||||||
|
*
|
||||||
|
* @brief SFTP server handling functions
|
||||||
|
*
|
||||||
|
* TODO
|
||||||
|
*
|
||||||
|
* @{
|
||||||
|
*/
|
||||||
|
|
||||||
|
#define SSH_SFTP_CALLBACK(name) \
|
||||||
|
static int name(sftp_client_message message)
|
||||||
|
|
||||||
|
typedef int (*sftp_server_message_callback)(sftp_client_message message);
|
||||||
|
|
||||||
|
struct sftp_message_handler
|
||||||
|
{
|
||||||
|
const char *name;
|
||||||
|
const char *extended_name;
|
||||||
|
uint8_t type;
|
||||||
|
|
||||||
|
sftp_server_message_callback cb;
|
||||||
|
};
|
||||||
|
|
||||||
|
LIBSSH_API int sftp_channel_default_subsystem_request(ssh_session session,
|
||||||
|
ssh_channel channel,
|
||||||
|
const char *subsystem,
|
||||||
|
void *userdata);
|
||||||
|
LIBSSH_API int sftp_channel_default_data_callback(ssh_session session,
|
||||||
|
ssh_channel channel,
|
||||||
|
void *data,
|
||||||
|
uint32_t len,
|
||||||
|
int is_stderr,
|
||||||
|
void *userdata);
|
||||||
|
|
||||||
|
/** @} */
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif /* SFTP_SERVER_H */
|
932
src/sftpserver.c
932
src/sftpserver.c
@ -3,7 +3,8 @@
|
|||||||
*
|
*
|
||||||
* This file is part of the SSH Library
|
* This file is part of the SSH Library
|
||||||
*
|
*
|
||||||
* Copyright (c) 2005 by Aris Adamantiadis
|
* Copyright (c) 2005 Aris Adamantiadis
|
||||||
|
* Copyright (c) 2022 Zeyu Sheng <shengzeyu19_98@163.com>
|
||||||
*
|
*
|
||||||
* The SSH Library is free software; you can redistribute it and/or modify
|
* The SSH 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
|
* it under the terms of the GNU Lesser General Public License as published by
|
||||||
@ -23,16 +24,23 @@
|
|||||||
|
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
|
||||||
|
|
||||||
#ifndef _WIN32
|
#ifndef _WIN32
|
||||||
#include <netinet/in.h>
|
#include <netinet/in.h>
|
||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
#include <dirent.h>
|
||||||
|
#include <sys/statvfs.h>
|
||||||
#endif
|
#endif
|
||||||
|
#include <errno.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include "libssh/libssh.h"
|
#include "libssh/libssh.h"
|
||||||
#include "libssh/sftp.h"
|
#include "libssh/sftp.h"
|
||||||
#include "libssh/sftp_priv.h"
|
#include "libssh/sftp_priv.h"
|
||||||
|
#include "libssh/sftpserver.h"
|
||||||
#include "libssh/ssh2.h"
|
#include "libssh/ssh2.h"
|
||||||
#include "libssh/priv.h"
|
#include "libssh/priv.h"
|
||||||
#include "libssh/buffer.h"
|
#include "libssh/buffer.h"
|
||||||
@ -40,6 +48,9 @@
|
|||||||
|
|
||||||
#define SFTP_HANDLES 256
|
#define SFTP_HANDLES 256
|
||||||
|
|
||||||
|
#define MAX_ENTRIES_NUM_IN_PACKET 50
|
||||||
|
#define MAX_LONG_NAME_LEN 350
|
||||||
|
|
||||||
static sftp_client_message
|
static sftp_client_message
|
||||||
sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
sftp_make_client_message(sftp_session sftp, sftp_packet packet)
|
||||||
{
|
{
|
||||||
@ -641,3 +652,920 @@ void sftp_handle_remove(sftp_session sftp, void *handle) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Default SFTP handlers */
|
||||||
|
static const char *
|
||||||
|
ssh_str_error(int u_errno)
|
||||||
|
{
|
||||||
|
switch (u_errno)
|
||||||
|
{
|
||||||
|
case SSH_FX_NO_SUCH_FILE:
|
||||||
|
return "No such file";
|
||||||
|
case SSH_FX_PERMISSION_DENIED:
|
||||||
|
return "Permission denied";
|
||||||
|
case SSH_FX_BAD_MESSAGE:
|
||||||
|
return "Bad message";
|
||||||
|
case SSH_FX_OP_UNSUPPORTED:
|
||||||
|
return "Operation not supported";
|
||||||
|
default:
|
||||||
|
return "Operation failed";
|
||||||
|
}
|
||||||
|
return "Operation failed";
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
unix_errno_to_ssh_stat(int u_errno)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
switch (u_errno) {
|
||||||
|
case 0:
|
||||||
|
break;
|
||||||
|
case ENOENT:
|
||||||
|
case ENOTDIR:
|
||||||
|
case EBADF:
|
||||||
|
case ELOOP:
|
||||||
|
ret = SSH_FX_NO_SUCH_FILE;
|
||||||
|
break;
|
||||||
|
case EPERM:
|
||||||
|
case EACCES:
|
||||||
|
case EFAULT:
|
||||||
|
ret = SSH_FX_PERMISSION_DENIED;
|
||||||
|
break;
|
||||||
|
case ENAMETOOLONG:
|
||||||
|
case EINVAL:
|
||||||
|
ret = SSH_FX_BAD_MESSAGE;
|
||||||
|
break;
|
||||||
|
case ENOSYS:
|
||||||
|
ret = SSH_FX_OP_UNSUPPORTED;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
ret = SSH_FX_FAILURE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
stat_to_filexfer_attrib(const struct stat *z_st, struct sftp_attributes_struct *z_attr)
|
||||||
|
{
|
||||||
|
z_attr->flags = 0 | (uint32_t)SSH_FILEXFER_ATTR_SIZE;
|
||||||
|
z_attr->size = z_st->st_size;
|
||||||
|
|
||||||
|
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_UIDGID;
|
||||||
|
z_attr->uid = z_st->st_uid;
|
||||||
|
z_attr->gid = z_st->st_gid;
|
||||||
|
|
||||||
|
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS;
|
||||||
|
z_attr->permissions = z_st->st_mode;
|
||||||
|
|
||||||
|
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_ACMODTIME;
|
||||||
|
z_attr->atime = z_st->st_atime;
|
||||||
|
z_attr->mtime = z_st->st_mtime;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
clear_filexfer_attrib(struct sftp_attributes_struct *z_attr)
|
||||||
|
{
|
||||||
|
z_attr->flags = 0;
|
||||||
|
z_attr->size = 0;
|
||||||
|
z_attr->uid = 0;
|
||||||
|
z_attr->gid = 0;
|
||||||
|
z_attr->permissions = 0;
|
||||||
|
z_attr->atime = 0;
|
||||||
|
z_attr->mtime = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifndef _WIN32
|
||||||
|
/* internal */
|
||||||
|
enum sftp_handle_type
|
||||||
|
{
|
||||||
|
SFTP_NULL_HANDLE,
|
||||||
|
SFTP_DIR_HANDLE,
|
||||||
|
SFTP_FILE_HANDLE
|
||||||
|
};
|
||||||
|
|
||||||
|
struct sftp_handle
|
||||||
|
{
|
||||||
|
enum sftp_handle_type type;
|
||||||
|
int fd;
|
||||||
|
DIR *dirp;
|
||||||
|
char *name;
|
||||||
|
};
|
||||||
|
|
||||||
|
SSH_SFTP_CALLBACK(process_unsupposed);
|
||||||
|
SSH_SFTP_CALLBACK(process_open);
|
||||||
|
SSH_SFTP_CALLBACK(process_read);
|
||||||
|
SSH_SFTP_CALLBACK(process_write);
|
||||||
|
SSH_SFTP_CALLBACK(process_close);
|
||||||
|
SSH_SFTP_CALLBACK(process_opendir);
|
||||||
|
SSH_SFTP_CALLBACK(process_readdir);
|
||||||
|
SSH_SFTP_CALLBACK(process_rmdir);
|
||||||
|
SSH_SFTP_CALLBACK(process_mkdir);
|
||||||
|
SSH_SFTP_CALLBACK(process_lstat);
|
||||||
|
SSH_SFTP_CALLBACK(process_readlink);
|
||||||
|
SSH_SFTP_CALLBACK(process_symlink);
|
||||||
|
SSH_SFTP_CALLBACK(process_remove);
|
||||||
|
SSH_SFTP_CALLBACK(process_extended_statvfs);
|
||||||
|
|
||||||
|
const struct sftp_message_handler message_handlers[] = {
|
||||||
|
{"open", NULL, SSH_FXP_OPEN, process_open},
|
||||||
|
{"close", NULL, SSH_FXP_CLOSE, process_close},
|
||||||
|
{"read", NULL, SSH_FXP_READ, process_read},
|
||||||
|
{"write", NULL, SSH_FXP_WRITE, process_write},
|
||||||
|
{"lstat", NULL, SSH_FXP_LSTAT, process_lstat},
|
||||||
|
{"fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
|
||||||
|
{"setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed},
|
||||||
|
{"fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
|
||||||
|
{"opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
|
||||||
|
{"readdir", NULL, SSH_FXP_READDIR, process_readdir},
|
||||||
|
{"remove", NULL, SSH_FXP_REMOVE, process_remove},
|
||||||
|
{"mkdir", NULL, SSH_FXP_MKDIR, process_mkdir},
|
||||||
|
{"rmdir", NULL, SSH_FXP_RMDIR, process_rmdir},
|
||||||
|
{"realpath", NULL, SSH_FXP_REALPATH, process_unsupposed},
|
||||||
|
{"stat", NULL, SSH_FXP_STAT, process_unsupposed},
|
||||||
|
{"rename", NULL, SSH_FXP_RENAME, process_unsupposed},
|
||||||
|
{"readlink", NULL, SSH_FXP_READLINK, process_readlink},
|
||||||
|
{"symlink", NULL, SSH_FXP_SYMLINK, process_symlink},
|
||||||
|
{"init", NULL, SSH_FXP_INIT, sftp_process_init_packet},
|
||||||
|
{NULL, NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
const struct sftp_message_handler extended_handlers[] = {
|
||||||
|
/* here are some extended type handlers */
|
||||||
|
{"statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
|
||||||
|
{NULL, NULL, 0, NULL},
|
||||||
|
};
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_open(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
uint32_t msg_flag = sftp_client_message_get_flags(client_msg);
|
||||||
|
ssh_string handle_s = NULL;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
int file_flag;
|
||||||
|
int fd = -1;
|
||||||
|
int status;
|
||||||
|
|
||||||
|
if (((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) &&
|
||||||
|
((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE)) {
|
||||||
|
file_flag = O_RDWR; // file must exist
|
||||||
|
if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
|
||||||
|
file_flag |= O_CREAT;
|
||||||
|
} else if ((msg_flag & (uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE) {
|
||||||
|
file_flag = O_WRONLY;
|
||||||
|
if ((msg_flag & (uint32_t)SSH_FXF_APPEND) == SSH_FXF_APPEND)
|
||||||
|
file_flag |= O_APPEND;
|
||||||
|
if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
|
||||||
|
file_flag |= O_CREAT;
|
||||||
|
} else if ((msg_flag & (uint32_t)SSH_FXF_READ) == SSH_FXF_READ) {
|
||||||
|
file_flag = O_RDONLY;
|
||||||
|
} else {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "undefined message flag: %d", errno);
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Flag error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
fd = open(filename, file_flag, 0600);
|
||||||
|
if (fd == -1) {
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %d", errno);
|
||||||
|
sftp_reply_status(client_msg, status, "Write error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = calloc(1, sizeof (struct sftp_handle));
|
||||||
|
if (h == NULL) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE,
|
||||||
|
"Failed to allocate new handle");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
h->fd = fd;
|
||||||
|
h->type = SFTP_FILE_HANDLE;
|
||||||
|
handle_s = sftp_handle_alloc(client_msg->sftp, h);
|
||||||
|
if (handle_s != NULL) {
|
||||||
|
sftp_reply_handle(client_msg, handle_s);
|
||||||
|
ssh_string_free(handle_s);
|
||||||
|
} else {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "opening file failed: %d", errno);
|
||||||
|
close(fd);
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_read(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
sftp_session sftp = client_msg->sftp;
|
||||||
|
ssh_string handle = client_msg->handle;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
uint32_t readn;
|
||||||
|
int fd = -1;
|
||||||
|
char *buffer = NULL;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
h = sftp_handle(sftp, handle);
|
||||||
|
if (h->type == SFTP_FILE_HANDLE) {
|
||||||
|
fd = h->fd;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "error reading file fd: %d", fd);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
rv = lseek(fd, client_msg->offset, SEEK_SET);
|
||||||
|
if (rv == -1) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||||
|
"error seeking file fd: %d at offset: %" PRIu64,
|
||||||
|
fd,
|
||||||
|
client_msg->offset);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer = malloc(client_msg->len);
|
||||||
|
readn = read(fd, buffer, client_msg->len);
|
||||||
|
|
||||||
|
if (readn > 0) {
|
||||||
|
sftp_reply_data(client_msg, buffer, readn);
|
||||||
|
} else if (readn == 0) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_EOF, "EOF encountered");
|
||||||
|
} else {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "read file error!");
|
||||||
|
free(buffer);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(buffer);
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_write(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
sftp_session sftp = client_msg->sftp;
|
||||||
|
ssh_string handle = client_msg->handle;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
int written;
|
||||||
|
int fd = -1;
|
||||||
|
const char *msg_data = NULL;
|
||||||
|
uint32_t len;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
h = sftp_handle(sftp, handle);
|
||||||
|
if (h->type == SFTP_FILE_HANDLE) {
|
||||||
|
fd = h->fd;
|
||||||
|
}
|
||||||
|
if (fd < 0) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg_data = ssh_string_get_char(client_msg->data);
|
||||||
|
len = ssh_string_len(client_msg->data);
|
||||||
|
|
||||||
|
rv = lseek(fd, client_msg->offset, SEEK_SET);
|
||||||
|
if (rv == -1) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file at offset: %" PRIu64,
|
||||||
|
client_msg->offset);
|
||||||
|
}
|
||||||
|
written = write(fd, msg_data, len);
|
||||||
|
if (written == (int)len) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_OK, NULL);
|
||||||
|
} else if (written == -1) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error");
|
||||||
|
} else {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Partial write");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_close(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
sftp_session sftp = client_msg->sftp;
|
||||||
|
ssh_string handle = client_msg->handle;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
h = sftp_handle(sftp, handle);
|
||||||
|
if (h->type == SFTP_FILE_HANDLE) {
|
||||||
|
int fd = h->fd;
|
||||||
|
close(fd);
|
||||||
|
ret = SSH_OK;
|
||||||
|
} else if (h->type == SFTP_DIR_HANDLE) {
|
||||||
|
DIR *dir = h->dirp;
|
||||||
|
closedir(dir);
|
||||||
|
ret = SSH_OK;
|
||||||
|
} else {
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
}
|
||||||
|
SAFE_FREE(h->name);
|
||||||
|
sftp_handle_remove(sftp, h);
|
||||||
|
SAFE_FREE(h);
|
||||||
|
|
||||||
|
if (ret == SSH_OK) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_OK, NULL);
|
||||||
|
} else {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed");
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_opendir(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
DIR *dir = NULL;
|
||||||
|
const char *dir_name = sftp_client_message_get_filename(client_msg);
|
||||||
|
ssh_string handle_s = NULL;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
|
||||||
|
dir = opendir(dir_name);
|
||||||
|
if (dir == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
h = calloc(1, sizeof (struct sftp_handle));
|
||||||
|
if (h == NULL) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "failed to allocate a new handle");
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE,
|
||||||
|
"Failed to allocate new handle");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
h->dirp = dir;
|
||||||
|
h->name = strdup(dir_name);
|
||||||
|
h->type = SFTP_DIR_HANDLE;
|
||||||
|
handle_s = sftp_handle_alloc(client_msg->sftp, h);
|
||||||
|
|
||||||
|
if (handle_s != NULL) {
|
||||||
|
sftp_reply_handle(client_msg, handle_s);
|
||||||
|
ssh_string_free(handle_s);
|
||||||
|
} else {
|
||||||
|
closedir(dir);
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
readdir_long_name(char *z_file_name, struct stat *z_st, char *z_long_name)
|
||||||
|
{
|
||||||
|
char tmpbuf[MAX_LONG_NAME_LEN];
|
||||||
|
char time[50];
|
||||||
|
char *ptr = z_long_name;
|
||||||
|
int mode = z_st->st_mode;
|
||||||
|
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
switch (mode & S_IFMT) {
|
||||||
|
case S_IFDIR:
|
||||||
|
*ptr++ = 'd';
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
*ptr++ = '-';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* user */
|
||||||
|
if (mode & 0400)
|
||||||
|
*ptr++ = 'r';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
|
||||||
|
if (mode & 0200)
|
||||||
|
*ptr++ = 'w';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
|
||||||
|
if (mode & 0100) {
|
||||||
|
if (mode & S_ISUID)
|
||||||
|
*ptr++ = 's';
|
||||||
|
else
|
||||||
|
*ptr++ = 'x';
|
||||||
|
} else
|
||||||
|
*ptr++ = '-';
|
||||||
|
|
||||||
|
/* group */
|
||||||
|
if (mode & 040)
|
||||||
|
*ptr++ = 'r';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
if (mode & 020)
|
||||||
|
*ptr++ = 'w';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
if (mode & 010)
|
||||||
|
*ptr++ = 'x';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
|
||||||
|
/* other */
|
||||||
|
if (mode & 04)
|
||||||
|
*ptr++ = 'r';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
if (mode & 02)
|
||||||
|
*ptr++ = 'w';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
if (mode & 01)
|
||||||
|
*ptr++ = 'x';
|
||||||
|
else
|
||||||
|
*ptr++ = '-';
|
||||||
|
|
||||||
|
*ptr++ = ' ';
|
||||||
|
*ptr = '\0';
|
||||||
|
|
||||||
|
snprintf(tmpbuf, sizeof(tmpbuf), "%3d %d %d %d", (int)z_st->st_nlink,
|
||||||
|
(int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size);
|
||||||
|
strcat(z_long_name, tmpbuf);
|
||||||
|
|
||||||
|
ctime_r(&z_st->st_mtime, time);
|
||||||
|
if ((ptr = strchr(time, '\n'))) {
|
||||||
|
*ptr = '\0';
|
||||||
|
}
|
||||||
|
snprintf(tmpbuf, sizeof(tmpbuf), " %s %s", time + 4, z_file_name);
|
||||||
|
strcat(z_long_name, tmpbuf);
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_readdir(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
sftp_session sftp = client_msg->sftp;
|
||||||
|
ssh_string handle = client_msg->handle;
|
||||||
|
struct sftp_handle *h = NULL;
|
||||||
|
int ret = SSH_OK;
|
||||||
|
int entries = 0;
|
||||||
|
struct dirent *dentry = NULL;
|
||||||
|
DIR *dir = NULL;
|
||||||
|
char long_path[PATH_MAX];
|
||||||
|
int srclen;
|
||||||
|
const char *handle_name = NULL;
|
||||||
|
|
||||||
|
h = sftp_handle(sftp, client_msg->handle);
|
||||||
|
if (h->type == SFTP_DIR_HANDLE) {
|
||||||
|
dir = h->dirp;
|
||||||
|
handle_name = h->name;
|
||||||
|
}
|
||||||
|
if (dir == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "read dir handle error!");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handle_name == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
srclen = strlen(handle_name);
|
||||||
|
if (srclen + 2 >= PATH_MAX) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "handle string length exceed max length!");
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i = 0; i < MAX_ENTRIES_NUM_IN_PACKET; i++) {
|
||||||
|
dentry = readdir(dir);
|
||||||
|
|
||||||
|
if (dentry != NULL) {
|
||||||
|
struct sftp_attributes_struct attr;
|
||||||
|
struct stat st;
|
||||||
|
char long_name[MAX_LONG_NAME_LEN];
|
||||||
|
|
||||||
|
if (strlen(dentry->d_name) + srclen + 1 >= PATH_MAX) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL,
|
||||||
|
"handle string length exceed max length!");
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
snprintf(long_path, PATH_MAX, "%s/%s", handle_name, dentry->d_name);
|
||||||
|
|
||||||
|
if (lstat(long_path, &st) == 0) {
|
||||||
|
stat_to_filexfer_attrib(&st, &attr);
|
||||||
|
} else {
|
||||||
|
clear_filexfer_attrib(&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (readdir_long_name(dentry->d_name, &st, long_name) == 0) {
|
||||||
|
sftp_reply_names_add(client_msg, dentry->d_name, long_name, &attr);
|
||||||
|
} else {
|
||||||
|
printf("readdir long name error\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
entries++;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entries > 0) {
|
||||||
|
ret = sftp_reply_names(client_msg);
|
||||||
|
} else {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_mkdir(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
uint32_t msg_flags = client_msg->flags;
|
||||||
|
uint32_t permission = client_msg->attr->permissions;
|
||||||
|
uint32_t mode = (msg_flags & (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS)
|
||||||
|
? permission & (uint32_t)07777 : 0777;
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (filename == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = mkdir(filename, mode);
|
||||||
|
if (rv < 0) {
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_reply_status(client_msg, status, NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_rmdir(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (filename == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = rmdir(filename);
|
||||||
|
if (rv < 0) {
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_reply_status(client_msg, status, NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_lstat(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
struct sftp_attributes_struct attr;
|
||||||
|
struct stat st;
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
if (filename == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = lstat(filename, &st);
|
||||||
|
if (rv < 0) {
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
sftp_reply_status(client_msg, status, NULL);
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
} else {
|
||||||
|
stat_to_filexfer_attrib(&st, &attr);
|
||||||
|
sftp_reply_attr(client_msg, &attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_readlink(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
char buf[PATH_MAX];
|
||||||
|
int len = -1;
|
||||||
|
const char *err_msg;
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
|
||||||
|
if (filename == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
len = readlink(filename, buf, sizeof(buf) - 1);
|
||||||
|
if (len < 0) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "read link error with reason: %d", errno);
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
err_msg = ssh_str_error(status);
|
||||||
|
sftp_reply_status(client_msg, status, err_msg);
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
} else {
|
||||||
|
buf[len] = '\0';
|
||||||
|
sftp_reply_name(client_msg, buf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_symlink(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *destpath = sftp_client_message_get_filename(client_msg);
|
||||||
|
const char *srcpath = ssh_string_get_char(client_msg->data);
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "try to create link with src: %s and dest: %s",
|
||||||
|
srcpath, destpath);
|
||||||
|
|
||||||
|
if (srcpath == NULL || destpath == NULL) {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
rv = symlink(srcpath, destpath);
|
||||||
|
if (rv < 0) {
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "error symlink with error: %d", errno);
|
||||||
|
sftp_reply_status(client_msg, status, "Write error");
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
} else {
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_OK, "write success");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_remove(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int ret = SSH_OK;
|
||||||
|
const char *filename = sftp_client_message_get_filename(client_msg);
|
||||||
|
int rv;
|
||||||
|
int status = SSH_FX_OK;
|
||||||
|
|
||||||
|
rv = unlink(filename);
|
||||||
|
if (rv < 0) {
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "unlink error with reason: %d", errno);
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
ret = SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_reply_status(client_msg, status, NULL);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_unsupposed(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED,
|
||||||
|
"Operation not supported");
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "Message type %d not implemented",
|
||||||
|
sftp_client_message_get_type(client_msg));
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_extended_statvfs(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
const char *path = sftp_client_message_get_filename(client_msg);
|
||||||
|
struct statvfs st;
|
||||||
|
int status;
|
||||||
|
int rv;
|
||||||
|
|
||||||
|
rv = statvfs(path, &st);
|
||||||
|
if (rv == 0) {
|
||||||
|
sftp_statvfs_t sftp_statvfs;
|
||||||
|
u_int64_t flag;
|
||||||
|
|
||||||
|
sftp_statvfs = calloc(1, sizeof(struct sftp_statvfs_struct));
|
||||||
|
if (sftp_statvfs != NULL) {
|
||||||
|
flag = (st.f_flag & ST_RDONLY) ? SSH_FXE_STATVFS_ST_RDONLY : 0;
|
||||||
|
flag |= (st.f_flag & ST_NOSUID) ? SSH_FXE_STATVFS_ST_NOSUID : 0;
|
||||||
|
|
||||||
|
sftp_statvfs->f_bsize = st.f_bsize;
|
||||||
|
sftp_statvfs->f_frsize = st.f_frsize;
|
||||||
|
sftp_statvfs->f_blocks = st.f_blocks;
|
||||||
|
sftp_statvfs->f_bfree = st.f_bfree;
|
||||||
|
sftp_statvfs->f_bavail = st.f_bavail;
|
||||||
|
sftp_statvfs->f_files = st.f_files;
|
||||||
|
sftp_statvfs->f_ffree = st.f_ffree;
|
||||||
|
sftp_statvfs->f_favail = st.f_favail;
|
||||||
|
sftp_statvfs->f_fsid = st.f_fsid;
|
||||||
|
sftp_statvfs->f_flag = flag;
|
||||||
|
sftp_statvfs->f_namemax = st.f_namemax;
|
||||||
|
|
||||||
|
rv = sftp_reply_statvfs(client_msg, sftp_statvfs);
|
||||||
|
free(sftp_statvfs);
|
||||||
|
if (rv == 0) {
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
status = unix_errno_to_ssh_stat(errno);
|
||||||
|
sftp_reply_status(client_msg, status, NULL);
|
||||||
|
|
||||||
|
printf("statvfs send failed!\n");
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_extended(sftp_client_message sftp_msg)
|
||||||
|
{
|
||||||
|
int status = SSH_ERROR;
|
||||||
|
const char *subtype = sftp_msg->submessage;
|
||||||
|
sftp_server_message_callback handler = NULL;
|
||||||
|
|
||||||
|
for (int i = 0; extended_handlers[i].cb != NULL; i++) {
|
||||||
|
if (strcmp(subtype, extended_handlers[i].extended_name) == 0) {
|
||||||
|
handler = extended_handlers[i].cb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (handler != NULL) {
|
||||||
|
status = handler(sftp_msg);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
|
||||||
|
"Extended Operation not supported");
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "Extended Message type %s not implemented",
|
||||||
|
subtype);
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
dispatch_sftp_request(sftp_client_message sftp_msg)
|
||||||
|
{
|
||||||
|
int status = SSH_ERROR;
|
||||||
|
sftp_server_message_callback handler = NULL;
|
||||||
|
uint8_t type = sftp_client_message_get_type(sftp_msg);
|
||||||
|
|
||||||
|
for (int i = 0; message_handlers[i].cb != NULL; i++) {
|
||||||
|
if (type == message_handlers[i].type) {
|
||||||
|
handler = message_handlers[i].cb;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (handler != NULL) {
|
||||||
|
status = handler(sftp_msg);
|
||||||
|
} else {
|
||||||
|
sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED,
|
||||||
|
"Operation not supported");
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "Message type %u not implemented", type);
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
process_client_message(sftp_client_message client_msg)
|
||||||
|
{
|
||||||
|
int status = SSH_OK;
|
||||||
|
if (client_msg == NULL) {
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (client_msg->type) {
|
||||||
|
case SSH_FXP_EXTENDED:
|
||||||
|
status = process_extended(client_msg);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
status = dispatch_sftp_request(client_msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (status != SSH_OK)
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "error occur in process client message!");
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default subsystem request handler for SFTP subsystem
|
||||||
|
*
|
||||||
|
* @param[in] session The ssh session
|
||||||
|
* @param[in] channel The existing ssh channel
|
||||||
|
* @param[in] subsystem The subsystem name. Only "sftp" is handled
|
||||||
|
* @param[out] userdata The pointer to sftp_session which will get the
|
||||||
|
* resulting SFTP session
|
||||||
|
*
|
||||||
|
* @return SSH_OK when the SFTP server was successfully initialized, SSH_ERROR
|
||||||
|
* otherwise.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sftp_channel_default_subsystem_request(ssh_session session,
|
||||||
|
ssh_channel channel,
|
||||||
|
const char *subsystem,
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
if (strcmp(subsystem, "sftp") == 0) {
|
||||||
|
sftp_session *sftp = (sftp_session *)userdata;
|
||||||
|
|
||||||
|
/* initialize sftp session and file handler */
|
||||||
|
*sftp = sftp_server_new(session, channel);
|
||||||
|
if (*sftp == NULL) {
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
return SSH_OK;
|
||||||
|
}
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Default data callback for sftp server
|
||||||
|
*
|
||||||
|
* @param[in] session The ssh session
|
||||||
|
* @param[in] channel The ssh channel with SFTP session opened
|
||||||
|
* @param[in] data The data to be processed.
|
||||||
|
* @param[in] len The length of input data to be processed
|
||||||
|
* @param[in] is_stderr Unused channel flag for stderr flagging
|
||||||
|
* @param[in] userdata The pointer to sftp_session
|
||||||
|
*
|
||||||
|
* @return number of bytes processed, -1 when error occurs.
|
||||||
|
*/
|
||||||
|
int
|
||||||
|
sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||||
|
UNUSED_PARAM(ssh_channel channel),
|
||||||
|
void *data,
|
||||||
|
UNUSED_PARAM(uint32_t len),
|
||||||
|
UNUSED_PARAM(int is_stderr),
|
||||||
|
void *userdata)
|
||||||
|
{
|
||||||
|
sftp_session *sftpp = (sftp_session *)userdata;
|
||||||
|
sftp_session sftp = NULL;
|
||||||
|
sftp_client_message msg;
|
||||||
|
int decode_len;
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
if (sftpp == NULL) {
|
||||||
|
SSH_LOG(SSH_LOG_WARNING, "NULL userdata passed to callback");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
sftp = *sftpp;
|
||||||
|
|
||||||
|
decode_len = sftp_decode_channel_data_to_packet(sftp, data);
|
||||||
|
if (decode_len == -1)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
msg = sftp_get_client_message_from_packet(sftp);
|
||||||
|
rc = process_client_message(msg);
|
||||||
|
sftp_client_message_free(msg);
|
||||||
|
if (rc != SSH_OK)
|
||||||
|
SSH_LOG(SSH_LOG_PROTOCOL, "process sftp failed!");
|
||||||
|
|
||||||
|
return decode_len;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
/* Not available on Windows for now */
|
||||||
|
int
|
||||||
|
sftp_channel_default_data_callback(UNUSED_PARAM(ssh_session session),
|
||||||
|
UNUSED_PARAM(ssh_channel channel),
|
||||||
|
UNUSED_PARAM(void *data),
|
||||||
|
UNUSED_PARAM(uint32_t len),
|
||||||
|
UNUSED_PARAM(int is_stderr),
|
||||||
|
UNUSED_PARAM(void *userdata))
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
sftp_channel_default_subsystem_request(UNUSED_PARAM(ssh_session session),
|
||||||
|
UNUSED_PARAM(ssh_channel channel),
|
||||||
|
UNUSED_PARAM(const char *subsystem),
|
||||||
|
UNUSED_PARAM(void *userdata))
|
||||||
|
{
|
||||||
|
return SSH_ERROR;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
@ -29,6 +29,7 @@
|
|||||||
#include <libssh/server.h>
|
#include <libssh/server.h>
|
||||||
#include <libssh/priv.h>
|
#include <libssh/priv.h>
|
||||||
#include <libssh/sftp.h>
|
#include <libssh/sftp.h>
|
||||||
|
#include <libssh/sftpserver.h>
|
||||||
|
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
@ -69,906 +70,6 @@
|
|||||||
#define BUF_SIZE 1048576
|
#define BUF_SIZE 1048576
|
||||||
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
|
#define SESSION_END (SSH_CLOSED | SSH_CLOSED_ERROR)
|
||||||
|
|
||||||
#define MAX_HANDLE_NUM 10
|
|
||||||
#define MAX_ENTRIES_NUM_IN_PACKET 50
|
|
||||||
#define MAX_LONG_NAME_LEN 300
|
|
||||||
|
|
||||||
#define SSH_SFTP_CALLBACK(name) \
|
|
||||||
static int name (sftp_client_message message)
|
|
||||||
|
|
||||||
typedef int (*client_message_callback) (sftp_client_message message);
|
|
||||||
|
|
||||||
struct message_handler{
|
|
||||||
const char *name;
|
|
||||||
const char *extended_name;
|
|
||||||
u_int type;
|
|
||||||
|
|
||||||
client_message_callback cb;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sftp_handle {
|
|
||||||
uint8_t type;
|
|
||||||
int fd;
|
|
||||||
DIR *dirp;
|
|
||||||
char *name;
|
|
||||||
void *session_id;
|
|
||||||
};
|
|
||||||
|
|
||||||
enum handle_type {
|
|
||||||
NULL_HANDLE,
|
|
||||||
DIR_HANDLE,
|
|
||||||
FILE_HANDLE
|
|
||||||
};
|
|
||||||
|
|
||||||
SSH_SFTP_CALLBACK(process_unsupposed);
|
|
||||||
SSH_SFTP_CALLBACK(process_open);
|
|
||||||
SSH_SFTP_CALLBACK(process_read);
|
|
||||||
SSH_SFTP_CALLBACK(process_write);
|
|
||||||
SSH_SFTP_CALLBACK(process_close);
|
|
||||||
SSH_SFTP_CALLBACK(process_opendir);
|
|
||||||
SSH_SFTP_CALLBACK(process_readdir);
|
|
||||||
SSH_SFTP_CALLBACK(process_rmdir);
|
|
||||||
SSH_SFTP_CALLBACK(process_mkdir);
|
|
||||||
SSH_SFTP_CALLBACK(process_lstat);
|
|
||||||
SSH_SFTP_CALLBACK(process_readlink);
|
|
||||||
SSH_SFTP_CALLBACK(process_symlink);
|
|
||||||
SSH_SFTP_CALLBACK(process_remove);
|
|
||||||
SSH_SFTP_CALLBACK(process_extended_statvfs);
|
|
||||||
|
|
||||||
const struct message_handler message_handlers[] = {
|
|
||||||
{ "open", NULL, SSH_FXP_OPEN, process_open},
|
|
||||||
{ "close", NULL, SSH_FXP_CLOSE, process_close},
|
|
||||||
{ "read", NULL, SSH_FXP_READ, process_read},
|
|
||||||
{ "write", NULL, SSH_FXP_WRITE, process_write},
|
|
||||||
{ "lstat", NULL, SSH_FXP_LSTAT, process_lstat},
|
|
||||||
{ "fstat", NULL, SSH_FXP_FSTAT, process_unsupposed},
|
|
||||||
{ "setstat", NULL, SSH_FXP_SETSTAT, process_unsupposed},
|
|
||||||
{ "fsetstat", NULL, SSH_FXP_FSETSTAT, process_unsupposed},
|
|
||||||
{ "opendir", NULL, SSH_FXP_OPENDIR, process_opendir},
|
|
||||||
{ "readdir", NULL, SSH_FXP_READDIR, process_readdir},
|
|
||||||
{ "remove", NULL, SSH_FXP_REMOVE, process_remove},
|
|
||||||
{ "mkdir", NULL, SSH_FXP_MKDIR, process_mkdir},
|
|
||||||
{ "rmdir", NULL, SSH_FXP_RMDIR, process_rmdir},
|
|
||||||
{ "realpath", NULL, SSH_FXP_REALPATH, process_unsupposed},
|
|
||||||
{ "stat", NULL, SSH_FXP_STAT, process_unsupposed},
|
|
||||||
{ "rename", NULL, SSH_FXP_RENAME, process_unsupposed},
|
|
||||||
{ "readlink", NULL, SSH_FXP_READLINK, process_readlink},
|
|
||||||
{ "symlink", NULL, SSH_FXP_SYMLINK, process_symlink},
|
|
||||||
{ "init", NULL, SSH_FXP_INIT, sftp_process_init_packet},
|
|
||||||
{ NULL, NULL, 0, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
const struct message_handler extended_handlers[] = {
|
|
||||||
/* here are some extended type handlers */
|
|
||||||
{ "statvfs", "statvfs@openssh.com", 0, process_extended_statvfs},
|
|
||||||
{ NULL, NULL, 0, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
struct sftp_handle s_handle_table[MAX_HANDLE_NUM];
|
|
||||||
|
|
||||||
void init_handle_table(int handle_num) {
|
|
||||||
int obj_size = sizeof(struct sftp_handle);
|
|
||||||
memset(s_handle_table, 0, obj_size*handle_num);
|
|
||||||
}
|
|
||||||
|
|
||||||
static void reinit_single_handle(struct sftp_handle* handle) {
|
|
||||||
handle->type = NULL_HANDLE;
|
|
||||||
handle->session_id = NULL;
|
|
||||||
handle->dirp = NULL;
|
|
||||||
handle->name = NULL;
|
|
||||||
handle->fd = -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_is_ok(int i, int type) {
|
|
||||||
return i >= 0 && (u_int)i < MAX_HANDLE_NUM && s_handle_table[i].type == type;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_close(int handle_ind) {
|
|
||||||
int ret = SSH_ERROR;
|
|
||||||
|
|
||||||
if (handle_is_ok(handle_ind, FILE_HANDLE)) {
|
|
||||||
close(s_handle_table[handle_ind].fd);
|
|
||||||
ret = SSH_OK;
|
|
||||||
} else if (handle_is_ok(handle_ind, DIR_HANDLE)) {
|
|
||||||
closedir((DIR *)s_handle_table[handle_ind].dirp);
|
|
||||||
ret = SSH_OK;
|
|
||||||
} else if (handle_is_ok(handle_ind, NULL_HANDLE)) {
|
|
||||||
ret = SSH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (s_handle_table[handle_ind].name != NULL) {
|
|
||||||
free(s_handle_table[handle_ind].name);
|
|
||||||
s_handle_table[handle_ind].name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int handle_close_by_pointer(struct sftp_handle* handle) {
|
|
||||||
if(handle->type == NULL_HANDLE)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
if(handle->fd > 0)
|
|
||||||
close(handle->fd);
|
|
||||||
|
|
||||||
if(handle->dirp!=NULL)
|
|
||||||
closedir(handle->dirp);
|
|
||||||
|
|
||||||
if(handle->name!=NULL){
|
|
||||||
free(handle->name);
|
|
||||||
handle->name = NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void free_handles(int handle_num) {
|
|
||||||
|
|
||||||
for(int i = 0; i < handle_num; i++) {
|
|
||||||
handle_close(i);
|
|
||||||
//reinit this handle
|
|
||||||
reinit_single_handle(&s_handle_table[i]);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int add_handle(int type, void *dirp, int fd, const char *name, void *session_id) {
|
|
||||||
int ret = SSH_ERROR;
|
|
||||||
if(dirp == NULL && fd < 0){
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
for(int i = 0; i<MAX_HANDLE_NUM; i++) {
|
|
||||||
if(s_handle_table[i].type == NULL_HANDLE){
|
|
||||||
s_handle_table[i].type = type;
|
|
||||||
s_handle_table[i].session_id = session_id;
|
|
||||||
s_handle_table[i].fd = fd;
|
|
||||||
s_handle_table[i].dirp = dirp;
|
|
||||||
s_handle_table[i].name = malloc((strlen(name) + 1) * sizeof(char));
|
|
||||||
strcpy(s_handle_table[i].name, name);
|
|
||||||
|
|
||||||
ret = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(ret==SSH_ERROR)
|
|
||||||
printf("no other space for new handle\n");
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static char* get_handle_name(struct sftp_handle* handle) {
|
|
||||||
char *ret = NULL;
|
|
||||||
|
|
||||||
if(handle!=NULL&&handle->name!=NULL)
|
|
||||||
ret = handle->name;
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* ssh_str_error(int u_errno) {
|
|
||||||
switch (u_errno)
|
|
||||||
{
|
|
||||||
case SSH_FX_NO_SUCH_FILE:
|
|
||||||
return "No such file";
|
|
||||||
case SSH_FX_PERMISSION_DENIED:
|
|
||||||
return "Permission denied";
|
|
||||||
case SSH_FX_BAD_MESSAGE:
|
|
||||||
return "Bad message";
|
|
||||||
case SSH_FX_OP_UNSUPPORTED:
|
|
||||||
return "Operation not supported";
|
|
||||||
default:
|
|
||||||
return "Operation failed";
|
|
||||||
}
|
|
||||||
return "Operation failed";
|
|
||||||
}
|
|
||||||
|
|
||||||
static int unix_errno_to_ssh_stat(int u_errno) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
switch (u_errno) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
case ENOTDIR:
|
|
||||||
case EBADF:
|
|
||||||
case ELOOP:
|
|
||||||
ret = SSH_FX_NO_SUCH_FILE;
|
|
||||||
break;
|
|
||||||
case EPERM:
|
|
||||||
case EACCES:
|
|
||||||
case EFAULT:
|
|
||||||
ret = SSH_FX_PERMISSION_DENIED;
|
|
||||||
break;
|
|
||||||
case ENAMETOOLONG:
|
|
||||||
case EINVAL:
|
|
||||||
ret = SSH_FX_BAD_MESSAGE;
|
|
||||||
break;
|
|
||||||
case ENOSYS:
|
|
||||||
ret = SSH_FX_OP_UNSUPPORTED;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
ret = SSH_FX_FAILURE;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void stat_to_filexfer_attrib(const struct stat* z_st, struct sftp_attributes_struct* z_attr)
|
|
||||||
{
|
|
||||||
z_attr->flags = 0;
|
|
||||||
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_SIZE;
|
|
||||||
z_attr->size = z_st->st_size;
|
|
||||||
|
|
||||||
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_UIDGID;
|
|
||||||
z_attr->uid = z_st->st_uid;
|
|
||||||
z_attr->gid = z_st->st_gid;
|
|
||||||
|
|
||||||
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS;
|
|
||||||
z_attr->permissions = z_st->st_mode;
|
|
||||||
|
|
||||||
z_attr->flags |= (uint32_t)SSH_FILEXFER_ATTR_ACMODTIME;
|
|
||||||
z_attr->atime = z_st->st_atime;
|
|
||||||
z_attr->mtime = z_st->st_mtime;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void clear_filexfer_attrib(struct sftp_attributes_struct* z_attr)
|
|
||||||
{
|
|
||||||
z_attr->flags = 0;
|
|
||||||
z_attr->size = 0;
|
|
||||||
z_attr->uid = 0;
|
|
||||||
z_attr->gid = 0;
|
|
||||||
z_attr->permissions = 0;
|
|
||||||
z_attr->atime = 0;
|
|
||||||
z_attr->mtime = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int readdir_long_name(char* z_file_name, struct stat* z_st, char* z_long_name)
|
|
||||||
{
|
|
||||||
char tmpbuf[MAX_LONG_NAME_LEN];
|
|
||||||
char time[50];
|
|
||||||
char* ptr = z_long_name;
|
|
||||||
int mode = z_st->st_mode;
|
|
||||||
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
switch(mode & S_IFMT)
|
|
||||||
{
|
|
||||||
case S_IFDIR:
|
|
||||||
{
|
|
||||||
*ptr++ = 'd';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
*ptr++ = '-';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* user */
|
|
||||||
if(mode & 0400)
|
|
||||||
*ptr++ = 'r';
|
|
||||||
else
|
|
||||||
*ptr++ ='-';
|
|
||||||
|
|
||||||
if(mode & 0200)
|
|
||||||
*ptr++ = 'w';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
|
|
||||||
if(mode & 0100) {
|
|
||||||
if(mode & S_ISUID)
|
|
||||||
*ptr++ = 's';
|
|
||||||
else
|
|
||||||
*ptr++ = 'x';
|
|
||||||
} else
|
|
||||||
*ptr++ = '-';
|
|
||||||
|
|
||||||
/* group */
|
|
||||||
if(mode & 040)
|
|
||||||
*ptr++ = 'r';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
if(mode & 020)
|
|
||||||
*ptr++ = 'w';
|
|
||||||
else
|
|
||||||
*ptr++ ='-';
|
|
||||||
if(mode & 010)
|
|
||||||
*ptr++ = 'x';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
|
|
||||||
/* other */
|
|
||||||
if(mode & 04)
|
|
||||||
*ptr++ = 'r';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
if(mode & 02)
|
|
||||||
*ptr++ = 'w';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
if(mode & 01)
|
|
||||||
*ptr++ = 'x';
|
|
||||||
else
|
|
||||||
*ptr++ = '-';
|
|
||||||
|
|
||||||
*ptr++ = ' ';
|
|
||||||
*ptr = '\0';
|
|
||||||
|
|
||||||
snprintf(tmpbuf, sizeof(tmpbuf),"%3d %d %d %d", (int)z_st->st_nlink,
|
|
||||||
(int)z_st->st_uid, (int)z_st->st_gid, (int)z_st->st_size);
|
|
||||||
strcat(z_long_name, tmpbuf);
|
|
||||||
|
|
||||||
ctime_r(&z_st->st_mtime, time);
|
|
||||||
if((ptr = strchr(time,'\n')))
|
|
||||||
{
|
|
||||||
*ptr = '\0';
|
|
||||||
}
|
|
||||||
snprintf(tmpbuf,sizeof(tmpbuf)," %s %s", time + 4, z_file_name);
|
|
||||||
strcat(z_long_name, tmpbuf);
|
|
||||||
|
|
||||||
return SSH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_open(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
uint32_t msg_flag = sftp_client_message_get_flags(client_msg);
|
|
||||||
int file_flag;
|
|
||||||
int fd = -1;
|
|
||||||
int handle_ind = -1;
|
|
||||||
int status;
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to open file: %s", filename);
|
|
||||||
|
|
||||||
if (( (msg_flag&(uint32_t)SSH_FXF_READ) == SSH_FXF_READ) &&
|
|
||||||
( (msg_flag&(uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE)) {
|
|
||||||
file_flag = O_RDWR; //file must exist
|
|
||||||
if ((msg_flag & (uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT)
|
|
||||||
file_flag |= O_CREAT;
|
|
||||||
} else if ( (msg_flag&(uint32_t)SSH_FXF_WRITE) == SSH_FXF_WRITE ) {
|
|
||||||
file_flag = O_WRONLY;
|
|
||||||
if ( (msg_flag&(uint32_t)SSH_FXF_APPEND) == SSH_FXF_APPEND )
|
|
||||||
file_flag |= O_APPEND;
|
|
||||||
if ( (msg_flag&(uint32_t)SSH_FXF_CREAT) == SSH_FXF_CREAT )
|
|
||||||
file_flag |= O_CREAT;
|
|
||||||
} else {
|
|
||||||
file_flag = O_RDONLY;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = open(filename, file_flag, 0600);
|
|
||||||
if (fd == -1) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "error open file with error: %d", errno);
|
|
||||||
sftp_reply_status(client_msg, status, "Write error");
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_ind = add_handle(FILE_HANDLE, NULL, fd, filename, client_msg->sftp);
|
|
||||||
if (handle_ind >= 0) {
|
|
||||||
void *handle_ptr = &s_handle_table[handle_ind];
|
|
||||||
ssh_string handle_s = sftp_handle_alloc(client_msg->sftp, handle_ptr);
|
|
||||||
sftp_reply_handle(client_msg, handle_s);
|
|
||||||
ssh_string_free(handle_s);
|
|
||||||
ret = SSH_OK;
|
|
||||||
} else {
|
|
||||||
close(fd);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "opening file failed", errno);
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_read(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
struct sftp_handle *client_handle;
|
|
||||||
uint32_t readn;
|
|
||||||
int fd;
|
|
||||||
char *buffer;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp,
|
|
||||||
client_msg->handle);
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to read a file from handle");
|
|
||||||
|
|
||||||
if (client_handle == NULL || client_handle->session_id != client_msg->sftp) {
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg");
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = client_handle->fd;
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "error reading file fd: %d", fd);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
rv = lseek(fd, client_msg->offset, SEEK_SET);
|
|
||||||
if (rv == -1) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file fd: %d at offset: %ld",
|
|
||||||
fd, client_msg->offset);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
buffer = malloc((client_msg->len)*sizeof(char));
|
|
||||||
readn = read(fd, buffer, client_msg->len);
|
|
||||||
|
|
||||||
if (readn > 0) {
|
|
||||||
sftp_reply_data(client_msg, buffer, readn);
|
|
||||||
} else if (readn == 0) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_EOF, "EOF encountered");
|
|
||||||
} else {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "read file error!");
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_write(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
struct sftp_handle *client_handle;
|
|
||||||
uint32_t writen;
|
|
||||||
int fd;
|
|
||||||
const char *msg_data;
|
|
||||||
uint32_t len;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp,
|
|
||||||
client_msg->handle);
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to write a file from handle");
|
|
||||||
|
|
||||||
if (client_handle == NULL || client_handle->session_id != client_msg->sftp) {
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "got wrong handle from msg");
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
fd = client_handle->fd;
|
|
||||||
|
|
||||||
if (fd < 0) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "write file fd error!");
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
msg_data = ssh_string_get_char(client_msg->data);
|
|
||||||
len = ssh_string_len(client_msg->data);
|
|
||||||
|
|
||||||
rv = lseek(fd, client_msg->offset, SEEK_SET);
|
|
||||||
if (rv == -1) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "error seeking file at offset: %ld\n",
|
|
||||||
client_msg->offset);
|
|
||||||
}
|
|
||||||
writen = write(fd, msg_data, len);
|
|
||||||
if(writen == len) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_OK, NULL);
|
|
||||||
}else {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "Write error");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_close(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
struct sftp_handle *client_handle;
|
|
||||||
|
|
||||||
client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp,
|
|
||||||
client_msg->handle);
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to close handle");
|
|
||||||
|
|
||||||
if (client_handle == NULL) {
|
|
||||||
printf("get wrong handle from msg\n");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = handle_close_by_pointer(client_handle);
|
|
||||||
if (ret == SSH_OK) {
|
|
||||||
reinit_single_handle(client_handle);
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_OK, NULL);
|
|
||||||
} else {
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "closing file failed");
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_BAD_MESSAGE, "Invalid handle");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_opendir(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
DIR *dir = NULL;
|
|
||||||
const char *dir_name = sftp_client_message_get_filename(client_msg);
|
|
||||||
int handle_ind = -1;
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to open dir: %s", dir_name);
|
|
||||||
|
|
||||||
dir = opendir(dir_name);
|
|
||||||
if (dir == NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "No such directory");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle_ind = add_handle(DIR_HANDLE, dir, -1, dir_name, client_msg->sftp);
|
|
||||||
if (handle_ind >= 0) {
|
|
||||||
ssh_string handle_s = sftp_handle_alloc(client_msg->sftp, &s_handle_table[handle_ind]);
|
|
||||||
sftp_reply_handle(client_msg, handle_s);
|
|
||||||
ssh_string_free(handle_s);
|
|
||||||
ret = SSH_OK;
|
|
||||||
} else {
|
|
||||||
closedir(dir);
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, "No handle available");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_readdir(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
struct sftp_handle *client_handle = (struct sftp_handle*)sftp_handle(client_msg->sftp, client_msg->handle);
|
|
||||||
int entries = 0;
|
|
||||||
struct dirent *dentry;
|
|
||||||
DIR *dir = NULL;
|
|
||||||
|
|
||||||
char long_path[PATH_MAX];
|
|
||||||
int path_length;
|
|
||||||
char *handle_name;
|
|
||||||
|
|
||||||
if (client_handle == NULL || client_handle->session_id != client_msg->sftp) {
|
|
||||||
printf("get wrong handle from msg\n");
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_FAILURE, NULL);
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
dir = client_handle->dirp;
|
|
||||||
if (dir == NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
|
||||||
printf("read dir handle error!\n");
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = SSH_OK;
|
|
||||||
handle_name = get_handle_name(client_handle);
|
|
||||||
if (handle_name == NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_INVALID_HANDLE, NULL);
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
strcpy(long_path, handle_name);
|
|
||||||
strcat(long_path, "/");
|
|
||||||
path_length = (int)strlen(long_path);
|
|
||||||
|
|
||||||
for(int i=0; i < MAX_ENTRIES_NUM_IN_PACKET; i++) {
|
|
||||||
dentry = readdir(dir);
|
|
||||||
|
|
||||||
if (dentry!=NULL) {
|
|
||||||
struct sftp_attributes_struct attr;
|
|
||||||
struct stat st;
|
|
||||||
char long_name[MAX_LONG_NAME_LEN];
|
|
||||||
|
|
||||||
strcpy(&long_path[path_length], dentry->d_name);
|
|
||||||
|
|
||||||
if(lstat(long_path, &st) == 0) {
|
|
||||||
stat_to_filexfer_attrib(&st, &attr);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
clear_filexfer_attrib(&attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(readdir_long_name(dentry->d_name, &st, long_name) == 0){
|
|
||||||
sftp_reply_names_add(client_msg, dentry->d_name, long_name, &attr);
|
|
||||||
} else {
|
|
||||||
printf("readdir long name error\n");
|
|
||||||
}
|
|
||||||
|
|
||||||
entries++;
|
|
||||||
} else {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entries > 0) {
|
|
||||||
ret = sftp_reply_names(client_msg);
|
|
||||||
} else {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_EOF, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_mkdir(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
uint32_t msg_flags = client_msg->flags;
|
|
||||||
uint32_t permission = client_msg->attr->permissions;
|
|
||||||
uint32_t mode = (msg_flags & (uint32_t)SSH_FILEXFER_ATTR_PERMISSIONS) ? permission & (uint32_t)07777 : 0777;
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (filename==NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = mkdir(filename, mode);
|
|
||||||
if (rv < 0) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
sftp_reply_status(client_msg, status, NULL);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_rmdir(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (filename==NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = rmdir(filename);
|
|
||||||
if (rv < 0) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
sftp_reply_status(client_msg, status, NULL);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_lstat(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
struct sftp_attributes_struct attr;
|
|
||||||
struct stat st;
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
if (filename==NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = lstat(filename, &st);
|
|
||||||
if (rv < 0) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
sftp_reply_status(client_msg, status, NULL);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
} else {
|
|
||||||
stat_to_filexfer_attrib(&st, &attr);
|
|
||||||
sftp_reply_attr(client_msg, &attr);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_readlink(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
char buf[PATH_MAX];
|
|
||||||
int len = -1;
|
|
||||||
const char *err_msg;
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
|
|
||||||
if (filename == NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "read link : %s", filename);
|
|
||||||
|
|
||||||
len = readlink(filename, buf, sizeof(buf) - 1);
|
|
||||||
if (len < 0) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
err_msg = ssh_str_error(status);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "read link error with reason: %s", err_msg);
|
|
||||||
sftp_reply_status(client_msg, status, err_msg);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
} else {
|
|
||||||
buf[len] = '\0';
|
|
||||||
sftp_reply_name(client_msg, buf, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_symlink(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *destpath = sftp_client_message_get_filename(client_msg);
|
|
||||||
const char *srcpath = ssh_string_get_char(client_msg->data);
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to create link with src: %s and dest: %s",
|
|
||||||
srcpath, destpath);
|
|
||||||
|
|
||||||
if (srcpath == NULL || destpath == NULL) {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_NO_SUCH_FILE, "File name error");
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
rv = symlink(srcpath, destpath);
|
|
||||||
if (rv < 0) {
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
printf("error symlink with error: %d\n", errno);
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "error symlink with error: %s\n",
|
|
||||||
strerror(errno));
|
|
||||||
sftp_reply_status(client_msg, status, strerror(errno));
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
} else {
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_OK, "write success");
|
|
||||||
}
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_remove(sftp_client_message client_msg)
|
|
||||||
{
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *filename = sftp_client_message_get_filename(client_msg);
|
|
||||||
int rv;
|
|
||||||
int status = SSH_FX_OK;
|
|
||||||
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "try to remove: %s ", filename);
|
|
||||||
|
|
||||||
rv = unlink(filename);
|
|
||||||
if (rv < 0) {
|
|
||||||
SSH_LOG(SSH_LOG_PROTOCOL, "unlink error with reason: %s ",
|
|
||||||
strerror(errno));
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
sftp_reply_status(client_msg, status, NULL);
|
|
||||||
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_unsupposed(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
sftp_reply_status(client_msg, SSH_FX_OP_UNSUPPORTED, "Operation not supported");
|
|
||||||
printf("Message type %d not implemented\n", sftp_client_message_get_type(client_msg));
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_extended_statvfs(sftp_client_message client_msg) {
|
|
||||||
int ret = SSH_OK;
|
|
||||||
const char *path = sftp_client_message_get_filename(client_msg);
|
|
||||||
struct statvfs st;
|
|
||||||
int status;
|
|
||||||
int rv;
|
|
||||||
|
|
||||||
rv = statvfs(path, &st);
|
|
||||||
if (rv == 0) {
|
|
||||||
sftp_statvfs_t sftp_statvfs;
|
|
||||||
u_int64_t flag;
|
|
||||||
|
|
||||||
sftp_statvfs = calloc(1, sizeof(struct sftp_statvfs_struct));
|
|
||||||
if (sftp_statvfs == NULL) {
|
|
||||||
goto error;
|
|
||||||
}
|
|
||||||
|
|
||||||
flag = (st.f_flag & ST_RDONLY) ? SSH_FXE_STATVFS_ST_RDONLY : 0;
|
|
||||||
flag |= (st.f_flag & ST_NOSUID) ? SSH_FXE_STATVFS_ST_NOSUID : 0;
|
|
||||||
|
|
||||||
sftp_statvfs->f_bsize = st.f_bsize;
|
|
||||||
sftp_statvfs->f_frsize = st.f_frsize;
|
|
||||||
sftp_statvfs->f_blocks = st.f_blocks;
|
|
||||||
sftp_statvfs->f_bfree = st.f_bfree;
|
|
||||||
sftp_statvfs->f_bavail = st.f_bavail;
|
|
||||||
sftp_statvfs->f_files = st.f_files;
|
|
||||||
sftp_statvfs->f_ffree = st.f_ffree;
|
|
||||||
sftp_statvfs->f_favail = st.f_favail;
|
|
||||||
sftp_statvfs->f_fsid = st.f_fsid;
|
|
||||||
sftp_statvfs->f_flag = flag;
|
|
||||||
sftp_statvfs->f_namemax = st.f_namemax;
|
|
||||||
|
|
||||||
if(sftp_reply_statvfs(client_msg, sftp_statvfs) == 0)
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
error:
|
|
||||||
status = unix_errno_to_ssh_stat(errno);
|
|
||||||
ret = SSH_ERROR;
|
|
||||||
sftp_reply_status(client_msg, status, NULL);
|
|
||||||
|
|
||||||
printf("statvfs send failed!\n");
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_extended(sftp_client_message sftp_msg) {
|
|
||||||
int status = SSH_ERROR;
|
|
||||||
|
|
||||||
const char *subtype = sftp_msg->submessage;
|
|
||||||
client_message_callback handler = NULL;
|
|
||||||
for(int i=0;extended_handlers[i].cb!=NULL;i++){
|
|
||||||
if (strcmp(subtype, extended_handlers[i].extended_name) == 0) {
|
|
||||||
handler = extended_handlers[i].cb;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (handler!=NULL) {
|
|
||||||
status = handler(sftp_msg);
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED, "Extended Operation not supported");
|
|
||||||
printf("Extended Message type %s not implemented\n", subtype);
|
|
||||||
return SSH_OK;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int dispatch_sftp_request(sftp_client_message sftp_msg) {
|
|
||||||
int status = SSH_ERROR;
|
|
||||||
client_message_callback handler = NULL;
|
|
||||||
u_int sft_msg_type = sftp_client_message_get_type(sftp_msg);
|
|
||||||
|
|
||||||
for(int i=0;message_handlers[i].cb!=NULL;i++){
|
|
||||||
if (sft_msg_type == message_handlers[i].type) {
|
|
||||||
handler = message_handlers[i].cb;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (handler!=NULL)
|
|
||||||
status = handler(sftp_msg);
|
|
||||||
else
|
|
||||||
goto not_implemented;
|
|
||||||
|
|
||||||
return status;
|
|
||||||
|
|
||||||
not_implemented:
|
|
||||||
sftp_reply_status(sftp_msg, SSH_FX_OP_UNSUPPORTED, "Operation not supported");
|
|
||||||
printf("Message type %d not implemented\n", sft_msg_type);
|
|
||||||
return SSH_OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int process_client_message(sftp_client_message client_msg) {
|
|
||||||
int status = SSH_OK;
|
|
||||||
if (client_msg == NULL) {
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
switch(client_msg->type) {
|
|
||||||
case SSH_FXP_EXTENDED:
|
|
||||||
status = process_extended(client_msg);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
status = dispatch_sftp_request(client_msg);
|
|
||||||
}
|
|
||||||
|
|
||||||
// status = dispatch_sftp_request(client_msg);
|
|
||||||
if (status!=SSH_OK)
|
|
||||||
printf("error occur in process client message!\n");
|
|
||||||
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* TODO implement proper pam authentication cb */
|
/* TODO implement proper pam authentication cb */
|
||||||
static int sftp_auth_password_cb(UNUSED_PARAM(ssh_session session),
|
static int sftp_auth_password_cb(UNUSED_PARAM(ssh_session session),
|
||||||
@ -1016,60 +117,6 @@ null_userdata:
|
|||||||
return SSH_AUTH_DENIED;
|
return SSH_AUTH_DENIED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static int sftp_channel_data_cb(UNUSED_PARAM(ssh_session session),
|
|
||||||
UNUSED_PARAM(ssh_channel channel),
|
|
||||||
void *data,
|
|
||||||
uint32_t len,
|
|
||||||
UNUSED_PARAM(int is_stderr),
|
|
||||||
void *userdata)
|
|
||||||
{
|
|
||||||
struct channel_data_st *cdata = (struct channel_data_st *) userdata;
|
|
||||||
sftp_session sftp = cdata->sftp;
|
|
||||||
sftp_client_message msg;
|
|
||||||
int decode_len;
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
decode_len = sftp_decode_channel_data_to_packet(sftp, data);
|
|
||||||
if(decode_len == -1)
|
|
||||||
return -1;
|
|
||||||
|
|
||||||
msg = sftp_get_client_message_from_packet(cdata->sftp);
|
|
||||||
rc = process_client_message(msg);
|
|
||||||
sftp_client_message_free(msg);
|
|
||||||
if(rc != SSH_OK)
|
|
||||||
printf("process sftp failed!\n");
|
|
||||||
|
|
||||||
return decode_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sftp_channel_subsystem_request_cb(ssh_session session,
|
|
||||||
ssh_channel channel,
|
|
||||||
const char *subsystem,
|
|
||||||
void *userdata)
|
|
||||||
{
|
|
||||||
if (strcmp(subsystem, "sftp") == 0) {
|
|
||||||
struct channel_data_st* cdata = (struct channel_data_st*) userdata;
|
|
||||||
|
|
||||||
/* initialize sftp session and file handler */
|
|
||||||
cdata->sftp = sftp_server_new(session, channel);
|
|
||||||
init_handle_table(MAX_HANDLE_NUM);
|
|
||||||
|
|
||||||
return SSH_OK;
|
|
||||||
}
|
|
||||||
return SSH_ERROR;
|
|
||||||
}
|
|
||||||
|
|
||||||
static int sftp_channel_write_wontblock_cb(UNUSED_PARAM(ssh_session session),
|
|
||||||
UNUSED_PARAM(ssh_channel channel),
|
|
||||||
UNUSED_PARAM(size_t bytes),
|
|
||||||
UNUSED_PARAM(void *userdata))
|
|
||||||
{
|
|
||||||
/* TODO */
|
|
||||||
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
static ssh_channel sftp_channel_new_session_cb(ssh_session session, void *userdata)
|
static ssh_channel sftp_channel_new_session_cb(ssh_session session, void *userdata)
|
||||||
{
|
{
|
||||||
struct session_data_st *sdata = NULL;
|
struct session_data_st *sdata = NULL;
|
||||||
@ -1178,8 +225,8 @@ struct ssh_channel_callbacks_struct *get_sftp_channel_cb(void)
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
cb->channel_subsystem_request_function = sftp_channel_subsystem_request_cb;
|
cb->channel_data_function = sftp_channel_default_data_callback;
|
||||||
cb->channel_data_function = sftp_channel_data_cb;
|
cb->channel_subsystem_request_function = sftp_channel_default_subsystem_request;
|
||||||
|
|
||||||
end:
|
end:
|
||||||
return cb;
|
return cb;
|
||||||
@ -1309,7 +356,7 @@ void sftp_handle_session_cb(ssh_event event,
|
|||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
channel_cb->userdata = &cdata;
|
channel_cb->userdata = &(cdata.sftp);
|
||||||
|
|
||||||
ssh_callbacks_init(channel_cb);
|
ssh_callbacks_init(channel_cb);
|
||||||
rc = ssh_set_channel_callbacks(sdata.channel, channel_cb);
|
rc = ssh_set_channel_callbacks(sdata.channel, channel_cb);
|
||||||
|
Reference in New Issue
Block a user