• Mickaël Turnel's avatar
    Improve C# wrapper management of strings to ensure proper UTF-8 conversion... · 507637db
    Mickaël Turnel authored
    Improve C# wrapper management of strings to ensure proper UTF-8 conversion while passing from and to C library.
    Fix memory leaks.
    Add .NET wrapper tester.
    
    Add setlocale() to force utf-8 when loading Factory.Instance.
    
    Fix invalid wide string to string conversion when fetching the
    application data directory, causing non-UTF8 string to be returned.
    Add missing directory separators.
    507637db
sqlite3_bctbx_vfs.c 12.56 KiB
/*
 * Copyright (c) 2010-2022 Belledonne Communications SARL.
 * This file is part of Liblinphone
 * (see https://gitlab.linphone.org/BC/public/liblinphone).
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * This program 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 Affero General Public License for more details.
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "bctoolbox/charconv.h"
#include <bctoolbox/defs.h>
#include "sqlite3_bctbx_vfs.h"
#include <sqlite3.h>
#ifndef _WIN32_WCE
#include <errno.h>
#endif /*_WIN32_WCE*/
#include "private.h"
/**
 * Closes the file whose file descriptor is stored in the file handle p.
 * @param  p 	sqlite3_file file handle pointer.
 * @return      SQLITE_OK if successful,  SQLITE_IOERR_CLOSE otherwise.
static int sqlite3bctbx_Close(sqlite3_file *p) {
	int ret;
	sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p;
	ret = bctbx_file_close(pFile->pbctbx_file);
	if (!ret) {
		return SQLITE_OK;
	} else {
		free(pFile);
		return SQLITE_IOERR_CLOSE;
/**
 * Read count bytes from the open file given by p, starting at offset and puts them in
 * the buffer pointed by buf.
 * Calls bctbx_file_read.
 * @param  p  		sqlite3_file file handle pointer.
 * @param  buf    	buffer to write the read bytes to.
 * @param  count  	number of bytes to read
 * @param  offset 	file offset where to start reading
 * @return 			SQLITE_OK if read bytes equals count,
 *                  SQLITE_IOERR_SHORT_READ if the number of bytes read is inferior to count
 *                  SQLITE_IOERR_READ if an error occurred.
static int sqlite3bctbx_Read(sqlite3_file *p, void *buf, int count, sqlite_int64 offset) {
	int ret;
	sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p;
	if (pFile) {
		ret = (int)bctbx_file_read(pFile->pbctbx_file, buf, (size_t)count, (off_t)offset);
		if (ret == count) {
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
return SQLITE_OK; } else if (ret >= 0) { /*fill in unread portion of buffer, as requested by sqlite3 documentation*/ memset(((uint8_t *)buf) + ret, 0, (size_t)(count - ret)); return SQLITE_IOERR_SHORT_READ; } else { return SQLITE_IOERR_READ; } } return SQLITE_IOERR_READ; } /** * Writes directly to the open file given through the p argument. * Calls bctbx_file_write . * @param p sqlite3_file file handle pointer. * @param buf Buffer containing data to write * @param count Size of data to write in bytes * @param offset File offset where to write to * @return SQLITE_OK on success, SQLITE_IOERR_WRITE if an error occurred. */ static int sqlite3bctbx_Write(sqlite3_file *p, const void *buf, int count, sqlite_int64 offset) { sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p; int ret; if (pFile) { ret = (int)bctbx_file_write(pFile->pbctbx_file, buf, (size_t)count, (off_t)offset); if (ret > 0) return SQLITE_OK; else { return SQLITE_IOERR_WRITE; } } return SQLITE_IOERR_WRITE; } /** * TRuncates or extends a file depending on the size provided. * @param p sqlite3_file file handle pointer. * @param size New file size. * @return SQLITE_OK on success, SQLITE_IOERR_TRUNCATE if an error occurred during truncate, * SQLITE_ERROR if ther was a problem on the file descriptor. */ static int sqlite3bctbx_Truncate(sqlite3_file *p, sqlite_int64 size) { int rc; sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p; if (pFile->pbctbx_file) { rc = bctbx_file_truncate(pFile->pbctbx_file, size); if (rc < 0) { return SQLITE_IOERR_TRUNCATE; } if (rc == 0) { return SQLITE_OK; } } return SQLITE_ERROR; } /** * Saves the file size associated with the file handle p into the argument pSize. * @param p sqlite3_file file handle pointer. * @return SQLITE_OK if read bytes equals count, * SQLITE_IOERR_FSTAT if the file size returned is negative * SQLITE_ERROR if an error occurred. */ static int sqlite3bctbx_FileSize(sqlite3_file *p, sqlite_int64 *pSize) { int64_t rc; /* Return code from fstat() call */ sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p; if (pFile->pbctbx_file) { rc = bctbx_file_size(pFile->pbctbx_file);
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
if (rc < 0) { return SQLITE_IOERR_FSTAT; } if (pSize) { *pSize = rc; return SQLITE_OK; } } return SQLITE_ERROR; } /************************ PLACE HOLDER FUNCTIONS ***********************/ /** These functions were implemented to please the SQLite VFS implementation. Some of them are just stubs, some do a very limited job. */ /** * Returns the device characteristics for the file. Stub function * to fill the SQLite VFS function pointer structure . * @param p sqlite3_file file handle pointer. * @return value 4096. */ static int sqlite3bctbx_DeviceCharacteristics(BCTBX_UNUSED(sqlite3_file *p)) { int rc = 0x00001000; return rc; } /** * Stub function for information and control over the open file. * @param p sqlite3_file file handle pointer. * @param op operation * @param pArg unused * @return SQLITE_OK on success, SALITE_NOTFOUND otherwise. */ static int sqlite3bctbx_FileControl(BCTBX_UNUSED(sqlite3_file *p), BCTBX_UNUSED(int op), BCTBX_UNUSED(void *pArg)) { #ifdef SQLITE_FCNTL_MMAP_SIZE if (op == SQLITE_FCNTL_MMAP_SIZE) return SQLITE_OK; #endif return SQLITE_NOTFOUND; } /** * The lock file mechanism is not used with this VFS : checking * the reserved lock is always OK. * @param pUnused sqlite3_file file handle pointer. * @param pResOut set to 0 since there is no lock mechanism. * @return SQLITE_OK */ static int sqlite3bctbx_nolockCheckReservedLock(BCTBX_UNUSED(sqlite3_file *pUnused), int *pResOut) { *pResOut = 0; return SQLITE_OK; } /** * The lock file mechanism is not used with this VFS : locking the file * is always OK. * @param pUnused sqlite3_file file handle pointer. * @param unused unused * @return SQLITE_OK */ static int sqlite3bctbx_nolockLock(BCTBX_UNUSED(sqlite3_file *pUnused), BCTBX_UNUSED(int unused)) { return SQLITE_OK; } /** * The lock file mechanism is not used with this VFS : unlocking the file * is always OK. * @param pUnused sqlite3_file file handle pointer. * @param unused unused * @return SQLITE_OK */
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
static int sqlite3bctbx_nolockUnlock(BCTBX_UNUSED(sqlite3_file *pUnused), BCTBX_UNUSED(int unused)) { return SQLITE_OK; } /** * Simply sync the file contents given through the file handle p * to the persistent media. * @param p sqlite3_file file handle pointer. * @param flags unused * @return SQLITE_OK on success, SLITE_IOERR_FSYNC if an error occurred. */ static int sqlite3bctbx_Sync(sqlite3_file *p, BCTBX_UNUSED(int flags)) { sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p; int ret = bctbx_file_sync(pFile->pbctbx_file); return (ret == BCTBX_VFS_OK ? SQLITE_OK : SQLITE_IOERR_FSYNC); } /************************ END OF PLACE HOLDER FUNCTIONS ***********************/ /** * Opens the file fName and populates the structure pointed by p * with the necessary io_methods * Methods not implemented for version 1 : xTruncate, xSectorSize. * Initializes some fields in the p structure, some of which where already * initialized by SQLite. * @param pVfs sqlite3_vfs VFS pointer. * @param fName filename * @param p file handle pointer * @param flags db file access flags * @param pOutFlags flags used by SQLite * @return SQLITE_CANTOPEN on error, SQLITE_OK on success. */ static int sqlite3bctbx_Open(BCTBX_UNUSED(sqlite3_vfs *pVfs), const char *fName, sqlite3_file *p, int flags, int *pOutFlags) { static const sqlite3_io_methods sqlite3_bctbx_io = { 1, /* iVersion Structure version number */ sqlite3bctbx_Close, /* xClose */ sqlite3bctbx_Read, /* xRead */ sqlite3bctbx_Write, /* xWrite */ sqlite3bctbx_Truncate, /* xTruncate */ sqlite3bctbx_Sync, sqlite3bctbx_FileSize, sqlite3bctbx_nolockLock, sqlite3bctbx_nolockUnlock, sqlite3bctbx_nolockCheckReservedLock, sqlite3bctbx_FileControl, NULL, /* xSectorSize */ sqlite3bctbx_DeviceCharacteristics /*other function points follows, all NULL but not present in all sqlite3 versions.*/ }; sqlite3_bctbx_file_t *pFile = (sqlite3_bctbx_file_t *)p; /*File handle sqlite3_bctbx_file_t*/ int openFlags = 0; char *wFname; /*returns error if filename is empty or file handle not initialized*/ if (pFile == NULL || fName == NULL) { return SQLITE_IOERR; } /* Set flags to open the file with */ if (flags & SQLITE_OPEN_EXCLUSIVE) openFlags |= O_EXCL; if (flags & SQLITE_OPEN_CREATE) openFlags |= O_CREAT; if (flags & SQLITE_OPEN_READONLY) openFlags |= O_RDONLY; if (flags & SQLITE_OPEN_READWRITE) openFlags |= O_RDWR; #if defined(_WIN32) openFlags |= O_BINARY; #endif /* sqlite3 requires an UTF-8 encoded path, but UTF-8 may not be the
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
* process current locale. */ wFname = bctbx_utf8_to_locale(fName); if (wFname != NULL) { pFile->pbctbx_file = bctbx_file_open2(bctbx_vfs_get_default(), wFname, openFlags); bctbx_free(wFname); } else { pFile->pbctbx_file = NULL; } if (pFile->pbctbx_file == NULL) { return SQLITE_CANTOPEN; } if (pOutFlags) { *pOutFlags = flags; } pFile->base.pMethods = &sqlite3_bctbx_io; return SQLITE_OK; } sqlite3_vfs *sqlite3_bctbx_vfs_create(void) { static sqlite3_vfs bctbx_vfs = { 1, /* iVersion */ sizeof(sqlite3_bctbx_file_t), /* szOsFile */ MAXPATHNAME, /* mxPathname */ NULL, /* pNext */ BCTBX_SQLITE3_VFS, /* zName */ NULL, /* pAppData */ sqlite3bctbx_Open, /* xOpen */ NULL, /* xDelete */ NULL, /* xAccess */ NULL /* xFullPathname */ }; return &bctbx_vfs; } /*static int sqlite3bctbx_winFullPathname( sqlite3_vfs *pVfs, // Pointer to vfs object const char *zRelative, // Possibly relative input path int nFull, // Size of output buffer in bytes char *zFull){ //LPWSTR zTemp; //DWORD nByte; // If this path name begins with "/X:", where "X" is any alphabetic // character, discard the initial "/" from the pathname. // //if (zRelative[0] == '/' && sqlite3Isalpha(zRelative[1]) && zRelative[2] == ':'){ // zRelative++; //} nByte = GetFullPathNameW((LPCWSTR)zRelative, 0, 0, 0); if (nByte == 0){ return SQLITE_CANTOPEN_FULLPATH; } nByte += 3; zTemp = bctbx_malloc(nByte*sizeof(zTemp[0])); memset(zTemp, 0, nByte*sizeof(zTemp[0])); if (zTemp == 0){ return SQLITE_IOERR_NOMEM; } nByte = GetFullPathNameW((LPCWSTR)zRelative, nByte, zTemp, 0); if (nByte == 0){ bctbx_free(zTemp); return SQLITE_CANTOPEN_FULLPATH; } if (zTemp){ sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zTemp); bctbx_free(zTemp); return SQLITE_OK;
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392
} else{ return SQLITE_IOERR_NOMEM; } sqlite3_snprintf(MIN(nFull, pVfs->mxPathname), zFull, "%s", zRelative); return SQLITE_OK; }*/ void sqlite3_bctbx_vfs_register(int makeDefault) { sqlite3_vfs *pVfsToUse = sqlite3_bctbx_vfs_create(); #if _WIN32 sqlite3_vfs *pDefault = sqlite3_vfs_find("win32"); #else sqlite3_vfs *pDefault = sqlite3_vfs_find("unix-none"); #endif pVfsToUse->xCurrentTime = pDefault->xCurrentTime; pVfsToUse->xAccess = pDefault->xAccess; pVfsToUse->xFullPathname = pDefault->xFullPathname; pVfsToUse->xDelete = pDefault->xDelete; pVfsToUse->xSleep = pDefault->xSleep; pVfsToUse->xRandomness = pDefault->xRandomness; pVfsToUse->xGetLastError = pDefault->xGetLastError; /* Not implemented by sqlite3 :place holder */ /*Functions below should not be a problem sincve we are declaring ourselves in version 1 */ /* used in version 2 xCurrentTimeInt64;*/ /* used in version 3 xGetSystemCall xSetSystemCall xNextSystemCall*/ sqlite3_vfs_register(pVfsToUse, makeDefault); } void sqlite3_bctbx_vfs_unregister(void) { sqlite3_vfs *pVfs = sqlite3_vfs_find(BCTBX_SQLITE3_VFS); sqlite3_vfs_unregister(pVfs); }