first commit

This commit is contained in:
Maschell 2019-11-24 14:20:08 +01:00
commit 51247b0dc1
23 changed files with 2957 additions and 0 deletions

4
.gitignore vendored Normal file
View File

@ -0,0 +1,4 @@
build/*
*.mod
sysapp.layout
sysapp.cbp

272
Makefile Normal file
View File

@ -0,0 +1,272 @@
# You probably never need to adjust this Makefile.
# All changes can be done in the makefile.mk
#---------------------------------------------------------------------------------
# Clear the implicit built in rules
#---------------------------------------------------------------------------------
.SUFFIXES:
#---------------------------------------------------------------------------------
ifeq ($(strip $(DEVKITPPC)),)
$(error "Please set DEVKITPPC in your environment. export DEVKITPPC=<path to>devkitPPC")
endif
ifeq ($(strip $(DEVKITPRO)),)
$(error "Please set DEVKITPRO in your environment. export DEVKITPRO=<path to>devkitPRO")
endif
export PATH := $(DEVKITPPC)/bin:$(PORTLIBS)/bin:$(PATH)
export PORTLIBS := $(DEVKITPRO)/portlibs/ppc
export WUPSDIR := $(DEVKITPRO)/wups
export GCC_VER := $(shell $(DEVKITPPC)/bin/powerpc-eabi-gcc -dumpversion)
PREFIX := powerpc-eabi-
export AS := $(PREFIX)as
export CC := $(PREFIX)gcc
export CXX := $(PREFIX)g++
export LD := $(PREFIX)ld
export AR := $(PREFIX)ar
export OBJCOPY := $(PREFIX)objcopy
#---------------------------------------------------------------------------------
# TARGET is the name of the output
# BUILD is the directory where object files & intermediate files will be placed
# SOURCES is a list of directories containing source code
# INCLUDES is a list of directories containing extra header files
#---------------------------------------------------------------------------------
TARGET := $(notdir $(CURDIR))
BUILD := build
ifeq ($(notdir $(CURDIR)),$(BUILD))
include ../makefile.mk
else
include makefile.mk
endif
include $(WUPSDIR)/plugin_makefile.mk
#MAP ?= $(TARGET:.mod=.map)
#---------------------------------------------------------------------------------
# options for code generation
#---------------------------------------------------------------------------------
MACHDEP = -DESPRESSO -mcpu=750 -meabi -mhard-float
# -Os: optimise size
# -Wall: generate lots of warnings
# -D__wiiu__: define the symbol __wiiu__ (used in some headers)
# -mcpu=750: enable processor specific compilation
# -meabi: enable eabi specific compilation
# -mhard-float: enable hardware floating point instructions
# -nostartfiles: Do not use the standard system startup files when linking
# -ffunction-sections: split up functions so linker can garbage collect
# -fdata-sections: split up data so linker can garbage collect
COMMON_CFLAGS := -O0 -Wall $(MACHDEP) -meabi -ffunction-sections -fdata-sections -Wl,-q $(COMMON_CFLAGS)
CFLAGS += -D__WIIU__ -D__WUT__
# -x c: compile as c code
# -std=c11: use the c11 standard
CFLAGS := $(COMMON_CFLAGS) -x c -std=gnu11 $(CFLAGS)
# -x c++: compile as c++ code
# -std=gnu++11: use the c++11 standard
CXXFLAGS := $(COMMON_CFLAGS) -x c++ -std=gnu++11 $(CXXFLAGS)
#---------------------------------------------------------------------------------
# any extra ld flags
#--------------------------------------------------------------------------------
# --gc-sections: remove unneeded symbols
# -Map: generate a map file
LDFLAGS += $(ARCH) -Wl,-Map,$(notdir $@).map,--gc-sections,-wrap,__gxx_personality_v0
#---------------------------------------------------------------------------------
Q := @
MAKEFLAGS += --no-print-directory
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS +=
#
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS +=
#---------------------------------------------------------------------------------
# no real need to edit anything past this point unless you need to add additional
# rules for different file extensions
#---------------------------------------------------------------------------------
ifneq ($(BUILD),$(notdir $(CURDIR)))
#---------------------------------------------------------------------------------
export PROJECTDIR := $(CURDIR)
export OUTPUT := $(CURDIR)/$(TARGETDIR)/$(TARGET)
export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \
$(foreach dir,$(DATA),$(CURDIR)/$(dir))
export DEPSDIR := $(CURDIR)/$(BUILD)
#---------------------------------------------------------------------------------
# automatically build a list of object files for our project
#---------------------------------------------------------------------------------
CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c)))
CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp)))
sFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s)))
SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.S)))
BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*)))
TTFFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.ttf)))
PNGFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.png)))
#---------------------------------------------------------------------------------
# use CXX for linking C++ projects, CC for standard C
#---------------------------------------------------------------------------------
ifeq ($(strip $(CPPFILES)),)
export REAL_LD := $(CC)
else
export REAL_LD := $(CXX)
endif
export OFILES := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) \
$(sFILES:.s=.o) $(SFILES:.S=.o) \
$(PNGFILES:.png=.png.o) $(addsuffix .o,$(BINFILES))
#---------------------------------------------------------------------------------
# build a list of include paths
#---------------------------------------------------------------------------------
export INCLUDE_FULL += $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \
$(foreach dir,$(LIBDIRS),-I$(dir)/include) \
$(EXTERNAL_INCLUDE)
#---------------------------------------------------------------------------------
# build a list of library paths
#---------------------------------------------------------------------------------
export LIBPATHS_FULL += $(foreach dir,$(LIBDIRS),-L$(dir)/lib) \
$(EXTERNAL_LIBPATHS)
export OUTPUT := $(CURDIR)/$(TARGET)
.PHONY: $(BUILD) clean install
#---------------------------------------------------------------------------------
$(BUILD):
@[ -d $@ ] || mkdir -p $@
@$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile
#---------------------------------------------------------------------------------
clean:
@echo clean ...
@rm -fr $(BUILD) $(OUTPUT).mod $(OUTPUT)
#---------------------------------------------------------------------------------
else
DEPENDS := $(OFILES:.o=.d)
THIS_DIR := $(dir $(abspath $(lastword $(MAKEFILE_LIST))))
###############################################################################
# Rule to make everything.
PHONY += all
all : $(OUTPUT)
###############################################################################
# Special build rules
# Rule to make the module file.
$(OUTPUT) : $(OFILES)
@echo "linking ... " $@
@$(REAL_LD) $(OFILES) $(LDFLAGS) $(LIBS) $(LIBPATHS_FULL) -o $@
###############################################################################
# Standard build rules
#---------------------------------------------------------------------------------
%.a:
#---------------------------------------------------------------------------------
@echo $(notdir $@)
@rm -f $@
@$(AR) -rc $@ $^
#---------------------------------------------------------------------------------
%.o: %.cpp
@echo $(notdir $<)
@$(CXX) -MMD -MP -MF $(DEPSDIR)/$*.d $(CXXFLAGS) $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER)
#---------------------------------------------------------------------------------
%.o: %.c
@echo $(notdir $<)
@$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d $(CFLAGS) $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER)
#---------------------------------------------------------------------------------
%.o: %.S
@echo $(notdir $<)
$(CC) -MMD -MP -MF $(DEPSDIR)/$*.d -x assembler-with-cpp $(INCLUDE_FULL) -c $< -o $@ $(ERROR_FILTER)
#---------------------------------------------------------------------------------
%.png.o : %.png
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.jpg.o : %.jpg
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.ttf.o : %.ttf
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.bin.o : %.bin
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.wav.o : %.wav
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.mp3.o : %.mp3
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
#---------------------------------------------------------------------------------
%.ogg.o : %.ogg
@echo $(notdir $<)
@bin2s -a 32 $< | $(AS) -o $(@)
###############################################################################
# Assembly listing rules
# Rule to make assembly listing.
PHONY += list
list : $(LIST)
# Rule to make the listing file.
%.list : $(TARGET)
$(LOG)
-$Qmkdir -p $(dir $@)
$Q$(OBJDUMP) -d $< > $@
###############################################################################
# Clean rule
# Rule to clean files.
PHONY += clean
clean :
$Qrm -rf $(wildcard $(BUILD) $(BIN))
###############################################################################
# Phony targets
.PHONY : $(PHONY)
-include $(DEPENDS)
#---------------------------------------------------------------------------------
endif
#---------------------------------------------------------------------------------

50
makefile.mk Normal file
View File

@ -0,0 +1,50 @@
# Compiling the projects with libutils logging code?
DO_LOGGING := 1
# Target filename
TARGET := $(notdir $(CURDIR)).mod
# Source directories
SOURCES := src src/utils
# Data directories
DATA :=
# Include directories
INCLUDES := src
#---------------------------------------------------------------------------------
# options for code generation and linking
#---------------------------------------------------------------------------------
# Extra C AND C++ compiler flags
COMMON_CFLAGS :=
# Extra C compiler flags
CFLAGS :=
# Extra C++ compiler flags
CXXFLAGS :=
# Extra linking flags for all linking steps
LDFLAGS :=
#---------------------------------------------------------------------------------
# list of directories containing libraries, this must be the top level containing
# include and lib
#---------------------------------------------------------------------------------
LIBDIRS := $(WUPSDIR) $(WUT_ROOT) $(PORTLIBS)
#---------------------------------------------------------------------------------
# any extra libraries we wish to link with the project
#---------------------------------------------------------------------------------
LIBS := -lwups -lwut -liosuhax
#---------------------------------------------------------------------------------
# Will be added to the final lib paths
# example:
# -L$C:/library1/lib
#---------------------------------------------------------------------------------
EXTERNAL_LIBPATHS :=
#---------------------------------------------------------------------------------
# Will be added to the final include paths
# -IC:/library1/include
#---------------------------------------------------------------------------------
EXTERNAL_INCLUDE :=

53
src/BackgroundThread.cpp Normal file
View File

@ -0,0 +1,53 @@
#include "BackgroundThread.hpp"
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <coreinit/cache.h>
#include <utils/logger.h>
#include "ftp.h"
#include "net.h"
BackgroundThread * BackgroundThread::instance = NULL;
BackgroundThread::BackgroundThread(): BackgroundThreadWrapper(BackgroundThread::getPriority()) {
DEBUG_FUNCTION_LINE("Create new Server\n");
mutex.lock();
this->serverSocket = create_server(PORT);
DCFlushRange(&(this->serverSocket), 4);
mutex.unlock();
DEBUG_FUNCTION_LINE("handle %d\n", this->serverSocket);
resumeThread();
}
BackgroundThread::~BackgroundThread() {
DEBUG_FUNCTION_LINE("Clean up FTP\n");
if(this->serverSocket != -1){
mutex.lock();
cleanup_ftp();
network_close(this->serverSocket);
mutex.unlock();
this->serverSocket = -1;
}
DEBUG_FUNCTION_LINE("Cleaned up FTP\n");
}
BOOL BackgroundThread::whileLoop() {
if(this->serverSocket != -1){
mutex.lock();
network_down = process_ftp_events(this->serverSocket);
mutex.unlock();
if(network_down) {
DEBUG_FUNCTION_LINE("Network is down %d\n", this->serverSocket);
mutex.lock();
cleanup_ftp();
network_close(this->serverSocket);
this->serverSocket = -1;
DCFlushRange(&(this->serverSocket), 4);
mutex.unlock();
}
OSSleepTicks(OSMillisecondsToTicks(16));
}
return true;
}

50
src/BackgroundThread.hpp Normal file
View File

@ -0,0 +1,50 @@
#pragma once
#include "utils/BackgroundThreadWrapper.hpp"
#include <coreinit/cache.h>
#include "utils/logger.h"
#define PORT 21
class BackgroundThread: BackgroundThreadWrapper {
public:
static BackgroundThread *getInstance() {
DCFlushRange(&instance, sizeof(instance));
ICInvalidateRange(&instance, sizeof(instance));
if(instance == NULL) {
instance = new BackgroundThread();
DCFlushRange(&instance, sizeof(instance));
ICInvalidateRange(&instance, sizeof(instance));
}
return instance;
}
static void destroyInstance() {
DCFlushRange(&instance, sizeof(instance));
ICInvalidateRange(&instance, sizeof(instance));
DEBUG_FUNCTION_LINE("Instance is %08X\n", instance);
OSSleepTicks(OSSecondsToTicks(1));
if(instance != NULL) {
delete instance;
instance = NULL;
DCFlushRange(&instance, sizeof(instance));
ICInvalidateRange(&instance, sizeof(instance));
}
}
BackgroundThread();
virtual ~BackgroundThread();
private:
static int32_t getPriority() {
return 16;
}
virtual BOOL whileLoop();
static BackgroundThread * instance;
int serverSocket = -1;
int network_down = 0;
};

921
src/ftp.c Normal file
View File

@ -0,0 +1,921 @@
/*
ftpii -- an FTP server for the Wii
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#include <malloc.h>
#include <stdlib.h>
#include <string.h>
#include <sys/dir.h>
#include <coreinit/thread.h>
#include <unistd.h>
#include <nsysnet/socket.h>
#include "main.h"
#include "utils/logger.h"
//! TODO: fix those function
#define gettime() OSGetTick()
#define errno wiiu_geterrno()
#include "ftp.h"
#include "virtualpath.h"
#include "net.h"
#include "vrt.h"
#define UNUSED __attribute__((unused))
#define FTP_BUFFER_SIZE 1024
#define MAX_CLIENTS 5
static const uint16_t SRC_PORT = 20;
static const int32_t EQUIT = 696969;
static const char *CRLF = "\r\n";
static const uint32_t CRLF_LENGTH = 2;
static uint8_t num_clients = 0;
static uint16_t passive_port = 1024;
static char *password = NULL;
void console_printf(const char *format, ...){
}
typedef int32_t (*data_connection_callback)(int32_t data_socket, void *arg);
struct client_struct {
int32_t socket;
char representation_type;
int32_t passive_socket;
int32_t data_socket;
char cwd[MAXPATHLEN];
char pending_rename[MAXPATHLEN];
off_t restart_marker;
struct sockaddr_in address;
bool authenticated;
char buf[FTP_BUFFER_SIZE];
int32_t offset;
bool data_connection_connected;
data_connection_callback data_callback;
void *data_connection_callback_arg;
void (*data_connection_cleanup)(void *arg);
uint64_t data_connection_timer;
};
typedef struct client_struct client_t;
static client_t *clients[MAX_CLIENTS] = { NULL };
void set_ftp_password(char *new_password) {
if (password)
free(password);
if (new_password) {
password = malloc(strlen(new_password) + 1);
if (!password)
return;
strcpy((char *)password, new_password);
} else {
password = NULL;
}
}
static bool compare_ftp_password(char *password_attempt) {
return !password || !strcmp((char *)password, password_attempt);
}
/*
TODO: support multi-line reply
*/
static int32_t write_reply(client_t *client, uint16_t code, char *msg) {
uint32_t msglen = 4 + strlen(msg) + CRLF_LENGTH;
char * msgbuf = (char *) malloc(msglen + 1);
if (msgbuf == NULL)
return -ENOMEM;
sprintf(msgbuf, "%u %s\r\n", code, msg);
console_printf("Wrote reply: %s", msgbuf);
int32_t ret = send_exact(client->socket, msgbuf, msglen);
free(msgbuf);
return ret;
}
static void close_passive_socket(client_t *client) {
if (client->passive_socket >= 0) {
network_close_blocking(client->passive_socket);
client->passive_socket = -1;
}
}
/*
result must be able to hold up to maxsplit+1 null-terminated strings of length strlen(s)
returns the number of strings stored in the result array (up to maxsplit+1)
*/
static uint32_t split(char *s, char sep, uint32_t maxsplit, char *result[]) {
uint32_t num_results = 0;
uint32_t result_pos = 0;
uint32_t trim_pos = 0;
bool in_word = false;
for (; *s; s++) {
if (*s == sep) {
if (num_results <= maxsplit) {
in_word = false;
continue;
} else if (!trim_pos) {
trim_pos = result_pos;
}
} else if (trim_pos) {
trim_pos = 0;
}
if (!in_word) {
in_word = true;
if (num_results <= maxsplit) {
num_results++;
result_pos = 0;
}
}
result[num_results - 1][result_pos++] = *s;
result[num_results - 1][result_pos] = '\0';
}
if (trim_pos) {
result[num_results - 1][trim_pos] = '\0';
}
uint32_t i = num_results;
for (i = num_results; i <= maxsplit; i++) {
result[i][0] = '\0';
}
return num_results;
}
static int32_t ftp_USER(client_t *client, char *username UNUSED) {
return write_reply(client, 331, "User name okay, need password.");
}
static int32_t ftp_PASS(client_t *client, char *password_attempt) {
if (compare_ftp_password(password_attempt)) {
client->authenticated = true;
return write_reply(client, 230, "User logged in, proceed.");
} else {
return write_reply(client, 530, "Login incorrect.");
}
}
static int32_t ftp_REIN(client_t *client, char *rest UNUSED) {
close_passive_socket(client);
strcpy(client->cwd, "/");
client->representation_type = 'A';
client->authenticated = false;
return write_reply(client, 220, "Service ready for new user.");
}
static int32_t ftp_QUIT(client_t *client, char *rest UNUSED) {
// TODO: dont quit if xfer in progress
int32_t result = write_reply(client, 221, "Service closing control connection.");
return result < 0 ? result : -EQUIT;
}
static int32_t ftp_SYST(client_t *client, char *rest UNUSED) {
return write_reply(client, 215, "UNIX Type: L8 Version: ftpii");
}
static int32_t ftp_TYPE(client_t *client, char *rest) {
char representation_type[FTP_BUFFER_SIZE], param[FTP_BUFFER_SIZE];
char *args[] = { representation_type, param };
uint32_t num_args = split(rest, ' ', 1, args);
if (num_args == 0) {
return write_reply(client, 501, "Syntax error in parameters.");
} else if ((!strcasecmp("A", representation_type) && (!*param || !strcasecmp("N", param))) ||
(!strcasecmp("I", representation_type) && num_args == 1)) {
client->representation_type = *representation_type;
} else {
return write_reply(client, 501, "Syntax error in parameters.");
}
char msg[15];
snprintf(msg, sizeof(msg), "Type set to %s.", representation_type);
return write_reply(client, 200, msg);
}
static int32_t ftp_MODE(client_t *client, char *rest) {
if (!strcasecmp("S", rest)) {
return write_reply(client, 200, "Mode S ok.");
} else {
return write_reply(client, 501, "Syntax error in parameters.");
}
}
static int32_t ftp_PWD(client_t *client, char *rest UNUSED) {
char msg[MAXPATHLEN + 24];
// TODO: escape double-quotes
sprintf(msg, "\"%s\" is current directory.", client->cwd);
return write_reply(client, 257, msg);
}
static int32_t ftp_CWD(client_t *client, char *path) {
int32_t result;
if (!vrt_chdir(client->cwd, path)) {
result = write_reply(client, 250, "CWD command successful.");
} else {
result = write_reply(client, 550, strerror(errno));
}
return result;
}
static int32_t ftp_CDUP(client_t *client, char *rest UNUSED) {
int32_t result;
if (!vrt_chdir(client->cwd, "..")) {
result = write_reply(client, 250, "CDUP command successful.");
} else {
result = write_reply(client, 550, strerror(errno));
}
return result;
}
static int32_t ftp_DELE(client_t *client, char *path) {
if (!vrt_unlink(client->cwd, path)) {
return write_reply(client, 250, "File or directory removed.");
} else {
return write_reply(client, 550, strerror(errno));
}
}
static int32_t ftp_MKD(client_t *client, char *path) {
if (!*path) {
return write_reply(client, 501, "Syntax error in parameters.");
}
if (!vrt_mkdir(client->cwd, path, 0777)) {
char msg[MAXPATHLEN + 21];
char abspath[MAXPATHLEN];
strcpy(abspath, client->cwd);
vrt_chdir(abspath, path); // TODO: error checking
// TODO: escape double-quotes
sprintf(msg, "\"%s\" directory created.", abspath);
return write_reply(client, 257, msg);
} else {
return write_reply(client, 550, strerror(errno));
}
}
static int32_t ftp_RNFR(client_t *client, char *path) {
strcpy(client->pending_rename, path);
return write_reply(client, 350, "Ready for RNTO.");
}
static int32_t ftp_RNTO(client_t *client, char *path) {
if (!*client->pending_rename) {
return write_reply(client, 503, "RNFR required first.");
}
int32_t result;
if (!vrt_rename(client->cwd, client->pending_rename, path)) {
result = write_reply(client, 250, "Rename successful.");
} else {
result = write_reply(client, 550, strerror(
errno));
}
*client->pending_rename = '\0';
return result;
}
static int32_t ftp_SIZE(client_t *client, char *path) {
struct stat st;
if (!vrt_stat(client->cwd, path, &st)) {
char size_buf[12];
sprintf(size_buf, "%llu", st.st_size);
return write_reply(client, 213, size_buf);
} else {
return write_reply(client, 550, strerror(errno));
}
}
static int32_t ftp_PASV(client_t *client, char *rest UNUSED) {
close_passive_socket(client);
client->passive_socket = network_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (client->passive_socket < 0) {
return write_reply(client, 520, "Unable to create listening socket.");
}
set_blocking(client->passive_socket, false);
struct sockaddr_in bindAddress;
memset(&bindAddress, 0, sizeof(bindAddress));
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = htons(passive_port++); // XXX: BUG: This will overflow eventually, with interesting results...
bindAddress.sin_addr.s_addr = htonl(INADDR_ANY);
int32_t result;
if ((result = network_bind(client->passive_socket, (struct sockaddr *)&bindAddress, sizeof(bindAddress))) < 0) {
close_passive_socket(client);
return write_reply(client, 520, "Unable to bind listening socket.");
}
if ((result = network_listen(client->passive_socket, 1)) < 0) {
close_passive_socket(client);
return write_reply(client, 520, "Unable to listen on socket.");
}
char reply[49];
uint16_t port = bindAddress.sin_port;
uint32_t ip = network_gethostip();
struct in_addr addr;
addr.s_addr = ip;
console_printf("Listening for data connections at %s:%u...\n", inet_ntoa(addr), port);
sprintf(reply, "Entering Passive Mode (%u,%u,%u,%u,%u,%u).", (ip >> 24) & 0xff, (ip >> 16) & 0xff, (ip >> 8) & 0xff, ip & 0xff, (port >> 8) & 0xff, port & 0xff);
return write_reply(client, 227, reply);
}
static int32_t ftp_PORT(client_t *client, char *portspec) {
uint32_t h1, h2, h3, h4, p1, p2;
if (sscanf(portspec, "%3u,%3u,%3u,%3u,%3u,%3u", &h1, &h2, &h3, &h4, &p1, &p2) < 6) {
return write_reply(client, 501, "Syntax error in parameters.");
}
char addr_str[44];
sprintf(addr_str, "%u.%u.%u.%u", h1, h2, h3, h4);
struct in_addr sin_addr;
if (!inet_aton(addr_str, &sin_addr)) {
return write_reply(client, 501, "Syntax error in parameters.");
}
close_passive_socket(client);
uint16_t port = ((p1 &0xff) << 8) | (p2 & 0xff);
client->address.sin_addr = sin_addr;
client->address.sin_port = htons(port);
console_printf("Set client address to %s:%u\n", addr_str, port);
return write_reply(client, 200, "PORT command successful.");
}
typedef int32_t (*data_connection_handler)(client_t *client, data_connection_callback callback, void *arg);
static int32_t prepare_data_connection_active(client_t *client, data_connection_callback callback UNUSED, void *arg UNUSED) {
int32_t data_socket = network_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (data_socket < 0)
return data_socket;
set_blocking(data_socket, false);
struct sockaddr_in bindAddress;
memset(&bindAddress, 0, sizeof(bindAddress));
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = htons(SRC_PORT);
bindAddress.sin_addr.s_addr = htonl(INADDR_ANY);
int32_t result;
if ((result = network_bind(data_socket, (struct sockaddr *)&bindAddress, sizeof(bindAddress))) < 0) {
network_close(data_socket);
return result;
}
client->data_socket = data_socket;
console_printf("Attempting to connect to client at %s:%u\n", inet_ntoa(client->address.sin_addr), client->address.sin_port);
return 0;
}
static int32_t prepare_data_connection_passive(client_t *client, data_connection_callback callback UNUSED, void *arg UNUSED) {
client->data_socket = client->passive_socket;
console_printf("Waiting for data connections...\n");
return 0;
}
static int32_t prepare_data_connection(client_t *client, void *callback, void *arg, void *cleanup) {
int32_t result = write_reply(client, 150, "Transferring data.");
if (result >= 0) {
data_connection_handler handler = prepare_data_connection_active;
if (client->passive_socket >= 0)
handler = prepare_data_connection_passive;
result = handler(client, (data_connection_callback)callback, arg);
if (result < 0) {
result = write_reply(client, 520, "Closing data connection, error occurred during transfer.");
} else {
client->data_connection_connected = false;
client->data_callback = callback;
client->data_connection_callback_arg = arg;
client->data_connection_cleanup = cleanup;
client->data_connection_timer = gettime() + OSSecondsToTicks(10);
}
}
return result;
}
static int32_t send_nlst(int32_t data_socket, DIR_P *iter) {
int32_t result = 0;
char filename[MAXPATHLEN];
struct dirent *dirent = NULL;
while ((dirent = vrt_readdir(iter)) != 0) {
size_t end_index = strlen(dirent->d_name);
if(end_index + 2 >= MAXPATHLEN)
continue;
strcpy(filename, dirent->d_name);
filename[end_index] = CRLF[0];
filename[end_index + 1] = CRLF[1];
filename[end_index + 2] = '\0';
if ((result = send_exact(data_socket, filename, strlen(filename))) < 0) {
break;
}
}
return result < 0 ? result : 0;
}
static int32_t send_list(int32_t data_socket, DIR_P *iter) {
struct stat st;
int32_t result = 0;
time_t mtime = 0;
uint64_t size = 0;
char filename[MAXPATHLEN];
char line[MAXPATHLEN + 56 + CRLF_LENGTH + 1];
struct dirent *dirent = NULL;
while ((dirent = vrt_readdir(iter)) != 0) {
snprintf(filename, sizeof(filename), "%s/%s", iter->path, dirent->d_name);
if(stat(filename, &st) == 0) {
mtime = st.st_mtime;
size = st.st_size;
} else {
mtime = time(0);
size = 0;
}
char timestamp[13];
strftime(timestamp, sizeof(timestamp), "%b %d %Y", localtime(&mtime));
snprintf(line, sizeof(line), "%crwxr-xr-x 1 0 0 %10llu %s %s\r\n", (dirent->d_type & DT_DIR) ? 'd' : '-', size, timestamp, dirent->d_name);
if ((result = send_exact(data_socket, line, strlen(line))) < 0) {
break;
}
}
return result < 0 ? result : 0;
}
static int32_t ftp_NLST(client_t *client, char *path) {
if (!*path) {
path = ".";
}
DIR_P *dir = vrt_opendir(client->cwd, path);
if (dir == NULL) {
return write_reply(client, 550, strerror(errno));
}
int32_t result = prepare_data_connection(client, send_nlst, dir, vrt_closedir);
if (result < 0)
vrt_closedir(dir);
return result;
}
static int32_t ftp_LIST(client_t *client, char *path) {
if (*path == '-') {
// handle buggy clients that use "LIST -aL" or similar, at the expense of breaking paths that begin with '-'
char flags[FTP_BUFFER_SIZE];
char rest[FTP_BUFFER_SIZE];
char *args[] = { flags, rest };
split(path, ' ', 1, args);
path = rest;
}
if (!*path) {
path = ".";
}
if(path && client->cwd) {
if(strcmp(path, ".") == 0 && strcmp(client->cwd, "/") == 0) {
UnmountVirtualPaths();
MountVirtualDevices();
}
}
DIR_P *dir = vrt_opendir(client->cwd, path);
if (dir == NULL) {
return write_reply(client, 550, strerror(errno));
}
int32_t result = prepare_data_connection(client, send_list, dir, vrt_closedir);
if (result < 0)
vrt_closedir(dir);
return result;
}
static int32_t ftp_RETR(client_t *client, char *path) {
FILE *f = vrt_fopen(client->cwd, path, "rb");
if (!f) {
return write_reply(client, 550, strerror(errno));
}
int fd = fileno(f);
if (client->restart_marker && lseek(fd, client->restart_marker, SEEK_SET) != client->restart_marker) {
int32_t lseek_error = errno;
fclose(f);
client->restart_marker = 0;
return write_reply(client, 550, strerror(lseek_error));
}
client->restart_marker = 0;
int32_t result = prepare_data_connection(client, send_from_file, f, fclose);
if (result < 0)
fclose(f);
return result;
}
static int32_t stor_or_append(client_t *client, FILE *f) {
if (!f) {
return write_reply(client, 550, strerror(errno));
}
int32_t result = prepare_data_connection(client, recv_to_file, f, fclose);
if (result < 0)
fclose(f);
return result;
}
static int32_t ftp_STOR(client_t *client, char *path) {
FILE *f = vrt_fopen(client->cwd, path, "wb");
int fd;
if (f)
fd = fileno(f);
if (f && client->restart_marker && lseek(fd, client->restart_marker, SEEK_SET) != client->restart_marker) {
int32_t lseek_error = errno;
fclose(f);
client->restart_marker = 0;
return write_reply(client, 550, strerror(lseek_error));
}
client->restart_marker = 0;
return stor_or_append(client, f);
}
static int32_t ftp_APPE(client_t *client, char *path) {
return stor_or_append(client, vrt_fopen(client->cwd, path, "ab"));
}
static int32_t ftp_REST(client_t *client, char *offset_str) {
off_t offset;
if (sscanf(offset_str, "%lli", &offset) < 1 || offset < 0) {
return write_reply(client, 501, "Syntax error in parameters.");
}
client->restart_marker = offset;
char msg[FTP_BUFFER_SIZE];
sprintf(msg, "Restart position accepted (%lli).", offset);
return write_reply(client, 350, msg);
}
static int32_t ftp_SITE_LOADER(client_t *client, char *rest UNUSED) {
int32_t result = write_reply(client, 200, "Exiting to loader.");
//set_reset_flag();
return result;
}
static int32_t ftp_SITE_CLEAR(client_t *client, char *rest UNUSED) {
int32_t result = write_reply(client, 200, "Cleared.");
uint32_t i;
for (i = 0; i < 18; i++)
console_printf("\n");
//console_printf("\x1b[2;0H");
return result;
}
/*
This is implemented as a no-op to prevent some FTP clients
from displaying skip/abort/retry type prompts.
*/
static int32_t ftp_SITE_CHMOD(client_t *client, char *rest UNUSED) {
return write_reply(client, 250, "SITE CHMOD command ok.");
}
static int32_t ftp_SITE_PASSWD(client_t *client, char *new_password) {
set_ftp_password(new_password);
return write_reply(client, 200, "Password changed.");
}
static int32_t ftp_SITE_NOPASSWD(client_t *client, char *rest UNUSED) {
set_ftp_password(NULL);
return write_reply(client, 200, "Authentication disabled.");
}
static int32_t ftp_SITE_EJECT(client_t *client, char *rest UNUSED) {
//if (dvd_eject()) return write_reply(client, 550, "Unable to eject DVD.");
return write_reply(client, 200, "DVD ejected.");
}
static int32_t ftp_SITE_MOUNT(client_t *client, char *path UNUSED) {
//if (!mount_virtual(path)) return write_reply(client, 550, "Unable to mount.");
return write_reply(client, 250, "Mounted.");
}
static int32_t ftp_SITE_UNMOUNT(client_t *client, char *path UNUSED) {
//if (!unmount_virtual(path)) return write_reply(client, 550, "Unable to unmount.");
return write_reply(client, 250, "Unmounted.");
}
static int32_t ftp_SITE_UNKNOWN(client_t *client, char *rest UNUSED) {
return write_reply(client, 501, "Unknown SITE command.");
}
static int32_t ftp_SITE_LOAD(client_t *client, char *path UNUSED) {
// FILE *f = vrt_fopen(client->cwd, path, "rb");
// if (!f) return write_reply(client, 550, strerror(errno));
// char *real_path = to_real_path(client->cwd, path);
// if (!real_path) goto end;
// load_from_file(f, real_path);
// free(real_path);
// end:
// fclose(f);
return write_reply(client, 500, "Unable to load.");
}
typedef int32_t (*ftp_command_handler)(client_t *client, char *args);
static int32_t dispatch_to_handler(client_t *client, char *cmd_line, const char **commands, const ftp_command_handler *handlers) {
char cmd[FTP_BUFFER_SIZE], rest[FTP_BUFFER_SIZE];
char *args[] = { cmd, rest };
split(cmd_line, ' ', 1, args);
int32_t i;
for (i = 0; commands[i]; i++) {
if (!strcasecmp(commands[i], cmd))
break;
}
return handlers[i](client, rest);
}
static const char *site_commands[] = { "LOADER", "CLEAR", "CHMOD", "PASSWD", "NOPASSWD", "EJECT", "MOUNT", "UNMOUNT", "LOAD", NULL };
static const ftp_command_handler site_handlers[] = { ftp_SITE_LOADER, ftp_SITE_CLEAR, ftp_SITE_CHMOD, ftp_SITE_PASSWD, ftp_SITE_NOPASSWD, ftp_SITE_EJECT, ftp_SITE_MOUNT, ftp_SITE_UNMOUNT, ftp_SITE_LOAD, ftp_SITE_UNKNOWN };
static int32_t ftp_SITE(client_t *client, char *cmd_line) {
return dispatch_to_handler(client, cmd_line, site_commands, site_handlers);
}
static int32_t ftp_NOOP(client_t *client, char *rest UNUSED) {
return write_reply(client, 200, "NOOP command successful.");
}
static int32_t ftp_SUPERFLUOUS(client_t *client, char *rest UNUSED) {
return write_reply(client, 202, "Command not implemented, superfluous at this site.");
}
static int32_t ftp_NEEDAUTH(client_t *client, char *rest UNUSED) {
return write_reply(client, 530, "Please login with USER and PASS.");
}
static int32_t ftp_UNKNOWN(client_t *client, char *rest UNUSED) {
return write_reply(client, 502, "Command not implemented.");
}
static const char *unauthenticated_commands[] = { "USER", "PASS", "QUIT", "REIN", "NOOP", NULL };
static const ftp_command_handler unauthenticated_handlers[] = { ftp_USER, ftp_PASS, ftp_QUIT, ftp_REIN, ftp_NOOP, ftp_NEEDAUTH };
static const char *authenticated_commands[] = {
"USER", "PASS", "LIST", "PWD", "CWD", "CDUP",
"SIZE", "PASV", "PORT", "TYPE", "SYST", "MODE",
"RETR", "STOR", "APPE", "REST", "DELE", "MKD",
"RMD", "RNFR", "RNTO", "NLST", "QUIT", "REIN",
"SITE", "NOOP", "ALLO", NULL
};
static const ftp_command_handler authenticated_handlers[] = {
ftp_USER, ftp_PASS, ftp_LIST, ftp_PWD, ftp_CWD, ftp_CDUP,
ftp_SIZE, ftp_PASV, ftp_PORT, ftp_TYPE, ftp_SYST, ftp_MODE,
ftp_RETR, ftp_STOR, ftp_APPE, ftp_REST, ftp_DELE, ftp_MKD,
ftp_DELE, ftp_RNFR, ftp_RNTO, ftp_NLST, ftp_QUIT, ftp_REIN,
ftp_SITE, ftp_NOOP, ftp_SUPERFLUOUS, ftp_UNKNOWN
};
/*
returns negative to signal an error that requires closing the connection
*/
static int32_t process_command(client_t *client, char *cmd_line) {
if (strlen(cmd_line) == 0) {
return 0;
}
console_printf("Got command: %s\n", cmd_line);
const char **commands = unauthenticated_commands;
const ftp_command_handler *handlers = unauthenticated_handlers;
if (client->authenticated) {
commands = authenticated_commands;
handlers = authenticated_handlers;
}
return dispatch_to_handler(client, cmd_line, commands, handlers);
}
static void cleanup_data_resources(client_t *client) {
if (client->data_socket >= 0 && client->data_socket != client->passive_socket) {
network_close_blocking(client->data_socket);
}
client->data_socket = -1;
client->data_connection_connected = false;
client->data_callback = NULL;
if (client->data_connection_cleanup) {
client->data_connection_cleanup(client->data_connection_callback_arg);
}
client->data_connection_callback_arg = NULL;
client->data_connection_cleanup = NULL;
client->data_connection_timer = 0;
}
static void cleanup_client(client_t *client) {
network_close_blocking(client->socket);
cleanup_data_resources(client);
close_passive_socket(client);
int client_index;
for (client_index = 0; client_index < MAX_CLIENTS; client_index++) {
if (clients[client_index] == client) {
clients[client_index] = NULL;
break;
}
}
free(client);
num_clients--;
console_printf("Client disconnected.\n");
}
void cleanup_ftp() {
int client_index;
for (client_index = 0; client_index < MAX_CLIENTS; client_index++) {
client_t *client = clients[client_index];
if (client) {
write_reply(client, 421, "Service not available, closing control connection.");
cleanup_client(client);
}
}
}
static bool process_accept_events(int32_t server) {
int32_t peer;
struct sockaddr_in client_address;
int32_t addrlen = sizeof(client_address);
while ((peer = network_accept(server, (struct sockaddr *)&client_address, &addrlen)) != -WIIU_EAGAIN) {
if (peer < 0) {
console_printf("Error accepting connection: [%i] %s\n", -peer, strerror(-peer));
return false;
}
console_printf("Accepted connection from %s!\n", inet_ntoa(client_address.sin_addr));
if (num_clients == MAX_CLIENTS) {
console_printf("Maximum of %u clients reached, not accepting client.\n", MAX_CLIENTS);
network_close(peer);
return true;
}
client_t *client = malloc(sizeof(client_t));
if (!client) {
console_printf("Could not allocate memory for client state, not accepting client.\n");
network_close(peer);
return true;
}
client->socket = peer;
client->representation_type = 'A';
client->passive_socket = -1;
client->data_socket = -1;
strcpy(client->cwd, "/");
*client->pending_rename = '\0';
client->restart_marker = 0;
client->authenticated = false;
client->offset = 0;
client->data_connection_connected = false;
client->data_callback = NULL;
client->data_connection_callback_arg = NULL;
client->data_connection_cleanup = NULL;
client->data_connection_timer = 0;
memcpy(&client->address, &client_address, sizeof(client_address));
int client_index;
if (write_reply(client, 220, "ftpii") < 0) {
console_printf("Error writing greeting.\n");
network_close_blocking(peer);
free(client);
} else {
for (client_index = 0; client_index < MAX_CLIENTS; client_index++) {
if (!clients[client_index]) {
clients[client_index] = client;
break;
}
}
num_clients++;
}
}
return true;
}
static void process_data_events(client_t *client) {
int32_t result;
if (!client->data_connection_connected) {
if (client->passive_socket >= 0) {
struct sockaddr_in data_peer_address;
int32_t addrlen = sizeof(data_peer_address);
result = network_accept(client->passive_socket, (struct sockaddr *)&data_peer_address,&addrlen);
if (result >= 0) {
client->data_socket = result;
client->data_connection_connected = true;
}
} else {
if ((result = network_connect(client->data_socket, (struct sockaddr *)&client->address, sizeof(client->address))) < 0) {
if (result == -EINPROGRESS || result == -EALREADY)
result = -WIIU_EAGAIN;
if ((result != -WIIU_EAGAIN) && (result != -EISCONN)) {
console_printf("Unable to connect to client: [%i] %s\n", -result, strerror(-result));
}
}
if (result >= 0 || result == -EISCONN) {
client->data_connection_connected = true;
}
}
if (client->data_connection_connected) {
result = 1;
console_printf("Connected to client! Transferring data...\n");
} else if (gettime() > client->data_connection_timer) {
result = -2;
console_printf("Timed out waiting for data connection.\n");
}
} else {
result = client->data_callback(client->data_socket, client->data_connection_callback_arg);
}
if (result <= 0 && result != -WIIU_EAGAIN) {
cleanup_data_resources(client);
if (result < 0) {
result = write_reply(client, 520, "Closing data connection, error occurred during transfer.");
} else {
result = write_reply(client, 226, "Closing data connection, transfer successful.");
}
if (result < 0) {
cleanup_client(client);
}
}
}
static void process_control_events(client_t *client) {
int32_t bytes_read;
while (client->offset < (FTP_BUFFER_SIZE - 1)) {
if (client->data_callback) {
return;
}
char *offset_buf = client->buf + client->offset;
if ((bytes_read = network_read(client->socket, offset_buf, FTP_BUFFER_SIZE - 1 - client->offset)) < 0) {
if (bytes_read != -EAGAIN) {
console_printf("Read error %i occurred, closing client.\n", bytes_read);
goto recv_loop_end;
}
return;
} else if (bytes_read == 0) {
goto recv_loop_end; // EOF from client
}
client->offset += bytes_read;
client->buf[client->offset] = '\0';
if (strchr(offset_buf, '\0') != (client->buf + client->offset)) {
console_printf("Received a null byte from client, closing connection ;-)\n"); // i have decided this isn't allowed =P
goto recv_loop_end;
}
char *next;
char *end;
for (next = client->buf; (end = strstr(next, CRLF)) && !client->data_callback; next = end + CRLF_LENGTH) {
*end = '\0';
if (strchr(next, '\n')) {
console_printf("Received a line-feed from client without preceding carriage return, closing connection ;-)\n"); // i have decided this isn't allowed =P
goto recv_loop_end;
}
if (*next) {
int32_t result;
if ((result = process_command(client, next)) < 0) {
if (result != -EQUIT) {
console_printf("Closing connection due to error while processing command: %s\n", next);
}
goto recv_loop_end;
}
}
}
if (next != client->buf) { // some lines were processed
client->offset = strlen(next);
char tmp_buf[client->offset];
memcpy(tmp_buf, next, client->offset);
memcpy(client->buf, tmp_buf, client->offset);
}
}
console_printf("Received line longer than %u bytes, closing client.\n", FTP_BUFFER_SIZE - 1);
recv_loop_end:
cleanup_client(client);
}
bool process_ftp_events(int32_t server) {
bool network_down = !process_accept_events(server);
int client_index;
for (client_index = 0; client_index < MAX_CLIENTS; client_index++) {
client_t *client = clients[client_index];
if (client) {
if (client->data_callback) {
process_data_events(client);
} else {
process_control_events(client);
}
}
}
return network_down;
}

