commit x64 compilation from lulu cause the other branch dont seems to compile properly at home
This commit is contained in:
@@ -0,0 +1,160 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/data_store.h"
|
||||
#include "rand/rand.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
DataStore::Entry *DataStore::GetEntry(const String &id) const
|
||||
{
|
||||
ListForeachPtr(Entry *, e, entries)
|
||||
if (e->id == id)
|
||||
return e;
|
||||
return NULL;
|
||||
}
|
||||
String DataStore::GetNewId()
|
||||
{
|
||||
for ( ; ; ++id_seed)
|
||||
{
|
||||
String id = String::Format("store_%012d", id_seed);
|
||||
if (GetEntry(id) == NULL)
|
||||
return id;
|
||||
}
|
||||
return String();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t DataStore::GetEntrySize(const String &id) const
|
||||
{
|
||||
if (Entry *e = GetEntry(id))
|
||||
return e->size;
|
||||
return 0;
|
||||
}
|
||||
size_t DataStore::GetStoreSize() const
|
||||
{
|
||||
size_t size = 0;
|
||||
ListForeachPtr(Entry *, e, entries)
|
||||
size += e->size;
|
||||
return size;
|
||||
}
|
||||
size_t DataStore::GetFreeStore() const
|
||||
{ return limit - GetStoreSize(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String DataStore::Reserve(size_t size)
|
||||
{
|
||||
Threading::MutexLock lock(&mutex);
|
||||
|
||||
if (limit > 0) // enforce size limit
|
||||
{
|
||||
size_t store_size = GetStoreSize();
|
||||
if (size > (limit - store_size))
|
||||
return String(); // store is full
|
||||
}
|
||||
|
||||
// Reserve a new entry.
|
||||
Entry *entry = new Entry;
|
||||
|
||||
entry->id = GetNewId();
|
||||
entry->size = size;
|
||||
entries.Add(entry);
|
||||
|
||||
return entry->id;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool DataStore::Store(const String &id, const void *data, size_t size, const char *user)
|
||||
{
|
||||
Threading::MutexLock lock(&mutex);
|
||||
|
||||
Entry *entry = GetEntry(id);
|
||||
if ((entry == NULL) || (entry->size != size))
|
||||
return false; // invalid id/store size
|
||||
|
||||
lock.Unlock();
|
||||
|
||||
entry->user = user;
|
||||
|
||||
AutoPtr <Handle> h(io->Open(id, ModeWrite));
|
||||
return h.IsValid() && (h->Write(data, size) == size);
|
||||
}
|
||||
bool DataStore::Free(const String &id)
|
||||
{
|
||||
Threading::MutexLock lock(&mutex);
|
||||
|
||||
Entry *entry = GetEntry(id);
|
||||
if (entry == NULL)
|
||||
return false; // invalid id
|
||||
|
||||
if (!io->Delete(id))
|
||||
return false;
|
||||
|
||||
return entries.Remove(entry);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
// Store format
|
||||
//
|
||||
// 18 bytes - id
|
||||
// 4 bytes - size in bytes
|
||||
// -----------------------------
|
||||
bool DataStore::Load(const char *path)
|
||||
{
|
||||
AutoPtr <Handle> h(io->Open(path));
|
||||
if (h.IsNull())
|
||||
return true; // nothing to restore
|
||||
|
||||
entries.Clear();
|
||||
|
||||
char id[18];
|
||||
while (!h->IsEOF())
|
||||
{
|
||||
if (h->Read((void *)id, 18) != 18)
|
||||
return false;
|
||||
|
||||
Entry *e = new Entry;
|
||||
|
||||
e->id.Set(id, id + 18);
|
||||
e->size = h->Read <size_t> ();
|
||||
|
||||
uint user_data_size = h->Read <uint> ();
|
||||
if (!e->user.Allocate(user_data_size))
|
||||
return false;
|
||||
|
||||
h->Read((void *)e->user.c_str(), user_data_size);
|
||||
|
||||
entries.Add(e);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool DataStore::Save(const char *path)
|
||||
{
|
||||
AutoPtr <Handle> h(io->Open(path, ModeWrite));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
ListForeachPtr(Entry *, e, entries)
|
||||
{
|
||||
h->Write(e->id.c_str(), 18);
|
||||
h->Write(&e->size, 4);
|
||||
|
||||
uint user_data_size = e->user.Len();
|
||||
h->Write(user_data_size);
|
||||
if (user_data_size > 0)
|
||||
h->Write(e->user.c_str(), user_data_size);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,218 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/filesystem.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "log/log.h"
|
||||
|
||||
|
||||
#if __PLATFORM_POSIX__
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#elif __PLATFORM_WINDOWS__
|
||||
#include <direct.h>
|
||||
#endif
|
||||
|
||||
#include "filesystem/io_cfile.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
|
||||
using GS::String;
|
||||
using namespace GS::IO;
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Filesystem::Exists(const char *uri) const
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri));
|
||||
return h.IsValid();
|
||||
}
|
||||
size_t Filesystem::FileSize(const char *uri) const
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri));
|
||||
return h.IsNull() ? 0 : h->GetSize();
|
||||
}
|
||||
bool Filesystem::FileLoad(const char *uri, GS::Array <char> &buffer, bool verbose) const
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri));
|
||||
if (h.IsNull())
|
||||
{
|
||||
if (verbose)
|
||||
__LOG_W__ << "Failed to open '" << uri << "'.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
// Warning: Do not load through IO::Base::FileLoad. That would create another handle!
|
||||
size_t size = h->GetSize();
|
||||
if (!buffer.Allocate(size))
|
||||
__ERR__(__LOG_W__ << "Failed to allocate memory to load '" << uri << "'.\n", false)
|
||||
|
||||
return asbool(h->Read(buffer.c_ptr(), size) == size);
|
||||
}
|
||||
bool Filesystem::FileSave(const char *uri, const GS::Array <char> &buffer) const
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri, ModeWrite));
|
||||
if (h.IsNull())
|
||||
__ERR__(__LOG_W__ << "Failed to open '" << uri << "'.\n", false)
|
||||
return asbool(h->Write(buffer.c_ptr(), buffer.GetSize()) == buffer.GetSize());
|
||||
}
|
||||
bool Filesystem::FileCopy(const char *src, const char *dst) const
|
||||
{
|
||||
Array <char> buffer;
|
||||
return FileLoad(src, buffer) && FileSave(dst, buffer);
|
||||
}
|
||||
bool Filesystem::FileMove(const char *src, const char *dst) const
|
||||
{
|
||||
Array <char> buffer;
|
||||
return FileLoad(src, buffer) && FileSave(dst, buffer) && Delete(src);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String Filesystem::MapToAbsolute(const char *uri) const
|
||||
{
|
||||
String _uri(uri);
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (_uri.StartsWith(m->mount_point))
|
||||
return m->io_sys->MapToAbsolute(_uri.Mid(m->mount_point.Len())).CleanFilePath();
|
||||
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
{
|
||||
AutoPtr <Handle> h(i->Open(_uri));
|
||||
if (h.IsValid())
|
||||
return i->MapToAbsolute(_uri).CleanFilePath();
|
||||
}
|
||||
return _uri;
|
||||
}
|
||||
String Filesystem::StripRootPath(const char *path) const
|
||||
{
|
||||
String _path(path);
|
||||
_path.FileCleanName();
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
{
|
||||
String rpath = i->MapToRelative(_path);
|
||||
if (!rpath.IsEmpty())
|
||||
if (rpath != _path)
|
||||
return rpath[0] == '/' ? rpath.Mid(1) : rpath; // Ensure no leading '/' remains.
|
||||
}
|
||||
return _path;
|
||||
}
|
||||
bool Filesystem::Mount(Base *io_sys, const char *mount_point)
|
||||
{
|
||||
if (mount_point)
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (m->mount_point == mount_point)
|
||||
{
|
||||
delete io_sys;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return asbool(mount_list.Prepend(new MountPoint(mount_point, io_sys)));
|
||||
}
|
||||
else
|
||||
{
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
if (i == io_sys)
|
||||
__ERR__(__LOG_E__ << "Cannot mount IO system twice as root.\n", false)
|
||||
|
||||
return asbool(root_mount.Prepend(io_sys));
|
||||
}
|
||||
}
|
||||
void Filesystem::Unmount(const char *mount_point)
|
||||
{ Unmount(GetIOSystem(mount_point)); }
|
||||
void Filesystem::Unmount(Base *io_sys)
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (m->io_sys.c_ptr() == io_sys)
|
||||
mount_list.Remove(m);
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
if (i == io_sys)
|
||||
root_mount.Remove(i);
|
||||
}
|
||||
void Filesystem::UnmountAll()
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
mount_list.Remove(m);
|
||||
while (List <Base *> ::Item *m = root_mount.GetRoot())
|
||||
root_mount.Remove(m);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Base *Filesystem::GetIOSystem(const char *mount_point) const
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (m->mount_point == mount_point)
|
||||
return m->io_sys;
|
||||
return NULL;
|
||||
}
|
||||
const char *Filesystem::GetMountPoint(const Base *io_sys) const
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (m->io_sys == io_sys)
|
||||
return m->mount_point;
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle *Filesystem::Open(const char *uri, Mode mode) const
|
||||
{
|
||||
String _uri(uri);
|
||||
|
||||
if (_uri.IsEmpty())
|
||||
return NULL;
|
||||
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (_uri.StartsWith(m->mount_point))
|
||||
return m->io_sys->Open(_uri.Mid(m->mount_point.Len()), mode);
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
if (Handle *h = i->Open(uri, mode))
|
||||
return h;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
void Filesystem::Close(Handle *h) const
|
||||
{
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (m->io_sys.c_ptr() == h->GetIOSystem())
|
||||
m->io_sys->Close(h);
|
||||
ListForeachPtr(Base *, i, root_mount)
|
||||
if (i == h->GetIOSystem())
|
||||
i->Close(h);
|
||||
}
|
||||
bool Filesystem::Delete(const char *uri) const
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri));
|
||||
if (h.IsValid())
|
||||
{
|
||||
Base *io_sys = h->GetIOSystem();
|
||||
h = NULL;
|
||||
return io_sys->Delete(uri);
|
||||
}
|
||||
else
|
||||
return asbool(unlink(uri) == 0);
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Filesystem::MkDir(const char *path) const
|
||||
{
|
||||
String _path(path);
|
||||
|
||||
if (_path.IsEmpty())
|
||||
return false;
|
||||
|
||||
ListForeachPtr(MountPoint *, m, mount_list)
|
||||
if (_path.StartsWith(m->mount_point))
|
||||
return m->io_sys->MkDir(_path.Mid(m->mount_point.Len()));
|
||||
|
||||
// [EJ] No creation on a root filesystem here!
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,984 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/ftp_lib.h"
|
||||
#include "nstring/nstring.h"
|
||||
#include "platform_config.h"
|
||||
#include "alloc/ialloc.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using namespace GS;
|
||||
|
||||
#define ConnectionBufferSize 1024
|
||||
|
||||
#if __PLATFORM_WINDOWS__
|
||||
|
||||
#define SETSOCKOPT_OPTVAL_TYPE (const char *)
|
||||
#define net_read(x,y,z) recv(x,(char*)y,z,0)
|
||||
#define net_write(x,y,z) send(x,(char*)y,z,0)
|
||||
#define net_close closesocket
|
||||
typedef int socklen_t;
|
||||
|
||||
#elif (__PLATFORM_LINUX__ || __PLATFORM_OSX__)
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
|
||||
#define SETSOCKOPT_OPTVAL_TYPE (void *)
|
||||
#define net_read read
|
||||
#define net_write write
|
||||
#define net_close close
|
||||
#define SOCKET int
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
#if (__PLATFORM_WINDOWS__ || __PLATFORM_LINUX__ || __PLATFORM_OSX__)
|
||||
|
||||
|
||||
#define FTP_PROPAGATE_ERROR__(__C__) { State s = __C__; if (s != FtpOk) return s; }
|
||||
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
|
||||
//----------------------------
|
||||
FtpConnection::FtpConnection()
|
||||
//----------------------------
|
||||
{
|
||||
handle = -1;
|
||||
ready = false;
|
||||
|
||||
buffer = 0;
|
||||
buffer_usage = 0;
|
||||
}
|
||||
|
||||
//---------------------------------------------
|
||||
void FtpConnection::FreeBuffer()
|
||||
//---------------------------------------------
|
||||
{ _safe_delete_array(buffer); }
|
||||
|
||||
//-------------------------------------------------
|
||||
bool FtpConnection::AllocateBuffer()
|
||||
//-------------------------------------------------
|
||||
{
|
||||
FreeBuffer();
|
||||
return (buffer = new char[ConnectionBufferSize]) != NULL ? true : false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------------------
|
||||
FtpLibrary::State FtpLibrary::WaitSocket(FtpConnection *connection)
|
||||
//-------------------------------------------------------------------
|
||||
{
|
||||
fd_set fd, *rfd = NULL, *wfd = NULL;
|
||||
|
||||
FD_ZERO(&fd);
|
||||
if (connection->direction == FtpConnection::Upload)
|
||||
wfd = &fd;
|
||||
else
|
||||
rfd = &fd;
|
||||
|
||||
forever
|
||||
{
|
||||
FD_SET(connection->handle, &fd);
|
||||
|
||||
// 5 seconds timeout.
|
||||
timeval tv;
|
||||
tv.tv_sec = 4;
|
||||
tv.tv_usec = 100000;
|
||||
|
||||
SOCKET rv = select((int)(connection->handle + 1), rfd, wfd, NULL, &tv);
|
||||
|
||||
if (rv == -1)
|
||||
return FtpError;
|
||||
else if (!rv)
|
||||
return FtpSocketTimeout;
|
||||
else
|
||||
break;
|
||||
/*
|
||||
if (!IdleCallback())
|
||||
FtpSocketTimeout;
|
||||
*/
|
||||
}
|
||||
return FtpOk;
|
||||
}
|
||||
|
||||
//---------------------------------------------------
|
||||
bool FtpLibrary::CheckResponse(char c)
|
||||
//---------------------------------------------------
|
||||
{
|
||||
char match[5];
|
||||
int length;
|
||||
if (ReadASCII(response, 256, &master_connection, length) != FtpOk)
|
||||
return false;
|
||||
|
||||
if (response[3] == '-')
|
||||
{
|
||||
strncpy(match, response, 3);
|
||||
match[3] = ' ';
|
||||
match[4] = '\0';
|
||||
|
||||
do
|
||||
{
|
||||
if (ReadASCII(response, 256, &master_connection, length) != FtpOk)
|
||||
return false;
|
||||
} while (strncmp(response, match, 4));
|
||||
}
|
||||
return (response[0] == c) ? true : false;
|
||||
}
|
||||
|
||||
//-------------------------------------------------------
|
||||
bool FtpLibrary::Connect(const char *host)
|
||||
//-------------------------------------------------------
|
||||
{
|
||||
sockaddr_in sin;
|
||||
memset(&sin,0,sizeof(sin));
|
||||
sin.sin_family = AF_INET;
|
||||
|
||||
char *lhost = strdup(host), *pnum = strchr(lhost,':');
|
||||
|
||||
servent *pse;
|
||||
if (!pnum)
|
||||
{
|
||||
if ((pse = getservbyname("ftp", "tcp")) == NULL)
|
||||
__ERR__(__LOG__ << "[!] (FTP) Failed to get service port.\n", false)
|
||||
sin.sin_port = pse->s_port;
|
||||
}
|
||||
else
|
||||
{
|
||||
*pnum++ = 0;
|
||||
if (isdigit(*pnum))
|
||||
sin.sin_port = htons((u_short)atoi(pnum));
|
||||
else
|
||||
{
|
||||
pse = getservbyname(pnum, "tcp");
|
||||
sin.sin_port = pse->s_port;
|
||||
}
|
||||
}
|
||||
|
||||
#if __PLATFORM_WINDOWS__
|
||||
if ((sin.sin_addr.s_addr = inet_addr(lhost)) == -1)
|
||||
#else
|
||||
if (!inet_aton(lhost, &sin.sin_addr))
|
||||
#endif
|
||||
{
|
||||
hostent *phe;
|
||||
if ((phe = gethostbyname(lhost)) == NULL)
|
||||
__ERR__(__LOG__ << "[!] (FTP) Failed to resolve host.\n", false)
|
||||
memcpy((char *)&sin.sin_addr, phe->h_addr, phe->h_length);
|
||||
}
|
||||
free(lhost);
|
||||
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
#define __ConnectError__(_S_)\
|
||||
{ __LOG__ << _S_; net_close(master_connection.handle); master_connection.handle = 0; return false; }
|
||||
//-------------------------------------------------------------------------------------------------------
|
||||
|
||||
master_connection.handle = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (master_connection.handle == -1)
|
||||
__ConnectError__("[!] (FTP) Failed to create socket.\n")
|
||||
|
||||
int on = 1;
|
||||
if (setsockopt(master_connection.handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
|
||||
__ConnectError__("[!] (FTP) Set socket option failed.\n")
|
||||
if (connect(master_connection.handle, (struct sockaddr *)&sin, sizeof(sin)) == -1)
|
||||
__ConnectError__("[!] (FTP) Socket failed to connect.\n")
|
||||
if (!CheckResponse('2'))
|
||||
__ConnectError__("")
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//-----------------------------------------------------------------
|
||||
bool FtpLibrary::CheckPASVResponse(unsigned char *v)
|
||||
//-----------------------------------------------------------------
|
||||
{
|
||||
sockaddr sa;
|
||||
socklen_t l = sizeof(sa);
|
||||
|
||||
if (getpeername(master_connection.handle, &sa, &l) == -1)
|
||||
{
|
||||
net_close(master_connection.handle);
|
||||
return false;
|
||||
}
|
||||
for (int i = 2; i < 6; ++i)
|
||||
v[i] = sa.sa_data[i];
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool FtpLibrary::FtpSendCmd(const char *command, char expresp)
|
||||
//---------------------------------------------------------------------------
|
||||
{
|
||||
if (!master_connection.handle)
|
||||
return 0;
|
||||
|
||||
char ftp_command[256];
|
||||
_snprintf(ftp_command, 255, "%s\r\n", command);
|
||||
if (net_write(master_connection.handle, ftp_command, (int)strlen(ftp_command)) <= 0)
|
||||
return false;
|
||||
|
||||
SendCommandCallback(ftp_command);
|
||||
return CheckResponse(expresp);
|
||||
}
|
||||
|
||||
//---------------------------------------------------------------------------
|
||||
bool FtpLibrary::Login(const char *user, const char *password)
|
||||
//---------------------------------------------------------------------------
|
||||
{
|
||||
char ftp_command[64];
|
||||
|
||||
// Send user.
|
||||
_snprintf(ftp_command, 63, "USER %s", user);
|
||||
if (!FtpSendCmd(ftp_command, '3'))
|
||||
{
|
||||
if (*GetLastResponse() == '2')
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Send password.
|
||||
_snprintf(ftp_command, 63, "PASS %s", password);
|
||||
return FtpSendCmd(ftp_command, '2');
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
FtpConnection *FtpLibrary::CreatePORTConnection(TransferMode mode, FtpConnection::Direction dir, char *connection_command)
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
union
|
||||
{
|
||||
sockaddr sa;
|
||||
sockaddr_in in;
|
||||
} sin;
|
||||
|
||||
// Create the new connection.
|
||||
FtpConnection *connection = new FtpConnection;
|
||||
if (!connection)
|
||||
__ERR__(__LOG__ << "[!] (FTP) Failed to allocate new connection object.\n", NULL)
|
||||
|
||||
// Get socket from the master connection.
|
||||
socklen_t l = sizeof(sin);
|
||||
if (getsockname(master_connection.handle, &sin.sa, &l) < 0)
|
||||
return connection;
|
||||
|
||||
// Create socket.
|
||||
connection->handle = (int)socket(PF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
if (connection->handle == -1)
|
||||
return connection;
|
||||
|
||||
// Set socket options.
|
||||
int on = 1;
|
||||
if (setsockopt(connection->handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1)
|
||||
return connection;
|
||||
|
||||
linger lng = { 0, 0 };
|
||||
if (setsockopt(connection->handle, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng, sizeof(lng)) == -1)
|
||||
return connection;
|
||||
|
||||
// Bind socket.
|
||||
sin.in.sin_port = 0;
|
||||
if (
|
||||
(bind(connection->handle, &sin.sa, sizeof(sin)) == -1) ||
|
||||
(listen(connection->handle, 1) < 0) ||
|
||||
(getsockname(connection->handle, &sin.sa, &l) < 0)
|
||||
)
|
||||
return connection;
|
||||
|
||||
// Open PORT connection.
|
||||
char ftp_command[256];
|
||||
_snprintf(ftp_command, 255, "PORT %hhu,%hhu,%hhu,%hhu,%hhu,%hhu",
|
||||
(unsigned char)sin.sa.sa_data[2],
|
||||
(unsigned char)sin.sa.sa_data[3],
|
||||
(unsigned char)sin.sa.sa_data[4],
|
||||
(unsigned char)sin.sa.sa_data[5],
|
||||
(unsigned char)sin.sa.sa_data[0],
|
||||
(unsigned char)sin.sa.sa_data[1] );
|
||||
|
||||
if (!FtpSendCmd(ftp_command, '2'))
|
||||
return connection;
|
||||
|
||||
// Handle resuming.
|
||||
if (offset)
|
||||
{
|
||||
_snprintf(ftp_command, 255, "REST %lld", offset);
|
||||
if (!FtpSendCmd(ftp_command, '3'))
|
||||
return connection; // TODO allow for a full restart here?
|
||||
}
|
||||
|
||||
// Allocate buffer for binary transaction.
|
||||
if ((mode == TransferASCII) && !connection->AllocateBuffer())
|
||||
return connection;
|
||||
|
||||
// Finally send the connection command.
|
||||
if (!FtpSendCmd(connection_command, '1'))
|
||||
return connection;
|
||||
|
||||
// Connection is ready.
|
||||
connection->direction = dir;
|
||||
connection->ready = true;
|
||||
return connection;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
FtpConnection *FtpLibrary::CreatePASVConnection(TransferMode mode, FtpConnection::Direction dir, char *connection_command)
|
||||
//------------------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
// Set PASV mode.
|
||||
if (!FtpSendCmd("PASV", '2'))
|
||||
return NULL;
|
||||
|
||||
// Check server answer.
|
||||
char *cp = strchr(response,'(');
|
||||
if (!cp)
|
||||
return NULL;
|
||||
|
||||
unsigned char v[6];
|
||||
sscanf(++cp, "%hhu,%hhu,%hhu,%hhu,%hhu,%hhu", &v[2], &v[3], &v[4], &v[5], &v[0], &v[1]);
|
||||
if (correctpasv && !CheckPASVResponse(v))
|
||||
__ERR__(__LOG__ << "[!] (FTP) Incorrect PASV response.\n", NULL)
|
||||
|
||||
struct sockaddr sa;
|
||||
sa.sa_family = AF_INET;
|
||||
sa.sa_data[2] = v[2];
|
||||
sa.sa_data[3] = v[3];
|
||||
sa.sa_data[4] = v[4];
|
||||
sa.sa_data[5] = v[5];
|
||||
sa.sa_data[0] = v[0];
|
||||
sa.sa_data[1] = v[1];
|
||||
|
||||
// Handle resume.
|
||||
char ftp_command[256];
|
||||
|
||||
if (offset)
|
||||
{
|
||||
_snprintf(ftp_command, 255, "REST %lld", offset);
|
||||
if (!FtpSendCmd(ftp_command, '3'))
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// Create the new connection.
|
||||
FtpConnection *connection = new FtpConnection;
|
||||
if (!connection)
|
||||
__ERR__(__LOG__ << "[!] (FTP) Failed to allocate new connection object.\n", NULL)
|
||||
|
||||
// Create socket.
|
||||
int on = 1;
|
||||
linger lng = { 0, 0 };
|
||||
connection->handle = (int)socket(PF_INET,SOCK_STREAM,IPPROTO_TCP);
|
||||
if (
|
||||
(connection->handle == -1) ||
|
||||
(setsockopt(connection->handle, SOL_SOCKET, SO_REUSEADDR, SETSOCKOPT_OPTVAL_TYPE &on, sizeof(on)) == -1) ||
|
||||
(setsockopt(connection->handle, SOL_SOCKET, SO_LINGER, SETSOCKOPT_OPTVAL_TYPE &lng, sizeof(lng)) == -1)
|
||||
)
|
||||
return connection;
|
||||
|
||||
// Setup connection.
|
||||
_snprintf(ftp_command, 255, "%s\r\n", connection_command);
|
||||
if (net_write(master_connection.handle, ftp_command, (int)strlen(ftp_command)) <= 0)
|
||||
return connection;
|
||||
|
||||
// Connect socket.
|
||||
if ((connect(connection->handle, &sa, sizeof(sa)) == -1) || !CheckResponse('1'))
|
||||
return connection;
|
||||
|
||||
// Allocate buffer for binary transaction.
|
||||
if ((mode == TransferASCII) && !connection->AllocateBuffer())
|
||||
return connection;
|
||||
|
||||
// Connection is ready.
|
||||
connection->direction = dir;
|
||||
connection->ready = true;
|
||||
return connection;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool FtpLibrary::FtpAcceptConnection(FtpConnection *connection)
|
||||
//----------------------------------------------------------------------------
|
||||
{
|
||||
// Reset all connections.
|
||||
fd_set mask;
|
||||
|
||||
FD_ZERO(&mask);
|
||||
FD_SET(master_connection.handle, &mask);
|
||||
FD_SET(connection->handle, &mask);
|
||||
|
||||
// Setup timeout.
|
||||
timeval tv;
|
||||
tv.tv_usec = 0;
|
||||
tv.tv_sec = 30;
|
||||
|
||||
// Select handle.
|
||||
int i = (int)master_connection.handle;
|
||||
if (i < connection->handle)
|
||||
i = connection->handle;
|
||||
i = select((int)(i + 1), &mask, NULL, NULL, &tv);
|
||||
|
||||
switch (i)
|
||||
{
|
||||
case -1: // Error.
|
||||
strncpy(response, strerror(errno), sizeof(response));
|
||||
break;
|
||||
|
||||
case 0: // Time out.
|
||||
strcpy(response, "Time out waiting for connection.");
|
||||
break;
|
||||
|
||||
default:
|
||||
if (FD_ISSET(connection->handle, &mask))
|
||||
{
|
||||
sockaddr addr;
|
||||
socklen_t l = sizeof(addr);
|
||||
int handle = (int)accept(connection->handle, &addr, &l);
|
||||
i = errno;
|
||||
net_close(connection->handle);
|
||||
|
||||
if (handle > 0)
|
||||
{
|
||||
connection->handle = handle;
|
||||
return true;
|
||||
}
|
||||
|
||||
strncpy(response, strerror((int)i), sizeof(response));
|
||||
connection->handle = 0;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
if (FD_ISSET(connection->handle, &mask))
|
||||
CheckResponse('2');
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
net_close(connection->handle);
|
||||
connection->handle = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FtpConnection *FtpLibrary::CreateConnection(const char *path, AccessType type, TransferMode mode)
|
||||
{
|
||||
if (!path && ((type == AccessFileWrite) || (type == AccessFileRead) || (type == AccessFileReadAppend) || (type == AccessFileWriteAppend)))
|
||||
__ERR__(__LOG__ << "[!] (FTP) Missing path argument.\n", NULL)
|
||||
|
||||
// Setup transfer mode.
|
||||
char ftp_command[256];
|
||||
_snprintf(ftp_command, 255, "TYPE %c", mode);
|
||||
if (!FtpSendCmd(ftp_command, '2'))
|
||||
__ERR__(__LOG__ << "[!] (FTP) Failed to set tranfer mode.\n", NULL)
|
||||
|
||||
// Select sub command.
|
||||
const char *sub_command;
|
||||
FtpConnection::Direction direction = FtpConnection::Download;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
case AccessDir:
|
||||
sub_command = "NLST";
|
||||
break;
|
||||
|
||||
case AccessDirVerbose:
|
||||
sub_command = "LIST -aL";
|
||||
break;
|
||||
|
||||
case AccessFileReadAppend:
|
||||
case AccessFileRead:
|
||||
sub_command = "RETR";
|
||||
break;
|
||||
|
||||
case AccessFileWriteAppend:
|
||||
case AccessFileWrite:
|
||||
sub_command = "STOR";
|
||||
direction = FtpConnection::Upload;
|
||||
break;
|
||||
|
||||
default:
|
||||
__ERR__(__LOG__ << "[!] (FTP) Invalid access type.\n", NULL)
|
||||
}
|
||||
|
||||
// Append path.
|
||||
if (path)
|
||||
_snprintf(ftp_command, 255, "%s %s", sub_command, path);
|
||||
|
||||
// Open connection.
|
||||
FtpConnection *connection = NULL;
|
||||
|
||||
switch (connection_mode)
|
||||
{
|
||||
case ConnectionPASV:
|
||||
connection = CreatePASVConnection(mode, direction, ftp_command);
|
||||
break;
|
||||
|
||||
case ConnectionPORT:
|
||||
connection = CreatePORTConnection(mode, direction, ftp_command);
|
||||
if (!connection || !connection->ready)
|
||||
break;
|
||||
|
||||
if (!FtpAcceptConnection(connection))
|
||||
{
|
||||
CloseConnection(connection);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return connection;
|
||||
}
|
||||
bool FtpLibrary::CloseConnection(FtpConnection *connection)
|
||||
{
|
||||
// Sanity check.
|
||||
if (connection == &master_connection)
|
||||
return false;
|
||||
|
||||
// Purge writing cache.
|
||||
int length;
|
||||
if (connection->direction == FtpConnection::Upload)
|
||||
if (connection->buffer)
|
||||
WriteASCII(NULL, 0, connection, length);
|
||||
|
||||
shutdown(connection->handle, 2);
|
||||
net_close(connection->handle);
|
||||
_safe_delete(connection);
|
||||
|
||||
return CheckResponse('2');
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FtpLibrary::State FtpLibrary::ReadASCII(char *buf, int max, FtpConnection *connection, int &total_read_count)
|
||||
{
|
||||
/// TODO
|
||||
// Early exit case.
|
||||
if (max == 0)
|
||||
return FtpOk;
|
||||
if ((connection != &master_connection) && (connection->direction != FtpConnection::Download))
|
||||
return FtpError;
|
||||
|
||||
// Read.
|
||||
total_read_count = 0;
|
||||
char *p_buffer = buf;
|
||||
|
||||
forever
|
||||
{
|
||||
// Check available data on cache.
|
||||
if (connection->buffer_usage > 0)
|
||||
{
|
||||
// Determine read size.
|
||||
char *eol = (char *)memchr(connection->buffer, '\n', connection->buffer_usage);
|
||||
int read_size = (int)(eol ? eol - connection->buffer + 1 : connection->buffer_usage);
|
||||
|
||||
if (read_size > (max - 1))
|
||||
read_size = max - 1;
|
||||
|
||||
// Perform reading.
|
||||
memcpy(p_buffer, connection->buffer, read_size);
|
||||
|
||||
max -= read_size;
|
||||
total_read_count += read_size;
|
||||
p_buffer += read_size;
|
||||
|
||||
// Update cache.
|
||||
if (read_size < connection->buffer_usage)
|
||||
memcpy(connection->buffer, &connection->buffer[read_size], connection->buffer_usage - read_size);
|
||||
connection->buffer_usage -= read_size;
|
||||
|
||||
// Catch end of buffer.
|
||||
if ((max == 1) || eol)
|
||||
{
|
||||
p_buffer[0] = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Wait socket.
|
||||
FTP_PROPAGATE_ERROR__(WaitSocket(connection))
|
||||
|
||||
// Fill cache.
|
||||
int buffer_left = (ConnectionBufferSize - connection->buffer_usage) - 1,
|
||||
read_count = net_read(connection->handle, &connection->buffer[connection->buffer_usage], buffer_left);
|
||||
|
||||
connection->buffer_usage += read_count;
|
||||
|
||||
if (read_count == -1)
|
||||
__ERR__(__LOG__ << "[!] (FTP) Read error.\n", FtpError)
|
||||
else if (!read_count)
|
||||
break; // EOF
|
||||
}
|
||||
return FtpOk;
|
||||
}
|
||||
FtpLibrary::State FtpLibrary::WriteASCII(char *output, int length, FtpConnection *connection, int &x)
|
||||
{
|
||||
/// TODO
|
||||
if (connection->direction != FtpConnection::Upload)
|
||||
return FtpError;
|
||||
|
||||
FTP_PROPAGATE_ERROR__(WaitSocket(connection))
|
||||
if ((x = net_write(connection->handle, output, length)) != length)
|
||||
return FtpError;
|
||||
|
||||
return FtpOk;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FtpLibrary::State FtpLibrary::ReadBinary(void *buf, int max, FtpConnection *connection, int &length)
|
||||
{
|
||||
if (connection->direction != FtpConnection::Download)
|
||||
return FtpError;
|
||||
|
||||
FTP_PROPAGATE_ERROR__(WaitSocket(connection))
|
||||
length = net_read(connection->handle, buf, max);
|
||||
if (length == -1)
|
||||
return FtpError;
|
||||
|
||||
DataReadCallback(connection);
|
||||
return FtpOk;
|
||||
}
|
||||
FtpLibrary::State FtpLibrary::WriteBinary(void *buf, int len, FtpConnection *connection)
|
||||
{
|
||||
if (connection->direction != FtpConnection::Upload)
|
||||
return FtpError;
|
||||
|
||||
FTP_PROPAGATE_ERROR__(WaitSocket(connection))
|
||||
if (net_write(connection->handle, buf, len) != len)
|
||||
return FtpError;
|
||||
|
||||
DataWriteCallback(connection);
|
||||
return FtpOk;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------------------------------------------------
|
||||
FtpLibrary::State FtpLibrary::DataTransfer(const char *localfile, const char *path, AccessType type, TransferMode mode)
|
||||
//-----------------------------------------------------------------------------------------------------------------------
|
||||
{
|
||||
// Open local file or I/O stream.
|
||||
FILE *file;
|
||||
|
||||
if (localfile)
|
||||
{
|
||||
const char *access;
|
||||
|
||||
switch (type)
|
||||
{
|
||||
default:
|
||||
case AccessDir:
|
||||
case AccessDirVerbose:
|
||||
case AccessFileRead:
|
||||
access = (mode == TransferBinary) ? "wb" : "w";
|
||||
break;
|
||||
|
||||
case AccessFileReadAppend:
|
||||
access = (mode == TransferBinary) ? "ab" : "a";
|
||||
break;
|
||||
|
||||
case AccessFileWriteAppend:
|
||||
case AccessFileWrite:
|
||||
access = (mode == TransferBinary) ? "rb" : "r";
|
||||
break;
|
||||
}
|
||||
|
||||
file = fopen(localfile, access);
|
||||
if (!file)
|
||||
__ERR__(__LOG_E__ << "Failed to open FTP output file.\n", FtpError)
|
||||
|
||||
if (type == AccessFileWriteAppend)
|
||||
if (fseek(file, offset, SEEK_SET))
|
||||
{
|
||||
fclose(file);
|
||||
__ERR__(__LOG_E__ << "Failed to seek in FTP file for transfer.\n", FtpError)
|
||||
}
|
||||
}
|
||||
else
|
||||
file = (type == AccessFileWrite || type == AccessFileWriteAppend) ? stdin : stdout;
|
||||
|
||||
// Create a new connection.
|
||||
FtpConnection *connection = CreateConnection(path, type, mode);
|
||||
if (!connection)
|
||||
{
|
||||
if (localfile)
|
||||
fclose(file);
|
||||
return FtpError;
|
||||
}
|
||||
|
||||
// Perform transfer.
|
||||
State retv = FtpOk;
|
||||
GS::Array <char> temp_buffer(ConnectionBufferSize);
|
||||
int length;
|
||||
|
||||
if ((type == AccessFileWrite) || (type == AccessFileWriteAppend))
|
||||
{
|
||||
while ((length = (int)fread(temp_buffer, 1, ConnectionBufferSize, file)) > 0)
|
||||
if (WriteBinary(temp_buffer, length, connection) != FtpOk)
|
||||
{
|
||||
retv = FtpError; // FTP write failed.
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
while ((retv = ReadBinary(temp_buffer, ConnectionBufferSize, connection, length)) == FtpOk)
|
||||
if (!length || (fwrite(temp_buffer, 1, length, file) <= 0))
|
||||
break; // Done.
|
||||
}
|
||||
|
||||
// Flush file.
|
||||
fflush(file);
|
||||
if (localfile)
|
||||
fclose(file);
|
||||
|
||||
CloseConnection(connection);
|
||||
return retv;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FtpLibrary::State FtpLibrary::Download(const char *file, const char *path, TransferMode mode, int _offset)
|
||||
{
|
||||
offset = _offset;
|
||||
if (!offset)
|
||||
return DataTransfer(file, path, AccessFileRead, mode);
|
||||
else return DataTransfer(file, path, AccessFileReadAppend, mode);
|
||||
}
|
||||
FtpLibrary::State FtpLibrary::Upload(const char *file, const char *path, TransferMode mode, int _offset)
|
||||
{
|
||||
offset = _offset;
|
||||
if (!offset)
|
||||
return DataTransfer(file, path, AccessFileWrite, mode);
|
||||
else return DataTransfer(file, path, AccessFileWriteAppend, mode);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------
|
||||
bool FtpLibrary::Quit()
|
||||
//------------------------------------
|
||||
{
|
||||
if (!master_connection.handle)
|
||||
return true;
|
||||
|
||||
bool r = FtpSendCmd("QUIT", '2');
|
||||
|
||||
net_close(master_connection.handle);
|
||||
master_connection.handle = 0;
|
||||
return r;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool FtpLibrary::DeleteFile(const char *path)
|
||||
{ return FtpSendCmd(String::Format("DELE %s", path).c_str(), '2'); }
|
||||
bool FtpLibrary::ChangeDirectory(const char *path)
|
||||
{ return FtpSendCmd(String::Format("CWD %s", path).c_str(), '2'); }
|
||||
bool FtpLibrary::UpDirectory()
|
||||
{ return FtpSendCmd("CDUP", '2'); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
int FtpLibrary::GetFileSize(const char *path, TransferMode mode)
|
||||
//------------------------------------------------------------------------------
|
||||
{
|
||||
int rs, sz = -1;
|
||||
if ( FtpSendCmd(String::Format("TYPE %c", mode).c_str(), '2') &&
|
||||
FtpSendCmd(String::Format("SIZE %s", path).c_str(), '2') &&
|
||||
(sscanf(response, "%d %d", &rs, &sz) == 2) )
|
||||
return sz;
|
||||
return -1;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
bool FtpLibrary::Nlst(const char *outputfile, const char *path)
|
||||
//----------------------------------------------------------------------------
|
||||
{
|
||||
offset = 0;
|
||||
return DataTransfer(outputfile, path, FtpLibrary::AccessDir, FtpLibrary::TransferASCII) == FtpOk ? true : false;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
FtpLibrary::FtpLibrary()
|
||||
{
|
||||
#if __PLATFORM_WINDOWS__
|
||||
WSADATA wsa;
|
||||
if (WSAStartup(MAKEWORD(1, 1), &wsa))
|
||||
__LOG__ << "[!] (FTP) WINSOCK startup error.\n";
|
||||
#endif
|
||||
|
||||
connection_mode = ConnectionPORT;
|
||||
master_connection.AllocateBuffer();
|
||||
|
||||
//
|
||||
offset = 0;
|
||||
correctpasv = false;
|
||||
}
|
||||
FtpLibrary::~FtpLibrary()
|
||||
{ Quit(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/*
|
||||
int FtpLibrary::Site(const char *cmd)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
if ((strlen(cmd) + 7) > sizeof(buf)) return 0;
|
||||
sprintf(buf,"SITE %s",cmd);
|
||||
if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpLibrary::Raw(const char *cmd)
|
||||
{
|
||||
char buf[256];
|
||||
strncpy(buf, cmd, 256);
|
||||
if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpLibrary::SysType(char *buf, int max)
|
||||
{
|
||||
int l = max;
|
||||
char *b = buf;
|
||||
char *s;
|
||||
if (!FtpSendCmd("SYST",'2',mp_ftphandle)) return 0;
|
||||
s = &mp_ftphandle->response[4];
|
||||
while ((--l) && (*s != ' ')) *b++ = *s++;
|
||||
*b++ = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpLibrary::Mkdir(const char *path)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
if ((strlen(path) + 6) > sizeof(buf)) return 0;
|
||||
sprintf(buf,"MKD %s",path);
|
||||
if (!FtpSendCmd(buf,'2', mp_ftphandle)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
int FtpLibrary::Rmdir(const char *path)
|
||||
{
|
||||
char buf[256];
|
||||
|
||||
if ((strlen(path) + 6) > sizeof(buf)) return 0;
|
||||
sprintf(buf,"RMD %s",path);
|
||||
if (!FtpSendCmd(buf,'2',mp_ftphandle)) return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpLibrary::Pwd(char *path, int max)
|
||||
{
|
||||
int l = max;
|
||||
char *b = path;
|
||||
char *s;
|
||||
|
||||
if (!FtpSendCmd("PWD",'2',mp_ftphandle)) return 0;
|
||||
s = strchr(mp_ftphandle->response, '"');
|
||||
if (s == NULL) return 0;
|
||||
s++;
|
||||
while ((--l) && (*s) && (*s != '"')) *b++ = *s++;
|
||||
*b++ = '\0';
|
||||
return 1;
|
||||
}
|
||||
|
||||
int FtpLibrary::Dir(const char *outputfile, const char *path)
|
||||
{
|
||||
mp_ftphandle->offset = 0;
|
||||
return FtpXfer(outputfile, path, mp_ftphandle, FtpLibrary::dirverbose, FtpLibrary::ascii);
|
||||
}
|
||||
|
||||
int FtpLibrary::ModDate(const char *path, char *dt, int max)
|
||||
{
|
||||
char buf[256];
|
||||
int rv = 1;
|
||||
|
||||
if ((strlen(path) + 7) > sizeof(buf)) return 0;
|
||||
sprintf(buf,"MDTM %s",path);
|
||||
if (!FtpSendCmd(buf,'2',mp_ftphandle)) rv = 0;
|
||||
else strncpy(dt, &mp_ftphandle->response[4], max);
|
||||
return rv;
|
||||
}
|
||||
|
||||
int FtpLibrary::Rename(const char *src, const char *dst)
|
||||
{
|
||||
char cmd[256];
|
||||
|
||||
if (((strlen(src) + 7) > sizeof(cmd)) || ((strlen(dst) + 7) > sizeof(cmd))) return 0;
|
||||
sprintf(cmd,"RNFR %s",src);
|
||||
if (!FtpSendCmd(cmd,'3',mp_ftphandle)) return 0;
|
||||
sprintf(cmd,"RNTO %s",dst);
|
||||
if (!FtpSendCmd(cmd,'2',mp_ftphandle)) return 0;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void FtpLibrary::SetConnmode(connmode mode)
|
||||
{
|
||||
mp_ftphandle->cmode = mode;
|
||||
}
|
||||
|
||||
ftphandle* FtpLibrary::RawOpen(const char *path, accesstype type, transfermode mode)
|
||||
{
|
||||
int ret;
|
||||
ftphandle* datahandle;
|
||||
ret = CreateConnection(path, type, mode, mp_ftphandle, &datahandle);
|
||||
if (ret) return datahandle;
|
||||
else return NULL;
|
||||
}
|
||||
|
||||
int FtpLibrary::RawClose(ftphandle* handle)
|
||||
{
|
||||
return FtpClose(handle);
|
||||
}
|
||||
|
||||
int FtpLibrary::RawWrite(void* buf, int len, ftphandle* handle)
|
||||
{
|
||||
return FtpWrite(buf, len, handle);
|
||||
}
|
||||
|
||||
int FtpLibrary::RawRead(void* buf, int max, ftphandle* handle)
|
||||
{
|
||||
return FtpRead(buf, max, handle);
|
||||
}
|
||||
*/
|
||||
@@ -0,0 +1,59 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_base.h"
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
#include "nstring/nstring.h"
|
||||
#include "hash/nsha1.h"
|
||||
#include "log/log.h"
|
||||
|
||||
using GS::String;
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Base::FileLoad(const char *uri, GS::Array <char> &buffer)
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
size_t size = h->GetSize();
|
||||
if (!buffer.Allocate(size))
|
||||
return false;
|
||||
|
||||
return asbool(h->Read(buffer.c_ptr(), size) == size);
|
||||
}
|
||||
bool Base::FileSave(const char *uri, const GS::Array <char> &buffer)
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri, ModeWrite));
|
||||
if (h.IsNull())
|
||||
return false;
|
||||
|
||||
return asbool(h->Write(buffer.c_ptr(), buffer.GetSize()) == buffer.GetSize());
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Base::Exists(const char *uri)
|
||||
{
|
||||
AutoPtr <Handle> h(Open(uri, ModeRead));
|
||||
return h.IsValid();
|
||||
}
|
||||
String Base::Hash(const char *uri)
|
||||
{
|
||||
Array <char> data;
|
||||
return FileLoad(uri, data) ? SHA1::ComputeHexa(data) : String();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String Base::MapToAbsolute(const char *uri) const
|
||||
{ return String(uri); }
|
||||
String Base::MapToRelative(const char *path) const
|
||||
{ return String(path); }
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,130 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_buffer.h"
|
||||
#include "log/log.h"
|
||||
#include "ntypes.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Buffer::GetCaps() const { return io->GetCaps(); }
|
||||
|
||||
Handle *Buffer::Open(const char *path, Mode mode)
|
||||
{
|
||||
AutoPtr <Handle> io_h(io->Open(path, mode));
|
||||
if (io_h.IsNull())
|
||||
return NULL;
|
||||
|
||||
AutoPtr <BufferHandle> b_h(new BufferHandle(this, io_h));
|
||||
if (b_h.IsNull())
|
||||
return NULL;
|
||||
|
||||
if (!b_h->read_buffer.buffer.Allocate(read_buffer_size))
|
||||
return NULL;
|
||||
|
||||
b_h->size = io_h->GetSize();
|
||||
|
||||
io_h.Detach();
|
||||
return b_h.Detach();
|
||||
}
|
||||
void Buffer::Close(Handle *h)
|
||||
{
|
||||
if (BufferHandle *b_h = (BufferHandle *)h)
|
||||
b_h->handle = NULL;
|
||||
}
|
||||
|
||||
bool Buffer::Delete(const char *path) { return io->Delete(path); }
|
||||
|
||||
size_t Buffer::Tell(Handle *h) { return ((BufferHandle *)h)->pos; }
|
||||
size_t Buffer::Seek(Handle *h, ptrdiff_t offset, SeekRef seek)
|
||||
{
|
||||
if (BufferHandle *c_h = (BufferHandle *)h)
|
||||
{
|
||||
switch (seek)
|
||||
{
|
||||
case SeekStart:
|
||||
c_h->pos = Types::Clamp <ptrdiff_t> (offset, 0, c_h->size);
|
||||
break;
|
||||
case SeekCurrent:
|
||||
c_h->pos = Types::Clamp <ptrdiff_t> (c_h->pos + offset, 0, c_h->size);
|
||||
break;
|
||||
case SeekEnd:
|
||||
c_h->pos = Types::Clamp <ptrdiff_t> (c_h->size + offset, 0, c_h->size);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
return size_t(-1);
|
||||
}
|
||||
size_t Buffer::Read(Handle *h, void *data, size_t size)
|
||||
{
|
||||
// __LOG_V__ << "Buffer::Read: size = " << size << ".\n";
|
||||
|
||||
size_t read_size = 0;
|
||||
if (BufferHandle *b_h = (BufferHandle *)h)
|
||||
{
|
||||
while (read_size < size)
|
||||
{
|
||||
ptrdiff_t buffer_pos = (b_h->pos + read_size) - b_h->read_buffer.start_pos;
|
||||
|
||||
if ((buffer_pos >= 0) && (buffer_pos < ptrdiff_t(b_h->read_buffer.usage)))
|
||||
{
|
||||
ptrdiff_t copy_size = Types::Min <ptrdiff_t> (b_h->read_buffer.usage - buffer_pos, size - read_size);
|
||||
Memory::Copy((char *)data + read_size, b_h->read_buffer.buffer.c_ptr() + buffer_pos, copy_size);
|
||||
// __LOG_V__ << "Buffer read: pos = " << buffer_pos << ", size = " << copy_size << ".\n";
|
||||
read_size += copy_size;
|
||||
}
|
||||
else // refill cache
|
||||
{
|
||||
b_h->read_buffer.start_pos = b_h->pos + read_size;
|
||||
if (b_h->read_buffer.start_pos >= b_h->size)
|
||||
{
|
||||
b_h->read_buffer.usage = 0;
|
||||
break; // EJ 01/05: Improves EOF performance for the OGG streaming interface which makes many out of bound calls.
|
||||
}
|
||||
|
||||
io->Seek(b_h->handle, b_h->read_buffer.start_pos, SeekStart);
|
||||
b_h->read_buffer.usage = io->Read(b_h->handle, b_h->read_buffer.buffer.c_ptr(), b_h->read_buffer.buffer.GetSize());
|
||||
// __LOG_V__ << "Buffer fill: pos = " << b_h->read_buffer.start_pos << ", size = " << b_h->read_buffer.usage << ".\n";
|
||||
if (b_h->read_buffer.usage == 0)
|
||||
break; // EOF
|
||||
}
|
||||
}
|
||||
b_h->pos += read_size;
|
||||
}
|
||||
return read_size;
|
||||
}
|
||||
size_t Buffer::Write(Handle *h, const void *data, size_t size)
|
||||
{
|
||||
if (BufferHandle *b_h = (BufferHandle *)h)
|
||||
{
|
||||
size_t write_size = io->Write(b_h->handle, data, size);
|
||||
b_h->pos += write_size;
|
||||
return write_size;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GS::String Buffer::Hash(const char *path)
|
||||
{ return io->Hash(path); } // [EJ] Do not perform a FileLoad here, IO::Buffer is typically sitting in front of a slow IO backend which potentially optimizes hash transfer.
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Buffer::MkDir(const char *path)
|
||||
{ return io->MkDir(path); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Buffer::Buffer(Base *base, size_t read_size, size_t write_size) : io(base)
|
||||
{
|
||||
read_buffer_size = Types::Clamp <size_t> (read_size, 0, Units::MB(16));
|
||||
write_buffer_size = Types::Clamp <size_t> (write_size, 0, Units::MB(16));
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,280 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_cache.h"
|
||||
#include "hash/nsha1.h"
|
||||
#include "platform.h"
|
||||
#include "log/log.h"
|
||||
#include "ntypes.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
struct FreeEntry // should be in ReserveOnStore but local type on template is prohibited until C++0x
|
||||
{
|
||||
Cache::Entry *entry;
|
||||
int score;
|
||||
|
||||
FreeEntry(Cache::Entry *e, int s) : entry(e), score(s) {}
|
||||
|
||||
static int ComputeScore(const Cache::Entry *e, size_t request_size, const GS::Time &ctime)
|
||||
{
|
||||
int time_bonus = (int)(ctime - e->last_use).toSec();
|
||||
int size_bonus = e->size - request_size;
|
||||
|
||||
return time_bonus + size_bonus / 8;
|
||||
}
|
||||
static int CompareScore(FreeEntry *a, FreeEntry *b)
|
||||
{ return b->score - a->score; }
|
||||
};
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
GS::String Cache::ReserveOnStore(size_t size)
|
||||
{
|
||||
String id = store.Reserve(size);
|
||||
if (!id.IsEmpty())
|
||||
return id;
|
||||
|
||||
// Build a list of reference free entries.
|
||||
Time ctime = Platform::Get().GetTime();
|
||||
|
||||
// No need to drop anything if the request can't fit anyway...
|
||||
size_t total_freeable_store = 0;
|
||||
ListForeachPtr(Entry *, e, entries)
|
||||
if (e->refc == 0)
|
||||
total_freeable_store += e->size;
|
||||
|
||||
if (size > (store.GetFreeStore() + total_freeable_store))
|
||||
return "";
|
||||
|
||||
// Drop entries until the request fits in the store.
|
||||
List <FreeEntry *> free_entries;
|
||||
ListForeachPtr(Entry *, e, entries)
|
||||
if (e->refc == 0)
|
||||
free_entries.Add(new FreeEntry(e, FreeEntry::ComputeScore(e, size, ctime)));
|
||||
|
||||
free_entries.MergeSort(FreeEntry::CompareScore);
|
||||
|
||||
ListForeachPtr(FreeEntry *, e, free_entries)
|
||||
{
|
||||
__LOG_V__ << "IO::Cache: Disposing of cache entry '" << e->entry->path << "' (score: " << e->score << ").\n";
|
||||
|
||||
DeleteCacheEntry(e->entry);
|
||||
|
||||
id = store.Reserve(size);
|
||||
if (!id.IsEmpty())
|
||||
break;
|
||||
}
|
||||
|
||||
ListDeleteAllPtr(FreeEntry *, free_entries)
|
||||
|
||||
store.Save();
|
||||
return id;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Cache::Entry *Cache::GetCacheEntry(const char *path) const
|
||||
{
|
||||
ListForeachPtr(Entry *, entry, entries)
|
||||
if (entry->path == path)
|
||||
return entry;
|
||||
return NULL;
|
||||
}
|
||||
Cache::Entry *Cache::CreateCacheEntry(const char *path)
|
||||
{
|
||||
__LOG_V__ << "Create cache entry for '" << path << "'.\n";
|
||||
|
||||
Array <char> data;
|
||||
if (!io->FileLoad(path, data))
|
||||
return NULL;
|
||||
|
||||
AutoPtr <Entry> entry(new Entry);
|
||||
if (entry.IsNull())
|
||||
return NULL;
|
||||
|
||||
entry->path = path;
|
||||
entry->id = ReserveOnStore(data.GetSize());
|
||||
if (entry->id.IsEmpty())
|
||||
return NULL; // store full
|
||||
|
||||
Entry *e = entry.Detach();
|
||||
entries.Add(e);
|
||||
|
||||
return UpdateCacheEntry(e, &data) ? e : NULL;
|
||||
}
|
||||
bool Cache::UpdateCacheEntry(Entry *entry, GS::Array <char> *preloaded_data)
|
||||
{
|
||||
__LOG_V__ << "Update cache entry for '" << entry->path << "'.\n";
|
||||
|
||||
Array <char> data;
|
||||
if (preloaded_data)
|
||||
data.Transfer(*preloaded_data);
|
||||
else
|
||||
if (!io->FileLoad(entry->path, data))
|
||||
return false;
|
||||
|
||||
// Check current store entry size.
|
||||
if (store.GetEntrySize(entry->id) != data.GetSize())
|
||||
{
|
||||
store.Free(entry->id);
|
||||
entry->id = ReserveOnStore(data.GetSize());
|
||||
|
||||
if (entry->id.IsEmpty())
|
||||
return false; // store is full
|
||||
}
|
||||
|
||||
// Update store data.
|
||||
if (!store.Store(entry->id, data.c_ptr(), data.GetSize(), entry->path))
|
||||
return false;
|
||||
store.Save();
|
||||
|
||||
entry->hash = SHA1::ComputeHexa(data);
|
||||
entry->size = data.GetSize();
|
||||
return true;
|
||||
}
|
||||
bool Cache::DeleteCacheEntry(Entry *entry)
|
||||
{
|
||||
if (entry->refc > 0)
|
||||
return false;
|
||||
|
||||
store.Free(entry->id);
|
||||
store.Save();
|
||||
|
||||
return entries.Remove(entry);
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Cache::GetCaps() const { return io->GetCaps(); }
|
||||
|
||||
Handle *Cache::Open(const char *path, Mode mode)
|
||||
{
|
||||
if (mode == ModeRead)
|
||||
{
|
||||
Threading::MutexLock lock(&mutex);
|
||||
|
||||
// Update cache.
|
||||
Entry *entry = GetCacheEntry(path);
|
||||
|
||||
if (entry)
|
||||
{
|
||||
__LOG_V__ << "Entry match for '" << path << "'.\n";
|
||||
|
||||
if (entry->hash != io->Hash(path))
|
||||
{
|
||||
__LOG_V__ << "Hash mismatch for '" << path << "'.\n";
|
||||
|
||||
if (entry->refc > 0)
|
||||
__LOG_W__ << "IO::Cache: Support file '" << entry->path << "' has changed but its cached version is in use and cannot be updated.\n";
|
||||
else
|
||||
{
|
||||
if (!UpdateCacheEntry(entry)) // contention risk here due to the mutex lock and a potentially long update (eg. networked fs)
|
||||
DeleteCacheEntry(entry);
|
||||
|
||||
// Check for a match in updated cache.
|
||||
entry = GetCacheEntry(path);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
entry = CreateCacheEntry(path);
|
||||
|
||||
// Return handler to store fs.
|
||||
if (entry)
|
||||
{
|
||||
__LOG_V__ << "Opening '" << path << "' from cache.\n";
|
||||
|
||||
entry->last_use = Platform::Get().GetTime();
|
||||
entry->refc++;
|
||||
|
||||
return new CacheHandle(this, path, store.GetIO()->Open(entry->id, mode));
|
||||
}
|
||||
}
|
||||
|
||||
// Direct access to the underlying fs.
|
||||
return io->Open(path, mode);
|
||||
}
|
||||
void Cache::Close(Handle *h)
|
||||
{
|
||||
if (CacheHandle *c_h = (CacheHandle *)h)
|
||||
{
|
||||
Threading::MutexLock lock(&mutex);
|
||||
|
||||
if (Entry *entry = GetCacheEntry(c_h->path))
|
||||
entry->refc--;
|
||||
c_h->handle = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
bool Cache::Delete(const char *path)
|
||||
{
|
||||
/*
|
||||
[EJ] Minor synchronization issue warning.
|
||||
|
||||
If a cached handle is already in use and the support fs file is deleted
|
||||
the cached handle will remain valid. Further open requests will then
|
||||
unexpectedly succeed as long as a single cached handler remains open.
|
||||
|
||||
The correct fix would be to prevent deleting a support file as long as
|
||||
a cached entry with a non-zero reference count exists for it.
|
||||
*/
|
||||
return io->Delete(path);
|
||||
}
|
||||
|
||||
size_t Cache::Tell(Handle *h)
|
||||
{ return ((CacheHandle *)h)->handle->Tell(); }
|
||||
size_t Cache::Seek(Handle *h, ptrdiff_t offset, SeekRef seek)
|
||||
{ return ((CacheHandle *)h)->handle->Seek(offset, seek); }
|
||||
|
||||
size_t Cache::Read(Handle *h, void *data, size_t size)
|
||||
{ return ((CacheHandle *)h)->handle->Read(data, size); }
|
||||
size_t Cache::Write(Handle *h, const void *data, size_t size)
|
||||
{ return ((CacheHandle *)h)->handle->Write(data, size); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Cache::SynchronizeWithStore(const char *store_path)
|
||||
{
|
||||
if (!store.Load(store_path))
|
||||
return false;
|
||||
|
||||
entries.Clear();
|
||||
|
||||
__LOG_H__ << "IO::Cache: Synchronizing with store.\n";
|
||||
|
||||
ListForeachPtr(DataStore::Entry *, e, store.GetEntries())
|
||||
{
|
||||
Entry *entry = new Entry;
|
||||
|
||||
entry->id = e->id;
|
||||
entry->size = e->size;
|
||||
entry->path = e->user;
|
||||
entry->hash = store.GetIO()->Hash(entry->id);
|
||||
|
||||
entries.Add(entry);
|
||||
}
|
||||
|
||||
__LOG__ << "Done, " << entries.GetCount() << " entries synchronized.\n";
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Cache::MkDir(const char *path)
|
||||
{ return io->MkDir(path); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Cache::Cache(Base *base, Base *store_io, size_t store_size) : io(base), store(store_io, store_size) {}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//-----------------------------------------------------------------------------
|
||||
CacheHandle::~CacheHandle()
|
||||
{ GetIOSystem()->Close(this); }
|
||||
//-----------------------------------------------------------------------------
|
||||
@@ -0,0 +1,137 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#if __PLATFORM_POSIX__
|
||||
#include <unistd.h>
|
||||
#include <sys/stat.h>
|
||||
#elif __PLATFORM_WINDOWS__
|
||||
#include <direct.h>
|
||||
#endif
|
||||
#include "filesystem/io_cfile.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
using GS::String;
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void CFile::SetRootPath(const char *_root)
|
||||
{ root = _root; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint CFile::GetCaps() const
|
||||
{
|
||||
uint flags = CanRead | CanWrite | CanSeek | CanMkDir;
|
||||
#ifndef __PLATFORM_WINDOWS__
|
||||
flags |= IsCaseSensitive;
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String CFile::MapToAbsolute(const char *uri) const
|
||||
{
|
||||
if (!root.IsEmpty())
|
||||
return root + "/" + uri;
|
||||
return String(uri);
|
||||
}
|
||||
String CFile::MapToRelative(const char *path) const
|
||||
{
|
||||
String _path(path);
|
||||
if (!root.IsEmpty() && _path.StartsWith(root, GetCaps() & IsCaseSensitive ? String::CaseSensitive : String::CaseInsensitive))
|
||||
return _path.Mid(root.Len());
|
||||
return _path;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle *CFile::Open(const char *path, Mode mode)
|
||||
{
|
||||
String _path(root.IsEmpty() ? path : (root + "/" + path).c_str()),
|
||||
_access(mode == ModeRead ? "rb" : "wb");
|
||||
|
||||
AutoPtr <CFileHandle> h(new CFileHandle(this));
|
||||
#if __PLATFORM_WINDOWS__
|
||||
if (h->file = _wfopen((const wchar_t *)_path.toUcs2().c_ptr(), (const wchar_t *)_access.toUcs2().c_ptr()))
|
||||
return h.Detach();
|
||||
#else
|
||||
if ((h->file = fopen(_path, _access)) != NULL)
|
||||
return h.Detach();
|
||||
#endif
|
||||
return NULL;
|
||||
}
|
||||
void CFile::Close(Handle *h)
|
||||
{
|
||||
if (CFileHandle *ch = (CFileHandle *)h)
|
||||
if (ch->file)
|
||||
fclose(ch->file);
|
||||
}
|
||||
|
||||
bool CFile::Delete(const char *uri)
|
||||
{ return asbool(unlink(root + "/" + uri) == 0); }
|
||||
|
||||
size_t CFile::Seek(Handle *h, ptrdiff_t offset, SeekRef ref)
|
||||
{
|
||||
if (CFileHandle *ch = (CFileHandle *)h)
|
||||
if (ch->file)
|
||||
{
|
||||
int c_seek[] = { SEEK_SET, SEEK_CUR, SEEK_END };
|
||||
return fseek(ch->file, offset, c_seek[ref]);
|
||||
}
|
||||
return (size_t)-1;
|
||||
}
|
||||
size_t CFile::Tell(Handle *h)
|
||||
{
|
||||
if (CFileHandle *ch = (CFileHandle *)h)
|
||||
if (ch->file)
|
||||
return ftell(ch->file);
|
||||
return (size_t)-1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t CFile::Read(Handle *h, void *b, size_t size)
|
||||
{
|
||||
if (CFileHandle *ch = (CFileHandle *)h)
|
||||
if (ch->file)
|
||||
return fread(b, 1, size, ch->file);
|
||||
return 0;
|
||||
}
|
||||
size_t CFile::Write(Handle *h, const void *b, size_t size)
|
||||
{
|
||||
if (CFileHandle *ch = (CFileHandle *)h)
|
||||
if (ch->file)
|
||||
return fwrite(b, size, 1, ch->file) * size;
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool CFile::MkDir(const char *path)
|
||||
{
|
||||
String _path(root.IsEmpty() ? path : (root + "/" + path).c_str());
|
||||
|
||||
bool r = false;
|
||||
#if __PLATFORM_WINDOWS__
|
||||
r = _wmkdir((const wchar_t *)_path.toUcs2().c_ptr()) == 0;
|
||||
#else
|
||||
r = mkdir(path, 01777) == 0;
|
||||
#endif
|
||||
return r;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
CFile::CFile(const char *root_path)
|
||||
{ SetRootPath(root_path); }
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CFileHandle::CFileHandle(Base *io) : Handle(io)
|
||||
{ file = NULL; }
|
||||
CFileHandle::~CFileHandle()
|
||||
{ GetIOSystem()->Close(this); }
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,173 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_crypto.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Crypto::GetCaps() const
|
||||
{ return wrapped_io->GetCaps(); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
void Crypto::Encrypt(GS::Array <char> &data)
|
||||
{
|
||||
size_t len = key.Len();
|
||||
for (size_t n = 0; n < data.GetSize(); ++n)
|
||||
data[(int)n] = data[(int)n] ^ key[(int)(n % len)];
|
||||
}
|
||||
void Crypto::Decrypt(GS::Array <char> &data)
|
||||
{
|
||||
size_t len = key.Len();
|
||||
for (size_t n = 0; n < data.GetSize(); ++n)
|
||||
data[(int)n] = data[(int)n] ^ key[(int)(n % len)];
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle *Crypto::Open(const char *path, Mode mode)
|
||||
{
|
||||
/*
|
||||
When opening a file in read mode, the wrapped io file is decrypted and
|
||||
stored in the cached io file system for further access.
|
||||
When opening a file in write mode, the file is first created in clear
|
||||
on the cached io then encrypted and committed to the wrapped io.
|
||||
*/
|
||||
Handle *h = NULL;
|
||||
|
||||
switch (mode)
|
||||
{
|
||||
case ModeRead:
|
||||
{
|
||||
// Load wrapped IO file.
|
||||
AutoPtr <Handle> _h(wrapped_io->Open(path, mode));
|
||||
if (_h.IsNull())
|
||||
break;
|
||||
|
||||
size_t size = _h->GetSize();
|
||||
Array <char> data(size);
|
||||
if (data.IsNull())
|
||||
break;
|
||||
if (_h->Read(data.c_ptr(), size) != size)
|
||||
break;
|
||||
|
||||
// Decrypt buffer.
|
||||
Decrypt(data);
|
||||
|
||||
// Write decrypted content to cache IO.
|
||||
if ((h = cache_io->Open(path, ModeWrite)) != NULL)
|
||||
h->Write(data.c_ptr(), size);
|
||||
_safe_delete(h);
|
||||
|
||||
h = cache_io->Open(path, mode);
|
||||
}
|
||||
break;
|
||||
|
||||
case ModeWrite:
|
||||
h = cache_io->Open(path, mode);
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
|
||||
return h ? new CryptoHandle(this, h, path, mode) : NULL;
|
||||
}
|
||||
void Crypto::Close(Handle *_h)
|
||||
{
|
||||
if (CryptoHandle *h = (CryptoHandle *)_h)
|
||||
{
|
||||
h->cached_h = NULL;
|
||||
|
||||
switch (h->io_mode)
|
||||
{
|
||||
case ModeRead:
|
||||
cache_io->Delete(h->path);
|
||||
break;
|
||||
|
||||
case ModeWrite:
|
||||
{
|
||||
// Retrieve the whole cached file content and drop it from the cache IO.
|
||||
h->cached_h = cache_io->Open(h->path);
|
||||
|
||||
size_t size = h->cached_h->GetSize();
|
||||
Array <char> data(size);
|
||||
|
||||
if (data.IsValid())
|
||||
h->cached_h->Read(data.c_ptr(), size);
|
||||
|
||||
h->cached_h = NULL;
|
||||
cache_io->Delete(h->path);
|
||||
|
||||
// Encrypt buffer.
|
||||
Encrypt(data);
|
||||
|
||||
// Commit file to the wrapped IO.
|
||||
AutoPtr <Handle> _h(wrapped_io->Open(h->path, ModeWrite));
|
||||
if (_h.IsValid())
|
||||
_h->Write(data.c_ptr(), size);
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
}
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Crypto::Delete(const char *path)
|
||||
{ return wrapped_io->Delete(path); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Crypto::Tell(Handle *_h)
|
||||
{
|
||||
if (CryptoHandle *h = (CryptoHandle *)_h)
|
||||
return h->cached_h->Tell();
|
||||
return (size_t)-1;
|
||||
}
|
||||
size_t Crypto::Seek(Handle *_h, ptrdiff_t offset, SeekRef seek)
|
||||
{
|
||||
if (CryptoHandle *h = (CryptoHandle *)_h)
|
||||
return h->cached_h->Seek(offset, seek);
|
||||
return (size_t)-1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Crypto::Read(Handle *_h, void *p, size_t s)
|
||||
{
|
||||
if (CryptoHandle *h = (CryptoHandle *)_h)
|
||||
return h->cached_h->Read(p, s);
|
||||
return 0;
|
||||
}
|
||||
size_t Crypto::Write(Handle *_h, const void *p, size_t s)
|
||||
{
|
||||
if (CryptoHandle *h = (CryptoHandle *)_h)
|
||||
return h->cached_h->Write(p, s);
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Crypto::MkDir(const char *path)
|
||||
{ return wrapped_io->MkDir(path); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
Crypto::Crypto(Base *_io, const char *_key) : wrapped_io(_io)
|
||||
{
|
||||
cache_io = new Memory;
|
||||
key = _key;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
CryptoHandle::CryptoHandle(Base *io, Handle *h, const char *_path, Mode mode) : Handle(io), cached_h(h), path(_path), io_mode(mode)
|
||||
{}
|
||||
CryptoHandle::~CryptoHandle()
|
||||
{ GetIOSystem()->Close(this); }
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,141 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#if __PLATFORM_POSIX__
|
||||
#include <unistd.h>
|
||||
#endif
|
||||
#include "filesystem/io_dispatcher.h"
|
||||
#include "memory/nauto_ptr.h"
|
||||
|
||||
using namespace GS;
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Dispatcher::AddDispatch(Base *fs, const char *prefix)
|
||||
{
|
||||
if (prefix)
|
||||
{
|
||||
DispatchFS *d = new DispatchFS;
|
||||
if (!d)
|
||||
return false;
|
||||
|
||||
d->fs = fs;
|
||||
d->prefix = prefix;
|
||||
mounts.Add(d);
|
||||
}
|
||||
else
|
||||
roots.Add(fs);
|
||||
|
||||
return true;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Dispatcher::GetCaps() const
|
||||
{
|
||||
uint flags = CanRead | CanWrite | CanSeek;
|
||||
#ifndef __PLATFORM_WINDOWS__
|
||||
flags |= IsCaseSensitive;
|
||||
#endif
|
||||
return flags;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Base *Dispatcher::Dispatch(String &uri, Mode mode)
|
||||
{
|
||||
ListForeachPtr(DispatchFS *, d, mounts)
|
||||
if (uri.StartsWith(d->prefix))
|
||||
{
|
||||
uri = uri.Mid(d->prefix.Len());
|
||||
return d->fs;
|
||||
}
|
||||
|
||||
// Root FS make no sense for write operations.
|
||||
if (mode == ModeRead)
|
||||
ListForeachPtr(Base *, d, roots)
|
||||
if (d->Exists(uri))
|
||||
return d;
|
||||
|
||||
return NULL;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle *Dispatcher::Open(const char *uri, Mode mode)
|
||||
{
|
||||
String _uri(uri);
|
||||
|
||||
Base *base = Dispatch(_uri, mode);
|
||||
if (!base)
|
||||
return NULL;
|
||||
|
||||
Handle *h = base->Open(_uri, mode);
|
||||
return h ? new DispatcherHandle(this, h) : NULL;
|
||||
}
|
||||
void Dispatcher::Close(Handle *h)
|
||||
{
|
||||
if (DispatcherHandle *dh = (DispatcherHandle *)h)
|
||||
dh->handle->GetIOSystem()->Close(dh->handle);
|
||||
}
|
||||
bool Dispatcher::Delete(const char *uri)
|
||||
{ return false; }
|
||||
size_t Dispatcher::Seek(Handle *h, ptrdiff_t offset, SeekRef ref)
|
||||
{
|
||||
if (DispatcherHandle *dh = (DispatcherHandle *)h)
|
||||
return dh->handle->GetIOSystem()->Seek(dh->handle, offset, ref);
|
||||
return (size_t)-1;
|
||||
}
|
||||
size_t Dispatcher::Tell(Handle *h)
|
||||
{
|
||||
if (DispatcherHandle *dh = (DispatcherHandle *)h)
|
||||
return dh->handle->GetIOSystem()->Tell(dh->handle);
|
||||
return (size_t)-1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Dispatcher::Read(Handle *h, void *b, size_t size)
|
||||
{
|
||||
if (DispatcherHandle *dh = (DispatcherHandle *)h)
|
||||
return dh->handle->GetIOSystem()->Read(dh->handle, b, size);
|
||||
return 0;
|
||||
}
|
||||
size_t Dispatcher::Write(Handle *h, const void *b, size_t size)
|
||||
{
|
||||
if (DispatcherHandle *dh = (DispatcherHandle *)h)
|
||||
return dh->handle->GetIOSystem()->Write(dh->handle, b, size);
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Dispatcher::MkDir(const char *path)
|
||||
{
|
||||
String _path(path);
|
||||
if (Base *base = Dispatch(_path, ModeWrite))
|
||||
return base->MkDir(_path);
|
||||
return false;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
String Dispatcher::Hash(const char *uri)
|
||||
{
|
||||
String _uri(uri);
|
||||
if (Base *base = Dispatch(_uri, ModeRead))
|
||||
return base->Hash(_uri);
|
||||
return String();
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
DispatcherHandle::DispatcherHandle(Base *io, Handle *h) : Handle(io), handle(h)
|
||||
{}
|
||||
DispatcherHandle::~DispatcherHandle()
|
||||
{ GetIOSystem()->Close(this); }
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,58 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include <string.h>
|
||||
#include "filesystem/io_handle.h"
|
||||
#include "filesystem/io_base.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Handle::GetSize()
|
||||
{
|
||||
size_t p = Tell();
|
||||
Seek(0, Base::SeekEnd);
|
||||
size_t s = Tell();
|
||||
Seek(p, Base::SeekStart);
|
||||
return s;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
bool Handle::IsEOF()
|
||||
{ return Tell() >= GetSize(); }
|
||||
size_t Handle::Rewind()
|
||||
{ return Seek(0, Base::SeekStart); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Handle::Tell()
|
||||
{ return io_sys.IsValid() ? io_sys->Tell(this) : 0; }
|
||||
size_t Handle::Seek(ptrdiff_t offset, Base::SeekRef seek_ref)
|
||||
{ return io_sys.IsValid() ? io_sys->Seek(this, offset, seek_ref) : 0; }
|
||||
|
||||
size_t Handle::Read(void *p, size_t size)
|
||||
{ return io_sys.IsValid() ? io_sys->Read(this, p, size) : 0; }
|
||||
size_t Handle::Write(const void *p, size_t size)
|
||||
{ return io_sys.IsValid() ? io_sys->Write(this, p, size) : 0; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle &Handle::operator << (const char *s)
|
||||
{
|
||||
if (s)
|
||||
Write(s, strlen(s));
|
||||
return *this;
|
||||
}
|
||||
Handle &Handle::operator << (char *s)
|
||||
{ return *this << ((const char *)s); }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle::Handle(Base *io) : io_sys(io) {}
|
||||
Handle::~Handle() {}
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,80 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_handle_segment.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t HandleSegment::GetSize()
|
||||
{ return size; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t HandleSegment::Tell()
|
||||
{ return cursor; }
|
||||
size_t HandleSegment::Seek(ptrdiff_t offset, Base::SeekRef ref)
|
||||
{
|
||||
switch (ref)
|
||||
{
|
||||
case Base::SeekStart:
|
||||
cursor = offset;
|
||||
break;
|
||||
case Base::SeekCurrent:
|
||||
cursor += offset;
|
||||
break;
|
||||
case Base::SeekEnd:
|
||||
cursor = size - offset;
|
||||
break;
|
||||
}
|
||||
|
||||
if (cursor > size)
|
||||
{
|
||||
cursor = 0;
|
||||
return (size_t)-1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
/*
|
||||
IO segment must always restore the original handle position and act as
|
||||
transparently as possible.
|
||||
*/
|
||||
size_t HandleSegment::Read(void *b, size_t s)
|
||||
{
|
||||
size_t o = 0;
|
||||
|
||||
size_t t = handle->Tell();
|
||||
|
||||
if (!handle->Seek(offset + cursor, Base::SeekStart))
|
||||
{
|
||||
if (s > (size - cursor))
|
||||
s = size - cursor;
|
||||
o = handle->Read(b, s);
|
||||
cursor += o;
|
||||
}
|
||||
handle->Seek(t, Base::SeekStart);
|
||||
|
||||
return o;
|
||||
}
|
||||
size_t HandleSegment::Write(const void *b, size_t s)
|
||||
{
|
||||
size_t o = 0;
|
||||
|
||||
size_t t = handle->Tell();
|
||||
if (!handle->Seek(offset + cursor, Base::SeekStart))
|
||||
{
|
||||
o = handle->Write(b, s);
|
||||
cursor += o;
|
||||
}
|
||||
handle->Seek(t, Base::SeekStart);
|
||||
|
||||
return o;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
@@ -0,0 +1,132 @@
|
||||
/* -----------------------------------------------------------------------------
|
||||
GSFramework
|
||||
Copyright 2001-2013 Emmanuel Julien. All Rights Reserved.
|
||||
----------------------------------------------------------------------------- */
|
||||
|
||||
|
||||
#include "filesystem/io_memory.h"
|
||||
|
||||
using namespace GS::IO;
|
||||
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
uint Memory::GetCaps() const
|
||||
{ return CanRead | CanWrite | CanSeek| IsCaseSensitive; }
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
Handle *Memory::Open(const char *uri, Mode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ModeRead:
|
||||
ListForeachPtr(MemoryFile *, f, fat)
|
||||
if (f->uri == uri)
|
||||
return new MemoryHandle(this, f, mode);
|
||||
break;
|
||||
|
||||
case ModeWrite:
|
||||
{
|
||||
MemoryFile *f_entry = NULL;
|
||||
ListForeachPtr(MemoryFile *, f, fat)
|
||||
if (f->uri == uri)
|
||||
{
|
||||
f_entry = f;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!f_entry)
|
||||
fat.Add(f_entry = new MemoryFile(uri));
|
||||
|
||||
return f_entry ? new MemoryHandle(this, f_entry, mode) : NULL;
|
||||
}
|
||||
break;
|
||||
|
||||
default: break;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
void Memory::Close(Handle *h)
|
||||
{ /* Nothing to be done. */ }
|
||||
|
||||
bool Memory::Delete(const char *uri)
|
||||
{
|
||||
ListForeachPtr(MemoryFile *, f, fat)
|
||||
if (f->uri == uri)
|
||||
return fat.Remove(f);
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t Memory::Tell(Handle *h)
|
||||
{
|
||||
if (MemoryHandle *_h = (MemoryHandle *)h)
|
||||
if (_h->file)
|
||||
return _h->cursor;
|
||||
return (size_t)-1;
|
||||
}
|
||||
size_t Memory::Seek(Handle *h, ptrdiff_t offset, SeekRef seek_ref)
|
||||
{
|
||||
if (MemoryHandle *_h = (MemoryHandle *)h)
|
||||
if (_h->file)
|
||||
{
|
||||
switch (seek_ref)
|
||||
{
|
||||
case SeekStart:
|
||||
_h->cursor = Types::Clamp <ptrdiff_t> (offset, 0, _h->file->size);
|
||||
break;
|
||||
case SeekCurrent:
|
||||
_h->cursor = Types::Clamp <ptrdiff_t> (_h->cursor + offset, 0, _h->file->size);
|
||||
break;
|
||||
case SeekEnd:
|
||||
_h->cursor = Types::Clamp <ptrdiff_t> (_h->file->size - offset, 0, _h->file->size);
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (size_t)-1;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
size_t Memory::Read(Handle *h, void *ptr, size_t size)
|
||||
{
|
||||
size_t read_size = 0;
|
||||
if (MemoryHandle *_h = (MemoryHandle *)h)
|
||||
if (_h->file && (_h->mode == ModeRead))
|
||||
{
|
||||
read_size = Types::Min <ptrdiff_t> (size, _h->file->size - _h->cursor);
|
||||
GS::Memory::Copy(ptr, &_h->file->data[(int)_h->cursor], read_size);
|
||||
_h->cursor += read_size;
|
||||
}
|
||||
|
||||
return read_size;
|
||||
}
|
||||
size_t Memory::Write(Handle *h, const void *ptr, size_t size)
|
||||
{
|
||||
size_t write_size = 0;
|
||||
if (MemoryHandle *_h = (MemoryHandle *)h)
|
||||
if (_h->file && (_h->mode == ModeWrite))
|
||||
{
|
||||
size_t req_size = _h->cursor + size;
|
||||
if (req_size > _h->file->data.GetSize())
|
||||
if (!_h->file->data.Reallocate(req_size + 16384)) // required size + 16 kilobytes
|
||||
return 0;
|
||||
|
||||
GS::Memory::Copy(&_h->file->data[(int)_h->cursor], ptr, size);
|
||||
write_size = size;
|
||||
|
||||
_h->cursor += size;
|
||||
_h->file->size = Types::Max(_h->file->size, req_size);
|
||||
}
|
||||
|
||||
return write_size;
|
||||
}
|
||||
//------------------------------------------------------------------------------
|
||||
|
||||
//------------------------------------------------------------------------------
|
||||
MemoryHandle::MemoryHandle(Base *io, MemoryFile *_file, Mode _mode) : Handle(io), file(_file), cursor(0), mode(_mode)
|
||||
{}
|
||||
MemoryHandle::~MemoryHandle()
|
||||
{ GetIOSystem()->Close(this); }
|
||||
//------------------------------------------------------------------------------
|
||||
Reference in New Issue
Block a user