mirror of
https://github.com/wiiu-env/ftpiiu_plugin.git
synced 2024-11-04 20:15:09 +01:00
first commit
This commit is contained in:
commit
51247b0dc1
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
build/*
|
||||
*.mod
|
||||
sysapp.layout
|
||||
sysapp.cbp
|
272
Makefile
Normal file
272
Makefile
Normal 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
50
makefile.mk
Normal 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
53
src/BackgroundThread.cpp
Normal 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
50
src/BackgroundThread.hpp
Normal 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
921
src/ftp.c
Normal 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
43
src/ftp.h
Normal 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
138
src/main.cpp
Normal 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
32
src/main.h
Normal 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
283
src/net.c
Normal 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
63
src/net.h
Normal 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_ */
|
30
src/utils/BackgroundThreadWrapper.cpp
Normal file
30
src/utils/BackgroundThreadWrapper.cpp
Normal 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");
|
||||
}
|
||||
|
31
src/utils/BackgroundThreadWrapper.hpp
Normal file
31
src/utils/BackgroundThreadWrapper.hpp
Normal 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
69
src/utils/CMutex.h
Normal 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
134
src/utils/CThread.h
Normal 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
82
src/utils/logger.c
Normal 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
38
src/utils/logger.h
Normal 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
41
src/utils/utils.c
Normal 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
35
src/utils/utils.h
Normal 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
125
src/virtualpath.c
Normal 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
59
src/virtualpath.h
Normal 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
346
src/vrt.c
Normal 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
58
src/vrt.h
Normal 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_ */
|
Loading…
Reference in New Issue
Block a user