43
src/ftp.h Normal file
View File

@ -0,0 +1,43 @@
/*
ftpii -- an FTP server for the Wii
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#ifndef _FTP_H_
#define _FTP_H_
#ifdef __cplusplus
extern "C"{
#endif
#include <stdbool.h>
void accept_ftp_client(int32_t server);
void set_ftp_password(char *new_password);
bool process_ftp_events(int32_t server);
void cleanup_ftp();
#ifdef __cplusplus
}
#endif
#endif /* _FTP_H_ */

138
src/main.cpp Normal file
View File

@ -0,0 +1,138 @@
#include <wups.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <malloc.h>
#include <stdint.h>
#include <iosuhax.h>
#include <iosuhax_devoptab.h>
#include <iosuhax_disc_interface.h>
#include <proc_ui/procui.h>
#include <coreinit/foreground.h>
#include <coreinit/screen.h>
#include <sysapp/launch.h>
#include <coreinit/dynload.h>
#include <nn/ac.h>
#include <stdarg.h>
#include <whb/proc.h>
#include <stdlib.h>
#include <coreinit/thread.h>
#include <coreinit/cache.h>
#include <coreinit/time.h>
#include <stdio.h>
#include <whb/libmanager.h>
#include "utils/logger.h"
#include "utils/utils.h"
#include "virtualpath.h"
#include "net.h"
#include "BackgroundThread.hpp"
#define MAX_CONSOLE_LINES_TV 27
#define MAX_CONSOLE_LINES_DRC 18
WUPS_PLUGIN_NAME("FTPiiU");
WUPS_PLUGIN_DESCRIPTION("FTP Server");
WUPS_PLUGIN_VERSION("0.1");
WUPS_PLUGIN_AUTHOR("Maschell");
WUPS_PLUGIN_LICENSE("GPL");
WUPS_USE_WUT_CRT()
uint32_t hostIpAddress = 0;
int iosuhaxMount = 0;
int fsaFd = 0;
BackgroundThread * thread = NULL;
/* Entry point */
ON_APPLICATION_START(args) {
WHBInitializeSocketLibrary();
nn::ac::ConfigIdNum configId;
nn::ac::Initialize();
nn::ac::GetStartupId(&configId);
nn::ac::Connect(configId);
ACGetAssignedAddress(&hostIpAddress);
log_init();
//!*******************************************************************
//! Initialize FS *
//!*******************************************************************
int fsaFd = -1;
DEBUG_FUNCTION_LINE("IOSUHAX_Open\n");
int res = IOSUHAX_Open(NULL);
if(res < 0) {
DEBUG_FUNCTION_LINE("IOSUHAX_open failed\n");
VirtualMountDevice("fs:/");
} else {
iosuhaxMount = 1;
//fatInitDefault();
DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open\n");
fsaFd = IOSUHAX_FSA_Open();
if(fsaFd < 0) {
DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open failed\n");
}
DEBUG_FUNCTION_LINE("IOSUHAX_FSA_Open\n");
mount_fs("slccmpt01", fsaFd, "/dev/slccmpt01", "/vol/storage_slccmpt01");
mount_fs("storage_odd_tickets", fsaFd, "/dev/odd01", "/vol/storage_odd_tickets");
mount_fs("storage_odd_updates", fsaFd, "/dev/odd02", "/vol/storage_odd_updates");
mount_fs("storage_odd_content", fsaFd, "/dev/odd03", "/vol/storage_odd_content");
mount_fs("storage_odd_content2", fsaFd, "/dev/odd04", "/vol/storage_odd_content2");
mount_fs("storage_slc", fsaFd, NULL, "/vol/system");
mount_fs("storage_mlc", fsaFd, NULL, "/vol/storage_mlc01");
mount_fs("storage_usb", fsaFd, NULL, "/vol/storage_usb01");
VirtualMountDevice("fs:/");
VirtualMountDevice("slccmpt01:/");
VirtualMountDevice("storage_odd_tickets:/");
VirtualMountDevice("storage_odd_updates:/");
VirtualMountDevice("storage_odd_content:/");
VirtualMountDevice("storage_odd_content2:/");
VirtualMountDevice("storage_slc:/");
VirtualMountDevice("storage_mlc:/");
VirtualMountDevice("storage_usb:/");
VirtualMountDevice("usb:/");
}
thread = BackgroundThread::getInstance();
DCFlushRange(&thread, 4);
}
void stopThread(){
BackgroundThread::destroyInstance();
}
ON_APPLICATION_END(){
DEBUG_FUNCTION_LINE("Ending ftp server\n");
stopThread();
if(iosuhaxMount) {
IOSUHAX_sdio_disc_interface.shutdown();
IOSUHAX_usb_disc_interface.shutdown();
unmount_fs("slccmpt01");
unmount_fs("storage_odd_tickets");
unmount_fs("storage_odd_updates");
unmount_fs("storage_odd_content");
unmount_fs("storage_odd_content2");
unmount_fs("storage_slc");
unmount_fs("storage_mlc");
unmount_fs("storage_usb");
IOSUHAX_FSA_Close(fsaFd);
IOSUHAX_Close();
}
UnmountVirtualPaths();
}

