mirror of https://github.com/wolfSSL/wolfssh.git
971 lines
26 KiB
C
971 lines
26 KiB
C
/* port.c
|
|
*
|
|
* Copyright (C) 2014-2026 wolfSSL Inc.
|
|
*
|
|
* This file is part of wolfSSH.
|
|
*
|
|
* wolfSSH is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 3 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* wolfSSH 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 General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with wolfSSH. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
|
|
/*
|
|
* The port module wraps standard C library functions with macros to
|
|
* cover portablility issues when building in environments that rename
|
|
* those functions. This module also provides local versions of some
|
|
* standard C library functions that are missing on some platforms.
|
|
*/
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif
|
|
|
|
|
|
#include <wolfssh/port.h>
|
|
#if !defined(USE_WINDOWS_API) && !defined(FREESCALE_MQX)
|
|
#include <stdio.h>
|
|
#endif
|
|
|
|
/*
|
|
Flags:
|
|
WOLFSSH_LOCAL_PREAD_PWRITE
|
|
Defined to use local implementations of pread() and pwrite(). Switched
|
|
on automatically if pread() or pwrite() aren't found by configure.
|
|
*/
|
|
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM) && \
|
|
!defined(WOLFSSH_ZEPHYR)
|
|
#if defined(MICROCHIP_MPLAB_HARMONY)
|
|
int wfopen(WFILE* f, const char* filename, SYS_FS_FILE_OPEN_ATTRIBUTES mode)
|
|
{
|
|
if (f != NULL) {
|
|
*f = SYS_FS_FileOpen(filename, mode);
|
|
if (*f == WBADFILE) {
|
|
WLOG(WS_LOG_SFTP, "Failed to open file %s", filename);
|
|
return 1;
|
|
}
|
|
else {
|
|
WLOG(WS_LOG_SFTP, "Opened file %s", filename);
|
|
return 0;
|
|
}
|
|
}
|
|
return 1;
|
|
}
|
|
#else
|
|
int wfopen(WFILE** f, const char* filename, const char* mode)
|
|
{
|
|
#ifdef USE_WINDOWS_API
|
|
return fopen_s(f, filename, mode) != 0;
|
|
#elif defined(WOLFSSL_NUCLEUS)
|
|
int m = WOLFSSH_O_CREAT;
|
|
|
|
if (WSTRSTR(mode, "r") && WSTRSTR(mode, "w")) {
|
|
m |= WOLFSSH_O_RDWR;
|
|
}
|
|
else {
|
|
if (WSTRSTR(mode, "r")) {
|
|
m |= WOLFSSH_O_RDONLY;
|
|
}
|
|
if (WSTRSTR(mode, "w")) {
|
|
m |= WOLFSSH_O_WRONLY;
|
|
}
|
|
}
|
|
|
|
if (filename != NULL && f != NULL) {
|
|
if ((**f = WOPEN(ssh->fs, filename, m, 0)) < 0) {
|
|
return **f;
|
|
}
|
|
|
|
/* fopen defaults to normal */
|
|
if (NU_Set_Attributes(filename, 0) != NU_SUCCESS) {
|
|
WCLOSE(ssh->fs, **f);
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
else {
|
|
return 1;
|
|
}
|
|
#else
|
|
if (f != NULL) {
|
|
*f = fopen(filename, mode);
|
|
return *f == NULL;
|
|
}
|
|
return 1;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
/* If either pread() or pwrite() are missing, use the local versions. */
|
|
#if (defined(USE_OSE_API) || \
|
|
!defined(HAVE_DECL_PREAD) || (HAVE_DECL_PREAD == 0) || \
|
|
!defined(HAVE_DECL_PWRITE) || (HAVE_DECL_PWRITE == 0))
|
|
#undef WOLFSSH_LOCAL_PREAD_PWRITE
|
|
#define WOLFSSH_LOCAL_PREAD_PWRITE
|
|
#endif
|
|
|
|
|
|
#if (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP)) && \
|
|
!defined(NO_WOLFSSH_SERVER)
|
|
|
|
#if defined(USE_WINDOWS_API) || defined(WOLFSSL_NUCLEUS) || \
|
|
defined(FREESCALE_MQX) || defined(WOLFSSH_ZEPHYR)
|
|
|
|
/* This is current inline in the source. */
|
|
|
|
#elif defined(MICROCHIP_MPLAB_HARMONY)
|
|
int wPwrite(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (WFSEEK_SUCCESS(WFSEEK(NULL, &fd, (int32_t)offset,
|
|
SYS_FS_SEEK_SET))) {
|
|
ret = (int)WFWRITE(NULL, buf, 1, sz, &fd);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wPread(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (WFSEEK_SUCCESS(WFSEEK(NULL, &fd, (int32_t)offset,
|
|
SYS_FS_SEEK_SET))) {
|
|
ret = (int)WFREAD(NULL, buf, 1, sz, &fd);
|
|
}
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#elif defined(WOLFSSH_LOCAL_PREAD_PWRITE)
|
|
|
|
int wPwrite(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (lseek(fd, (off_t)offset, SEEK_SET) != (off_t)-1)
|
|
ret = (int)write(fd, buf, sz);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int wPread(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (lseek(fd, (off_t)offset, SEEK_SET) != (off_t)-1)
|
|
ret = (int)read(fd, buf, sz);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
#else /* USE_WINDOWS_API WOLFSSH_LOCAL_PREAD_PWRITE */
|
|
|
|
int wPwrite(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) != 0)
|
|
return -1;
|
|
|
|
return (int)pwrite(fd, buf, sz, (off_t)offset);
|
|
}
|
|
|
|
|
|
int wPread(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
|
|
if (wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) != 0)
|
|
return -1;
|
|
|
|
return (int)pread(fd, buf, sz, (off_t)offset);
|
|
}
|
|
|
|
#endif /* USE_WINDOWS_API USE_OSE_API */
|
|
#endif /* WOLFSSH_SFTP WOLFSSH_SCP NO_WOLFSSH_SERVER */
|
|
|
|
#endif /* !NO_FILESYSTEM */
|
|
|
|
|
|
#if defined(USE_WINDOWS_API) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
|
|
|
|
/*
|
|
* SFTP paths all start with a leading root "/". Most Windows file routines
|
|
* expect a drive letter when dealing with an absolute path. If the provided
|
|
* path, f, is of the form "/C:...", adjust the pointer f to point to the "C",
|
|
* and decrement the file path size, fSz, by one.
|
|
*
|
|
* @param f pointer to a file name
|
|
* @param fSz size of f in bytes
|
|
* @return pointer to somewhere in f
|
|
*/
|
|
static const char* TrimFileName(const char* f, size_t* fSz)
|
|
{
|
|
if (f != NULL && fSz != NULL && *fSz >= 3 && f[0] == '/' && f[2] == ':') {
|
|
f++;
|
|
(*fSz)--;
|
|
}
|
|
return f;
|
|
}
|
|
|
|
void* WS_CreateFileA(const char* fileName, unsigned long desiredAccess,
|
|
unsigned long shareMode, unsigned long creationDisposition,
|
|
unsigned long flags, void* heap)
|
|
{
|
|
HANDLE fileHandle = INVALID_HANDLE_VALUE;
|
|
wchar_t* unicodeFileName;
|
|
size_t unicodeFileNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t fileNameSz = 0;
|
|
errno_t error;
|
|
|
|
fileNameSz = WSTRLEN(fileName);
|
|
fileName = TrimFileName(fileName, &fileNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0);
|
|
if (error)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
unicodeFileName = (wchar_t*)WMALLOC((unicodeFileNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeFileName == NULL)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeFileName, unicodeFileNameSz,
|
|
fileName, fileNameSz);
|
|
|
|
if (!error)
|
|
fileHandle = CreateFileW(unicodeFileName, desiredAccess, shareMode,
|
|
NULL, creationDisposition, flags, NULL);
|
|
|
|
WFREE(unicodeFileName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return (void*)(error ? INVALID_HANDLE_VALUE : fileHandle);
|
|
}
|
|
|
|
void* WS_FindFirstFileA(const char* fileName,
|
|
char* realFileName, size_t realFileNameSz, int* isDir, void* heap)
|
|
{
|
|
HANDLE findHandle = INVALID_HANDLE_VALUE;
|
|
WIN32_FIND_DATAW findFileData;
|
|
wchar_t* unicodeFileName;
|
|
size_t unicodeFileNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t fileNameSz = 0;
|
|
errno_t error;
|
|
|
|
fileNameSz = WSTRLEN(fileName);
|
|
fileName = TrimFileName(fileName, &fileNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0);
|
|
if (error)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
unicodeFileName = (wchar_t*)WMALLOC((unicodeFileNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeFileName == NULL)
|
|
return INVALID_HANDLE_VALUE;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeFileName, unicodeFileNameSz,
|
|
fileName, fileNameSz);
|
|
|
|
if (!error)
|
|
findHandle = FindFirstFileW(unicodeFileName, &findFileData);
|
|
|
|
WFREE(unicodeFileName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
if (findHandle != INVALID_HANDLE_VALUE) {
|
|
error = wcstombs_s(NULL, realFileName, realFileNameSz,
|
|
findFileData.cFileName, realFileNameSz);
|
|
|
|
if (isDir != NULL) {
|
|
*isDir =
|
|
(findFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
|
|
}
|
|
}
|
|
|
|
return (void*)findHandle;
|
|
}
|
|
|
|
|
|
int WS_FindNextFileA_ex(void* findHandle,
|
|
char* realFileName, size_t realFileNameSz, unsigned long* lastError)
|
|
{
|
|
BOOL success;
|
|
WIN32_FIND_DATAW findFileData;
|
|
unsigned long err = 0;
|
|
|
|
success = FindNextFileW((HANDLE)findHandle, &findFileData);
|
|
|
|
if (success) {
|
|
if (wcstombs_s(NULL, realFileName, realFileNameSz,
|
|
findFileData.cFileName, realFileNameSz) != 0)
|
|
err = (unsigned long)ERROR_NO_UNICODE_TRANSLATION;
|
|
}
|
|
else {
|
|
err = (unsigned long)GetLastError();
|
|
}
|
|
|
|
if (lastError != NULL)
|
|
*lastError = err;
|
|
|
|
return (success != 0) && (err == 0);
|
|
}
|
|
|
|
|
|
int WS_FindNextFileA(void* findHandle,
|
|
char* realFileName, size_t realFileNameSz)
|
|
{
|
|
return WS_FindNextFileA_ex(findHandle, realFileName, realFileNameSz, NULL);
|
|
}
|
|
|
|
|
|
int WS_GetFileAttributesExA(const char* fileName, void* fileInfo, void* heap)
|
|
{
|
|
BOOL success = 0;
|
|
wchar_t* unicodeFileName;
|
|
size_t unicodeFileNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t fileNameSz = 0;
|
|
errno_t error;
|
|
|
|
fileNameSz = WSTRLEN(fileName);
|
|
fileName = TrimFileName(fileName, &fileNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0);
|
|
if (error != 0)
|
|
return 0;
|
|
|
|
unicodeFileName = (wchar_t*)WMALLOC((unicodeFileNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeFileName == NULL)
|
|
return 0;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeFileName, unicodeFileNameSz,
|
|
fileName, fileNameSz);
|
|
|
|
if (error == 0) {
|
|
success = GetFileAttributesExW(unicodeFileName,
|
|
GetFileExInfoStandard, fileInfo);
|
|
}
|
|
|
|
WFREE(unicodeFileName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return success != 0;
|
|
}
|
|
|
|
|
|
int WS_RemoveDirectoryA(const char* dirName, void* heap)
|
|
{
|
|
BOOL success = 0;
|
|
wchar_t* unicodeDirName;
|
|
size_t unicodeDirNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t dirNameSz = 0;
|
|
errno_t error;
|
|
|
|
dirNameSz = WSTRLEN(dirName);
|
|
dirName = TrimFileName(dirName, &dirNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeDirNameSz, NULL, 0, dirName, 0);
|
|
if (error != 0)
|
|
return 0;
|
|
|
|
unicodeDirName = (wchar_t*)WMALLOC((unicodeDirNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeDirName == NULL)
|
|
return 0;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeDirName, unicodeDirNameSz,
|
|
dirName, dirNameSz);
|
|
|
|
if (error == 0) {
|
|
success = RemoveDirectoryW(unicodeDirName);
|
|
}
|
|
|
|
WFREE(unicodeDirName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return success != 0;
|
|
}
|
|
|
|
|
|
int WS_CreateDirectoryA(const char* dirName, void* heap)
|
|
{
|
|
BOOL success = 0;
|
|
wchar_t* unicodeDirName;
|
|
size_t unicodeDirNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t dirNameSz = 0;
|
|
errno_t error;
|
|
|
|
dirNameSz = WSTRLEN(dirName);
|
|
dirName = TrimFileName(dirName, &dirNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeDirNameSz, NULL, 0, dirName, 0);
|
|
if (error != 0)
|
|
return 0;
|
|
|
|
unicodeDirName = (wchar_t*)WMALLOC((unicodeDirNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeDirName == NULL)
|
|
return 0;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeDirName, unicodeDirNameSz,
|
|
dirName, dirNameSz);
|
|
|
|
if (error == 0) {
|
|
success = CreateDirectoryW(unicodeDirName, NULL);
|
|
}
|
|
|
|
WFREE(unicodeDirName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return success != 0;
|
|
}
|
|
|
|
|
|
int WS_MoveFileA(const char* oldName, const char* newName, void* heap)
|
|
{
|
|
BOOL success = 0;
|
|
wchar_t* unicodeOldName;
|
|
wchar_t* unicodeNewName;
|
|
size_t unicodeOldNameSz = 0;
|
|
size_t unicodeNewNameSz = 0;
|
|
size_t oldNameSz = 0;
|
|
size_t newNameSz = 0;
|
|
size_t returnSz = 0;
|
|
errno_t error;
|
|
|
|
oldNameSz = WSTRLEN(oldName);
|
|
oldName = TrimFileName(oldName, &oldNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeOldNameSz, NULL, 0, oldName, 0);
|
|
if (error != 0)
|
|
return 0;
|
|
|
|
unicodeOldName = (wchar_t*)WMALLOC((unicodeOldNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeOldName == NULL)
|
|
return 0;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeOldName, unicodeOldNameSz,
|
|
oldName, oldNameSz);
|
|
if (error != 0) {
|
|
WFREE(unicodeOldName, heap, PORT_DYNTYPE_STRING);
|
|
return 0;
|
|
}
|
|
|
|
newNameSz = WSTRLEN(newName);
|
|
newName = TrimFileName(newName, &newNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeNewNameSz, NULL, 0, newName, 0);
|
|
if (error != 0) {
|
|
WFREE(unicodeOldName, heap, PORT_DYNTYPE_STRING);
|
|
return 0;
|
|
}
|
|
|
|
unicodeNewName = (wchar_t*)WMALLOC((unicodeNewNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeNewName == NULL) {
|
|
WFREE(unicodeOldName, heap, PORT_DYNTYPE_STRING);
|
|
return 0;
|
|
}
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeNewName, unicodeNewNameSz,
|
|
newName, newNameSz);
|
|
|
|
if (error == 0) {
|
|
success = MoveFileW(unicodeOldName, unicodeNewName);
|
|
}
|
|
|
|
WFREE(unicodeOldName, heap, PORT_DYNTYPE_STRING);
|
|
WFREE(unicodeNewName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return success != 0;
|
|
}
|
|
|
|
|
|
int WS_DeleteFileA(const char* fileName, void* heap)
|
|
{
|
|
BOOL success = 0;
|
|
wchar_t* unicodeFileName;
|
|
size_t unicodeFileNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t fileNameSz = 0;
|
|
errno_t error;
|
|
|
|
fileNameSz = WSTRLEN(fileName);
|
|
fileName = TrimFileName(fileName, &fileNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0);
|
|
if (error != 0)
|
|
return 0;
|
|
|
|
unicodeFileName = (wchar_t*)WMALLOC((unicodeFileNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeFileName == NULL)
|
|
return 0;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeFileName, unicodeFileNameSz,
|
|
fileName, fileNameSz);
|
|
|
|
if (error == 0) {
|
|
success = DeleteFileW(unicodeFileName);
|
|
}
|
|
|
|
WFREE(unicodeFileName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return success != 0;
|
|
}
|
|
|
|
|
|
|
|
#ifndef _WIN32_WCE
|
|
|
|
int WS_ChmodA(const char* fileName, int mode, void* heap)
|
|
{
|
|
int ret = -1;
|
|
wchar_t* unicodeFileName;
|
|
size_t unicodeFileNameSz = 0;
|
|
size_t returnSz = 0;
|
|
size_t fileNameSz = 0;
|
|
errno_t error;
|
|
|
|
fileNameSz = WSTRLEN(fileName);
|
|
fileName = TrimFileName(fileName, &fileNameSz);
|
|
|
|
error = mbstowcs_s(&unicodeFileNameSz, NULL, 0, fileName, 0);
|
|
if (error != 0)
|
|
return -1;
|
|
|
|
unicodeFileName = (wchar_t*)WMALLOC((unicodeFileNameSz+1)*sizeof(wchar_t),
|
|
heap, PORT_DYNTYPE_STRING);
|
|
if (unicodeFileName == NULL)
|
|
return -1;
|
|
|
|
error = mbstowcs_s(&returnSz, unicodeFileName, unicodeFileNameSz,
|
|
fileName, fileNameSz);
|
|
|
|
if (error == 0) {
|
|
ret = _wchmod(unicodeFileName, mode);
|
|
}
|
|
|
|
WFREE(unicodeFileName, heap, PORT_DYNTYPE_STRING);
|
|
|
|
return ret;
|
|
}
|
|
|
|
#endif /* !_WIN32_WCE */
|
|
|
|
#endif /* USE_WINDOWS_API WOLFSSH_SFTP WOLFSSH_SCP */
|
|
|
|
#if !defined(NO_FILESYSTEM) && \
|
|
defined(WOLFSSH_ZEPHYR) && (defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
|
|
|
|
int wssh_z_fstat(const char *p, struct fs_dirent *b)
|
|
{
|
|
size_t p_len;
|
|
|
|
if (p == NULL || b == NULL)
|
|
return -1;
|
|
|
|
p_len = WSTRLEN(p);
|
|
/* Detect if origin directory when it ends in ':' or ':/' */
|
|
if (p_len >= 3 && (p[p_len-1] == ':' ||
|
|
(p[p_len-1] == '/' && p[p_len-2] == ':'))) {
|
|
b->type = FS_DIR_ENTRY_DIR;
|
|
b->size = 0;
|
|
b->name[0] = '/';
|
|
b->name[1] = '\0';
|
|
return 0;
|
|
}
|
|
else
|
|
return fs_stat(p, b);
|
|
}
|
|
|
|
int z_fs_chdir(const char *path)
|
|
{
|
|
/* Just make sure that the path exists and is a directory */
|
|
struct fs_dirent dir;
|
|
int ret;
|
|
|
|
ret = wssh_z_fstat(path, &dir);
|
|
if (ret != 0 || dir.type != FS_DIR_ENTRY_DIR)
|
|
ret = -1;
|
|
return ret;
|
|
}
|
|
|
|
static struct {
|
|
byte open:1;
|
|
WFILE zfp;
|
|
} z_fds[WOLFSSH_MAX_DESCIPRTORS];
|
|
static wolfSSL_Mutex z_fds_mutex;
|
|
static int z_fds_setup = 0;
|
|
|
|
int wssh_z_fds_init(void)
|
|
{
|
|
int ret = 0;
|
|
if (!z_fds_setup) {
|
|
WMEMSET(z_fds, 0, sizeof(z_fds));
|
|
ret = wc_InitMutex(&z_fds_mutex);
|
|
if (ret == 0)
|
|
z_fds_setup = 1;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int wssh_z_fds_cleanup(void)
|
|
{
|
|
int ret = 0;
|
|
if (z_fds_setup) {
|
|
WMEMSET(z_fds, 0, sizeof(z_fds));
|
|
ret = wc_FreeMutex(&z_fds_mutex);
|
|
z_fds_setup = 0;
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
WFD wssh_z_open(const char *p, int f)
|
|
{
|
|
WFD ret = -1;
|
|
if (p != NULL) {
|
|
if (wc_LockMutex(&z_fds_mutex) == 0) {
|
|
WFD idx = 0;
|
|
/* find a free fd */
|
|
while(idx < WOLFSSH_MAX_DESCIPRTORS && z_fds[idx].open)
|
|
idx++;
|
|
if (idx < WOLFSSH_MAX_DESCIPRTORS) {
|
|
/* found a free fd */
|
|
fs_file_t_init(&z_fds[idx].zfp);
|
|
if (fs_open(&z_fds[idx].zfp, p, f) == 0) {
|
|
z_fds[idx].open = 1;
|
|
ret = idx;
|
|
}
|
|
}
|
|
wc_UnLockMutex(&z_fds_mutex);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int wssh_z_close(WFD fd)
|
|
{
|
|
int ret = -1;
|
|
if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS) {
|
|
if (wc_LockMutex(&z_fds_mutex) == 0) {
|
|
if (z_fds[fd].open) {
|
|
z_fds[fd].open = 0;
|
|
if (fs_close(&z_fds[fd].zfp) == 0)
|
|
ret = 0;
|
|
}
|
|
wc_UnLockMutex(&z_fds_mutex);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int wPwrite(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS &&
|
|
wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (wc_LockMutex(&z_fds_mutex) == 0) {
|
|
if (z_fds[fd].open) {
|
|
if (fs_seek(&z_fds[fd].zfp, (off_t)offset, FS_SEEK_SET) == 0)
|
|
ret = fs_write(&z_fds[fd].zfp, buf, sz);
|
|
}
|
|
wc_UnLockMutex(&z_fds_mutex);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
int wPread(WFD fd, unsigned char* buf, unsigned int sz,
|
|
const unsigned int* shortOffset)
|
|
{
|
|
word64 offset;
|
|
int ret = -1;
|
|
|
|
if (fd >= 0 && fd < WOLFSSH_MAX_DESCIPRTORS &&
|
|
wResolveOffset(shortOffset, WOLFSSH_MAX_FILE_OFFSET,
|
|
&offset) == 0) {
|
|
if (wc_LockMutex(&z_fds_mutex) == 0) {
|
|
if (z_fds[fd].open) {
|
|
if (fs_seek(&z_fds[fd].zfp, (off_t)offset, FS_SEEK_SET) == 0)
|
|
ret = fs_read(&z_fds[fd].zfp, buf, sz);
|
|
}
|
|
wc_UnLockMutex(&z_fds_mutex);
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
#endif
|
|
|
|
#if !defined(NO_FILESYSTEM) && !defined(WOLFSSH_USER_FILESYSTEM)
|
|
#if defined(MICROCHIP_MPLAB_HARMONY)
|
|
int wChmod(const char *path, int mode)
|
|
{
|
|
SYS_FS_RESULT ret;
|
|
SYS_FS_FILE_DIR_ATTR attr = 0;
|
|
|
|
if ((mode & 0600) != 0600) {
|
|
attr |= SYS_FS_ATTR_RDO;
|
|
}
|
|
|
|
/* toggle the read only attribute */
|
|
ret = SYS_FS_FileDirectoryModeSet(path, attr, SYS_FS_ATTR_RDO);
|
|
if (ret != SYS_FS_RES_SUCCESS) {
|
|
WLOG(WS_LOG_SFTP, "Failed to set file/dir mode");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif
|
|
#endif /* NO_FILESYSTEM */
|
|
|
|
#if defined(WOLFSSH_HAVE_SYMLINK) && \
|
|
(defined(WOLFSSH_SFTP) || defined(WOLFSSH_SCP))
|
|
/* Returns 1 if path is a symbolic link (POSIX) or a reparse point such as a
|
|
* symlink or junction (Windows), otherwise 0. A non-existent path (stat
|
|
* fails) is reported as not-a-link so that create requests for a new leaf are
|
|
* still permitted by the caller. Shared by the SFTP and SCP path-confinement
|
|
* checks. */
|
|
int wIsSymlink(const char* path)
|
|
{
|
|
int isLink = 0;
|
|
#ifdef USE_WINDOWS_API
|
|
WIN32_FILE_ATTRIBUTE_DATA attrs;
|
|
|
|
/* Route through WS_GetFileAttributesExA so the wide-char API and any path
|
|
* trimming match the other Windows file ops. GetFileAttributesEx reports
|
|
* the link's own attributes; it does not follow the reparse point. */
|
|
if (path != NULL && WS_GetFileAttributesExA(path, &attrs, NULL) != 0 &&
|
|
(attrs.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT) != 0) {
|
|
isLink = 1;
|
|
}
|
|
#else
|
|
WSTAT_T lst;
|
|
|
|
if (path != NULL && WLSTAT(NULL, path, &lst) == 0 && S_ISLNK(lst.st_mode)) {
|
|
isLink = 1;
|
|
}
|
|
#endif
|
|
return isLink;
|
|
}
|
|
|
|
/* Open path for reading without following a final-component symbolic link.
|
|
* On POSIX this is atomic through O_NOFOLLOW: the open itself fails if the leaf
|
|
* is a link, closing the check-then-open race. Where no such primitive exists
|
|
* (Windows, or O_NOFOLLOW undefined) it falls back to a best-effort wIsSymlink
|
|
* test before the follow-prone open. Returns 0 on success and non-zero on
|
|
* failure, matching the wfopen convention. */
|
|
int wFopenNoFollow(void* fs, WFILE** f, const char* path)
|
|
{
|
|
int ret = 0;
|
|
#if !defined(USE_WINDOWS_API) && defined(O_NOFOLLOW)
|
|
WFD fd = -1;
|
|
#endif
|
|
|
|
if (f != NULL)
|
|
*f = NULL;
|
|
if (f == NULL || path == NULL)
|
|
ret = 1;
|
|
|
|
#if !defined(USE_WINDOWS_API) && defined(O_NOFOLLOW)
|
|
if (ret == 0) {
|
|
fd = WOPEN(fs, path, WOLFSSH_O_RDONLY | WOLFSSH_O_NOFOLLOW, 0);
|
|
if (fd < 0)
|
|
ret = 1;
|
|
}
|
|
if (ret == 0 && WFDOPEN(fs, f, fd, "rb") != 0) {
|
|
WCLOSE(fs, fd);
|
|
ret = 1;
|
|
}
|
|
#else
|
|
if (ret == 0 && wIsSymlink(path))
|
|
ret = 1;
|
|
if (ret == 0 && WFOPEN(fs, f, path, "rb") != 0)
|
|
ret = 1;
|
|
#endif
|
|
WOLFSSH_UNUSED(fs);
|
|
return ret;
|
|
}
|
|
|
|
#if !defined(USE_WINDOWS_API) && !defined(NO_WOLFSSH_DIR)
|
|
/* Open a directory without following a final-component symlink: POSIX uses
|
|
* O_DIRECTORY|O_NOFOLLOW + fdopendir (atomic), else falls back to wIsSymlink +
|
|
* opendir. Returns 0 on success, non-zero on failure (matches WOPENDIR). */
|
|
int wOpendirNoFollow(void* fs, WDIR* dir, const char* path)
|
|
{
|
|
int ret = 0;
|
|
#if defined(O_NOFOLLOW) && defined(O_DIRECTORY)
|
|
WFD fd = -1;
|
|
#endif
|
|
|
|
if (dir != NULL)
|
|
*dir = NULL;
|
|
if (dir == NULL || path == NULL)
|
|
ret = 1;
|
|
|
|
#if defined(O_NOFOLLOW) && defined(O_DIRECTORY)
|
|
if (ret == 0) {
|
|
fd = WOPEN(fs, path, WOLFSSH_O_RDONLY | WOLFSSH_O_DIRECTORY |
|
|
WOLFSSH_O_NOFOLLOW, 0);
|
|
if (fd < 0)
|
|
ret = 1;
|
|
}
|
|
if (ret == 0 && WFDOPENDIR(fs, dir, fd) != 0) {
|
|
WCLOSE(fs, fd);
|
|
ret = 1;
|
|
}
|
|
#else
|
|
if (ret == 0 && wIsSymlink(path))
|
|
ret = 1;
|
|
if (ret == 0 && WOPENDIR(fs, NULL, dir, path) != 0)
|
|
ret = 1;
|
|
#endif
|
|
WOLFSSH_UNUSED(fs);
|
|
return ret;
|
|
}
|
|
#endif /* !USE_WINDOWS_API && !NO_WOLFSSH_DIR */
|
|
#endif /* WOLFSSH_HAVE_SYMLINK && (WOLFSSH_SFTP || WOLFSSH_SCP) */
|
|
|
|
#ifndef WSTRING_USER
|
|
|
|
char* wstrdup(const char* s1, void* heap, int type)
|
|
{
|
|
char* s2 = NULL;
|
|
|
|
if (s1 != NULL) {
|
|
unsigned int sz;
|
|
sz = (unsigned int)WSTRLEN(s1) + 1;
|
|
s2 = (char*)WMALLOC(sz, heap, type);
|
|
if (s2 != NULL)
|
|
WSTRNCPY(s2, (const char*)s1, sz);
|
|
}
|
|
return s2;
|
|
}
|
|
|
|
|
|
char* wstrnstr(const char* s1, const char* s2, unsigned int n)
|
|
{
|
|
unsigned int s2_len = (unsigned int)WSTRLEN(s2);
|
|
|
|
if (s2_len == 0)
|
|
return (char*)s1;
|
|
|
|
while (n >= s2_len && s1[0]) {
|
|
if (s1[0] == s2[0])
|
|
if (WMEMCMP(s1, s2, s2_len) == 0)
|
|
return (char*)s1;
|
|
s1++;
|
|
n--;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
/* Returns s1 if successful. Returns NULL if unsuccessful.
|
|
* Copies the characters string s2 onto the end of s1. n is the size of the
|
|
* buffer s1 is stored in. Returns NULL if s2 is too large to fit onto the
|
|
* end of s1 including a null terminator. */
|
|
char* wstrncat(char* s1, const char* s2, size_t n)
|
|
{
|
|
size_t s1_len = 0;
|
|
size_t freeSpace;
|
|
|
|
while (s1_len < n && s1[s1_len] != '\0') {
|
|
s1_len++;
|
|
}
|
|
|
|
if (s1_len >= n) {
|
|
return NULL;
|
|
}
|
|
|
|
freeSpace = n - s1_len - 1;
|
|
|
|
if (freeSpace >= strlen(s2)) {
|
|
#ifndef USE_WINDOWS_API
|
|
strncat(s1, s2, freeSpace);
|
|
#else
|
|
strncat_s(s1, n, s2, freeSpace);
|
|
#endif
|
|
return s1;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
#ifdef USE_WINDOWS_API
|
|
/* strsep() equivalent for platforms whose C library does not provide the
|
|
* BSD extension (MSVCRT/MinGW). Splits *s1 on the first character found
|
|
* in delim, NUL-terminates the token in place, and advances *s1 past it
|
|
* (NULL when no delimiter remains). Returns the start of the token, or
|
|
* NULL if *s1 was already NULL. */
|
|
char* wstrsep(char** s1, const char* delim)
|
|
{
|
|
char* start = *s1;
|
|
char* p;
|
|
|
|
if (start == NULL)
|
|
return NULL;
|
|
|
|
for (p = start; *p != '\0'; p++) {
|
|
if (WSTRCHR(delim, *p) != NULL) {
|
|
*p = '\0';
|
|
*s1 = p + 1;
|
|
return start;
|
|
}
|
|
}
|
|
|
|
*s1 = NULL;
|
|
return start;
|
|
}
|
|
#endif /* USE_WINDOWS_API */
|
|
|
|
#endif /* WSTRING_USER */
|