Files
m241Neelima/components/ip_net2-6.9/ipssh/src/ipssh_sftp.c
T
2026-07-30 11:19:44 +05:30

2523 lines
76 KiB
C

/*
******************************************************************************
* SOURCE FILE
*
* Document no: @(#) $Name: VXWORKS_ITER40_2019102207 $ $RCSfile: ipssh_sftp.c,v $ $Revision: 1.89.10.13 $
* $Source: /home/interpeak/CVSRoot/ipssh/src/ipssh_sftp.c,v $
* $Author: yli10 $ $Date: 2019-09-06 06:45:53 $
* $State: Exp $ $Locker: $
*
* INTERPEAK_COPYRIGHT_STRING
* Design and implementation by Roger Boden <roger@interpeak.se>
******************************************************************************
*/
/*
****************************************************************************
* 1 DESCRIPTION
****************************************************************************
*/
/*
****************************************************************************
* 2 CONFIGURATION
****************************************************************************
*/
#define IPCOM_USE_CLIB_PROTO
#include <ipssh_config.h>
#ifdef IPSSH_USE_SFTP
/*
****************************************************************************
* 3 INCLUDE FILES
****************************************************************************
*/
#include "ipssh_h.h"
#include "ipssh.h"
#include "ipssh_transport.h"
#include "ipssh_connection.h"
#include "ipssh_sftp.h"
#include "ipssh_buffer.h"
#include <ipcom_clib.h>
#include <ipcom_type.h>
#include <ipcom_cstyle.h>
#include <ipcom_time.h>
#include <ipcom_err.h>
#include <ipcom_buffer.h>
#include <ipcom_syslog.h>
/*
****************************************************************************
* 4 EXTERN PROTOTYPES
****************************************************************************
*/
/*
****************************************************************************
* 5 DEFINES
****************************************************************************
*/
#define IPSSH_SFTP_READDIR_BUF_SIZE 4*1024
#define IPSSH_FILE_HANDLE 1
#define IPSSH_DIR_HANDLE 2
#define IPSSH_S_IFDIR 0x4000
#define IPSSH_S_IFREG 0x8000
/*
****************************************************************************
* 6 TYPES
****************************************************************************
*/
/* External handle, given to client */
struct Ipssh_sftp_ext_handle_st
{
Ip_u32 id;
Ip_u8 cookie[8];
Ip_u8 mac[8];
};
typedef struct Ipssh_sftp_ext_handle_st Ipssh_sftp_ext_handle;
struct Ipssh_sftp_int_handle_st
{
Ipcom_list list;
Ip_u32 type;
Ip_u32 id;
char* path;
Ip_s32 eof;
IP_DIR* dir;
Ip_fd fd;
Ip_u64 read_pos;
Ip_u64 write_pos;
Ipssh_sftp_func_cb* funcs;
};
typedef struct Ipssh_sftp_int_handle_st Ipssh_sftp_int_handle;
typedef Ipcom_list Ipssh_sftp_handle_map;
/*
****************************************************************************
* 7 LOCAL PROTOTYPES
****************************************************************************
*/
/* sftp message handlers */
IP_GLOBAL Ip_err ipssh_msg_fxp_init(Ipssh_channel* channel, Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_opendir(Ipssh_channel* channel, Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_readdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_open(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_lstat(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_setstat(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_read(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_write(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_realpath(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_close(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_eof(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_remove(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_mkdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_rmdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
IP_GLOBAL Ip_err ipssh_msg_fxp_rename(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf);
/* Send message functions */
IP_GLOBAL Ip_err ipssh_send_fxp_version(Ipssh_channel* channel);
IP_GLOBAL Ip_err ipssh_send_fxp_name(Ipssh_channel* channel, Ip_u32 req_id, char* path);
IP_GLOBAL Ip_err ipssh_send_fxp_handle(Ipssh_channel* channel, Ip_u32 req_id, Ipssh_sftp_ext_handle* handle, Ip_u32 handle_len);
IP_STATIC Ip_err ipssh_sftp_msg_handler(Ipssh_channel* channel, Ipcom_buffer* msg);
/*
****************************************************************************
* 8 DATA
****************************************************************************
*/
IP_STATIC Ip_u8 ipssh_handle_cookie[8] = { 4,7,1,7,4,7,1,7 };
/*
****************************************************************************
* 9 LOCAL FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_get_file_priv
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipssh_get_file_priv(Ip_mode_t mode, char *p)
{
/* print type */
if(IP_S_ISDIR(mode)) /* directory */
*p++ = 'd';
#ifdef IP_S_ISCHR
else if(IP_S_ISCHR(mode)) /* character special */
*p++ = 'c';
#endif
#ifdef IP_S_ISBLK
else if(IP_S_ISBLK(mode)) /* block special */
*p++ = 'b';
#endif
else if(IP_S_ISREG(mode)) /* regular */
*p++ = '-';
#ifdef IP_S_ISLNK
else if(IP_S_ISLNK(mode)) /* symbolic link */
*p++ = 'l';
#endif
else /* unknown */
*p++ = '?';
/* usr */
if(mode & IP_S_IRUSR)
*p++ = 'r';
else
*p++ = '-';
if(mode & IP_S_IWUSR)
*p++ = 'w';
else
*p++ = '-';
if(mode & IP_S_IXUSR)
*p++ = 'x';
else
*p++ = '-';
/* group */
if (mode & IP_S_IRGRP)
*p++ = 'r';
else
*p++ = '-';
if (mode & IP_S_IWGRP)
*p++ = 'w';
else
*p++ = '-';
if(mode & IP_S_IXGRP)
*p++ = 'x';
else
*p++ = '-';
/* other */
if (mode & IP_S_IROTH)
*p++ = 'r';
else
*p++ = '-';
if (mode & IP_S_IWOTH)
*p++ = 'w';
else
*p++ = '-';
if(mode & IP_S_IXOTH)
*p++ = 'x';
else
*p++ = '-';
*p = '\0';
}
/*
*===========================================================================
* ipssh_make_long_name
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC void
ipssh_make_long_name(const char* path, const char* filename,
struct Ip_stat* file_stats, char* long_list)
{
char *tmp_path;
char file_priv[11];
struct Ip_tm *tm;
tmp_path = ipcom_malloc(ipcom_strlen(path) + 1 + ipcom_strlen(filename)+1);
if(!tmp_path)
{
ipcom_strcpy(long_list,filename);
return;
}
ipcom_strcpy(tmp_path, path);
ipcom_strcat(tmp_path, "/");
ipcom_strcat(tmp_path, filename);
ipssh_get_file_priv(file_stats->st_mode, file_priv);
tm = (struct Ip_tm *)ipcom_localtime((void *)&(file_stats->st_mtime)); /*lint !e64*/
if (tm != IP_NULL)
ipcom_sprintf(long_list, "%10s %3d %-4ld %-4ld %-8ld %04d-%02d-%02d %02d:%02d %s",
file_priv, 1, file_stats->st_uid, file_stats->st_gid,
file_stats->st_size, tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, filename);
ipcom_free(tmp_path);
}
/*
*===========================================================================
* ipssh_pack_stat_data
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_pack_stat_data(Ipcom_buffer* pkt, struct Ip_stat* stat)
{
return ipssh_pack_attr(pkt, stat, IPSSH_FILEXFER_ATTR_SIZE|
IPSSH_FILEXFER_ATTR_ACMODTIME|
IPSSH_FILEXFER_ATTR_PERMISSIONS|
IPSSH_FILEXFER_ATTR_UIDGID);
}
/*
*===========================================================================
* ipssh_conv_flags
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_u32
ipssh_conv_flags(Ip_u32 flags)
{
Ip_u32 ret = 0;
if((flags & IPSSH_FXF_READ) && (flags & IPSSH_FXF_WRITE))
ret |= IP_O_RDWR;
if(flags & IPSSH_FXF_READ)
ret |= IP_O_RDONLY;
if(flags & IPSSH_FXF_WRITE)
ret |= IP_O_WRONLY;
if(flags & IPSSH_FXF_APPEND)
ret |= IP_O_APPEND;
if(flags & IPSSH_FXF_CREAT)
ret |= IP_O_CREAT;
#ifdef IP_O_EXCL
if(flags & IPSSH_FXF_EXCL)
ret |= IP_O_EXCL;
#endif
#ifdef IP_O_TRUNC
if(flags & IPSSH_FXF_TRUNC)
ret |= IP_O_TRUNC;
#endif
return ret;
}
/*
*===========================================================================
* ipssh_make_ext_int_paths
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_make_ext_int_paths(char* org_path,
char* start_dir,
Ipcom_shell_ctx* sh_ctx,
char** ext_path,
char** int_path)
{
char* session_root_dir = IP_NULL;
Ip_err ret;
char start_dir_buf[IPSSH_SFTP_MAX_PATH];
if (ipcom_strcmp(org_path, ".") != 0 && org_path[0] != '/')
return IPCOM_ERR_INVALID_ARG;
ret = ipcom_shell_ctx_get(sh_ctx, IPSSH_SFTP_ROOT_DIR, (Ip_u8**)&session_root_dir, IP_NULL);
if (ret != IPCOM_SUCCESS && ret != IPCOM_ERR_NOT_FOUND)
return ret;
if (session_root_dir)
IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: session root dir: %s", session_root_dir);
IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: start dir: %s", start_dir);
if (!ipcom_normpath(org_path, org_path))
return IPCOM_ERR_FAILED;
if (ipcom_strcmp(start_dir, ".") == 0 || ipcom_strcmp(start_dir, "./") == 0)
{
if (!ipcom_getcwd(start_dir_buf, sizeof(start_dir_buf)))
{
IPCOM_LOG1(DEBUG, "ipssh_make_ext_int_path :: ipcom_getcwd() failed (errno=%d)", ipcom_errno);
return IPCOM_ERR_FAILED;
}
if (!ipcom_normpath(start_dir_buf, start_dir_buf))
return IPCOM_ERR_FAILED;
start_dir = start_dir_buf;
}
ret = ipcom_shell_ctx_get(sh_ctx, IPSSH_SFTP_ROOT_DIR, (Ip_u8**)&session_root_dir, IP_NULL);
if (ret != IPCOM_SUCCESS && ret != IPCOM_ERR_NOT_FOUND)
return ret;
if (session_root_dir)
if (!ipcom_normpath(session_root_dir, session_root_dir))
{
ipcom_free(session_root_dir);
return IPCOM_ERR_FAILED;
}
if (ipcom_strcmp(org_path, ".") == 0)
{
if (session_root_dir)
{
/* If session_root_dir and start_dir are the same, return "/" as external path */
if (ipcom_strlen(session_root_dir) == ipcom_strlen(start_dir) &&
ipcom_strcmp(session_root_dir, start_dir) == 0)
{
*int_path = ipcom_strdup(start_dir);
if (ext_path)
*ext_path = ipcom_strdup("/");
}
/* If start dir is not a sub-directory of session root dir, make session root dir our start dir */
else if (ipcom_strlen(session_root_dir) > ipcom_strlen(start_dir) ||
ipcom_strncmp(session_root_dir, start_dir, ipcom_strlen(session_root_dir)))
{
*int_path = ipcom_strdup(session_root_dir);
if (ext_path)
*ext_path = ipcom_strdup("/");
}
else
{
*int_path = ipcom_strdup(start_dir);
if (ext_path)
{
*ext_path = ipcom_malloc(IP_MAX(ipcom_strlen(start_dir),ipcom_strlen(session_root_dir))+1);
if (!*ext_path)
goto cleanup;
/* 'Peel off' common path of session_root_dir from start_dir */
ipcom_strcpy(*ext_path, start_dir+ipcom_strlen(session_root_dir));
}
}
}
else
{
if (ext_path)
*ext_path = ipcom_strdup(start_dir);
*int_path = ipcom_strdup(start_dir);
}
}
else
{
if (session_root_dir)
{
if (ext_path)
*ext_path = ipcom_strdup(org_path);
*int_path = ipcom_malloc(ipcom_strlen(session_root_dir)+ipcom_strlen(org_path)+2);
if ((ext_path && !*ext_path) || !*int_path)
goto cleanup;
ipcom_strcpy(*int_path, session_root_dir);
if ((*int_path)[ipcom_strlen(session_root_dir)] != '/' && org_path[0] != '/')
ipcom_strcat(*int_path, "/");
ipcom_strcat(*int_path, org_path);
}
else
{
if (ext_path)
*ext_path = ipcom_strdup(org_path);
*int_path = ipcom_strdup(org_path);
if ((ext_path && !*ext_path) || !*int_path)
goto cleanup;
}
}
if (session_root_dir)
{
ipcom_free(session_root_dir);
session_root_dir = IP_NULL;
}
if ((!ext_path || *ext_path) && *int_path)
return IPCOM_SUCCESS;
cleanup:
if (ext_path && *ext_path)
ipcom_free(*ext_path);
if (*int_path)
ipcom_free(*int_path);
if (session_root_dir)
ipcom_free(session_root_dir);
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipssh_sftp_srv_pkt_dispatch
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_sftp_pkt_dispatch(Ipssh_channel* channel, Ipcom_buffer* pkt)
{
switch(channel->type)
{
#ifdef IPSSH_USE_SFTP_CLT
case IPSSH_CH_TYPE_SFTP_CLT:
return ipssh_sftp_clt_pkt_dispatch(channel, pkt);
#endif
case IPSSH_CH_TYPE_SFTP_SRV:
return ipssh_sftp_srv_pkt_dispatch(channel, pkt);
default:
IP_PANIC();
return IPCOM_ERR_FAILED;
}
}
/*
*===========================================================================
* ipssh_sftp_msg_handler
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_sftp_msg_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
{
Ip_u32 sftp_msg_len;
Ip_s32 rc;
if(!channel->data.sftp.msg) /* Is this the beginning of a fresh sftp message? */
{
if (ipcom_buffer_len(msg) < 4)
{
/* Since we don't have received enough bytes to read the packet length, we
* allocate a buffer that will fit a max-size packet. */
sftp_msg_len = IPSSH_CH_MAX_PKT + 128;
channel->data.sftp.msg_len = 0;
}
else
{
if(ipssh_buffer_get_uint(msg, &sftp_msg_len))
return IPCOM_ERR_FAILED;
if(sftp_msg_len >= IPSSH_CH_MAX_PKT + 128)
{
IPCOM_LOG1(ERR, "ipssh_sftp_msg_handler :: the packet's message length (%d) is more than max-size",
sftp_msg_len);
return IPCOM_ERR_FAILED;
}
channel->data.sftp.msg_len = sftp_msg_len;
}
if(sftp_msg_len > ipcom_buffer_len(msg))
{
/* We have only received a fragment of an sftp message */
channel->data.sftp.msg = ipcom_buffer_new(sftp_msg_len);
if(!channel->data.sftp.msg)
{
IPCOM_LOG0(ERR, "ipssh_sftp_msg_handler :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), ipcom_buffer_len(msg));
IPCOM_LOG2(DEBUG2, "ipssh_sftp_msg_handler :: incomplete packet: sftp_msg_len=%d, ch_data_len=%d",
sftp_msg_len, ipcom_buffer_len(msg));
(void)ipcom_buffer_consume(msg, ipcom_buffer_len(msg));
return IPCOM_SUCCESS;
}
rc = ipssh_sftp_pkt_dispatch(channel, msg);
}
else /* We have a packet fragment already */
{
Ip_size_t len;
Ip_size_t bytes_missing;
IPCOM_LOG3(DEBUG2, "ipssh_sftp_msg_handler :: incomplete packet: sftp_msg_len=%d, pos=%d, ch_data_len=%d",
channel->data.sftp.msg->alloc, ipcom_buffer_len(channel->data.sftp.msg), ipcom_buffer_len(msg));
if (channel->data.sftp.msg_len == 0)
{
Ip_size_t oldlen;
oldlen = ipcom_buffer_len(channel->data.sftp.msg);
if (oldlen >= 4)
return IPCOM_ERR_FAILED;
/*It mostly cannot occur*/
if (4 > (oldlen + ipcom_buffer_len(msg)))
{
(void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), ipcom_buffer_len(msg));
(void)ipcom_buffer_consume(msg, ipcom_buffer_len(msg));
return IPCOM_SUCCESS;
}
/* We only get the complete sftp packet length */
(void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), 4-oldlen);
(void)ipcom_buffer_consume(msg, 4-oldlen);
if (ipssh_buffer_get_uint(channel->data.sftp.msg, &channel->data.sftp.msg_len))
return IPCOM_ERR_FAILED;
}
bytes_missing = channel->data.sftp.msg_len-ipcom_buffer_len(channel->data.sftp.msg);
len = IP_MIN(bytes_missing, ipcom_buffer_len(msg)); /*lint !e666 */
(void)ipcom_buffer_put(channel->data.sftp.msg, ipcom_buffer_start_ptr(msg), len);
(void)ipcom_buffer_consume(msg, len);
if(channel->data.sftp.msg_len == 0 ||
ipcom_buffer_len(channel->data.sftp.msg) < (Ip_u32)channel->data.sftp.msg_len)
return IPCOM_SUCCESS; /* We do not have a complete sftp message at this point */
/* Right, now we have a complete sftp message */
rc = ipssh_sftp_pkt_dispatch(channel, channel->data.sftp.msg);
ipcom_buffer_free(channel->data.sftp.msg);
channel->data.sftp.msg = IP_NULL;
}
/* Did we receive more than one sftp message? */
if(rc == IPCOM_SUCCESS && ipcom_buffer_len(msg) > 0)
{
/* We have an additional packet */
rc = ipssh_sftp_msg_handler(channel, msg);
}
return rc;
}
/*
*===========================================================================
* ipssh_map_insert
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_map_insert(Ipssh_sftp_handle_map* sftp_map, Ipssh_sftp_int_handle* new_entry)
{
Ipcom_list* item;
Ipcom_list* map = (Ipcom_list*) sftp_map;
IPCOM_LOG2(DEBUG2, "ipssh_map_insert :: path = %s, id=%d",
new_entry->path ? new_entry->path : "null", new_entry->id);
/* Check that the ID is not already inserted */
for(item = ipcom_list_first(map); item; item = ipcom_list_next(item))
{
Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item;
if(entry->id == new_entry->id)
{
IPCOM_LOG1(ERR, "ipssh_map_insert :: id already in map: %d", new_entry->id);
return IPCOM_ERR_FAILED;
}
}
ipcom_list_insert_first(map, (Ipcom_list*) new_entry);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_map_lookup
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ipssh_sftp_int_handle*
ipssh_map_lookup(Ipssh_sftp_handle_map* sftp_map, Ip_u32 id)
{
Ipcom_list* item;
Ipcom_list* map = (Ipcom_list*) sftp_map;
IPCOM_LOG1(DEBUG2, "ipssh_map_lookup :: id=%d", id);
for(item = ipcom_list_first(map); item; item = ipcom_list_next(item))
{
Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item;
if(entry->id == id)
return entry;
}
IPCOM_LOG1(ERR, "ipssh_map_lookup :: failed to find id=%d", id);
return IP_NULL;
}
/*
*===========================================================================
* ipssh_map_remove
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_map_remove(Ipssh_sftp_handle_map* sftp_map, Ip_u32 id)
{
Ipcom_list* item;
Ipcom_list* map = (Ipcom_list*) sftp_map;
IPCOM_LOG1(DEBUG2, "ipssh_map_remove :: id=%d", id);
for(item = ipcom_list_first(map); item; item = ipcom_list_next(item))
{
Ipssh_sftp_int_handle* entry = (Ipssh_sftp_int_handle*) item;
if(entry->id == id)
{
IPCOM_LOG2(DEBUG2, "ipssh_map_remove :: id=%d, path=%s", id, entry->path?entry->path:"null");
ipcom_list_remove(item);
if(entry->type == IPSSH_DIR_HANDLE )
{
if(entry->funcs->closedir_cb)
entry->funcs->closedir_cb(entry->dir); /*lint !e522 */
else
IP_PANIC();
}
else
{
if(entry->funcs->close_cb)
entry->funcs->close_cb(entry->fd); /*lint !e522 */
else
IP_PANIC();
}
if(entry->path)
ipcom_free(entry->path);
ipcom_free(entry);
return IPCOM_SUCCESS;
}
}
return IPCOM_ERR_FAILED;
}
/*
*===========================================================================
* ipssh_create_handle
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_create_handle(Ipssh_sftp_ext_handle** handle, Ip_u32* len)
{
static Ip_u32 id = 1;
Ipssh_sftp_ext_handle* tmp_handle;
if(!(tmp_handle = ipcom_malloc(sizeof(Ipssh_sftp_ext_handle))) )
{
IPCOM_LOG0(ERR, "ipssh_create_handle :: out of memory");
return IP_NULL;
}
tmp_handle->id = id++;
ipcom_memcpy(tmp_handle->cookie, ipssh_handle_cookie, sizeof(tmp_handle->cookie));
*handle = tmp_handle;
*len = sizeof(Ipssh_sftp_ext_handle);
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_validate_handle
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_STATIC Ip_err
ipssh_validate_handle(Ipssh_sftp_ext_handle* handle, Ip_u32 len)
{
if(len != sizeof(Ipssh_sftp_ext_handle) ||
ipcom_memcmp(handle->cookie, ipssh_handle_cookie, sizeof(handle->cookie)) )
{
IPCOM_LOG0(ERR, "ipssh_validate_handle :: invalid handle");
return IPCOM_ERR_FAILED;
}
return IPCOM_SUCCESS;
}
/*
****************************************************************************
* 10 PUBLIC FUNCTIONS
****************************************************************************
*/
/*
*===========================================================================
* ipssh_send_fxp_msg
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_msg(Ipssh_channel* channel, Ipcom_buffer* msg)
{
Ip_u32 sftp_msg_len;
sftp_msg_len = (Ip_u32)ipcom_buffer_len(msg)-18;
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(msg, 14), sftp_msg_len); /*lint !e666*/
return ipssh_send_channel_data_msg(channel, msg);
}
/*
*===========================================================================
* ipssh_sftp_srv_pkt_dispatch
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_sftp_srv_pkt_dispatch(Ipssh_channel* channel, Ipcom_buffer* pkt)
{
Ip_u32 req_id;
Ip_u8 pkt_type;
Ip_err rc;
if(ipcom_buffer_get_char(pkt, &pkt_type) != IPCOM_SUCCESS)
return IPCOM_ERR_FAILED;
IPCOM_LOG2(DEBUG2, "ipssh_sftp_srv_pkt_dispatch :: pkt type: %d, data_len:%d",
pkt_type, ipcom_buffer_len(pkt));
/* check the SFTP user access */
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_ACCESS, IP_NULL, IP_NULL) != IPCOM_SUCCESS) /*lint !e644*/
{
/* Unpack message */
(void)ipssh_buffer_get_uint(pkt, &req_id);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: unknown pkt type: %d, ch_id: %d, req_id: %d",
pkt_type, channel->my_ch_id, req_id);
return rc;
}
switch(pkt_type)
{
case IPSSH_FXP_INIT:
rc = ipssh_msg_fxp_init(channel, pkt);
break;
case IPSSH_FXP_OPENDIR:
rc = ipssh_msg_fxp_opendir(channel, pkt);
break;
case IPSSH_FXP_READDIR:
rc = ipssh_msg_fxp_readdir(channel, pkt);
break;
case IPSSH_FXP_CLOSE:
rc = ipssh_msg_fxp_close(channel, pkt);
break;
case IPSSH_FXP_OPEN:
rc = ipssh_msg_fxp_open(channel, pkt);
break;
case IPSSH_FXP_LSTAT:
case IPSSH_FXP_STAT:
rc = ipssh_msg_fxp_lstat(channel, pkt);
break;
case IPSSH_FXP_SETSTAT:
rc = ipssh_msg_fxp_setstat(channel, pkt);
break;
case IPSSH_FXP_REALPATH:
rc = ipssh_msg_fxp_realpath(channel, pkt);
break;
case IPSSH_FXP_READ:
rc = ipssh_msg_fxp_read(channel, pkt);
break;
case IPSSH_FXP_WRITE:
rc = ipssh_msg_fxp_write(channel, pkt);
break;
case IPSSH_FXP_REMOVE:
rc = ipssh_msg_fxp_remove(channel, pkt);
break;
case IPSSH_FXP_RMDIR:
rc = ipssh_msg_fxp_rmdir(channel, pkt);
break;
case IPSSH_FXP_MKDIR:
rc = ipssh_msg_fxp_mkdir(channel, pkt);
break;
case IPSSH_FXP_RENAME:
rc = ipssh_msg_fxp_rename(channel, pkt);
break;
/* Unsupported messages */
case IPSSH_FXP_FSTAT:
case IPSSH_FXP_FSETSTAT:
case IPSSH_FXP_READLINK:
case IPSSH_FXP_SYMLINK:
case IPSSH_FXP_EXTENDED:
case IPSSH_FXP_EXTENDED_REPLY:
if(ipssh_buffer_get_uint(pkt, &req_id) != IPCOM_SUCCESS)
{
ipcom_buffer_reset(pkt);
return IPCOM_ERR_FAILED;
}
else
{
IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: pkt type not supported: %d, ch_id: %d, req_id: %d",
pkt_type, channel->my_ch_id, req_id);
ipcom_buffer_reset(pkt);
return ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OP_UNSUPPORTED);
}
/* Messages a server shouldn't receive */
case IPSSH_FXP_ATTRS:
case IPSSH_FXP_VERSION:
case IPSSH_FXP_STATUS:
case IPSSH_FXP_NAME:
default:
if(ipssh_buffer_get_uint(pkt, &req_id) == IPCOM_SUCCESS)
{
(void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_BAD_MESSAGE);
IPCOM_LOG3(ERR, "ipssh_sftp_srv_pkt_dispatch :: unknown pkt type: %d, ch_id: %d, req_id: %d",
pkt_type, channel->my_ch_id, req_id);
}
IP_PANIC();
return IPCOM_ERR_FAILED;
}
return rc;
}
/*
*===========================================================================
* ipssh_mode_ipcom2ssh
*===========================================================================
* Description: Converts file mode bits from IPCOM values to SSH values.
*/
IP_GLOBAL Ip_mode_t
ipssh_mode_ipcom2ssh(Ip_mode_t mode)
{
Ip_mode_t ret;
/* Set access mode bits */
ret = mode & 0x1FF;
if(IP_S_ISDIR(mode))
ret |= IPSSH_S_IFDIR;
if(IP_S_ISREG(mode))
ret |= IPSSH_S_IFREG;
return ret;
}
/*
*===========================================================================
* ipssh_mode_ssh2ipcom
*===========================================================================
* Description: Converts file mode bits from SSH values to IPCOM values.
*/
IP_GLOBAL Ip_mode_t
ipssh_mode_ssh2ipcom(Ip_mode_t mode)
{
Ip_mode_t ret;
/* Set access mode bits */
ret = mode & 0x1FF;
if(mode & IPSSH_S_IFDIR)
ret |= IP_S_IFDIR;
if(mode & IPSSH_S_IFREG)
ret |= IP_S_IFREG;
return ret;
}
/*
*===========================================================================
* ipssh_unpack_attr
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_unpack_attr(Ipcom_buffer* buf, Ip_u32 attr_flags, struct Ip_stat* file_attr)
{
if(attr_flags & IPSSH_FILEXFER_ATTR_SIZE)
{
Ip_u32 len;
if(ipssh_buffer_get_uint(buf, &len) || /* hi */
ipssh_buffer_get_uint(buf, &len) ) /* lo */
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack size attribute");
return IPCOM_ERR_FAILED;
}
file_attr->st_size = len;
}
if(attr_flags & IPSSH_FILEXFER_ATTR_UIDGID)
{
Ip_u32 uid;
Ip_u32 gid;
if(ipssh_buffer_get_uint(buf, &uid) ||
ipssh_buffer_get_uint(buf, &gid) )
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack uid/gid attributes");
return IPCOM_ERR_FAILED;
}
}
if(attr_flags & IPSSH_FILEXFER_ATTR_PERMISSIONS )
{
Ip_u32 mode;
if(ipssh_buffer_get_uint(buf, &mode) )
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack perm attribute");
return IPCOM_ERR_FAILED;
}
file_attr->st_mode = ipssh_mode_ssh2ipcom((Ip_mode_t)mode);
}
if(attr_flags & IPSSH_FILEXFER_ATTR_ACMODTIME)
{
Ip_s32 atime, mtime;
if(ipssh_buffer_get_int(buf, &atime) ||
ipssh_buffer_get_int(buf, &mtime))
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack time attributes");
return IPCOM_ERR_FAILED;
}
file_attr->st_atime = ip_ntohl(atime);
file_attr->st_mtime = ip_ntohl(mtime);
}
if(attr_flags & IPSSH_FILEXFER_ATTR_EXTENDED)
{
Ip_u32 count;
char* type;
char* data;
if(ipssh_buffer_get_uint(buf, &count))
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext count");
return IPCOM_ERR_FAILED;
}
while(count)
{
type = ipssh_buffer_get_sshstr(buf);
if (type)
ipcom_free(type);
else
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext data");
return IPCOM_ERR_FAILED;
}
data = ipssh_buffer_get_sshstr(buf);
if (data)
ipcom_free(data);
else
{
IPCOM_LOG0(ERR, "ipssh_unpack_attr :: failed to unpack ext data");
return IPCOM_ERR_FAILED;
}
}
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_pack_attr
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_pack_attr(Ipcom_buffer* pkt, struct Ip_stat* buf, Ip_u32 flags)
{
if(ipcom_buffer_capacity(pkt) < 32)
return IPCOM_ERR_FAILED;
(void)ipssh_buffer_put_int(pkt, flags);
if(flags & IPSSH_FILEXFER_ATTR_SIZE)
{
/* 64 bits */
(void)ipcom_buffer_put_int(pkt, 0);
(void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_size));
}
if(flags & IPSSH_FILEXFER_ATTR_UIDGID)
{
(void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_uid));
(void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_gid));
}
if(flags & IPSSH_FILEXFER_ATTR_PERMISSIONS)
{
Ip_mode_t ssh_attr;
ssh_attr = ipssh_mode_ipcom2ssh(buf->st_mode);
(void)ipcom_buffer_put_int(pkt, ip_htonl(ssh_attr));
}
if(flags & IPSSH_FILEXFER_ATTR_ACMODTIME)
{
(void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_atime));
(void)ipcom_buffer_put_int(pkt, ip_htonl(buf->st_mtime));
}
return IPCOM_SUCCESS;
}
/*
*===========================================================================
* ipssh_channel_data_sftp_handler
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_channel_data_sftp_handler(Ipssh_channel* channel, Ipcom_buffer* msg)
{
Ip_u32 ch_data_len;
Ip_err rc;
/* Peel off channel data length */
if(ipssh_buffer_get_uint(msg, &ch_data_len))
return IPCOM_ERR_FAILED;
rc = ipssh_sftp_msg_handler(channel, msg);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_status
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_status(Ipssh_channel* channel,
Ip_u32 req_id,
Ip_u32 status)
{
Ipcom_buffer* pkt;
Ip_s32 rc;
IPCOM_LOG4(DEBUG, "ipssh_send_fxp_status :: my ch: %d, peer ch: %d, req_id: %d, status: %d",
channel->my_ch_id, channel->peer_ch_id, req_id, status);
pkt = ipssh_buffer_new_sftp_pkt(40, IPSSH_FXP_STATUS);
if(!pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_status :: out of memory");
return IPCOM_ERR_FAILED;
}
(void)ipcom_buffer_put_int(pkt, ip_htonl(req_id));
(void)ipcom_buffer_put_int(pkt, ip_htonl(status));
(void)ipssh_buffer_put_sshstr(pkt, ""); /* Empty error message */
(void)ipssh_buffer_put_sshstr(pkt, ""); /* Empty lang tag */
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_version
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_version(Ipssh_channel* channel)
{
Ip_err rc;
Ipcom_buffer* pkt;
IPCOM_LOG0(DEBUG, "ipssh_send_fxp_version");
pkt = ipssh_buffer_new_sftp_pkt(4, IPSSH_FXP_VERSION);
if( !pkt)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_version, failed to allocate packet");
return IPCOM_ERR_NO_MEMORY;
}
(void)ipssh_buffer_put_int(pkt, 3);
rc = ipssh_send_fxp_msg(channel, pkt);
ipcom_buffer_free(pkt);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_name
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_name(Ipssh_channel* channel,
Ip_u32 req_id,
char* path)
{
Ipcom_buffer* pkt_buf;
Ip_err rc;
IPCOM_LOG2(DEBUG, "ipssh_send_fxp_name :: my_ch_id: %d, req_id: %d", channel->my_ch_id, req_id);
pkt_buf = ipssh_buffer_new_sftp_pkt(64 + 2*ipcom_strlen(path), IPSSH_FXP_NAME); /* 21 + strlen path only required */
if(!pkt_buf)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_name :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipcom_buffer_put_int(pkt_buf, ip_htonl(req_id)) ||
ipcom_buffer_put_int(pkt_buf, ip_htonl(1)) || /* count */
ipssh_buffer_put_sshstr(pkt_buf, path) ||
ipssh_buffer_put_sshstr(pkt_buf, path) || /* Long name without long listing info */
ipcom_buffer_put_int(pkt_buf, ip_htonl(0)) ) /* attr, no attributes present */
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_name :: failed to pack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
rc = ipssh_send_fxp_msg(channel, pkt_buf);
cleanup:
if(pkt_buf)
ipcom_buffer_free(pkt_buf);
return rc;
}
/*
*===========================================================================
* ipssh_send_fxp_handle
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_send_fxp_handle(Ipssh_channel* channel,
Ip_u32 req_id,
Ipssh_sftp_ext_handle* handle,
Ip_u32 handle_len)
{
Ipcom_buffer* pkt_buf;
Ip_s32 rc;
IPCOM_LOG3(DEBUG, "ipssh_send_fxp_handle :: my_ch_id: %d, peer_ch_id: %d, req_id: %d",
channel->my_ch_id, channel->peer_ch_id, req_id);
pkt_buf = ipssh_buffer_new_sftp_pkt(64 + handle_len, IPSSH_FXP_HANDLE);
if(!pkt_buf)
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_handle :: out of memory");
return IPCOM_ERR_FAILED;
}
if(ipcom_buffer_put_int(pkt_buf, ip_htonl(req_id)) ||
ipssh_buffer_put_sshbuf(pkt_buf, (Ip_u8*)handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_send_fxp_handle :: failed to pack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
rc = ipssh_send_fxp_msg(channel, pkt_buf);
cleanup:
if(pkt_buf)
ipcom_buffer_free(pkt_buf);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_write
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_write(Ipssh_channel* channel,
Ipcom_buffer* in_pkt)
{
Ipssh_sftp_ext_handle* handle = IP_NULL;
Ip_s32 rc;
Ip_u32 handle_len;
Ipssh_sftp_int_handle* int_handle;
Ip_u32 req_id;
Ip_u64 offset;
Ip_u32 len;
Ip_s32 bytes_written;
/* Unpack message */
if(ipssh_buffer_get_uint(in_pkt, &req_id) ||
!(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(in_pkt, &handle_len)) ||
ipssh_buffer_get_u64(in_pkt, &offset) ||
ipssh_buffer_get_uint(in_pkt, &len) || ipcom_buffer_len(in_pkt) < len)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_write :: my_ch_id: %d, peer_ch_id: %d, req_id: %d, offset: %d, len: %d",
channel->my_ch_id, channel->peer_ch_id, req_id, IP_U64_GETLO(offset), len);
if(ipssh_validate_handle(handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: ipssh_validate_handle() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(!(int_handle = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_write :: ipssh_map_lookup() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(IP_U64_NEQUAL(offset,int_handle->write_pos))
{
ip_assert(int_handle->funcs->lseek_cb);
if(int_handle->funcs->lseek_cb(int_handle->fd, offset, IP_SEEK_SET) == -1)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_write :: lseek_cb() failed, %s", ipcom_strerror(ipcom_errno));
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
int_handle->write_pos = offset;
}
ip_assert(int_handle->funcs->write_cb);
bytes_written = (Ip_s32)int_handle->funcs->write_cb(int_handle->fd, (const char*)ipcom_buffer_start_ptr(in_pkt), (Ip_size_t)len); /*lint !e64*/
if(bytes_written != (Ip_s32)len)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_write :: write_cb() failed, %s", ipcom_strerror(ipcom_errno));
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
IP_U64_ADD32(int_handle->write_pos,bytes_written);
(void)ipcom_buffer_consume(in_pkt, len);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(handle)
ipcom_free(handle);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_read
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_read(Ipssh_channel* channel,
Ipcom_buffer* in_pkt)
{
Ipssh_sftp_ext_handle* handle = IP_NULL;
Ip_s32 rc = IPCOM_SUCCESS;
Ip_u32 handle_len;
Ipcom_buffer* out_pkt = IP_NULL;
Ipssh_sftp_int_handle* int_handle;
Ip_u32 req_id;
Ip_u64 offset;
Ip_u32 len;
int msg_size;
Ip_s32 bytes_read;
Ip_s32 bytes_requested;
Ip_u32 data_len_pos;
/* Unpack message */
if(ipssh_buffer_get_uint(in_pkt, &req_id) ||
!(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(in_pkt, &handle_len)) ||
ipssh_buffer_get_u64(in_pkt, &offset) ||
ipssh_buffer_get_uint(in_pkt, &len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_read :: my_ch_id: %d, peer_ch_id: %d, offset: %d, len: %d",
channel->my_ch_id, channel->peer_ch_id, IP_U64_GETLO(offset), len);
if(ipssh_validate_handle(handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_validate_handle() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(!(int_handle = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_map_lookup() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
/* Send IPSSH_FXP_DATA message */
if (channel->ssh_conn->flags & IPSSH_FLAG_BUG_UNRESTRICTED_SFTP_MSG_SIZE)
msg_size = len+8;
else
msg_size = IP_MIN(IPSSH_CH_MAX_PKT-5, len+8);
ip_assert(msg_size > 8);
out_pkt = ipssh_buffer_new_sftp_pkt(msg_size, IPSSH_FXP_DATA);
if(!out_pkt)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
(void)ipssh_buffer_put_int(out_pkt, req_id);
data_len_pos = (Ip_u32)ipcom_buffer_len(out_pkt);
(void)ipcom_buffer_decr_cap(out_pkt, 4);
if(IP_U64_NEQUAL(offset, int_handle->read_pos))
{
ip_assert(int_handle->funcs->lseek_cb);
if(int_handle->funcs->lseek_cb(int_handle->fd, offset, IP_SEEK_SET) == -1)
{
if(!int_handle->eof)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_read :: ipcom_lseek() failed, %d", ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
}
int_handle->read_pos = offset;
}
ip_assert(int_handle->funcs->read_cb);
bytes_requested = (Ip_s32)ipcom_buffer_capacity(out_pkt)-IPSSH_MAX_PAD; /* Reserve space for padding */
bytes_read = (Ip_s32)int_handle->funcs->read_cb(int_handle->fd,
(char*)ipcom_buffer_end_ptr(out_pkt),
bytes_requested);
if(bytes_read < 0 )
{
IPCOM_LOG1(INFO, "ipssh_msg_fxp_read :: read_cb() failed, errno=%d", ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(bytes_read == 0)
{
if(ipssh_send_fxp_status(channel, req_id, IPSSH_FX_EOF) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_read :: ipssh_send_fxp_status() failed");
rc = IPCOM_ERR_FAILED;
}
goto cleanup;
}
IP_U64_ADD32(int_handle->read_pos,bytes_read);
if(bytes_read < bytes_requested)
int_handle->eof = 1;
(void)ipcom_buffer_decr_cap(out_pkt, bytes_read);
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(out_pkt, data_len_pos), bytes_read); /*lint !e666*/
rc = ipssh_send_fxp_msg(channel, out_pkt);
cleanup:
if(handle)
ipcom_free(handle);
if(out_pkt)
ipcom_buffer_free(out_pkt);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_readdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_readdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ipssh_sftp_ext_handle* handle = IP_NULL;
Ip_s32 rc = IPCOM_SUCCESS;
Ip_u32 handle_len, count;
int count_pos;
Ipcom_buffer* out_pkt = IP_NULL;
Ipssh_sftp_int_handle* map_entry;
char* tmp_buf = IP_NULL;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) ||
!(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(pkt_buf, &handle_len)))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG2(DEBUG, "ipssh_msg_fxp_readdir :: my_ch_id: %d, peer_ch_id: %d",
channel->my_ch_id, channel->peer_ch_id);
if(ipssh_validate_handle(handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_validate_handle() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
out_pkt = ipssh_buffer_new_sftp_pkt(IPSSH_SFTP_READDIR_BUF_SIZE, IPSSH_FXP_NAME);
if(!out_pkt )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(!(map_entry = ipssh_map_lookup(&(channel->data.sftp.handle_map), handle->id)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_map_lookup() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(map_entry->eof)
{
if(ipssh_send_fxp_status(channel, req_id, IPSSH_FX_EOF) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: ipssh_send_fxp_status() failed");
rc = IPCOM_ERR_FAILED;
}
goto cleanup;
}
/* Construct FXP_NAME packet */
(void)ipssh_buffer_put_int(out_pkt, req_id);
count_pos = (int)ipcom_buffer_len(out_pkt);
(void)ipcom_buffer_decr_cap(out_pkt, 4);
count = 0;
tmp_buf = ipcom_malloc(256);
if(tmp_buf == IP_NULL)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: out of memory");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
ip_assert(map_entry->funcs->readdir_cb);
while( ipcom_buffer_capacity(out_pkt) > 256 )
{
struct Ip_stat file_stat;
struct Ip_dirent* dir_entry;
dir_entry = map_entry->funcs->readdir_cb(map_entry->dir);
if(!dir_entry)
{
map_entry->eof = 1;
break;
}
count++;
ipcom_strcpy(tmp_buf, map_entry->path);
if(tmp_buf[ipcom_strlen(tmp_buf)-1] != '/')
ipcom_strcat(tmp_buf, "/");
ipcom_strcat(tmp_buf, dir_entry->d_name);
if(!map_entry->funcs->stat_cb ||
map_entry->funcs->stat_cb(tmp_buf, &file_stat) != IPCOM_SUCCESS)
{
ipcom_memset(&file_stat, 0, sizeof(file_stat));
}
ipssh_make_long_name(map_entry->path, dir_entry->d_name, &file_stat, tmp_buf);
if(ipssh_buffer_put_sshstr(out_pkt, dir_entry->d_name) ||
ipssh_buffer_put_sshstr(out_pkt, tmp_buf) ||
ipssh_pack_stat_data(out_pkt, &file_stat))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_readdir :: failed to pack FXP_NAME packet");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
}
IPSSH_SET_32_PTR(ipcom_buffer_ptr2pos(out_pkt, count_pos), count); /*lint !e666*/
rc = ipssh_send_fxp_msg(channel, out_pkt);
cleanup:
if(handle)
ipcom_free(handle);
if(out_pkt)
ipcom_buffer_free(out_pkt);
if(tmp_buf)
ipcom_free(tmp_buf);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_close
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_close(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ipssh_sftp_ext_handle* handle = IP_NULL;
Ip_u32 req_id, handle_len;
Ip_s32 rc;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) ||
!(handle = (Ipssh_sftp_ext_handle*)ipssh_buffer_get_sshbuf(pkt_buf, &handle_len)))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_close :: failed to unpack message");
(void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_close, req_id: %d", req_id);
if(ipssh_validate_handle(handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_close :: ipssh_validate_handle() failed");
(void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_map_remove(&(channel->data.sftp.handle_map), handle->id) == IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
}
else
{
(void)ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
rc = IPCOM_ERR_FAILED;
}
cleanup:
if(handle)
ipcom_free(handle);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_lstat
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_lstat(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
char* path = IP_NULL;
Ip_u32 req_id;
Ip_s32 rc = IPCOM_ERR_FAILED;
Ipcom_buffer* out_pkt = IP_NULL;
struct Ip_stat buf;
Ipssh_sftp_func_cb* funcs;
char* int_path = IP_NULL;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path=ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: failed to unpack message");
goto cleanup;
}
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_lstat :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_lstat :: my_ch_id: %d, srv ch id: %d, req_id: %d, path=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, path);
funcs = ipssh_get_sftp_func_cb(int_path);
if(!funcs || !funcs->stat_cb)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: no stat callback");
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->stat_cb(int_path, &buf))
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_lstat :: stat_cb() failed, %s", ipcom_strerror(ipcom_errno));
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE);
goto cleanup;
}
out_pkt = ipssh_buffer_new_sftp_pkt(128, IPSSH_FXP_ATTRS);
if(!out_pkt)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_lstat :: out of memory");
goto cleanup;
}
(void)ipcom_buffer_put_int(out_pkt, ip_htonl(req_id));
(void)ipssh_pack_stat_data(out_pkt, &buf);
rc = ipssh_send_fxp_msg(channel, out_pkt);
cleanup:
if(path)
ipcom_free(path);
if(int_path)
ipcom_free(int_path);
if(out_pkt)
ipcom_buffer_free(out_pkt);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_setstat
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_setstat(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
char* path = IP_NULL;
Ip_u32 req_id;
Ip_u32 attr_flags;
Ip_s32 rc = IPCOM_ERR_FAILED;
struct Ip_stat file_attr;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path=ipssh_buffer_get_sshstr(pkt_buf)) ||
ipssh_buffer_get_uint(pkt_buf, &attr_flags) ||
ipssh_unpack_attr(pkt_buf, attr_flags, &file_attr))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_setstat :: failed to unpack message");
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_setstat :: my_ch_id: %d, srv ch id: %d, req_id: %d, path=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, path);
IPCOM_LOG0(INFO, "Received FXP_SETSTAT, ignoring");
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(path)
ipcom_free(path);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_rename
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_rename(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ip_s32 rc = IPCOM_ERR_FAILED;
char* oldpath = IP_NULL;
char* newpath = IP_NULL;
char* int_oldpath = IP_NULL;
char* int_newpath = IP_NULL;
Ipssh_sftp_func_cb* funcs;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(oldpath=ipssh_buffer_get_sshstr(pkt_buf)) ||
!(newpath=ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_rename :: failed to unpack message");
goto cleanup;
}
IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_rename :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, oldpath=%s, newpath=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, oldpath, newpath);
if (ipssh_make_ext_int_paths(oldpath, channel->ssh_conn->config.sftp_start_dir,
channel->ssh_conn->sh_ctx, IP_NULL, &int_oldpath) != IPCOM_SUCCESS ||
ipssh_make_ext_int_paths(newpath, channel->ssh_conn->config.sftp_start_dir,
channel->ssh_conn->sh_ctx, IP_NULL, &int_newpath) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_rename :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_REMOVE, int_oldpath, 0) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPEN, int_newpath, IPSSH_FXP_WRITE) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
funcs = ipssh_get_sftp_func_cb(int_oldpath);
if(!funcs || !funcs->rename_cb)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_rename :: no rename callback");
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->rename_cb(int_oldpath, int_newpath))
{
IPCOM_LOG3(ERR, "ipssh_msg_fxp_rename :: rename callback failed, oldpath: %s, newpath: %s, errno: %d",
int_oldpath, int_newpath, ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
else
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(newpath)
ipcom_free(newpath);
if(oldpath)
ipcom_free(oldpath);
if(int_newpath)
ipcom_free(int_newpath);
if(int_oldpath)
ipcom_free(int_oldpath);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_rmdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_rmdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ip_s32 rc = IPCOM_ERR_FAILED;
char* path = IP_NULL;
Ipssh_sftp_func_cb* funcs;
char* int_path = IP_NULL;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) || !(path=ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_rmdir :: failed to unpack message");
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_rmdir :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, path);
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_rmdir :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_RMDIR, int_path, 0) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
funcs = ipssh_get_sftp_func_cb(int_path);
if(!funcs || !funcs->rmdir_cb)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_rmdir :: failed to get rmdir callback for file %s", int_path);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->rmdir_cb(int_path) != 0 )
{
IPCOM_LOG2(ERR, "ipssh_msg_fxp_rmdir :: rmdir callback() failed for %s, errno: %d",
path, ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
}
else
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(path)
ipcom_free(path);
if(int_path)
ipcom_free(int_path);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_mkdir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_mkdir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ip_s32 rc = IPCOM_ERR_FAILED;
char* path = IP_NULL;
Ip_u32 attr_flags;
struct Ip_stat dir_attr;
Ipssh_sftp_func_cb* funcs;
char* int_path = IP_NULL;
/* Resonable default value in case message does not contain file attributes */
dir_attr.st_mode = 0777;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path=ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_mkdir :: failed to unpack message");
goto cleanup;
}
if(ipcom_buffer_len(pkt_buf) >= 4)
{
if(ipssh_buffer_get_uint(pkt_buf, &attr_flags) || ipssh_unpack_attr(pkt_buf, attr_flags, &dir_attr))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_mkdir :: failed to unpack file attributes");
goto cleanup;
}
}
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_mkdir :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_mkdir :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, int_path);
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_MKDIR, int_path, 0) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
funcs = ipssh_get_sftp_func_cb(int_path);
if(!funcs || !funcs->mkdir_cb)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_mkdir :: failed to get mkdir callback for file %s", int_path);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->mkdir_cb(int_path, dir_attr.st_mode) != 0 )
{
IPCOM_LOG2(ERR, "ipssh_msg_fxp_mkdir :: ipcom_mkdir() failed for %s, errno: %d",
int_path, ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
}
else
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(path)
ipcom_free(path);
if(int_path)
ipcom_free(int_path);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_remove
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_remove(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ip_s32 rc = IPCOM_ERR_FAILED;
char* path = IP_NULL;
char* int_path = IP_NULL;
Ipssh_sftp_func_cb* funcs;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path=ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_remove :: failed to unpack message");
goto cleanup;
}
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &int_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_remove :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG4(DEBUG, "ipssh_msg_fxp_remove :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s",
channel->my_ch_id, channel->peer_ch_id, req_id, int_path);
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_REMOVE, int_path, 0) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
funcs = ipssh_get_sftp_func_cb(int_path);
if(!funcs || !funcs->unlink_cb)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_remove :: no unlink callback: %s", int_path);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->unlink_cb(int_path) != 0 )
{
IPCOM_LOG2(ERR, "ipssh_msg_fxp_remove :: ipcom_unlink() failed for %s, errno: %d",
int_path, ipcom_errno);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
}
else
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_OK);
cleanup:
if(path)
ipcom_free(path);
if(int_path)
ipcom_free(int_path);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_open
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_open(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
Ip_u32 flags;
Ip_u32 attr_flags = 0;
Ip_u32 x_handle_len;
Ipssh_sftp_int_handle* handle;
Ipssh_sftp_ext_handle* x_handle = IP_NULL;
struct Ip_stat file_stat;
char* path = IP_NULL;
Ip_s32 rc = IPCOM_ERR_FAILED;
ipcom_memset(&file_stat, 0, sizeof(struct Ip_stat));
file_stat.st_mode = 0644; /* Set a reasonable default value (-rw-r--r--), in case client sends no file privs */
if(!(handle = ipcom_malloc(sizeof(Ipssh_sftp_int_handle))) )
{
IPCOM_LOG0(ERR,"ipssh_msg_fxp_open :: out of memory");
return IPCOM_ERR_FAILED;
}
ipcom_memset(handle, 0, sizeof(Ipssh_sftp_int_handle));
handle->fd = -1;
handle->type = IPSSH_FILE_HANDLE;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path=ipssh_buffer_get_sshstr(pkt_buf)) ||
ipssh_buffer_get_uint(pkt_buf, &flags) ||
ipssh_buffer_get_uint(pkt_buf, &attr_flags) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: failed to unpack message");
goto cleanup;
}
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &handle->path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_open :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG5(DEBUG, "ipssh_msg_fxp_open :: my_ch_id: %d, peer_ch_id: %d, req_id:%d, path=%s, flags: %d",
channel->my_ch_id, channel->peer_ch_id, req_id, handle->path, flags); /*lint !e644*/
/* Unpack attr according to attr_flag */
if(attr_flags != 0 )
{
if(ipssh_unpack_attr(pkt_buf, attr_flags, &file_stat) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: failed to unpack attrs");
goto cleanup;
}
}
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPEN, handle->path, flags) != IPCOM_SUCCESS) /*lint !e644*/
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
if(ipssh_create_handle(&(x_handle), &(x_handle_len)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_create_handle failed");
goto cleanup;
}
handle->id = x_handle->id;
/* Set up function pointers */
handle->funcs = ipssh_get_sftp_func_cb(handle->path);
if(!handle->funcs || !handle->funcs->open_cb || !handle->funcs->close_cb || !handle->funcs->stat_cb
|| !handle->funcs->read_cb || !handle->funcs->write_cb || !handle->funcs->lseek_cb
|| !handle->funcs->opendir_cb || !handle->funcs->closedir_cb || !handle->funcs->readdir_cb
|| !handle->funcs->mkdir_cb || !handle->funcs->rmdir_cb || !handle->funcs->unlink_cb || !handle->funcs->rename_cb)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_open :: failed to get callbacks for file: %s", handle->path);
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
handle->fd = handle->funcs->open_cb(handle->path, ipssh_conv_flags(flags), file_stat.st_mode);
if(handle->fd == -1)
{
IPCOM_LOG2(ERR, "ipssh_msg_fxp_open :: open() failed for file: %s, %s",
handle->path, ipcom_strerror(ipcom_errno));
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(ipssh_map_insert(&(channel->data.sftp.handle_map), handle))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_map_insert failed");
goto cleanup;
}
handle = IP_NULL;
if(ipssh_send_fxp_handle(channel, req_id, x_handle, x_handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_open :: ipssh_send_fxp_handle() failed");
goto cleanup;
}
rc = IPCOM_SUCCESS;
cleanup:
if (path)
ipcom_free(path);
if(handle)
{
if(handle->path)
ipcom_free(handle->path);
if(handle->fd != -1)
{
ip_assert(handle->funcs->close_cb);
handle->funcs->close_cb(handle->fd); /*lint !e522*/
}
ipcom_free(handle);
}
if(x_handle)
ipcom_free(x_handle);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_opendir
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_opendir(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ipssh_sftp_int_handle* entry;
Ipssh_sftp_ext_handle* handle = IP_NULL;
Ip_u32 handle_len = 0;
Ip_s32 rc = IPCOM_SUCCESS;
Ip_u32 req_id;
char* path = IP_NULL;
IPCOM_LOG0(DEBUG, "ipssh_msg_fxp_opendir");
if(!(entry = ipcom_malloc(sizeof(Ipssh_sftp_int_handle))) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: out of memory");
return IPCOM_ERR_FAILED;
}
ipcom_memset(entry, 0, sizeof(Ipssh_sftp_int_handle));
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &(req_id)) ||
!(path = ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, IP_NULL, &entry->path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_make_ext_int_paths failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(ipssh_sftp_check_access_cb(channel->ssh_conn->user, IPSSH_FXP_OPENDIR, entry->path, 0) != IPCOM_SUCCESS)
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_PERMISSION_DENIED);
goto cleanup;
}
entry->funcs = ipssh_get_sftp_func_cb(entry->path);
if(!entry->funcs || !entry->funcs->open_cb || !entry->funcs->close_cb || !entry->funcs->stat_cb
|| !entry->funcs->read_cb || !entry->funcs->write_cb || !entry->funcs->lseek_cb
|| !entry->funcs->opendir_cb || !entry->funcs->closedir_cb || !entry->funcs->readdir_cb
|| !entry->funcs->mkdir_cb || !entry->funcs->rmdir_cb || !entry->funcs->unlink_cb || !entry->funcs->rename_cb)
{
IPCOM_LOG1(ERR, "ipssh_msg_fxp_opendir :: failed to get callbacks for %s", entry->path);
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
if(!(entry->dir = entry->funcs->opendir_cb(entry->path)))
{
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE);
goto cleanup;
}
entry->type = IPSSH_DIR_HANDLE;
if(ipssh_create_handle(&(handle), &(handle_len)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_create_handle failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
entry->id = handle->id;
if(ipssh_map_insert(&(channel->data.sftp.handle_map), entry))
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_map_insert failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
entry = IP_NULL;
if(ipssh_send_fxp_handle(channel, req_id, handle, handle_len) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_opendir :: ipssh_send_fxp_handle() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
cleanup:
if (path)
ipcom_free(path);
if (entry)
{
if (entry->path)
ipcom_free(entry->path);
ipcom_free(entry);
}
if (handle)
ipcom_free(handle);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_realpath
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_realpath(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 req_id;
char * path = IP_NULL;
struct Ip_stat buf;
Ipssh_sftp_func_cb* funcs;
Ip_s32 rc = IPCOM_SUCCESS;
char* int_path = IP_NULL;
char* ext_path = IP_NULL;
/* Unpack message */
if(ipssh_buffer_get_uint(pkt_buf, &req_id) ||
!(path = ipssh_buffer_get_sshstr(pkt_buf)) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: failed to unpack message");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
IPCOM_LOG3(DEBUG, "ipssh_msg_fxp_realpath :: my_ch_id: %d, req_id:%d, path: %s",
channel->my_ch_id, req_id, path);
if (ipssh_make_ext_int_paths(path, channel->ssh_conn->config.sftp_start_dir, channel->ssh_conn->sh_ctx, &ext_path, &int_path) != IPCOM_SUCCESS)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: ipssh_make_ext_int_paths failed");
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_PATH);
goto cleanup;
}
/* Provide the absolute path in case custom routine requires it */
funcs = ipssh_get_sftp_func_cb(int_path);
if(!funcs)
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: no stat callback");
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_FAILURE);
goto cleanup;
}
if(funcs->stat_cb(int_path, &buf))
{
IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_realpath :: stat_cb() failed, %s", ipcom_strerror(ipcom_errno));
rc = ipssh_send_fxp_status(channel, req_id, IPSSH_FX_NO_SUCH_FILE);
goto cleanup;
}
if(ipssh_send_fxp_name(channel, req_id, ext_path) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_realpath :: ipssh_send_fxp_name() failed");
rc = IPCOM_ERR_FAILED;
goto cleanup;
}
cleanup:
if(path)
ipcom_free(path);
if(int_path)
ipcom_free(int_path);
if(ext_path)
ipcom_free(ext_path);
return rc;
}
/*
*===========================================================================
* ipssh_msg_fxp_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL Ip_err
ipssh_msg_fxp_init(Ipssh_channel* channel,
Ipcom_buffer* pkt_buf)
{
Ip_u32 version;
IPCOM_LOG1(DEBUG, "ipssh_msg_fxp_init :: my_ch_id:%d", channel->my_ch_id);
if(ipssh_buffer_get_uint(pkt_buf, &version) )
{
IPCOM_LOG0(ERR, "ipssh_msg_fxp_init :: ipssh_buffer_get_uint() failed");
return IPCOM_ERR_FAILED;
}
IPCOM_LOG2(DEBUG, "ipssh_msg_fxp_init :: my_ch_id:%d, version: %d", channel->my_ch_id, version);
return ipssh_send_fxp_version(channel);
}
/*
*===========================================================================
* ipssh_sftp_cleanup
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_sftp_cleanup(Ipssh_channel* channel)
{
Ipssh_sftp_int_handle* handle;
if(channel->data.sftp.msg)
ipcom_buffer_free(channel->data.sftp.msg);
for(handle = (Ipssh_sftp_int_handle*)ipcom_list_first(&channel->data.sftp.handle_map);
handle;
handle = (Ipssh_sftp_int_handle*)ipcom_list_first(&channel->data.sftp.handle_map))
{
ipcom_list_remove(&handle->list);
/* check whether a file or directory was opened */
if (handle->type == IPSSH_DIR_HANDLE)
{
if (handle->funcs->closedir_cb)
handle->funcs->closedir_cb(handle->dir);
else
IP_PANIC();
}
else if (handle->type == IPSSH_FILE_HANDLE)
{
if (handle->funcs->close_cb)
handle->funcs->close_cb(handle->fd);
else
IP_PANIC();
}
else
IP_PANIC();
if(handle->path)
ipcom_free (handle->path);
ipcom_free(handle);
}
}
#ifdef IPSSH_USE_SFTP
/*
*===========================================================================
* ipssh_channel_sftp_init
*===========================================================================
* Description:
* Parameters:
* Returns:
*
*/
IP_GLOBAL void
ipssh_channel_sftp_init(Ipssh_channel* channel, int ch_type)
{
channel->type = ch_type;
channel->handler = ipssh_channel_data_sftp_handler;
ipcom_list_init(&channel->data.sftp.handle_map);
}
#endif
IP_GLOBAL Ip_err
ipssh_sftp_subsystem_init_cb(Ipssh_channel* channel, Ip_fd* sock)
{
*sock = IP_INVALID_SOCKET;
if(!channel->ssh_conn->config.sftp)
{
IPCOM_LOG0(INFO, "channel request :: sftp session requested, but sftp service not active");
return IPCOM_ERR_FAILED;
}
ipssh_channel_sftp_init(channel, IPSSH_CH_TYPE_SFTP_SRV);
channel->ssh_conn->conn_state = IPSSH_CONN_STATE_SRV_SSH_SFTP;
return IPCOM_SUCCESS;
}
#else
int ipssh_sftp_empty_file;
#endif /* IPSSH_USE_SFTP */
/*
****************************************************************************
* END OF FILE
****************************************************************************
*/