32
src/main.h Normal file
View File

@ -0,0 +1,32 @@
#ifndef _MAIN_H_
#define _MAIN_H_
/* Main */
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#include <nsysnet/socket.h>
#define MAXPATHLEN 256
#define WIIU_EAGAIN EWOULDBLOCK
#define ENODATA 1
#define EISCONN 3
#define EWOULDBLOCK 6
#define EALREADY 10
#define EAGAIN EWOULDBLOCK
#define EINVAL 11
#define ENOMEM 18
#define EINPROGRESS 22
#define wiiu_geterrno() (socketlasterr())
//! C wrapper for our C++ functions
int Menu_Main(void);
#ifdef __cplusplus
}
#endif
#endif

283
src/net.c Normal file
View File

@ -0,0 +1,283 @@
/*
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <malloc.h>
#include <unistd.h>
#include <sys/fcntl.h>
#include <nsysnet/socket.h>
#include "main.h"
#define MIN(x, y) ((x) < (y) ? (x) : (y))
#include "net.h"
#define MAX_NET_BUFFER_SIZE (64*1024)
#define MIN_NET_BUFFER_SIZE 4096
#define FREAD_BUFFER_SIZE (64*1024)
extern uint32_t hostIpAddress;
static uint32_t NET_BUFFER_SIZE = MAX_NET_BUFFER_SIZE;
#if 0
void initialise_network() {
printf("Waiting for network to initialise...\n");
int32_t result = -1;
while (!check_reset_synchronous() && result < 0) {
net_deinit();
while (!check_reset_synchronous() && (result = net_init()) == -WIIU_EAGAIN);
if (result < 0)
printf("net_init() failed: [%i] %s, retrying...\n", result, strerror(-result));
}
if (result >= 0) {
uint32_t ip = 0;
do {
ip = net_gethostip();
if (!ip)
printf("net_gethostip() failed, retrying...\n");
} while (!check_reset_synchronous() && !ip);
if (ip) {
struct in_addr addr;
addr.s_addr = ip;
printf("Network initialised. Wii IP address: %s\n", inet_ntoa(addr));
}
}
}
#endif
int32_t network_socket(uint32_t domain,uint32_t type,uint32_t protocol) {
int sock = socket(domain, type, protocol);
if(sock < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : sock;
}
return sock;
}
int32_t network_bind(int32_t s,struct sockaddr *name,int32_t namelen) {
int res = bind(s, name, namelen);
if(res < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : res;
}
return res;
}
int32_t network_listen(int32_t s,uint32_t backlog) {
int res = listen(s, backlog);
if(res < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : res;
}
return res;
}
int32_t network_accept(int32_t s,struct sockaddr *addr,int32_t *addrlen) {
int res = accept(s, addr, addrlen);
if(res < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : res;
}
return res;
}
int32_t network_connect(int32_t s,struct sockaddr *addr, int32_t addrlen) {
int res = connect(s, addr, addrlen);
if(res < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : res;
}
return res;
}
int32_t network_read(int32_t s,void *mem,int32_t len) {
int res = recv(s, mem, len, 0);
if(res < 0) {
int err = -wiiu_geterrno();
return (err < 0) ? err : res;
}
return res;
}
uint32_t network_gethostip() {
return hostIpAddress;
}
int32_t network_write(int32_t s, const void *mem,int32_t len) {
int32_t transfered = 0;
while(len) {
int ret = send(s, mem, len, 0);
if(ret < 0) {
int err = -wiiu_geterrno();
transfered = (err < 0) ? err : ret;
break;
}
mem += ret;
transfered += ret;
len -= ret;
}
return transfered;
}
int32_t network_close(int32_t s) {
if(s < 0)
return -1;
return socketclose(s);
}
int32_t set_blocking(int32_t s, bool blocking) {
int32_t block = !blocking;
setsockopt(s, SOL_SOCKET, SO_NONBLOCK, &block, sizeof(block));
return 0;
}
int32_t network_close_blocking(int32_t s) {
set_blocking(s, true);
return network_close(s);
}
int32_t create_server(uint16_t port) {
int32_t server = network_socket(AF_INET, SOCK_STREAM, IPPROTO_IP);
if (server < 0)
return -1;
set_blocking(server, false);
uint32_t enable = 1;
setsockopt(server, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable));
struct sockaddr_in bindAddress;
memset(&bindAddress, 0, sizeof(bindAddress));
bindAddress.sin_family = AF_INET;
bindAddress.sin_port = htons(port);
bindAddress.sin_addr.s_addr = htonl(INADDR_ANY);
int32_t ret;
if ((ret = network_bind(server, (struct sockaddr *)&bindAddress, sizeof(bindAddress))) < 0) {
network_close(server);
//gxprintf("Error binding socket: [%i] %s\n", -ret, strerror(-ret));
return ret;
}
if ((ret = network_listen(server, 3)) < 0) {
network_close(server);
//gxprintf("Error listening on socket: [%i] %s\n", -ret, strerror(-ret));
return ret;
}
return server;
}
typedef int32_t (*transferrer_type)(int32_t s, void *mem, int32_t len);
static int32_t transfer_exact(int32_t s, char *buf, int32_t length, transferrer_type transferrer) {
int32_t result = 0;
int32_t remaining = length;
int32_t bytes_transferred;
set_blocking(s, true);
while (remaining) {
try_again_with_smaller_buffer:
bytes_transferred = transferrer(s, buf, MIN(remaining, (int) NET_BUFFER_SIZE));
if (bytes_transferred > 0) {
remaining -= bytes_transferred;
buf += bytes_transferred;
} else if (bytes_transferred < 0) {
if (bytes_transferred == -EINVAL && NET_BUFFER_SIZE == MAX_NET_BUFFER_SIZE) {
NET_BUFFER_SIZE = MIN_NET_BUFFER_SIZE;
usleep(1000);
goto try_again_with_smaller_buffer;
}
result = bytes_transferred;
break;
} else {
result = -ENODATA;
break;
}
}
set_blocking(s, false);
return result;
}
int32_t send_exact(int32_t s, char *buf, int32_t length) {
return transfer_exact(s, buf, length, (transferrer_type)network_write);
}
int32_t send_from_file(int32_t s, FILE *f) {
char * buf = (char *) malloc(FREAD_BUFFER_SIZE);
if(!buf)
return -1;
int32_t bytes_read;
int32_t result = 0;
bytes_read = fread(buf, 1, FREAD_BUFFER_SIZE, f);
if (bytes_read > 0) {
result = send_exact(s, buf, bytes_read);
if (result < 0)
goto end;
}
if (bytes_read < FREAD_BUFFER_SIZE) {
result = -!feof(f);
goto end;
}
free(buf);
return -WIIU_EAGAIN;
end:
free(buf);
return result;
}
int32_t recv_to_file(int32_t s, FILE *f) {
char * buf = (char *) malloc(NET_BUFFER_SIZE);
if(!buf)
return -1;
int32_t bytes_read;
while (1) {
try_again_with_smaller_buffer:
bytes_read = network_read(s, buf, NET_BUFFER_SIZE);
if (bytes_read < 0) {
if (bytes_read == -EINVAL && NET_BUFFER_SIZE == MAX_NET_BUFFER_SIZE) {
NET_BUFFER_SIZE = MIN_NET_BUFFER_SIZE;
usleep(1000);
goto try_again_with_smaller_buffer;
}
free(buf);
return bytes_read;
} else if (bytes_read == 0) {
free(buf);
return 0;
}
int32_t bytes_written = fwrite(buf, 1, bytes_read, f);
if (bytes_written < bytes_read) {
free(buf);
return -1;
}
}
return -1;
}

63
src/net.h Normal file
View File

@ -0,0 +1,63 @@
/*
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#ifndef _NET_H_
#define _NET_H_
#ifdef __cplusplus
extern "C"{
#endif
#include <stdio.h>
#include <stdbool.h>
#if 0
void initialise_network();
#endif
int32_t network_socket(uint32_t domain,uint32_t type,uint32_t protocol);
int32_t network_bind(int32_t s,struct sockaddr *name,int32_t namelen);
int32_t network_listen(int32_t s,uint32_t backlog);
int32_t network_accept(int32_t s,struct sockaddr *addr,int32_t *addrlen);
int32_t network_connect(int32_t s,struct sockaddr *,int32_t);
int32_t network_read(int32_t s,void *mem,int32_t len);
int32_t network_close(int32_t s);
uint32_t network_gethostip();
int32_t set_blocking(int32_t s, bool blocking);
int32_t network_close_blocking(int32_t s);
int32_t create_server(uint16_t port);
int32_t send_exact(int32_t s, char *buf, int32_t length);
int32_t send_from_file(int32_t s, FILE *f);
int32_t recv_to_file(int32_t s, FILE *f);
#ifdef __cplusplus
}
#endif
#endif /* _NET_H_ */

