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