View File

@ -0,0 +1,30 @@
#include "BackgroundThreadWrapper.hpp"
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <coreinit/cache.h>
#include "logger.h"
BackgroundThreadWrapper::BackgroundThreadWrapper(int32_t priority): CThread(CThread::eAttributeAffCore2, priority, 0x100000) {
}
BackgroundThreadWrapper::~BackgroundThreadWrapper() {
exitThread = 1;
DCFlushRange((void*)&exitThread, 4);
DEBUG_FUNCTION_LINE("Exit thread\n");
}
void BackgroundThreadWrapper::executeThread() {
while (1) {
if(exitThread) {
DEBUG_FUNCTION_LINE("We want to exit\n");
break;
}
if(!whileLoop()){
break;
}
}
DEBUG_FUNCTION_LINE("Exit!\n");
}

View File

@ -0,0 +1,31 @@
#pragma once
#include "CThread.h"
#include "CMutex.h"
#include <wut_types.h>
class BackgroundThreadWrapper: public CThread {
public:
BackgroundThreadWrapper(int32_t priority);
virtual ~BackgroundThreadWrapper();
protected:
BOOL shouldExit() {
return (exitThread == 1);
}
void setThreadPriority(int32_t priority) {
this->setThreadPriority(priority);
}
CMutex mutex;
private:
void executeThread();
/**
Called when a connection has be accepted.
**/
virtual BOOL whileLoop() = 0;
volatile int32_t exitThread = 0;
};

69
src/utils/CMutex.h Normal file
View File

@ -0,0 +1,69 @@
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef _CMUTEX_H_
#define _CMUTEX_H_
#include <malloc.h>
#include <coreinit/mutex.h>
class CMutex
{
public:
CMutex() {
pMutex = (OSMutex*) malloc(sizeof(OSMutex));
if(!pMutex)
return;
OSInitMutex(pMutex);
}
virtual ~CMutex() {
if(pMutex)
free(pMutex);
}
void lock(void) {
if(pMutex)
OSLockMutex(pMutex);
}
void unlock(void) {
if(pMutex)
OSUnlockMutex(pMutex);
}
BOOL tryLock(void) {
if(!pMutex)
return false;
return (OSTryLockMutex(pMutex) != 0);
}
private:
OSMutex *pMutex;
};
class CMutexLock
{
public:
CMutexLock() {
mutex.lock();
}
virtual ~CMutexLock() {
mutex.unlock();
}
private:
CMutex mutex;
};
#endif // _CMUTEX_H_

134
src/utils/CThread.h Normal file
View File

@ -0,0 +1,134 @@
/****************************************************************************
* Copyright (C) 2015 Dimok
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
****************************************************************************/
#ifndef CTHREAD_H_
#define CTHREAD_H_
#include <malloc.h>
#include <unistd.h>
#include <coreinit/systeminfo.h>
#include <coreinit/thread.h>
#include "utils/logger.h"
class CThread {
public:
typedef void (* Callback)(CThread *thread, void *arg);
//! constructor
CThread(int32_t iAttr, int32_t iPriority = 16, int32_t iStackSize = 0x8000, CThread::Callback callback = NULL, void *callbackArg = NULL)
: pThread(NULL)
, pThreadStack(NULL)
, pCallback(callback)
, pCallbackArg(callbackArg) {
//! save attribute assignment
iAttributes = iAttr;
//! allocate the thread
pThread = (OSThread*)memalign(8, sizeof(OSThread));
//! allocate the stack
pThreadStack = (uint8_t *) memalign(0x20, iStackSize);
//! create the thread
if(pThread && pThreadStack)
OSCreateThread(pThread, &CThread::threadCallback, 1, (char*)this, pThreadStack+iStackSize, iStackSize, iPriority, iAttributes);
}
//! destructor
virtual ~CThread() {
shutdownThread();
DEBUG_FUNCTION_LINE("END\n");
}
static CThread *create(CThread::Callback callback, void *callbackArg, int32_t iAttr = eAttributeNone, int32_t iPriority = 16, int32_t iStackSize = 0x8000) {
return ( new CThread(iAttr, iPriority, iStackSize, callback, callbackArg) );
}
//! Get thread ID
virtual void* getThread() const {
return pThread;
}
//! Thread entry function
virtual void executeThread(void) {
if(pCallback)
pCallback(this, pCallbackArg);
}
//! Suspend thread
virtual void suspendThread(void) {
if(isThreadSuspended()) return;
if(pThread) OSSuspendThread(pThread);
}
//! Resume thread
virtual void resumeThread(void) {
if(!isThreadSuspended()) return;
if(pThread) OSResumeThread(pThread);
}
//! Set thread priority
virtual void setThreadPriority(int prio) {
if(pThread) OSSetThreadPriority(pThread, prio);
}
//! Check if thread is suspended
virtual BOOL isThreadSuspended(void) const {
if(pThread) return OSIsThreadSuspended(pThread);
return false;
}
//! Check if thread is terminated
virtual BOOL isThreadTerminated(void) const {
if(pThread) return OSIsThreadTerminated(pThread);
return false;
}
//! Check if thread is running
virtual BOOL isThreadRunning(void) const {
return !isThreadSuspended() && !isThreadRunning();
}
//! Shutdown thread
virtual void shutdownThread(void) {
//! wait for thread to finish
if(pThread && !(iAttributes & eAttributeDetach)) {
if(isThreadSuspended())
resumeThread();
OSJoinThread(pThread, NULL);
}
//! free the thread stack buffer
if(pThreadStack)
free(pThreadStack);
if(pThread)
free(pThread);
pThread = NULL;
pThreadStack = NULL;
}
//! Thread attributes
enum eCThreadAttributes {
eAttributeNone = 0x07,
eAttributeAffCore0 = 0x01,
eAttributeAffCore1 = 0x02,
eAttributeAffCore2 = 0x04,
eAttributeDetach = 0x08,
eAttributePinnedAff = 0x10
};
private:
static int threadCallback(int argc, const char **argv) {
//! After call to start() continue with the internal function
((CThread *) argv)->executeThread();
return 0;
}
int iAttributes;
OSThread *pThread;
uint8_t *pThreadStack;
Callback pCallback;
void *pCallbackArg;
};
#endif

82
src/utils/logger.c Normal file
View File

@ -0,0 +1,82 @@
#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <utils/logger.h>
#include <nsysnet/socket.h>
#include <coreinit/debug.h>
#include <coreinit/systeminfo.h>
#include <coreinit/thread.h>
static int log_socket __attribute__((section(".data")))= -1;
static struct sockaddr_in connect_addr __attribute__((section(".data")));
static volatile int log_lock __attribute__((section(".data"))) = 0;
void log_init_() {
int broadcastEnable = 1;
log_socket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (log_socket < 0)
return;
setsockopt(log_socket, SOL_SOCKET, SO_BROADCAST, &broadcastEnable, sizeof(broadcastEnable));
memset(&connect_addr, 0, sizeof(struct sockaddr_in));
connect_addr.sin_family = AF_INET;
connect_addr.sin_port = 4405;
connect_addr.sin_addr.s_addr = htonl(INADDR_BROADCAST);
}
void log_print_(const char *str) {
// socket is always 0 initially as it is in the BSS
if(log_socket < 0) {
return;
}
while(log_lock)
OSSleepTicks(OSMicrosecondsToTicks(1000));
log_lock = 1;
int len = strlen(str);
int ret;
while (len > 0) {
int block = len < 1400 ? len : 1400; // take max 1400 bytes per UDP packet
ret = sendto(log_socket, str, block, 0, (struct sockaddr *)&connect_addr, sizeof(struct sockaddr_in));
if(ret < 0)
break;
len -= ret;
str += ret;
}
log_lock = 0;
}
void OSFatal_printf(const char *format, ...) {
char tmp[512];
tmp[0] = 0;
va_list va;
va_start(va, format);
if((vsprintf(tmp, format, va) >= 0)) {
OSFatal(tmp);
}
va_end(va);
}
void log_printf_(const char *format, ...) {
if(log_socket < 0) {
return;
}
char tmp[512];
tmp[0] = 0;
va_list va;
va_start(va, format);
if((vsprintf(tmp, format, va) >= 0)) {
log_print_(tmp);
}
va_end(va);
}

38
src/utils/logger.h Normal file
View File

@ -0,0 +1,38 @@
#ifndef __LOGGER_H_
#define __LOGGER_H_
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
void log_init_();
//void log_deinit_(void);
void log_print_(const char *str);
void log_printf_(const char *format, ...);
void OSFatal_printf(const char *format, ...);
#define __FILENAME_X__ (strrchr(__FILE__, '\\') ? strrchr(__FILE__, '\\') + 1 : __FILE__)
#define __FILENAME__ (strrchr(__FILE__, '/') ? strrchr(__FILE__, '/') + 1 : __FILENAME_X__)
#define OSFATAL_FUNCTION_LINE(FMT, ARGS...)do { \
OSFatal_printf("[%s]%s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0)
#define log_init() log_init_()
//#define log_deinit() log_deinit_()
#define log_print(str) log_print_(str)
#define log_printf(FMT, ARGS...) log_printf_(FMT, ## ARGS);
#define DEBUG_FUNCTION_LINE(FMT, ARGS...)do { \
log_printf("[%23s]%30s@L%04d: " FMT "",__FILENAME__,__FUNCTION__, __LINE__, ## ARGS); \
} while (0)
#ifdef __cplusplus
}
#endif
#endif

41
src/utils/utils.c Normal file
View File

@ -0,0 +1,41 @@
#include <string.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdio.h>
#include <malloc.h>
#include <utils/logger.h>
// https://gist.github.com/ccbrown/9722406
void dumpHex(const void* data, size_t size) {
char ascii[17];
size_t i, j;
ascii[16] = '\0';
DEBUG_FUNCTION_LINE("0x%08X (0x0000): ", data);
for (i = 0; i < size; ++i) {
log_printf("%02X ", ((unsigned char*)data)[i]);
if (((unsigned char*)data)[i] >= ' ' && ((unsigned char*)data)[i] <= '~') {
ascii[i % 16] = ((unsigned char*)data)[i];
} else {
ascii[i % 16] = '.';
}
if ((i+1) % 8 == 0 || i+1 == size) {
log_printf(" ");
if ((i+1) % 16 == 0) {
log_printf("| %s \n", ascii);
if(i + 1 < size) {
DEBUG_FUNCTION_LINE("0x%08X (0x%04X); ", data + i + 1,i+1);
}
} else if (i+1 == size) {
ascii[(i+1) % 16] = '\0';
if ((i+1) % 16 <= 8) {
log_printf(" ");
}
for (j = (i+1) % 16; j < 16; ++j) {
log_printf(" ");
}
log_printf("| %s \n", ascii);
}
}
}
}

35
src/utils/utils.h Normal file
View File

@ -0,0 +1,35 @@
#ifndef __UTILS_H_
#define __UTILS_H_
#include <malloc.h>
#ifdef __cplusplus
extern "C" {
#endif
#define LIMIT(x, min, max) \
({ \
typeof( x ) _x = x; \
typeof( min ) _min = min; \
typeof( max ) _max = max; \
( ( ( _x ) < ( _min ) ) ? ( _min ) : ( ( _x ) > ( _max ) ) ? ( _max) : ( _x ) ); \
})
#define DegToRad(a) ( (a) * 0.01745329252f )
#define RadToDeg(a) ( (a) * 57.29577951f )
#define ALIGN4(x) (((x) + 3) & ~3)
#define ALIGN32(x) (((x) + 31) & ~31)
#define le16(i) ((((uint16_t) ((i) & 0xFF)) << 8) | ((uint16_t) (((i) & 0xFF00) >> 8)))
#define le32(i) ((((uint32_t)le16((i) & 0xFFFF)) << 16) | ((uint32_t)le16(((i) & 0xFFFF0000) >> 16)))
#define le64(i) ((((uint64_t)le32((i) & 0xFFFFFFFFLL)) << 32) | ((uint64_t)le32(((i) & 0xFFFFFFFF00000000LL) >> 32)))
//Needs to have log_init() called beforehand.
void dumpHex(const void* data, size_t size);
#ifdef __cplusplus
}
#endif
#endif // __UTILS_H_

125
src/virtualpath.c Normal file
View File

@ -0,0 +1,125 @@
/****************************************************************************
* Copyright (C) 2008
* Joseph Jordan <joe.ftpii@psychlaw.com.au>
*
* Copyright (C) 2010
* by Dimok
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* for WiiXplorer 2010
***************************************************************************/
#include <malloc.h>
#include <string.h>
#include "virtualpath.h"
#include "utils/logger.h"
uint8_t MAX_VIRTUAL_PARTITIONS = 0;
VIRTUAL_PARTITION * VIRTUAL_PARTITIONS = NULL;
void VirtualMountDevice(const char * path)
{
if(!path)
return;
int i = 0;
char name[255];
char alias[255];
char prefix[255];
bool namestop = false;
alias[0] = '/';
do
{
if(path[i] == ':')
namestop = true;
if(!namestop)
{
name[i] = path[i];
name[i+1] = '\0';
alias[i+1] = path[i];
alias[i+2] = '\0';
}
prefix[i] = path[i];
prefix[i+1] = '\0';
i++;
}
while(path[i-1] != '/');
AddVirtualPath(name, alias, prefix);
}
void AddVirtualPath(const char *name, const char *alias, const char *prefix)
{
if(!VIRTUAL_PARTITIONS)
VIRTUAL_PARTITIONS = (VIRTUAL_PARTITION *) malloc(sizeof(VIRTUAL_PARTITION));
VIRTUAL_PARTITION * tmp = realloc(VIRTUAL_PARTITIONS, sizeof(VIRTUAL_PARTITION)*(MAX_VIRTUAL_PARTITIONS+1));
if(!tmp)
{
free(VIRTUAL_PARTITIONS);
MAX_VIRTUAL_PARTITIONS = 0;
return;
}
VIRTUAL_PARTITIONS = tmp;
VIRTUAL_PARTITIONS[MAX_VIRTUAL_PARTITIONS].name = strdup(name);
VIRTUAL_PARTITIONS[MAX_VIRTUAL_PARTITIONS].alias = strdup(alias);
VIRTUAL_PARTITIONS[MAX_VIRTUAL_PARTITIONS].prefix = strdup(prefix);
VIRTUAL_PARTITIONS[MAX_VIRTUAL_PARTITIONS].inserted = true;
MAX_VIRTUAL_PARTITIONS++;
}
void MountVirtualDevices()
{
VirtualMountDevice("fs:/");
VirtualMountDevice("slccmpt01:/");
VirtualMountDevice("storage_odd_tickets:/");
VirtualMountDevice("storage_odd_updates:/");
VirtualMountDevice("storage_odd_content:/");
VirtualMountDevice("storage_odd_content2:/");
VirtualMountDevice("storage_slc:/");
VirtualMountDevice("storage_mlc:/");
VirtualMountDevice("storage_usb:/");
VirtualMountDevice("usb:/");
}
void UnmountVirtualPaths()
{
uint32_t i = 0;
for(i = 0; i < MAX_VIRTUAL_PARTITIONS; i++)
{
if(VIRTUAL_PARTITIONS[i].name)
free(VIRTUAL_PARTITIONS[i].name);
if(VIRTUAL_PARTITIONS[i].alias)
free(VIRTUAL_PARTITIONS[i].alias);
if(VIRTUAL_PARTITIONS[i].prefix)
free(VIRTUAL_PARTITIONS[i].prefix);
}
if(VIRTUAL_PARTITIONS)
free(VIRTUAL_PARTITIONS);
VIRTUAL_PARTITIONS = NULL;
MAX_VIRTUAL_PARTITIONS = 0;
}

59
src/virtualpath.h Normal file
View File

@ -0,0 +1,59 @@
/****************************************************************************
* Copyright (C) 2010
* by Dimok
*
* Original VIRTUAL_PART Struct
* Copyright (C) 2008
* Joseph Jordan <joe.ftpii@psychlaw.com.au>
*
* This software is provided 'as-is', without any express or implied
* warranty. In no event will the authors be held liable for any
* damages arising from the use of this software.
*
* Permission is granted to anyone to use this software for any
* purpose, including commercial applications, and to alter it and
* redistribute it freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you
* must not claim that you wrote the original software. If you use
* this software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and
* must not be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source
* distribution.
*
* for WiiXplorer 2010
***************************************************************************/
#ifndef _VIRTUALPATH_H_
#define _VIRTUALPATH_H_
#ifdef __cplusplus
extern "C"{
#endif
#include <stdint.h>
#include <stdbool.h>
typedef struct {
char *name;
char *alias;
char *prefix;
bool inserted;
} VIRTUAL_PARTITION;
extern VIRTUAL_PARTITION * VIRTUAL_PARTITIONS;
extern uint8_t MAX_VIRTUAL_PARTITIONS;
void VirtualMountDevice(const char * devicepath);
void AddVirtualPath(const char *name, const char *alias, const char *prefix);
void MountVirtualDevices();
void UnmountVirtualPaths();
#ifdef __cplusplus
}
#endif
#endif /* _VIRTUALPART_H_ */

346
src/vrt.c Normal file
View File

@ -0,0 +1,346 @@
/*
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This work is derived from Daniel Ehlers' <danielehlers@mindeye.net> srg_vrt branch.
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#include <errno.h>
#include <malloc.h>
#include <stdarg.h>
#include <string.h>
#include <sys/dirent.h>
#include <unistd.h>
#include "main.h"
#include "virtualpath.h"
#include "vrt.h"
static char *virtual_abspath(char *virtual_cwd, char *virtual_path) {
char *path;
if (virtual_path[0] == '/') {
path = virtual_path;
} else {
size_t path_size = strlen(virtual_cwd) + strlen(virtual_path) + 1;
if (path_size > MAXPATHLEN || !(path = malloc(path_size))) return NULL;
strcpy(path, virtual_cwd);
strcat(path, virtual_path);
}
char *normalised_path = malloc(strlen(path) + 1);
if (!normalised_path) goto end;
*normalised_path = '\0';
char *curr_dir = normalised_path;
uint32_t state = 0; // 0:start, 1:slash, 2:dot, 3:dotdot
char *token = path;
while (1) {
switch (state) {
case 0:
if (*token == '/') {
state = 1;
curr_dir = normalised_path + strlen(normalised_path);
strncat(normalised_path, token, 1);
}
break;
case 1:
if (*token == '.') state = 2;
else if (*token != '/') state = 0;
break;
case 2:
if (*token == '/' || !*token) {
state = 1;
*(curr_dir + 1) = '\0';
} else if (*token == '.') state = 3;
else state = 0;
break;
case 3:
if (*token == '/' || !*token) {
state = 1;
*curr_dir = '\0';
char *prev_dir = strrchr(normalised_path, '/');
if (prev_dir) curr_dir = prev_dir;
else *curr_dir = '/';
*(curr_dir + 1) = '\0';
} else state = 0;
break;
}
if (!*token) break;
if (state == 0 || *token != '/') strncat(normalised_path, token, 1);
token++;
}
uint32_t end = strlen(normalised_path);
while (end > 1 && normalised_path[end - 1] == '/') {
normalised_path[--end] = '\x00';
}
end:
if (path != virtual_path) free(path);
return normalised_path;
}
/*
Converts a client-visible path to a real absolute path
E.g. "/sd/foo" -> "sd:/foo"
"/sd" -> "sd:/"
"/sd/../usb" -> "usb:/"
The resulting path will fit in an array of size MAXPATHLEN
Returns NULL to indicate that the client-visible path is invalid
*/
char *to_real_path(char *virtual_cwd, char *virtual_path) {
errno = ENOENT;
if (strchr(virtual_path, ':')) {
return NULL; // colon is not allowed in virtual path, i've decided =P
}
virtual_path = virtual_abspath(virtual_cwd, virtual_path);
if (!virtual_path) return NULL;
char *path = NULL;
char *rest = virtual_path;
if (!strcmp("/", virtual_path)) {
// indicate vfs-root with ""
path = "";
goto end;
}
const char *prefix = NULL;
uint32_t i;
for (i = 0; i < MAX_VIRTUAL_PARTITIONS; i++) {
VIRTUAL_PARTITION *partition = VIRTUAL_PARTITIONS + i;
const char *alias = partition->alias;
size_t alias_len = strlen(alias);
if (!strcasecmp(alias, virtual_path) || (!strncasecmp(alias, virtual_path, alias_len) && virtual_path[alias_len] == '/')) {
prefix = partition->prefix;
rest += alias_len;
if (*rest == '/') rest++;
break;
}
}
if (!prefix) {
errno = ENODEV;
goto end;
}
size_t real_path_size = strlen(prefix) + strlen(rest) + 1;
if (real_path_size > MAXPATHLEN) goto end;
path = malloc(real_path_size);
if (!path) goto end;
strcpy(path, prefix);
strcat(path, rest);
end:
free(virtual_path);
return path;
}
static int checkdir(char *path) {
DIR *dir = opendir(path);
if(dir)
{
closedir(dir);
return 0;
}
return -1;
}
typedef void * (*path_func)(char *path, ...);
static void *with_virtual_path(void *virtual_cwd, void *void_f, char *virtual_path, int32_t failed, ...) {
char *path = to_real_path(virtual_cwd, virtual_path);
if (!path || !*path) return (void *)failed;
path_func f = (path_func)void_f;
va_list ap;
void *args[3];
unsigned int num_args = 0;
va_start(ap, failed);
do {
void *arg = va_arg(ap, void *);
if (!arg) break;
args[num_args++] = arg;
} while (1);
va_end(ap);
void *result;
switch (num_args) {
case 0: result = f(path); break;
case 1: result = f(path, args[0]); break;
case 2: result = f(path, args[0], args[1]); break;
case 3: result = f(path, args[0], args[1], args[2]); break;
default: result = (void *)failed; break;
}
free(path);
return result;
}
FILE *vrt_fopen(char *cwd, char *path, char *mode) {
return with_virtual_path(cwd, fopen, path, 0, mode, NULL);
}
int vrt_stat(char *cwd, char *path, struct stat *st) {
char *real_path = to_real_path(cwd, path);
if (!real_path)
{
return -1;
}
else if (!*real_path || (strcmp(path, ".") == 0) || (strlen(cwd) == 1) || ((strlen(cwd) > 1) && (strcmp(path, "..") == 0)))
{
st->st_mode = S_IFDIR;
st->st_size = 31337;
return 0;
}
free(real_path);
return (int)with_virtual_path(cwd, stat, path, -1, st, NULL);
}
static int vrt_checkdir(char *cwd, char *path) {
char *real_path = to_real_path(cwd, path);
if (!real_path)
{
return -1;
}
else if (!*real_path || (strcmp(path, ".") == 0) || (strlen(cwd) == 1) || ((strlen(cwd) > 1) && (strcmp(path, "..") == 0)))
{
return 0;
}
free(real_path);
return (int)with_virtual_path(cwd, checkdir, path, -1, NULL);
}
int vrt_chdir(char *cwd, char *path) {
if (vrt_checkdir(cwd, path)) {
return -1;
}
char *abspath = virtual_abspath(cwd, path);
if (!abspath) {
errno = ENOMEM;
return -1;
}
strcpy(cwd, abspath);
if (cwd[1]) strcat(cwd, "/");
free(abspath);
return 0;
}
int vrt_unlink(char *cwd, char *path) {
return (int)with_virtual_path(cwd, unlink, path, -1, NULL);
}
int vrt_mkdir(char *cwd, char *path, mode_t mode) {
return (int)with_virtual_path(cwd, mkdir, path, -1, mode, NULL);
}
int vrt_rename(char *cwd, char *from_path, char *to_path) {
char *real_to_path = to_real_path(cwd, to_path);
if (!real_to_path || !*real_to_path) return -1;
int result = (int)with_virtual_path(cwd, rename, from_path, -1, real_to_path, NULL);
free(real_to_path);
return result;
}
/*
When in vfs-root this creates a fake DIR_ITER.
*/
DIR_P *vrt_opendir(char *cwd, char *path)
{
char *real_path = to_real_path(cwd, path);
if (!real_path) return NULL;
DIR_P *iter = malloc(sizeof(DIR_P));
if (!iter)
{
if (*real_path != 0)
free(real_path);
return NULL;
}
iter->virt_root = 0;
iter->path = real_path;
if (*iter->path == 0) {
iter->dir = malloc(sizeof(DIR));
if(!iter->dir) {
// root path is not allocated
free(iter);
return NULL;
}
memset(iter->dir, 0, sizeof(DIR));
iter->virt_root = 1; // we are at the virtual root
return iter;
}
iter->dir = with_virtual_path(cwd, opendir, path, 0, NULL);
if(!iter->dir)
{
free(iter->path);
free(iter);
return NULL;
}
return iter;
}
/*
Yields virtual aliases when pDir->virt_root
*/
struct dirent *vrt_readdir(DIR_P *pDir) {
if(!pDir || !pDir->dir) return NULL;
DIR *iter = pDir->dir;
if (pDir->virt_root) {
for (; (uint32_t)iter->position < MAX_VIRTUAL_PARTITIONS; iter->position++) {
VIRTUAL_PARTITION *partition = VIRTUAL_PARTITIONS + (int)iter->position;
if (partition->inserted) {
iter->fileData.d_type = DT_DIR;
strcpy(iter->fileData.d_name, partition->alias + 1);
iter->position++;
return &iter->fileData;
}
}
return NULL;
}
return readdir(iter);
}
int vrt_closedir(DIR_P *iter) {
if(!iter) return -1;
if(iter->dir)
{
if (iter->virt_root)
free(iter->dir);
else
closedir(iter->dir);
}
// root path is not allocated
if(iter->path && *iter->path != 0)
free(iter->path);
free(iter);
return 0;
}

58
src/vrt.h Normal file
View File

@ -0,0 +1,58 @@
/*
Copyright (C) 2008 Joseph Jordan <joe.ftpii@psychlaw.com.au>
This work is derived from Daniel Ehlers' <danielehlers@mindeye.net> srg_vrt branch.
This software is provided 'as-is', without any express or implied warranty.
In no event will the authors be held liable for any damages arising from
the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1.The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software in a
product, an acknowledgment in the product documentation would be
appreciated but is not required.
2.Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3.This notice may not be removed or altered from any source distribution.
*/
#ifndef _VRT_H_
#define _VRT_H_
#ifdef __cplusplus
extern "C"{
#endif
#include <stdio.h>
#include <sys/dirent.h>
typedef struct
{
DIR *dir;
char *path;
uint8_t virt_root;
} DIR_P;
char *to_real_path(char *virtual_cwd, char *virtual_path);
FILE *vrt_fopen(char *cwd, char *path, char *mode);
int vrt_stat(char *cwd, char *path, struct stat *st);
int vrt_chdir(char *cwd, char *path);
int vrt_unlink(char *cwd, char *path);
int vrt_mkdir(char *cwd, char *path, mode_t mode);
int vrt_rename(char *cwd, char *from_path, char *to_path);
DIR_P *vrt_opendir(char *cwd, char *path);
struct dirent *vrt_readdir(DIR_P *iter);
int vrt_closedir(DIR_P *iter);
#ifdef __cplusplus
}
#endif
#endif /* _VRT_H_ */