├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── CHANGELOG.md ├── LICENSE_GPLv2+.md ├── LICENSE_ISC.md ├── Makefile ├── README.md ├── example_callback ├── Makefile └── source │ └── main.c ├── example_event ├── Makefile └── source │ └── main.c ├── include └── usbhsfs.h └── source ├── fatfs ├── diskio.c ├── diskio.h ├── ff.c ├── ff.h ├── ff_dev.c ├── ff_dev.h ├── ffconf.h ├── ffsystem.c └── ffunicode.c ├── lwext4 ├── ext.c ├── ext.h ├── ext_dev.c ├── ext_dev.h ├── ext_disk_io.c └── ext_disk_io.h ├── ntfs-3g ├── ntfs.c ├── ntfs.h ├── ntfs_dev.c ├── ntfs_dev.h ├── ntfs_disk_io.c └── ntfs_disk_io.h ├── sxos ├── service_guard.h ├── usbfs.c ├── usbfs.h ├── usbfs_dev.c └── usbfs_dev.h ├── usb_common.h ├── usbhsfs_drive.c ├── usbhsfs_drive.h ├── usbhsfs_log.c ├── usbhsfs_log.h ├── usbhsfs_manager.c ├── usbhsfs_manager.h ├── usbhsfs_mount.c ├── usbhsfs_mount.h ├── usbhsfs_request.c ├── usbhsfs_request.h ├── usbhsfs_scsi.c ├── usbhsfs_scsi.h ├── usbhsfs_utils.c └── usbhsfs_utils.h /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help us improve 4 | title: '' 5 | labels: bug 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Click on '....' 17 | 3. Scroll down to '....' 18 | 4. See error 19 | 20 | **Please fill the following information:** 21 | - Horizon OS (Switch FW) version: [e.g. 10.0.0] 22 | - CFW: [e.g. Atmosphère, SX OS, etc.] 23 | - CFW version: [e.g. 0.11.1, 2.9.4, etc.] 24 | - Atmosphère launch method (if applicable): [e.g. Hekate, fusee-primary] 25 | - libusbhsfs version: [e.g. 0.0.2] 26 | - Homebrew launch method: [e.g. title override, applet] 27 | - USB Mass Storage device specs: [e.g. WD My Passport 1 TB, exFAT partition] 28 | 29 | **Logfile** 30 | Make sure you're using an application that's linked against the debug version of the library, and upload your logfile located at `sdmc:/libusbhsfs.log`. 31 | 32 | **Additional context** 33 | Add any other context about the problem here. 34 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Feature request 3 | about: Suggest an idea for this project 4 | title: '' 5 | labels: enhancement 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Is your feature request related to a problem? Please describe.** 11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] 12 | 13 | **Describe the solution you'd like** 14 | A clear and concise description of what you want to happen. 15 | 16 | **Describe alternatives you've considered** 17 | A clear and concise description of any alternative solutions or features you've considered. 18 | 19 | **Additional context** 20 | Add any other context or screenshots about the feature request here. 21 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.vscode 2 | /debug 3 | /release 4 | /lib 5 | *.tar* 6 | *.tgz 7 | *.zip 8 | *.log 9 | *.bin 10 | *.pcapng 11 | *.elf 12 | *.nacp 13 | *.nro 14 | *.code-workspace 15 | /example_callback/build 16 | /example_event/build 17 | -------------------------------------------------------------------------------- /LICENSE_ISC.md: -------------------------------------------------------------------------------- 1 | Copyright (c) 2020-2023, DarkMatterCore . 2 | Copyright (c) 2020-2021, XorTroll. 3 | Copyright (c) 2020-2021, Rhys Koedijk. 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 8 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | include $(DEVKITPRO)/libnx/switch_rules 10 | 11 | #--------------------------------------------------------------------------------- 12 | # TARGET is the name of the output 13 | # SOURCES is a list of directories containing source code 14 | # DATA is a list of directories containing data files 15 | # INCLUDES is a list of directories containing header files 16 | #--------------------------------------------------------------------------------- 17 | 18 | ROOTDIR ?= $(shell dirname $(realpath $(firstword $(MAKEFILE_LIST)))) 19 | 20 | BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC')) 21 | 22 | TARGET := usbhsfs 23 | SOURCES := source source/fatfs source/sxos 24 | DATA := data 25 | INCLUDES := include 26 | 27 | #--------------------------------------------------------------------------------- 28 | # options for code generation 29 | #--------------------------------------------------------------------------------- 30 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIC -ftls-model=local-exec 31 | 32 | CFLAGS := -g -Wall -Wextra -Werror -Wno-implicit-fallthrough -Wno-unused-function -ffunction-sections -fdata-sections $(ARCH) $(BUILD_CFLAGS) $(INCLUDE) 33 | CFLAGS += -DBUILD_TIMESTAMP="\"$(BUILD_TIMESTAMP)\"" -DLIB_TITLE="\"lib$(TARGET)\"" -fmacro-prefix-map=$(ROOTDIR)= 34 | 35 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 36 | 37 | ASFLAGS := -g $(ARCH) 38 | 39 | UC = $(strip $(subst a,A,$(subst b,B,$(subst c,C,$(subst d,D,$(subst e,E,$(subst f,F,$(subst g,G,$(subst h,H,$(subst i,I,$(subst j,J,$(subst k,K,\ 40 | $(subst l,L,$(subst m,M,$(subst n,N,$(subst o,O,$(subst p,P,$(subst q,Q,$(subst r,R,$(subst s,S,$(subst t,T,$(subst u,U,$(subst v,V,\ 41 | $(subst w,W,$(subst x,X,$(subst y,Y,$(subst z,Z,$1))))))))))))))))))))))))))) 42 | 43 | ifeq ($(filter $(MAKECMDGOALS),clean dist-src),) 44 | # Check BUILD_TYPE flag 45 | ifneq ($(origin BUILD_TYPE),undefined) 46 | # Convert BUILD_TYPE flag to uppercase 47 | ORIG_BUILD_TYPE := $(BUILD_TYPE) 48 | override BUILD_TYPE := $(call UC,$(ORIG_BUILD_TYPE)) 49 | # Check BUILD_TYPE flag value 50 | ifeq ($(BUILD_TYPE),ISC) 51 | # Do nothing 52 | else 53 | ifeq ($(BUILD_TYPE),GPL) 54 | # Update sources, set GPL_BUILD definition 55 | # We'll just assume the user has already installed the necessary libraries 56 | SOURCES += source/ntfs-3g source/lwext4 57 | CFLAGS += -DGPL_BUILD 58 | else 59 | $(error Invalid value for BUILD_TYPE flag. Expected ISC or GPL) 60 | endif 61 | endif 62 | else 63 | $(error BUILD_TYPE flag not set) 64 | endif 65 | endif 66 | 67 | #--------------------------------------------------------------------------------- 68 | # list of directories containing libraries, this must be the top level containing 69 | # include and lib 70 | #--------------------------------------------------------------------------------- 71 | LIBDIRS := $(PORTLIBS) $(LIBNX) 72 | 73 | #--------------------------------------------------------------------------------- 74 | # no real need to edit anything past this point unless you need to add additional 75 | # rules for different file extensions 76 | #--------------------------------------------------------------------------------- 77 | ifneq ($(BUILD),$(notdir $(CURDIR))) 78 | #--------------------------------------------------------------------------------- 79 | 80 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 81 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 82 | 83 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 84 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 85 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 86 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 87 | 88 | #--------------------------------------------------------------------------------- 89 | # use CXX for linking C++ projects, CC for standard C 90 | #--------------------------------------------------------------------------------- 91 | ifeq ($(strip $(CPPFILES)),) 92 | #--------------------------------------------------------------------------------- 93 | export LD := $(CC) 94 | #--------------------------------------------------------------------------------- 95 | else 96 | #--------------------------------------------------------------------------------- 97 | export LD := $(CXX) 98 | #--------------------------------------------------------------------------------- 99 | endif 100 | #--------------------------------------------------------------------------------- 101 | 102 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 103 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 104 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 105 | export HFILES := $(addsuffix .h,$(subst .,_,$(BINFILES))) 106 | 107 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 108 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 109 | -I$(CURDIR)/$(BUILD) 110 | 111 | .PHONY: clean all release release-dir debug debug-dir lib-dir example 112 | 113 | #--------------------------------------------------------------------------------- 114 | LIB_BRANCH := $(shell git rev-parse --abbrev-ref HEAD) 115 | LIB_HASH := $(shell git rev-parse --short HEAD) 116 | LIB_REV := $(LIB_BRANCH)-$(LIB_HASH) 117 | 118 | ifneq (, $(strip $(shell git status --porcelain 2>/dev/null))) 119 | LIB_REV := $(LIB_REV)-dirty 120 | endif 121 | 122 | $(eval LIB_VERSION_MAJOR = $(shell grep 'define LIBUSBHSFS_VERSION_MAJOR\b' include/usbhsfs.h | tr -s [:blank:] | cut -d' ' -f3)) 123 | $(eval LIB_VERSION_MINOR = $(shell grep 'define LIBUSBHSFS_VERSION_MINOR\b' include/usbhsfs.h | tr -s [:blank:] | cut -d' ' -f3)) 124 | $(eval LIB_VERSION_MICRO = $(shell grep 'define LIBUSBHSFS_VERSION_MICRO\b' include/usbhsfs.h | tr -s [:blank:] | cut -d' ' -f3)) 125 | $(eval LIB_VERSION = $(LIB_VERSION_MAJOR).$(LIB_VERSION_MINOR).$(LIB_VERSION_MICRO)-$(LIB_REV)) 126 | 127 | ifeq ($(BUILD_TYPE),ISC) 128 | LIB_LICENSE := ISC 129 | else 130 | LIB_LICENSE := GPLv2+ 131 | endif 132 | 133 | all: release debug 134 | 135 | release: lib/lib$(TARGET).a 136 | 137 | release-dir: 138 | @mkdir -p release 139 | 140 | debug: lib/lib$(TARGET)d.a 141 | 142 | debug-dir: 143 | @mkdir -p debug 144 | 145 | lib-dir: 146 | @mkdir -p lib 147 | 148 | example: all 149 | @$(MAKE) BUILD_TYPE=$(BUILD_TYPE) --no-print-directory -C example_callback 150 | @$(MAKE) BUILD_TYPE=$(BUILD_TYPE) --no-print-directory -C example_event 151 | 152 | lib/lib$(TARGET).a: release-dir lib-dir $(SOURCES) $(INCLUDES) 153 | @echo release 154 | @$(MAKE) BUILD=release OUTPUT=$(CURDIR)/$@ \ 155 | BUILD_CFLAGS="-DNDEBUG=1 -O2" \ 156 | ROOTDIR=$(ROOTDIR) \ 157 | DEPSDIR=$(CURDIR)/release \ 158 | --no-print-directory -C release \ 159 | -f $(CURDIR)/Makefile 160 | 161 | lib/lib$(TARGET)d.a: debug-dir lib-dir $(SOURCES) $(INCLUDES) 162 | @echo debug 163 | @$(MAKE) BUILD=debug OUTPUT=$(CURDIR)/$@ \ 164 | BUILD_CFLAGS="-DDEBUG=1 -Og" \ 165 | ROOTDIR=$(ROOTDIR) \ 166 | DEPSDIR=$(CURDIR)/debug \ 167 | --no-print-directory -C debug \ 168 | -f $(CURDIR)/Makefile 169 | 170 | dist-bin: example 171 | @cp example_callback/libusbhsfs-example-callback.nro libusbhsfs-example-callback.nro 172 | @cp example_event/libusbhsfs-example-event.nro libusbhsfs-example-event.nro 173 | @tar --exclude=*~ -cjf lib$(TARGET)_$(LIB_VERSION)_$(LIB_LICENSE).tar.bz2 include lib LICENSE_$(LIB_LICENSE).md README.md CHANGELOG.md \ 174 | libusbhsfs-example-callback.nro libusbhsfs-example-event.nro 175 | @rm libusbhsfs-example-callback.nro libusbhsfs-example-event.nro 176 | 177 | clean: 178 | @echo clean ... 179 | @rm -fr release debug lib *.bz2 180 | @$(MAKE) --no-print-directory -C example_callback clean 181 | @$(MAKE) --no-print-directory -C example_event clean 182 | 183 | dist-src: 184 | @tar --exclude=*~ -cjf lib$(TARGET)_$(LIB_VERSION)-src.tar.bz2 \ 185 | --exclude='example_callback/build' --exclude='example_callback/*.elf' --exclude='example_callback/*.nacp' --exclude='example_callback/*.nro' \ 186 | --exclude='example_event/build' --exclude='example_event/*.elf' --exclude='example_event/*.nacp' --exclude='example_event/*.nro' \ 187 | example_callback example_event include source LICENSE_ISC.md LICENSE_GPLv2+.md Makefile README.md CHANGELOG.md 188 | 189 | dist: dist-src dist-bin 190 | 191 | install: dist-bin 192 | @bzip2 -cd lib$(TARGET)_$(LIB_VERSION)_$(LIB_LICENSE).tar.bz2 | tar -xf - -C $(PORTLIBS) --exclude='*.md' --exclude='*.nro' 193 | 194 | #--------------------------------------------------------------------------------- 195 | else 196 | 197 | DEPENDS := $(OFILES:.o=.d) 198 | 199 | #--------------------------------------------------------------------------------- 200 | # main targets 201 | #--------------------------------------------------------------------------------- 202 | $(OUTPUT) : $(OFILES) 203 | 204 | $(OFILES_SRC) : $(HFILES) 205 | 206 | #--------------------------------------------------------------------------------- 207 | %_bin.h %.bin.o : %.bin 208 | #--------------------------------------------------------------------------------- 209 | @echo $(notdir $<) 210 | @$(bin2o) 211 | 212 | 213 | -include $(DEPENDS) 214 | 215 | #--------------------------------------------------------------------------------------- 216 | endif 217 | #--------------------------------------------------------------------------------------- 218 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # libusbhsfs 2 | USB Mass Storage Class Host + Filesystem Mounter static library for Nintendo Switch homebrew applications. 3 | 4 | Main features 5 | -------------- 6 | 7 | * Supports USB Mass Storage (UMS) devices that implement at least one USB interface descriptor with the following properties: 8 | * bInterfaceClass: 0x08 (USB Mass Storage Class). 9 | * bInterfaceSubClass: 0x06 (SCSI Transparent Command Set SubClass). 10 | * bInterfaceProtocol: 0x50 (Bulk-Only Transport [BOT] Protocol). 11 | * Bulk-Only Transport (BOT) driver written from scratch, which implements the most common SCSI Primary Command Set (SPC) commands as well as BOT class-specific requests. 12 | * Supported SPC commands: 13 | * TEST UNIT READY (0x00). 14 | * REQUEST SENSE (0x03). 15 | * INQUIRY (0x12). 16 | * MODE SENSE (6) (0x1A). 17 | * START STOP UNIT (0x1B). 18 | * PREVENT ALLOW MEDIUM REMOVAL (0x1E). 19 | * READ CAPACITY (10) (0x25). 20 | * READ (10) (0x28). 21 | * WRITE (10) (0x2A). 22 | * MODE SENSE (10) (0x5A). 23 | * READ (16) (0x88). 24 | * WRITE (16) (0x8A). 25 | * SERVICE ACTION IN (0x9E). 26 | * Supported SERVICE ACTION IN actions: 27 | * READ CAPACITY (16) (0x10). 28 | * Supported BOT class-specific requests: 29 | * Get Max LUN (0xFE). 30 | * Bulk-Only Mass Storage Reset (0xFF). 31 | * Supports UMS devices with long logical block addresses (64-bit LBAs) and variable logical block sizes (512 - 4096 bytes). 32 | * Background thread that takes care of starting all available logical units from each newly connected UMS device, as well as mounting the available filesystems from each one whenever possible. 33 | * Supported partitioning schemes: 34 | * Super Floppy Drive (SFD) (Volume Boot Record @ LBA 0). 35 | * Master Boot Record (MBR). 36 | * Extended Boot Record (EBR). 37 | * GUID Partition Table (GPT) + protective MBR. 38 | * Supported filesystems: 39 | * FAT12/FAT16/FAT32/exFAT (via FatFs). 40 | * NTFS (via NTFS-3G). 41 | * EXT2/3/4 (via lwext4). 42 | * Completely possible to add support for additional filesystems, as long as their libraries are ported over to Switch. 43 | * Uses devoptab virtual device interface to provide a way to use standard I/O calls from libc (e.g. `fopen()`, `opendir()`, etc.) on mounted filesystems from the available logical units. 44 | * Easy to use library interface: 45 | * Provides an autoclear user event that is signaled each time a status change is detected by the background thread (new device mounted, device removed). 46 | * Painless listing of mounted partitions using a simple struct that provides the devoptab device name, as well as other interesting information (filesystem index, filesystem type, write protection, raw logical unit capacity, etc.). 47 | * Provides a way to safely unmount UMS devices at runtime. 48 | * Supports the `usbfs` service from SX OS. 49 | 50 | Limitations 51 | -------------- 52 | 53 | * Bulk-Only Transport (BOT) driver: 54 | * Up to 32 different USB Mass Storage Class interfaces can be used at the same time. Increasing this limit isn't harmful, but makes the library take up additional heap memory. 55 | * Only a single SCSI operation can be performed at any given time per UMS device, regardless of their number of logical units. This is an official limitation of the BOT protocol. Mutexes are used to avoid multiple SCSI operations from taking place at the same time on the same UMS device. 56 | * Filesystem libraries: 57 | * FatFs: 58 | * Up to 64 FAT volumes can be mounted at the same time across all available UMS devices. Original limit was 10, but FatFs was slightly modified to allow for more volumes to be mounted simultaneously. 59 | * NTFS-3G: 60 | * Crypto operations aren't supported. 61 | * Security contexts are always ignored. 62 | * Only partial journaling is supported, so unexpected crashes or power loss can leave the mounted NTFS volume in an inconsistent state. In cases where there has been heavy activity prior to the crash or power loss, it is recommended to plug the UMS device into a Windows PC and let it replay the journal properly before remounting with NTFS-3G, in order to prevent possible data loss and/or corruption. 63 | * Symbolic links are transparent. This means that when a symbolic link in encountered, its hard link will be used instead. 64 | * lwext4: 65 | * Up to 8 EXT volumes can be mounted at the same time across all available UMS devices. This is because lwext4 uses an internal, stack-based registry of mount points and block devices, and increasing the limit can potentially exhaust the stack memory from the thread libusbhsfs runs under. 66 | * For the rest of the limitations, please take a look at the [README](https://github.com/gkostka/lwext4/blob/master/README.md) from the lwext4 repository. 67 | * Stack and/or heap memory consumption: 68 | * This library is *not* suitable for custom sysmodules and/or service MITM projects. It allocates a 8 MiB buffer per each UMS device, which is used for command and data transfers. It also relies heavily on libnx features, which are not always compatible with sysmodule/MITM program contexts. 69 | * Switch-specific FS features: 70 | * Concatenation files aren't supported. 71 | * `usbfs` service from SX OS: 72 | * Only a single FAT volume from a single drive can be mounted. No other filesystem types are supported. 73 | * Relative paths aren't supported. 74 | * `chdir()`, `rename()`, `dirreset()` and `utimes()` aren't supported. 75 | * There are probably other limitations we don't even know about, due to the closed-source nature of this CFW. 76 | 77 | Licensing 78 | -------------- 79 | 80 | Dual licensing is provided for this project depending on the way it is built: 81 | 82 | * If the library is built using the `BUILD_TYPE=ISC` parameter with `make`, it is distributed under the terms of the ISC License. You can find a copy of this license in the [LICENSE_ISC.md file](https://github.com/DarkMatterCore/libusbhsfs/blob/main/LICENSE_ISC.md). 83 | * ISC licensed builds only provide support for FAT filesystems via FatFs, which is licensed under the [FatFs license](http://elm-chan.org/fsw/ff/doc/appnote.html#license). 84 | * If the library is built using the `BUILD_TYPE=GPL` parameter with `make`, it is distributed under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. You can find a copy of this license in the [LICENSE_GPLv2+.md file](https://github.com/DarkMatterCore/libusbhsfs/blob/main/LICENSE_GPLv2+.md). GPLv2+ licensed builds provide support for: 85 | * FAT filesystems via FatFs, which is licensed under the [FatFs license](http://elm-chan.org/fsw/ff/doc/appnote.html#license). 86 | * NTFS via NTFS-3G, which is licensed under the [GPLv2+ license](https://github.com/tuxera/ntfs-3g/blob/edge/COPYING). 87 | * EXT filesystems via lwext4, which is licensed under the [GPLv2 license](https://github.com/gkostka/lwext4/blob/master/LICENSE). 88 | 89 | How to install 90 | -------------- 91 | 92 | This section assumes you've already installed devkitA64, libnx and devkitPro pacman. If not, please follow the steps from the [devkitPro wiki](https://devkitpro.org/wiki/Getting_Started). 93 | 94 | * **ISC licensed build**: run `make BUILD_TYPE=ISC install` on the root directory from the project. 95 | 96 | * **GPLv2+ licensed build**: 97 | 1. Install the NTFS-3G and lwext4 portlibs before proceeding any further: 98 | * If you're using a Linux distro with [pacman](https://wiki.archlinux.org/title/pacman), or msys2 + devkitPro under Windows, then run the following command: 99 | ``` 100 | pacman -S switch-ntfs-3g switch-lwext4 101 | ``` 102 | * If you're using a Linux distro without pacman, or macOS, then run the following command: 103 | ``` 104 | dkp-pacman -S switch-ntfs-3g switch-lwext4 105 | ``` 106 | You'll only need to carry out this step once. There's no need to manually reinstall these dependencies each time you intend to build libusbhsfs. 107 | 2. Finally, run `make BUILD_TYPE=GPL install`. 108 | 109 | Regardless of the build type you choose, libusbhsfs will be installed into the `portlibs` directory from devkitPro, and it'll be ready to use by any homebrew application. 110 | 111 | Installing the filesystem support libraries beforehand isn't needed if you intend to use the ISC licensed build -- it is guaranteed to not use any GPL licensed code and/or dependency at all. 112 | 113 | How to use 114 | -------------- 115 | 116 | This section assumes you've already built the library by following the steps from the previous section. 117 | 118 | 1. Update the `Makefile` from your homebrew application to reference the library. 119 | * Two different builds can be generated: a release build (`-lusbhsfs`) and a debug build with logging enabled (`-lusbhsfsd`). 120 | * If you're using a GPLv2+ licensed build, you'll also need to link your application against both NTFS-3G and lwext4: `-lusbhsfs -lntfs-3g -llwext4`. 121 | * In case you need to report any bugs, please make sure you're using the debug build and provide its logfile. 122 | 2. Include the `usbhsfs.h` header file somewhere in your code. 123 | 3. Initialize the USB Mass Storage Class Host interface with `usbHsFsInitialize()`. 124 | 4. Choose the population system you'll use to retrieve information from the library. For further details about the pros and cons of each system, please refer to the `usbhsfs.h` header file. 125 | * Event-driven system: 126 | * Retrieve a pointer to the user-mode UMS status change event with `usbHsFsGetStatusChangeUserEvent()` and wait for that event to be signaled (e.g. under a different thread). 127 | * Get the mounted device count with `usbHsFsGetMountedDeviceCount()`. 128 | * List mounted devices with `usbHsFsListMountedDevices()`. 129 | * Callback-based system: 130 | * Set a pointer to a callback function of your own with `usbHsFsSetPopulateCallback()`, which will receive a short-lived array of mounted devices and their count. 131 | 5. Perform I/O operations using the returned mount names from the listed devices. 132 | 6. If, for some reason, you need to safely unmount a UMS device at runtime before disconnecting it and without shutting down the whole library interface, use `usbHsFsUnmountDevice()`. 133 | 7. Close the USB Mass Storage Class Host interface with `usbHsFsExit()` when you're done. 134 | 135 | Please check both the header file located at `/include/usbhsfs.h` and the provided test applications in `/example_event` (event-driven system) and `/example_callback` (callback-based system) for additional information. 136 | 137 | Relative path support 138 | -------------- 139 | 140 | **Disclaimer #1:** all `fsdevMount*()` calls from libnx (and any wrappers around them) **can** and **will** override the default devoptab device if used after a successful `chdir()` call using an absolute path from a mounted volume in a UMS device. If such thing occurs, and you still need to perform additional operations with relative paths, just call `chdir()` again. 141 | 142 | **Disclaimer #2:** relative path support is not available under SX OS! 143 | 144 | A `chdir()` call using an absolute path to a directory from a mounted volume (e.g. `"ums0:/"`) must be issued to change both the default devoptab device and the current working directory. This will effectively place you at the provided directory, and all I/O operations performed with relative paths shall work on it. 145 | 146 | The SD card will be set as the new default devoptab device under two different conditions: 147 | 148 | * If the UMS device that holds the volume set as the default devoptab device is removed from the console. 149 | * If the USB Mass Storage Class Host interface is closed via `usbHsFsExit()` and a volume from an available UMS device was set as the default devoptab device. 150 | 151 | For an example, please check the provided test application in `/example`. 152 | 153 | Credits 154 | -------------- 155 | 156 | * [DarkMatterCore](https://github.com/DarkMatterCore): UMS device LUN/FS management, Bulk-Only Transport (BOT) driver, library interface. 157 | * [XorTroll](https://github.com/XorTroll): FS mounting system, devoptab device (un)registration, example test application. 158 | * [Rhys Koedijk](https://github.com/rhyskoedijk): NTFS support. 159 | * Lots of SPC/BOT docs across the Internet - these have been referenced in multiple files from the codebase. 160 | 161 | Thanks to 162 | -------------- 163 | 164 | * ChaN, for the [FatFs module](http://elm-chan.org/fsw/ff/00index_e.html). 165 | * Tuxera and NTFS-3G contributors, for the [NTFS-3G library](https://github.com/tuxera/ntfs-3g). 166 | * Grzegorz Kostka and lwext4 contributors, for the [lwext4 library](https://github.com/gkostka/lwext4). 167 | * Switchbrew and libnx contributors. Code from libnx was used for devoptab device management and path handling. 168 | * [blawar](https://github.com/blawar), for providing the updated `usbfs` SX OS service calls. 169 | * [Whovian9369](https://github.com/Whovian9369). I literally would have dropped Switch homebrew development altogether some months ago, if not for you. Thanks, mate. 170 | * [ITotalJustice](https://github.com/ITotalJustice), for testing the partition table parsing algorithm. 171 | * [FennecTECH](https://github.com/fennectech), for breaking stuff on a regular basis. 172 | * All the Alpha Testers and Super Users from the nxdumptool Discord server, for being a constant source of ideas (and memes). 173 | -------------------------------------------------------------------------------- /example_callback/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | # 32 | # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. 33 | # If not set, it attempts to use one of the following (in this order): 34 | # - .json 35 | # - config.json 36 | # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead 37 | # of a homebrew executable (.nro). This is intended to be used for sysmodules. 38 | # NACP building is skipped as well. 39 | #--------------------------------------------------------------------------------- 40 | 41 | BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC')) 42 | 43 | VERSION_MAJOR := 0 44 | VERSION_MINOR := 0 45 | VERSION_MICRO := 2 46 | 47 | APP_TITLE := libusbhsfs-example-callback 48 | APP_AUTHOR := DarkMatterCore, XorTroll, Rhys Koedijk 49 | APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} 50 | 51 | TARGET := ${APP_TITLE} 52 | BUILD := build 53 | SOURCES := source 54 | DATA := data 55 | INCLUDES := include 56 | #ROMFS := romfs 57 | 58 | #--------------------------------------------------------------------------------- 59 | # options for code generation 60 | #--------------------------------------------------------------------------------- 61 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 62 | 63 | CFLAGS := -g -Wall -Wextra -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__ 64 | CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DBUILD_TIMESTAMP="\"${BUILD_TIMESTAMP}\"" 65 | 66 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 67 | 68 | ASFLAGS := -g $(ARCH) 69 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 70 | 71 | # Use -lusbhsfsd instead of -lusbhsfs to use the library build with debug logging enabled 72 | LIBS := -lusbhsfsd 73 | 74 | ifeq ($(filter $(MAKECMDGOALS),clean),) 75 | # Check BUILD_TYPE flag 76 | ifneq ($(origin BUILD_TYPE),undefined) 77 | ifeq (${BUILD_TYPE},ISC) 78 | # Do nothing 79 | else 80 | ifeq (${BUILD_TYPE},GPL) 81 | # Update libs 82 | # We'll just assume the user has already installed the necessary libraries 83 | LIBS += -lntfs-3g -llwext4 84 | else 85 | $(error Invalid value for BUILD_TYPE flag. Expected ISC or GPL) 86 | endif 87 | endif 88 | else 89 | $(error BUILD_TYPE flag not set) 90 | endif 91 | endif 92 | 93 | LIBS += -lnx 94 | 95 | #--------------------------------------------------------------------------------- 96 | # list of directories containing libraries, this must be the top level containing 97 | # include and lib 98 | #--------------------------------------------------------------------------------- 99 | LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/.. 100 | 101 | 102 | #--------------------------------------------------------------------------------- 103 | # no real need to edit anything past this point unless you need to add additional 104 | # rules for different file extensions 105 | #--------------------------------------------------------------------------------- 106 | ifneq ($(BUILD),$(notdir $(CURDIR))) 107 | #--------------------------------------------------------------------------------- 108 | 109 | export OUTPUT := $(CURDIR)/$(TARGET) 110 | export TOPDIR := $(CURDIR) 111 | 112 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 113 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 114 | 115 | export DEPSDIR := $(CURDIR)/$(BUILD) 116 | 117 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 118 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 119 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 120 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 121 | 122 | #--------------------------------------------------------------------------------- 123 | # use CXX for linking C++ projects, CC for standard C 124 | #--------------------------------------------------------------------------------- 125 | ifeq ($(strip $(CPPFILES)),) 126 | #--------------------------------------------------------------------------------- 127 | export LD := $(CC) 128 | #--------------------------------------------------------------------------------- 129 | else 130 | #--------------------------------------------------------------------------------- 131 | export LD := $(CXX) 132 | #--------------------------------------------------------------------------------- 133 | endif 134 | #--------------------------------------------------------------------------------- 135 | 136 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 137 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 138 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 139 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 140 | 141 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 142 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 143 | -I$(CURDIR)/$(BUILD) 144 | 145 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 146 | 147 | ifeq ($(strip $(CONFIG_JSON)),) 148 | jsons := $(wildcard *.json) 149 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 150 | export APP_JSON := $(TOPDIR)/$(TARGET).json 151 | else 152 | ifneq (,$(findstring config.json,$(jsons))) 153 | export APP_JSON := $(TOPDIR)/config.json 154 | endif 155 | endif 156 | else 157 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 158 | endif 159 | 160 | ifeq ($(strip $(ICON)),) 161 | icons := $(wildcard *.jpg) 162 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 163 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 164 | else 165 | ifneq (,$(findstring icon.jpg,$(icons))) 166 | export APP_ICON := $(TOPDIR)/icon.jpg 167 | endif 168 | endif 169 | else 170 | export APP_ICON := $(TOPDIR)/$(ICON) 171 | endif 172 | 173 | ifeq ($(strip $(NO_ICON)),) 174 | export NROFLAGS += --icon=$(APP_ICON) 175 | endif 176 | 177 | ifeq ($(strip $(NO_NACP)),) 178 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 179 | endif 180 | 181 | ifneq ($(APP_TITLEID),) 182 | export NACPFLAGS += --titleid=$(APP_TITLEID) 183 | endif 184 | 185 | ifneq ($(ROMFS),) 186 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 187 | endif 188 | 189 | .PHONY: $(BUILD) clean all 190 | 191 | #--------------------------------------------------------------------------------- 192 | all: $(BUILD) 193 | 194 | $(BUILD): 195 | @[ -d $@ ] || mkdir -p $@ 196 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 197 | 198 | #--------------------------------------------------------------------------------- 199 | clean: 200 | @echo clean ... 201 | ifeq ($(strip $(APP_JSON)),) 202 | @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf 203 | else 204 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf 205 | endif 206 | 207 | 208 | #--------------------------------------------------------------------------------- 209 | else 210 | .PHONY: all 211 | 212 | DEPENDS := $(OFILES:.o=.d) 213 | 214 | #--------------------------------------------------------------------------------- 215 | # main targets 216 | #--------------------------------------------------------------------------------- 217 | ifeq ($(strip $(APP_JSON)),) 218 | 219 | all : $(OUTPUT).nro 220 | 221 | ifeq ($(strip $(NO_NACP)),) 222 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 223 | else 224 | $(OUTPUT).nro : $(OUTPUT).elf 225 | endif 226 | 227 | else 228 | 229 | all : $(OUTPUT).nsp 230 | 231 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 232 | 233 | $(OUTPUT).nso : $(OUTPUT).elf 234 | 235 | endif 236 | 237 | $(OUTPUT).elf : $(OFILES) 238 | 239 | $(OFILES_SRC) : $(HFILES_BIN) 240 | 241 | #--------------------------------------------------------------------------------- 242 | # you need a rule like this for each extension you use as binary data 243 | #--------------------------------------------------------------------------------- 244 | %.bin.o %_bin.h : %.bin 245 | #--------------------------------------------------------------------------------- 246 | @echo $(notdir $<) 247 | @$(bin2o) 248 | 249 | -include $(DEPENDS) 250 | 251 | #--------------------------------------------------------------------------------------- 252 | endif 253 | #--------------------------------------------------------------------------------------- 254 | -------------------------------------------------------------------------------- /example_callback/source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static Mutex g_usbDeviceMutex = 0; 11 | static u32 g_usbDeviceCount = 0; 12 | static UsbHsFsDevice *g_usbDevices = NULL; 13 | static bool g_updated = false; 14 | 15 | static void usbMscFileSystemTest(UsbHsFsDevice *device) 16 | { 17 | if (!device) return; 18 | 19 | char path[FS_MAX_PATH] = {0}, tmp[0x40] = {0}, new_path[FS_MAX_PATH] = {0}, *ptr = NULL; 20 | const char *test_str = "Hello world!"; 21 | size_t test_str_len = strlen(test_str); 22 | 23 | FILE *fd = NULL, *ums_fd = NULL; 24 | struct stat st = {0}; 25 | 26 | DIR *dp = NULL; 27 | struct dirent *dt = NULL; 28 | 29 | struct statvfs fsinfo = {0}; 30 | 31 | bool copy_failed = false; 32 | 33 | int ret = -1, val = 0; 34 | 35 | u8 *buf = NULL; 36 | size_t blksize = 0x800000; 37 | 38 | sprintf(path, "%s/test_dir", device->name); 39 | 40 | /* Create directory. */ 41 | printf("\t\t- Create directory (\"%s\"): ", path); 42 | consoleUpdate(NULL); 43 | 44 | if (!mkdir(path, 0)) 45 | { 46 | printf("OK!\n"); 47 | } else { 48 | printf("FAILED! (%d).\n", errno); 49 | } 50 | 51 | consoleUpdate(NULL); 52 | 53 | /* Write data to file. */ 54 | strcat(path, "/" APP_TITLE ".txt"); 55 | printf("\t\t- Write data to file (\"%s\") (\"%s\"): ", path, test_str); 56 | consoleUpdate(NULL); 57 | 58 | fd = fopen(path, "w"); 59 | if (fd) 60 | { 61 | val = fprintf(fd, test_str); 62 | if (val == (int)test_str_len) 63 | { 64 | printf("OK!\n"); 65 | } else { 66 | printf("FAILED! (%d, %d).\n", errno, val); 67 | } 68 | 69 | fclose(fd); 70 | } else { 71 | printf("FAILED! (%d).\n", errno); 72 | } 73 | 74 | consoleUpdate(NULL); 75 | 76 | /* Read data from file. */ 77 | printf("\t\t- Read data from file (\"%s\"): ", path); 78 | consoleUpdate(NULL); 79 | 80 | fd = fopen(path, "r"); 81 | if (fd) 82 | { 83 | if (fgets(tmp, test_str_len + 1, fd) != NULL) 84 | { 85 | printf("OK! (\"%s\").\n", tmp); 86 | } else { 87 | printf("FAILED! (%d).\n", errno); 88 | } 89 | 90 | fclose(fd); 91 | } else { 92 | printf("FAILED! (%d).\n", errno); 93 | } 94 | 95 | consoleUpdate(NULL); 96 | 97 | /* File stats. */ 98 | printf("\t\t- File stats (\"%s\"): ", path); 99 | consoleUpdate(NULL); 100 | 101 | if (!stat(path, &st)) 102 | { 103 | printf("OK!\n\t\t\t- ID: %i.\n\t\t\t- Type: %s.\n\t\t\t- Size: %lu.\n\t\t\t- Timestamp: %lu.\n", st.st_ino, st.st_mode & S_IFREG ? "file" : "dir", st.st_size, st.st_mtime); 104 | } else { 105 | printf("FAILED! (%d).\n", errno); 106 | } 107 | 108 | consoleUpdate(NULL); 109 | 110 | /* Rename file. */ 111 | ptr = strrchr(path, '/'); 112 | sprintf(new_path, "%.*s/test.txt", (int)(ptr - path), path); 113 | printf("\t\t- Rename file (\"%s\" -> \"%s\"): ", path, new_path); 114 | consoleUpdate(NULL); 115 | 116 | if (!rename(path, new_path)) 117 | { 118 | printf("OK!\n"); 119 | } else { 120 | printf("FAILED! (%d).\n", errno); 121 | } 122 | 123 | consoleUpdate(NULL); 124 | 125 | /* Change directory. */ 126 | *ptr = '\0'; 127 | printf("\t\t- Change directory (\"%s\"): ", path); 128 | consoleUpdate(NULL); 129 | 130 | ret = chdir(path); 131 | if (!ret) 132 | { 133 | printf("OK!\n"); 134 | 135 | /* Directory listing. */ 136 | printf("\t\t- Directory listing (\".\"): "); 137 | consoleUpdate(NULL); 138 | 139 | dp = opendir("."); /* Open current directory. */ 140 | if (dp) 141 | { 142 | printf("OK!\n"); 143 | consoleUpdate(NULL); 144 | 145 | while((dt = readdir(dp))) 146 | { 147 | printf("\t\t\t- [%c] ./%s\n", (dt->d_type & DT_DIR) ? 'D' : 'F', dt->d_name); 148 | consoleUpdate(NULL); 149 | } 150 | 151 | closedir(dp); 152 | } else { 153 | printf("FAILED! (%d).\n", errno); 154 | consoleUpdate(NULL); 155 | } 156 | } else { 157 | printf("FAILED! (%d).\n", errno); 158 | } 159 | 160 | consoleUpdate(NULL); 161 | 162 | /* Delete file. */ 163 | printf("\t\t- Delete file (\"%s\"): ", new_path); 164 | consoleUpdate(NULL); 165 | 166 | if (!unlink(new_path)) 167 | { 168 | printf("OK!\n"); 169 | } else { 170 | printf("FAILED! (%d).\n", errno); 171 | } 172 | 173 | consoleUpdate(NULL); 174 | 175 | /* Delete directory. */ 176 | printf("\t\t- Delete directory (\"%s\"): ", path); 177 | consoleUpdate(NULL); 178 | 179 | if (!rmdir(path)) 180 | { 181 | printf("OK!\n"); 182 | } else { 183 | printf("FAILED! (%d).\n", errno); 184 | } 185 | 186 | consoleUpdate(NULL); 187 | 188 | /* Filesystem stats. */ 189 | printf("\t\t- Filesystem stats: "); 190 | consoleUpdate(NULL); 191 | 192 | if (!statvfs(".", &fsinfo)) 193 | { 194 | u64 fsid = (u64)fsinfo.f_fsid; 195 | u64 total_size = ((u64)fsinfo.f_blocks * (u64)fsinfo.f_frsize); 196 | u64 free_space = ((u64)fsinfo.f_bfree * (u64)fsinfo.f_frsize); 197 | 198 | printf("OK!\n\t\t\t- ID: %lu.\n\t\t\t- Total FS size: 0x%lX bytes.\n\t\t\t- Free FS space: 0x%lX bytes.\n", fsid, total_size, free_space); 199 | } else { 200 | printf("FAILED! (%d).\n", errno); 201 | } 202 | 203 | consoleUpdate(NULL); 204 | 205 | /* File copy. */ 206 | sprintf(path, "sdmc:/test.file"); 207 | sprintf(new_path, "%s/test.file", device->name); 208 | printf("\t\t- File copy (\"%s\" -> \"%s\"): ", path, new_path); 209 | consoleUpdate(NULL); 210 | 211 | fd = fopen(path, "rb"); 212 | ums_fd = fopen(new_path, "wb"); 213 | buf = malloc(blksize); 214 | 215 | if (fd && ums_fd && buf) 216 | { 217 | printf("OK!\n"); 218 | consoleUpdate(NULL); 219 | 220 | fseek(fd, 0, SEEK_END); 221 | size_t file_size = ftell(fd); 222 | rewind(fd); 223 | 224 | printf("\t\t\t- File size (\"%s\"): 0x%lX bytes. Please wait.\n", path, file_size); 225 | consoleUpdate(NULL); 226 | 227 | time_t start = time(NULL), now = start; 228 | 229 | for(size_t off = 0; off < file_size; off += blksize) 230 | { 231 | if (blksize > (file_size - off)) blksize = (file_size - off); 232 | 233 | fread(buf, 1, blksize, fd); 234 | fwrite(buf, 1, blksize, ums_fd); 235 | } 236 | 237 | now = time(NULL); 238 | printf("\t\t\t- Process completed in %lu seconds.\n", now - start); 239 | consoleUpdate(NULL); 240 | } else { 241 | printf("FAILED! (%d).\n", errno); 242 | consoleUpdate(NULL); 243 | copy_failed = true; 244 | } 245 | 246 | if (buf) free(buf); 247 | 248 | if (ums_fd) 249 | { 250 | fclose(ums_fd); 251 | if (copy_failed) unlink(new_path); 252 | } 253 | 254 | if (fd) fclose(fd); 255 | 256 | printf("\n"); 257 | consoleUpdate(NULL); 258 | } 259 | 260 | static void usbMscPopulateFunc(const UsbHsFsDevice *devices, u32 device_count, void *user_data) 261 | { 262 | NX_IGNORE_ARG(user_data); 263 | 264 | mutexLock(&g_usbDeviceMutex); 265 | 266 | printf("USB Mass Storage status change triggered!\nMounted USB Mass Storage device count: %u.\n\n", device_count); 267 | consoleUpdate(NULL); 268 | 269 | /* Free mounted devices buffer. */ 270 | if (g_usbDevices) 271 | { 272 | free(g_usbDevices); 273 | g_usbDevices = NULL; 274 | } 275 | 276 | g_usbDeviceCount = 0; 277 | 278 | if (devices && device_count) 279 | { 280 | /* Allocate mounted devices buffer. */ 281 | g_usbDevices = calloc(device_count, sizeof(UsbHsFsDevice)); 282 | if (!g_usbDevices) 283 | { 284 | printf("Failed to allocate memory for mounted USB Mass Storage devices buffer!\n\n"); 285 | consoleUpdate(NULL); 286 | } else { 287 | /* Copy input data. */ 288 | memcpy(g_usbDevices, devices, device_count * sizeof(UsbHsFsDevice)); 289 | g_usbDeviceCount = device_count; 290 | } 291 | } 292 | 293 | g_updated = true; 294 | 295 | mutexUnlock(&g_usbDeviceMutex); 296 | } 297 | 298 | static void usbMscTestDevices(void) 299 | { 300 | mutexLock(&g_usbDeviceMutex); 301 | 302 | if (!g_updated || !g_usbDevices || !g_usbDeviceCount) 303 | { 304 | mutexUnlock(&g_usbDeviceMutex); 305 | return; 306 | } 307 | 308 | g_updated = false; 309 | 310 | /* Print info from mounted devices. */ 311 | for(u32 i = 0; i < g_usbDeviceCount; i++) 312 | { 313 | UsbHsFsDevice *device = &(g_usbDevices[i]); 314 | 315 | printf("Device #%u:\n" \ 316 | "\t- USB interface ID: %d.\n" \ 317 | "\t- Logical Unit Number: %u.\n" \ 318 | "\t- Filesystem index: %u.\n" \ 319 | "\t- Write protected: %s.\n" \ 320 | "\t- Vendor ID: 0x%04X.\n" \ 321 | "\t- Product ID: 0x%04X.\n" \ 322 | "\t- Manufacturer: \"%s\".\n" \ 323 | "\t- Product Name: \"%s\".\n" \ 324 | "\t- Serial Number: \"%s\".\n" \ 325 | "\t- Logical Unit Capacity: 0x%lX bytes.\n" \ 326 | "\t- Mount name: \"%s\".\n" \ 327 | "\t- Filesystem type: %s.\n" \ 328 | "\t- Mount flags: 0x%08X.\n" \ 329 | "\t- Filesystem tests:\n", \ 330 | i + 1, \ 331 | device->usb_if_id, \ 332 | device->lun, \ 333 | device->fs_idx, \ 334 | device->write_protect ? "yes" : "no", \ 335 | device->vid, \ 336 | device->pid, \ 337 | device->manufacturer, \ 338 | device->product_name, \ 339 | device->serial_number, \ 340 | device->capacity, \ 341 | device->name, \ 342 | LIBUSBHSFS_FS_TYPE_STR(device->fs_type), \ 343 | device->flags); 344 | 345 | consoleUpdate(NULL); 346 | 347 | /* Perform filesystem tests on current device. */ 348 | usbMscFileSystemTest(device); 349 | } 350 | 351 | /* Unmount devices. */ 352 | for(u32 i = 0; i < g_usbDeviceCount; i++) 353 | { 354 | UsbHsFsDevice *device = &(g_usbDevices[i]); 355 | usbHsFsUnmountDevice(device, false); 356 | } 357 | 358 | printf("%u device(s) safely unmounted. You may now disconnect them from the console.\n\n", g_usbDeviceCount); 359 | consoleUpdate(NULL); 360 | 361 | mutexUnlock(&g_usbDeviceMutex); 362 | } 363 | 364 | int main(int argc, char **argv) 365 | { 366 | NX_IGNORE_ARG(argc); 367 | NX_IGNORE_ARG(argv); 368 | 369 | Result rc = 0; 370 | int ret = 0; 371 | PadState pad = {0}; 372 | 373 | /* Initialize console output. */ 374 | consoleInit(NULL); 375 | 376 | /* Configure our supported input layout: a single player with full controller styles. */ 377 | padConfigureInput(1, HidNpadStyleSet_NpadFullCtrl); 378 | 379 | /* Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller. */ 380 | padInitializeDefault(&pad); 381 | 382 | printf(APP_TITLE ". Built on " BUILD_TIMESTAMP ".\nLibrary version: %u.%u.%u.\nPress + to exit.\n\n", LIBUSBHSFS_VERSION_MAJOR, LIBUSBHSFS_VERSION_MINOR, LIBUSBHSFS_VERSION_MICRO); 383 | consoleUpdate(NULL); 384 | 385 | /* Set populate callback function. */ 386 | usbHsFsSetPopulateCallback(&usbMscPopulateFunc, NULL); 387 | 388 | /* Initialize USB Mass Storage Host interface. */ 389 | rc = usbHsFsInitialize(0); 390 | if (R_FAILED(rc)) 391 | { 392 | printf("usbHsFsInitialize() failed! (0x%X).\n", rc); 393 | ret = - 1; 394 | goto end; 395 | } 396 | 397 | while(appletMainLoop()) 398 | { 399 | padUpdate(&pad); 400 | 401 | u64 keys_down = padGetButtonsDown(&pad); 402 | if (keys_down & HidNpadButton_Plus) 403 | { 404 | printf("Exiting...\n"); 405 | consoleUpdate(NULL); 406 | break; 407 | } 408 | 409 | /* Test available UMS devices. */ 410 | usbMscTestDevices(); 411 | 412 | svcSleepThread(10000000ULL); 413 | } 414 | 415 | /* Deinitialize USB Mass Storage Host interface. */ 416 | usbHsFsExit(); 417 | 418 | /* Free UMS devices buffer. */ 419 | if (g_usbDevices) free(g_usbDevices); 420 | 421 | end: 422 | /* Update console output. */ 423 | consoleUpdate(NULL); 424 | 425 | /* Wait some time (3 seconds). */ 426 | svcSleepThread(3000000000ULL); 427 | 428 | /* Deinitialize console output. */ 429 | consoleExit(NULL); 430 | 431 | return ret; 432 | } 433 | -------------------------------------------------------------------------------- /example_event/Makefile: -------------------------------------------------------------------------------- 1 | #--------------------------------------------------------------------------------- 2 | .SUFFIXES: 3 | #--------------------------------------------------------------------------------- 4 | 5 | ifeq ($(strip $(DEVKITPRO)),) 6 | $(error "Please set DEVKITPRO in your environment. export DEVKITPRO=/devkitpro") 7 | endif 8 | 9 | TOPDIR ?= $(CURDIR) 10 | include $(DEVKITPRO)/libnx/switch_rules 11 | 12 | #--------------------------------------------------------------------------------- 13 | # TARGET is the name of the output 14 | # BUILD is the directory where object files & intermediate files will be placed 15 | # SOURCES is a list of directories containing source code 16 | # DATA is a list of directories containing data files 17 | # INCLUDES is a list of directories containing header files 18 | # ROMFS is the directory containing data to be added to RomFS, relative to the Makefile (Optional) 19 | # 20 | # NO_ICON: if set to anything, do not use icon. 21 | # NO_NACP: if set to anything, no .nacp file is generated. 22 | # APP_TITLE is the name of the app stored in the .nacp file (Optional) 23 | # APP_AUTHOR is the author of the app stored in the .nacp file (Optional) 24 | # APP_VERSION is the version of the app stored in the .nacp file (Optional) 25 | # APP_TITLEID is the titleID of the app stored in the .nacp file (Optional) 26 | # ICON is the filename of the icon (.jpg), relative to the project folder. 27 | # If not set, it attempts to use one of the following (in this order): 28 | # - .jpg 29 | # - icon.jpg 30 | # - /default_icon.jpg 31 | # 32 | # CONFIG_JSON is the filename of the NPDM config file (.json), relative to the project folder. 33 | # If not set, it attempts to use one of the following (in this order): 34 | # - .json 35 | # - config.json 36 | # If a JSON file is provided or autodetected, an ExeFS PFS0 (.nsp) is built instead 37 | # of a homebrew executable (.nro). This is intended to be used for sysmodules. 38 | # NACP building is skipped as well. 39 | #--------------------------------------------------------------------------------- 40 | 41 | BUILD_TIMESTAMP := $(strip $(shell date --utc '+%Y-%m-%d %T UTC')) 42 | 43 | VERSION_MAJOR := 0 44 | VERSION_MINOR := 0 45 | VERSION_MICRO := 2 46 | 47 | APP_TITLE := libusbhsfs-example-event 48 | APP_AUTHOR := DarkMatterCore, XorTroll, Rhys Koedijk 49 | APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} 50 | 51 | TARGET := ${APP_TITLE} 52 | BUILD := build 53 | SOURCES := source 54 | DATA := data 55 | INCLUDES := include 56 | #ROMFS := romfs 57 | 58 | #--------------------------------------------------------------------------------- 59 | # options for code generation 60 | #--------------------------------------------------------------------------------- 61 | ARCH := -march=armv8-a+crc+crypto -mtune=cortex-a57 -mtp=soft -fPIE 62 | 63 | CFLAGS := -g -Wall -Wextra -Werror -O2 -ffunction-sections $(ARCH) $(DEFINES) $(INCLUDE) -D__SWITCH__ 64 | CFLAGS += -DAPP_TITLE=\"${APP_TITLE}\" -DBUILD_TIMESTAMP="\"${BUILD_TIMESTAMP}\"" 65 | 66 | CXXFLAGS := $(CFLAGS) -fno-rtti -fno-exceptions 67 | 68 | ASFLAGS := -g $(ARCH) 69 | LDFLAGS = -specs=$(DEVKITPRO)/libnx/switch.specs -g $(ARCH) -Wl,-Map,$(notdir $*.map) 70 | 71 | # Use -lusbhsfsd instead of -lusbhsfs to use the library build with debug logging enabled 72 | LIBS := -lusbhsfsd 73 | 74 | ifeq ($(filter $(MAKECMDGOALS),clean),) 75 | # Check BUILD_TYPE flag 76 | ifneq ($(origin BUILD_TYPE),undefined) 77 | ifeq (${BUILD_TYPE},ISC) 78 | # Do nothing 79 | else 80 | ifeq (${BUILD_TYPE},GPL) 81 | # Update libs 82 | # We'll just assume the user has already installed the necessary libraries 83 | LIBS += -lntfs-3g -llwext4 84 | else 85 | $(error Invalid value for BUILD_TYPE flag. Expected ISC or GPL) 86 | endif 87 | endif 88 | else 89 | $(error BUILD_TYPE flag not set) 90 | endif 91 | endif 92 | 93 | LIBS += -lnx 94 | 95 | #--------------------------------------------------------------------------------- 96 | # list of directories containing libraries, this must be the top level containing 97 | # include and lib 98 | #--------------------------------------------------------------------------------- 99 | LIBDIRS := $(PORTLIBS) $(LIBNX) $(CURDIR)/.. 100 | 101 | 102 | #--------------------------------------------------------------------------------- 103 | # no real need to edit anything past this point unless you need to add additional 104 | # rules for different file extensions 105 | #--------------------------------------------------------------------------------- 106 | ifneq ($(BUILD),$(notdir $(CURDIR))) 107 | #--------------------------------------------------------------------------------- 108 | 109 | export OUTPUT := $(CURDIR)/$(TARGET) 110 | export TOPDIR := $(CURDIR) 111 | 112 | export VPATH := $(foreach dir,$(SOURCES),$(CURDIR)/$(dir)) \ 113 | $(foreach dir,$(DATA),$(CURDIR)/$(dir)) 114 | 115 | export DEPSDIR := $(CURDIR)/$(BUILD) 116 | 117 | CFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.c))) 118 | CPPFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.cpp))) 119 | SFILES := $(foreach dir,$(SOURCES),$(notdir $(wildcard $(dir)/*.s))) 120 | BINFILES := $(foreach dir,$(DATA),$(notdir $(wildcard $(dir)/*.*))) 121 | 122 | #--------------------------------------------------------------------------------- 123 | # use CXX for linking C++ projects, CC for standard C 124 | #--------------------------------------------------------------------------------- 125 | ifeq ($(strip $(CPPFILES)),) 126 | #--------------------------------------------------------------------------------- 127 | export LD := $(CC) 128 | #--------------------------------------------------------------------------------- 129 | else 130 | #--------------------------------------------------------------------------------- 131 | export LD := $(CXX) 132 | #--------------------------------------------------------------------------------- 133 | endif 134 | #--------------------------------------------------------------------------------- 135 | 136 | export OFILES_BIN := $(addsuffix .o,$(BINFILES)) 137 | export OFILES_SRC := $(CPPFILES:.cpp=.o) $(CFILES:.c=.o) $(SFILES:.s=.o) 138 | export OFILES := $(OFILES_BIN) $(OFILES_SRC) 139 | export HFILES_BIN := $(addsuffix .h,$(subst .,_,$(BINFILES))) 140 | 141 | export INCLUDE := $(foreach dir,$(INCLUDES),-I$(CURDIR)/$(dir)) \ 142 | $(foreach dir,$(LIBDIRS),-I$(dir)/include) \ 143 | -I$(CURDIR)/$(BUILD) 144 | 145 | export LIBPATHS := $(foreach dir,$(LIBDIRS),-L$(dir)/lib) 146 | 147 | ifeq ($(strip $(CONFIG_JSON)),) 148 | jsons := $(wildcard *.json) 149 | ifneq (,$(findstring $(TARGET).json,$(jsons))) 150 | export APP_JSON := $(TOPDIR)/$(TARGET).json 151 | else 152 | ifneq (,$(findstring config.json,$(jsons))) 153 | export APP_JSON := $(TOPDIR)/config.json 154 | endif 155 | endif 156 | else 157 | export APP_JSON := $(TOPDIR)/$(CONFIG_JSON) 158 | endif 159 | 160 | ifeq ($(strip $(ICON)),) 161 | icons := $(wildcard *.jpg) 162 | ifneq (,$(findstring $(TARGET).jpg,$(icons))) 163 | export APP_ICON := $(TOPDIR)/$(TARGET).jpg 164 | else 165 | ifneq (,$(findstring icon.jpg,$(icons))) 166 | export APP_ICON := $(TOPDIR)/icon.jpg 167 | endif 168 | endif 169 | else 170 | export APP_ICON := $(TOPDIR)/$(ICON) 171 | endif 172 | 173 | ifeq ($(strip $(NO_ICON)),) 174 | export NROFLAGS += --icon=$(APP_ICON) 175 | endif 176 | 177 | ifeq ($(strip $(NO_NACP)),) 178 | export NROFLAGS += --nacp=$(CURDIR)/$(TARGET).nacp 179 | endif 180 | 181 | ifneq ($(APP_TITLEID),) 182 | export NACPFLAGS += --titleid=$(APP_TITLEID) 183 | endif 184 | 185 | ifneq ($(ROMFS),) 186 | export NROFLAGS += --romfsdir=$(CURDIR)/$(ROMFS) 187 | endif 188 | 189 | .PHONY: $(BUILD) clean all 190 | 191 | #--------------------------------------------------------------------------------- 192 | all: $(BUILD) 193 | 194 | $(BUILD): 195 | @[ -d $@ ] || mkdir -p $@ 196 | @$(MAKE) --no-print-directory -C $(BUILD) -f $(CURDIR)/Makefile 197 | 198 | #--------------------------------------------------------------------------------- 199 | clean: 200 | @echo clean ... 201 | ifeq ($(strip $(APP_JSON)),) 202 | @rm -fr $(BUILD) $(TARGET).nro $(TARGET).nacp $(TARGET).elf 203 | else 204 | @rm -fr $(BUILD) $(TARGET).nsp $(TARGET).nso $(TARGET).npdm $(TARGET).elf 205 | endif 206 | 207 | 208 | #--------------------------------------------------------------------------------- 209 | else 210 | .PHONY: all 211 | 212 | DEPENDS := $(OFILES:.o=.d) 213 | 214 | #--------------------------------------------------------------------------------- 215 | # main targets 216 | #--------------------------------------------------------------------------------- 217 | ifeq ($(strip $(APP_JSON)),) 218 | 219 | all : $(OUTPUT).nro 220 | 221 | ifeq ($(strip $(NO_NACP)),) 222 | $(OUTPUT).nro : $(OUTPUT).elf $(OUTPUT).nacp 223 | else 224 | $(OUTPUT).nro : $(OUTPUT).elf 225 | endif 226 | 227 | else 228 | 229 | all : $(OUTPUT).nsp 230 | 231 | $(OUTPUT).nsp : $(OUTPUT).nso $(OUTPUT).npdm 232 | 233 | $(OUTPUT).nso : $(OUTPUT).elf 234 | 235 | endif 236 | 237 | $(OUTPUT).elf : $(OFILES) 238 | 239 | $(OFILES_SRC) : $(HFILES_BIN) 240 | 241 | #--------------------------------------------------------------------------------- 242 | # you need a rule like this for each extension you use as binary data 243 | #--------------------------------------------------------------------------------- 244 | %.bin.o %_bin.h : %.bin 245 | #--------------------------------------------------------------------------------- 246 | @echo $(notdir $<) 247 | @$(bin2o) 248 | 249 | -include $(DEPENDS) 250 | 251 | #--------------------------------------------------------------------------------------- 252 | endif 253 | #--------------------------------------------------------------------------------------- 254 | -------------------------------------------------------------------------------- /example_event/source/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | static UEvent *g_statusChangeEvent = NULL, g_exitEvent = {0}; 11 | 12 | static u32 g_usbDeviceCount = 0; 13 | static UsbHsFsDevice *g_usbDevices = NULL; 14 | 15 | void usbMscFileSystemTest(UsbHsFsDevice *device) 16 | { 17 | if (!device) return; 18 | 19 | char path[FS_MAX_PATH] = {0}, tmp[0x40] = {0}, new_path[FS_MAX_PATH] = {0}, *ptr = NULL; 20 | const char *test_str = "Hello world!"; 21 | size_t test_str_len = strlen(test_str); 22 | 23 | FILE *fd = NULL, *ums_fd = NULL; 24 | struct stat st = {0}; 25 | 26 | DIR *dp = NULL; 27 | struct dirent *dt = NULL; 28 | 29 | struct statvfs fsinfo = {0}; 30 | 31 | bool copy_failed = false; 32 | 33 | int ret = -1, val = 0; 34 | 35 | u8 *buf = NULL; 36 | size_t blksize = 0x800000; 37 | 38 | sprintf(path, "%s/test_dir", device->name); 39 | 40 | /* Create directory. */ 41 | printf("\t\t- Create directory (\"%s\"): ", path); 42 | consoleUpdate(NULL); 43 | 44 | if (!mkdir(path, 0)) 45 | { 46 | printf("OK!\n"); 47 | } else { 48 | printf("FAILED! (%d).\n", errno); 49 | } 50 | 51 | consoleUpdate(NULL); 52 | 53 | /* Write data to file. */ 54 | strcat(path, "/" APP_TITLE ".txt"); 55 | printf("\t\t- Write data to file (\"%s\") (\"%s\"): ", path, test_str); 56 | consoleUpdate(NULL); 57 | 58 | fd = fopen(path, "w"); 59 | if (fd) 60 | { 61 | val = fprintf(fd, test_str); 62 | if (val == (int)test_str_len) 63 | { 64 | printf("OK!\n"); 65 | } else { 66 | printf("FAILED! (%d, %d).\n", errno, val); 67 | } 68 | 69 | fclose(fd); 70 | } else { 71 | printf("FAILED! (%d).\n", errno); 72 | } 73 | 74 | consoleUpdate(NULL); 75 | 76 | /* Read data from file. */ 77 | printf("\t\t- Read data from file (\"%s\"): ", path); 78 | consoleUpdate(NULL); 79 | 80 | fd = fopen(path, "r"); 81 | if (fd) 82 | { 83 | if (fgets(tmp, test_str_len + 1, fd) != NULL) 84 | { 85 | printf("OK! (\"%s\").\n", tmp); 86 | } else { 87 | printf("FAILED! (%d).\n", errno); 88 | } 89 | 90 | fclose(fd); 91 | } else { 92 | printf("FAILED! (%d).\n", errno); 93 | } 94 | 95 | consoleUpdate(NULL); 96 | 97 | /* File stats. */ 98 | printf("\t\t- File stats (\"%s\"): ", path); 99 | consoleUpdate(NULL); 100 | 101 | if (!stat(path, &st)) 102 | { 103 | printf("OK!\n\t\t\t- ID: %i.\n\t\t\t- Type: %s.\n\t\t\t- Size: %lu.\n\t\t\t- Timestamp: %lu.\n", st.st_ino, st.st_mode & S_IFREG ? "file" : "dir", st.st_size, st.st_mtime); 104 | } else { 105 | printf("FAILED! (%d).\n", errno); 106 | } 107 | 108 | consoleUpdate(NULL); 109 | 110 | /* Rename file. */ 111 | ptr = strrchr(path, '/'); 112 | sprintf(new_path, "%.*s/test.txt", (int)(ptr - path), path); 113 | printf("\t\t- Rename file (\"%s\" -> \"%s\"): ", path, new_path); 114 | consoleUpdate(NULL); 115 | 116 | if (!rename(path, new_path)) 117 | { 118 | printf("OK!\n"); 119 | } else { 120 | printf("FAILED! (%d).\n", errno); 121 | } 122 | 123 | consoleUpdate(NULL); 124 | 125 | /* Change directory. */ 126 | *ptr = '\0'; 127 | printf("\t\t- Change directory (\"%s\"): ", path); 128 | consoleUpdate(NULL); 129 | 130 | ret = chdir(path); 131 | if (!ret) 132 | { 133 | printf("OK!\n"); 134 | 135 | /* Directory listing. */ 136 | printf("\t\t- Directory listing (\".\"): "); 137 | consoleUpdate(NULL); 138 | 139 | dp = opendir("."); /* Open current directory. */ 140 | if (dp) 141 | { 142 | printf("OK!\n"); 143 | consoleUpdate(NULL); 144 | 145 | while((dt = readdir(dp))) 146 | { 147 | printf("\t\t\t- [%c] ./%s\n", (dt->d_type & DT_DIR) ? 'D' : 'F', dt->d_name); 148 | consoleUpdate(NULL); 149 | } 150 | 151 | closedir(dp); 152 | } else { 153 | printf("FAILED! (%d).\n", errno); 154 | consoleUpdate(NULL); 155 | } 156 | } else { 157 | printf("FAILED! (%d).\n", errno); 158 | } 159 | 160 | consoleUpdate(NULL); 161 | 162 | /* Delete file. */ 163 | printf("\t\t- Delete file (\"%s\"): ", new_path); 164 | consoleUpdate(NULL); 165 | 166 | if (!unlink(new_path)) 167 | { 168 | printf("OK!\n"); 169 | } else { 170 | printf("FAILED! (%d).\n", errno); 171 | } 172 | 173 | consoleUpdate(NULL); 174 | 175 | /* Delete directory. */ 176 | printf("\t\t- Delete directory (\"%s\"): ", path); 177 | consoleUpdate(NULL); 178 | 179 | if (!rmdir(path)) 180 | { 181 | printf("OK!\n"); 182 | } else { 183 | printf("FAILED! (%d).\n", errno); 184 | } 185 | 186 | consoleUpdate(NULL); 187 | 188 | /* Filesystem stats. */ 189 | printf("\t\t- Filesystem stats: "); 190 | consoleUpdate(NULL); 191 | 192 | if (!statvfs(".", &fsinfo)) 193 | { 194 | u64 fsid = (u64)fsinfo.f_fsid; 195 | u64 total_size = ((u64)fsinfo.f_blocks * (u64)fsinfo.f_frsize); 196 | u64 free_space = ((u64)fsinfo.f_bfree * (u64)fsinfo.f_frsize); 197 | 198 | printf("OK!\n\t\t\t- ID: %lu.\n\t\t\t- Total FS size: 0x%lX bytes.\n\t\t\t- Free FS space: 0x%lX bytes.\n", fsid, total_size, free_space); 199 | } else { 200 | printf("FAILED! (%d).\n", errno); 201 | } 202 | 203 | consoleUpdate(NULL); 204 | 205 | /* File copy. */ 206 | sprintf(path, "sdmc:/test.file"); 207 | sprintf(new_path, "%s/test.file", device->name); 208 | printf("\t\t- File copy (\"%s\" -> \"%s\"): ", path, new_path); 209 | consoleUpdate(NULL); 210 | 211 | fd = fopen(path, "rb"); 212 | ums_fd = fopen(new_path, "wb"); 213 | buf = malloc(blksize); 214 | 215 | if (fd && ums_fd && buf) 216 | { 217 | printf("OK!\n"); 218 | consoleUpdate(NULL); 219 | 220 | fseek(fd, 0, SEEK_END); 221 | size_t file_size = ftell(fd); 222 | rewind(fd); 223 | 224 | printf("\t\t\t- File size (\"%s\"): 0x%lX bytes. Please wait.\n", path, file_size); 225 | consoleUpdate(NULL); 226 | 227 | time_t start = time(NULL), now = start; 228 | 229 | for(size_t off = 0; off < file_size; off += blksize) 230 | { 231 | if (blksize > (file_size - off)) blksize = (file_size - off); 232 | 233 | fread(buf, 1, blksize, fd); 234 | fwrite(buf, 1, blksize, ums_fd); 235 | } 236 | 237 | now = time(NULL); 238 | printf("\t\t\t- Process completed in %lu seconds.\n", now - start); 239 | consoleUpdate(NULL); 240 | } else { 241 | printf("FAILED! (%d).\n", errno); 242 | consoleUpdate(NULL); 243 | copy_failed = true; 244 | } 245 | 246 | if (buf) free(buf); 247 | 248 | if (ums_fd) 249 | { 250 | fclose(ums_fd); 251 | if (copy_failed) unlink(new_path); 252 | } 253 | 254 | if (fd) fclose(fd); 255 | 256 | printf("\n"); 257 | consoleUpdate(NULL); 258 | } 259 | 260 | int usbMscThreadFunc(void *arg) 261 | { 262 | NX_IGNORE_ARG(arg); 263 | 264 | Result rc = 0; 265 | int idx = 0; 266 | u32 listed_device_count = 0; 267 | 268 | /* Generate waiters for our user events. */ 269 | Waiter status_change_event_waiter = waiterForUEvent(g_statusChangeEvent); 270 | Waiter exit_event_waiter = waiterForUEvent(&g_exitEvent); 271 | 272 | while(true) 273 | { 274 | /* Wait until an event is triggered. */ 275 | rc = waitMulti(&idx, -1, status_change_event_waiter, exit_event_waiter); 276 | if (R_FAILED(rc)) continue; 277 | 278 | /* Free mounted devices buffer. */ 279 | if (g_usbDevices) 280 | { 281 | free(g_usbDevices); 282 | g_usbDevices = NULL; 283 | } 284 | 285 | /* Exit event triggered. */ 286 | if (idx == 1) 287 | { 288 | printf("Exit event triggered!\n"); 289 | break; 290 | } 291 | 292 | /* Get mounted device count. */ 293 | g_usbDeviceCount = usbHsFsGetMountedDeviceCount(); 294 | 295 | printf("USB Mass Storage status change event triggered!\nMounted USB Mass Storage device count: %u.\n\n", g_usbDeviceCount); 296 | consoleUpdate(NULL); 297 | 298 | if (!g_usbDeviceCount) continue; 299 | 300 | /* Allocate mounted devices buffer. */ 301 | g_usbDevices = calloc(g_usbDeviceCount, sizeof(UsbHsFsDevice)); 302 | if (!g_usbDevices) 303 | { 304 | printf("Failed to allocate memory for mounted USB Mass Storage devices buffer!\n\n"); 305 | consoleUpdate(NULL); 306 | continue; 307 | } 308 | 309 | /* List mounted devices. */ 310 | if (!(listed_device_count = usbHsFsListMountedDevices(g_usbDevices, g_usbDeviceCount))) 311 | { 312 | printf("Failed to list mounted USB Mass Storage devices!\n\n"); 313 | consoleUpdate(NULL); 314 | continue; 315 | } 316 | 317 | /* Print info from mounted devices. */ 318 | for(u32 i = 0; i < listed_device_count; i++) 319 | { 320 | UsbHsFsDevice *device = &(g_usbDevices[i]); 321 | 322 | printf("Device #%u:\n" \ 323 | "\t- USB interface ID: %d.\n" \ 324 | "\t- Logical Unit Number: %u.\n" \ 325 | "\t- Filesystem index: %u.\n" \ 326 | "\t- Write protected: %s.\n" \ 327 | "\t- Vendor ID: 0x%04X.\n" \ 328 | "\t- Product ID: 0x%04X.\n" \ 329 | "\t- Manufacturer: \"%s\".\n" \ 330 | "\t- Product Name: \"%s\".\n" \ 331 | "\t- Serial Number: \"%s\".\n" \ 332 | "\t- Logical Unit Capacity: 0x%lX bytes.\n" \ 333 | "\t- Mount name: \"%s\".\n" \ 334 | "\t- Filesystem type: %s.\n" \ 335 | "\t- Mount flags: 0x%08X.\n" \ 336 | "\t- Filesystem tests:\n", \ 337 | i + 1, \ 338 | device->usb_if_id, \ 339 | device->lun, \ 340 | device->fs_idx, \ 341 | device->write_protect ? "yes" : "no", \ 342 | device->vid, \ 343 | device->pid, \ 344 | device->manufacturer, \ 345 | device->product_name, \ 346 | device->serial_number, \ 347 | device->capacity, \ 348 | device->name, \ 349 | LIBUSBHSFS_FS_TYPE_STR(device->fs_type), \ 350 | device->flags); 351 | 352 | consoleUpdate(NULL); 353 | 354 | /* Perform filesystem tests on current device. */ 355 | usbMscFileSystemTest(device); 356 | } 357 | 358 | /* Unmount devices. */ 359 | for(u32 i = 0; i < listed_device_count; i++) 360 | { 361 | UsbHsFsDevice *device = &(g_usbDevices[i]); 362 | usbHsFsUnmountDevice(device, false); 363 | } 364 | 365 | printf("%u device(s) safely unmounted. You may now disconnect them from the console.\n\n", listed_device_count); 366 | consoleUpdate(NULL); 367 | } 368 | 369 | /* Exit thread. */ 370 | return 0; 371 | } 372 | 373 | int main(int argc, char **argv) 374 | { 375 | NX_IGNORE_ARG(argc); 376 | NX_IGNORE_ARG(argv); 377 | 378 | Result rc = 0; 379 | int ret = 0; 380 | thrd_t thread = {0}; 381 | PadState pad = {0}; 382 | 383 | /* Initialize console output. */ 384 | consoleInit(NULL); 385 | 386 | /* Configure our supported input layout: a single player with full controller styles. */ 387 | padConfigureInput(1, HidNpadStyleSet_NpadFullCtrl); 388 | 389 | /* Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller. */ 390 | padInitializeDefault(&pad); 391 | 392 | printf(APP_TITLE ". Built on " BUILD_TIMESTAMP ".\nLibrary version: %u.%u.%u.\nPress + to exit.\n\n", LIBUSBHSFS_VERSION_MAJOR, LIBUSBHSFS_VERSION_MINOR, LIBUSBHSFS_VERSION_MICRO); 393 | consoleUpdate(NULL); 394 | 395 | /* Initialize USB Mass Storage Host interface. */ 396 | rc = usbHsFsInitialize(0); 397 | if (R_FAILED(rc)) 398 | { 399 | printf("usbHsFsInitialize() failed! (0x%X).\n", rc); 400 | ret = - 1; 401 | goto end; 402 | } 403 | 404 | /* Get USB Mass Storage status change event. */ 405 | g_statusChangeEvent = usbHsFsGetStatusChangeUserEvent(); 406 | 407 | /* Create usermode thread exit event. */ 408 | ueventCreate(&g_exitEvent, true); 409 | 410 | /* Create thread. */ 411 | thrd_create(&thread, usbMscThreadFunc, NULL); 412 | 413 | while(appletMainLoop()) 414 | { 415 | padUpdate(&pad); 416 | 417 | u64 keys_down = padGetButtonsDown(&pad); 418 | if (keys_down & HidNpadButton_Plus) 419 | { 420 | /* Signal background thread. */ 421 | ueventSignal(&g_exitEvent); 422 | 423 | /* Wait for the background thread to exit on its own. */ 424 | thrd_join(thread, NULL); 425 | 426 | /* Break out of this loop. */ 427 | break; 428 | } 429 | 430 | svcSleepThread(10000000ULL); 431 | } 432 | 433 | /* Deinitialize USB Mass Storage Host interface. */ 434 | usbHsFsExit(); 435 | 436 | end: 437 | /* Update console output. */ 438 | consoleUpdate(NULL); 439 | 440 | /* Wait some time (3 seconds). */ 441 | svcSleepThread(3000000000ULL); 442 | 443 | /* Deinitialize console output. */ 444 | consoleExit(NULL); 445 | 446 | return ret; 447 | } 448 | -------------------------------------------------------------------------------- /include/usbhsfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * Copyright (c) 2020-2021, Rhys Koedijk. 7 | * 8 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 9 | */ 10 | 11 | #pragma once 12 | 13 | #ifndef __USBHSFS_H__ 14 | #define __USBHSFS_H__ 15 | 16 | #include 17 | 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | /// Library version. 23 | #define LIBUSBHSFS_VERSION_MAJOR 0 24 | #define LIBUSBHSFS_VERSION_MINOR 2 25 | #define LIBUSBHSFS_VERSION_MICRO 9 26 | 27 | /// Helper macro to generate a string based on a filesystem type value. 28 | #define LIBUSBHSFS_FS_TYPE_STR(x) ((x) == UsbHsFsDeviceFileSystemType_FAT12 ? "FAT12" : ((x) == UsbHsFsDeviceFileSystemType_FAT16 ? "FAT16" : ((x) == UsbHsFsDeviceFileSystemType_FAT32 ? "FAT32" : \ 29 | ((x) == UsbHsFsDeviceFileSystemType_exFAT ? "exFAT" : ((x) == UsbHsFsDeviceFileSystemType_NTFS ? "NTFS" : ((x) == UsbHsFsDeviceFileSystemType_EXT2 ? "EXT2" : \ 30 | ((x) == UsbHsFsDeviceFileSystemType_EXT3 ? "EXT3" : ((x) == UsbHsFsDeviceFileSystemType_EXT4 ? "EXT4" : "Invalid")))))))) 31 | 32 | /// Used to identify the filesystem type from a mounted filesystem (e.g. filesize limitations, etc.). 33 | typedef enum { 34 | UsbHsFsDeviceFileSystemType_Invalid = 0, 35 | UsbHsFsDeviceFileSystemType_FAT12 = 1, 36 | UsbHsFsDeviceFileSystemType_FAT16 = 2, 37 | UsbHsFsDeviceFileSystemType_FAT32 = 3, 38 | UsbHsFsDeviceFileSystemType_exFAT = 4, 39 | UsbHsFsDeviceFileSystemType_NTFS = 5, ///< Only returned by the GPL build of the library. 40 | UsbHsFsDeviceFileSystemType_EXT2 = 6, ///< Only returned by the GPL build of the library. 41 | UsbHsFsDeviceFileSystemType_EXT3 = 7, ///< Only returned by the GPL build of the library. 42 | UsbHsFsDeviceFileSystemType_EXT4 = 8 ///< Only returned by the GPL build of the library. 43 | } UsbHsFsDeviceFileSystemType; 44 | 45 | /// Filesystem mount flags. 46 | /// Not all supported filesystems are compatible with these flags. 47 | /// It can be overriden via usbHsFsSetFileSystemMountFlags() (see below). 48 | typedef enum { 49 | UsbHsFsMountFlags_None = 0, ///< No special action is taken. 50 | UsbHsFsMountFlags_ReadOnly = BIT(0), ///< Filesystem is mounted as read-only. 51 | UsbHsFsMountFlags_ReplayJournal = BIT(1), ///< NTFS and EXT only. Replays the log/journal to restore filesystem consistency (e.g. fix unsafe device ejections). 52 | UsbHsFsMountFlags_IgnoreCaseSensitivity = BIT(2), ///< NTFS only. Case sensitivity is ignored for all filesystem operations. 53 | UsbHsFsMountFlags_UpdateAccessTimes = BIT(3), ///< NTFS only. File/directory access times are updated after each successful R/W operation. 54 | UsbHsFsMountFlags_ShowHiddenFiles = BIT(4), ///< NTFS only. Hidden file entries are returned while enumerating directories. 55 | UsbHsFsMountFlags_ShowSystemFiles = BIT(5), ///< NTFS only. System file entries are returned while enumerating directories. 56 | UsbHsFsMountFlags_IgnoreFileReadOnlyAttribute = BIT(6), ///< NTFS only. Allows writing to files even if they are marked as read-only. 57 | UsbHsFsMountFlags_IgnoreHibernation = BIT(7), ///< NTFS only. Filesystem is mounted even if it's in a hibernated state. The saved Windows session is completely lost. 58 | 59 | ///< Pre-generated bitmasks provided for convenience. 60 | UsbHsFsMountFlags_Default = (UsbHsFsMountFlags_ShowHiddenFiles | UsbHsFsMountFlags_UpdateAccessTimes | UsbHsFsMountFlags_ReplayJournal), 61 | UsbHsFsMountFlags_SuperUser = (UsbHsFsMountFlags_IgnoreFileReadOnlyAttribute | UsbHsFsMountFlags_ShowSystemFiles | UsbHsFsMountFlags_Default), 62 | UsbHsFsMountFlags_Force = (UsbHsFsMountFlags_IgnoreHibernation | UsbHsFsMountFlags_Default), 63 | UsbHsFsMountFlags_All = (UsbHsFsMountFlags_IgnoreHibernation | (UsbHsFsMountFlags_IgnoreHibernation - 1)) 64 | } UsbHsFsMountFlags; 65 | 66 | /// Struct used to list filesystems that have been mounted as virtual devices via devoptab. 67 | /// Everything but the manufacturer, product_name and name fields is empty/zeroed-out under SX OS. 68 | typedef struct { 69 | s32 usb_if_id; ///< USB interface ID. Internal use. 70 | u8 lun; ///< Logical unit. Internal use. 71 | u32 fs_idx; ///< Filesystem index. Internal use. 72 | bool write_protect; ///< Set to true if the logical unit is protected against write operations. 73 | u16 vid; ///< Vendor ID. Retrieved from the device descriptor. Useful if you wish to implement a filter in your application. 74 | u16 pid; ///< Product ID. Retrieved from the device descriptor. Useful if you wish to implement a filter in your application. 75 | char manufacturer[64]; ///< UTF-8 encoded manufacturer string. Retrieved from SCSI Inquiry data or the USB device descriptor. May be empty. 76 | char product_name[64]; ///< UTF-8 encoded product name string. Retrieved from SCSI Inquiry data or the USB device descriptor. May be empty. 77 | char serial_number[64]; ///< UTF-8 encoded serial number string. Retrieved from SCSI Inquiry data or the USB device descriptor. May be empty. 78 | u64 capacity; ///< Raw capacity from the logical unit this filesystem belongs to. Use statvfs() to get the actual filesystem capacity. May be shared with other UsbHsFsDevice entries. 79 | char name[32]; ///< Mount name used by the devoptab virtual device interface (e.g. "ums0:"). Use it as a prefix in libcstd I/O calls to perform operations on this filesystem. 80 | u8 fs_type; ///< UsbHsFsDeviceFileSystemType. 81 | u32 flags; ///< UsbHsFsMountFlags bitmask used at mount time. 82 | } UsbHsFsDevice; 83 | 84 | /// Used with usbHsFsSetPopulateCallback(). 85 | typedef void (*UsbHsFsPopulateCb)(const UsbHsFsDevice *devices, u32 device_count, void *user_data); 86 | 87 | /// Initializes the USB Mass Storage Host interface. 88 | /// `event_idx` represents the event index to use with usbHsCreateInterfaceAvailableEvent() / usbHsDestroyInterfaceAvailableEvent(). Must be within the [0, 2] range. 89 | /// If you're not using any usb:hs interface available events on your own, set this value to 0. If running under SX OS, this value will be ignored. 90 | /// This function will fail if the deprecated fsp-usb service is running in the background. 91 | Result usbHsFsInitialize(u8 event_idx); 92 | 93 | /// Closes the USB Mass Storage Host interface. 94 | /// If there are any UMS devices with mounted filesystems connected to the console when this function is called, their filesystems will be unmounted and their logical units will be stopped. 95 | void usbHsFsExit(void); 96 | 97 | /************************************************************************************************ 98 | * Event-based population system * 99 | * * 100 | * These functions make it possible to retrieve information on demand about the available UMS * 101 | * filesystems that have been mounted as virtual devoptab devices, using a background thread * 102 | * created by the user. * 103 | * * 104 | * This background thread can create a Waiter object using the UEvent object returned by * 105 | * usbHsFsGetStatusChangeUserEvent(), which can then be used with primitive waiting operations * 106 | * such as waitMulti() or waitObjects(). This is specially useful for applications that rely on * 107 | * other Switch-specific ABIs that are also event-driven: a single background thread can be * 108 | * dedicated to handle multiple types of events, including the UMS event provided here. * 109 | * * 110 | * Even though simultaneous usage of both event-based and callback-based systems should be * 111 | * possible, it is heavily discouraged. * 112 | ************************************************************************************************/ 113 | 114 | /// Returns a pointer to the user-mode status change event (with autoclear enabled). 115 | /// Useful to wait for USB Mass Storage status changes without having to constantly poll the interface. 116 | /// Returns NULL if the USB Mass Storage Host interface hasn't been initialized. 117 | UEvent *usbHsFsGetStatusChangeUserEvent(void); 118 | 119 | /// Lists up to `max_count` mounted virtual devices and stores their information in the provided UsbHsFsDevice array. 120 | /// Returns the total number of written entries. 121 | /// For better results, usbHsFsGetMountedDeviceCount() should be used before calling this function. 122 | u32 usbHsFsListMountedDevices(UsbHsFsDevice *out, u32 max_count); 123 | 124 | /************************************************************************************************ 125 | * Callback-based population system * 126 | * * 127 | * Makes it possible to automatically retrieve information about the available UMS filesystems * 128 | * that have been mounted as virtual devoptab devices by providing a pointer to a user function * 129 | * that acts as a callback, which is executed under the library's very own background thread. * 130 | * * 131 | * This essentially enables the user to receive updates from the library without creating an * 132 | * additional background thread. However, in order to achieve thread-safety and avoid possible * 133 | * race conditions, the provided user callback must also handle all concurrency-related tasks * 134 | * on its own, if needed (e.g. [un]locking a mutex, etc.). * 135 | * * 136 | * Even though simultaneous usage of both event-based and callback-based systems should be * 137 | * possible, it is heavily discouraged. * 138 | ************************************************************************************************/ 139 | 140 | /// Sets the pointer to the user-provided callback function, which will automatically provide updates whenever a USB Mass Storage status change is triggered. 141 | /// The provided user callback must treat all input data as read-only and short-lived -- that means, it must copy the provided UsbHsFsDevice entries into a buffer of its own. 142 | /// A NULL `devices` pointer and/or a `device_count` of zero are valid inputs, and must be interpreted as no virtual devoptab devices being currently available. 143 | /// Optionally, a `user_data` pointer may be passed into this function, which will in turn be passed to the provided callback whenever it is executed. 144 | /// `populate_cb` may be a NULL pointer, in which case a previously set callback will just be unset. 145 | void usbHsFsSetPopulateCallback(UsbHsFsPopulateCb populate_cb, void *user_data); 146 | 147 | /************************************************************************************************ 148 | * Miscellaneous functions * 149 | * * 150 | * These can be safely used with both population systems. * 151 | ************************************************************************************************/ 152 | 153 | /// Returns the number of physical UMS devices currently connected to the console with at least one underlying filesystem mounted as a virtual device. 154 | u32 usbHsFsGetPhysicalDeviceCount(void); 155 | 156 | /// Returns the total number of filesystems across all available UMS devices currently mounted as virtual devices via devoptab. 157 | u32 usbHsFsGetMountedDeviceCount(void); 158 | 159 | /// Unmounts all filesystems from the UMS device with a USB interface ID that matches the one from the provided UsbHsFsDevice, and stops all of its logical units. 160 | /// Can be used to safely unmount a UMS device at runtime, if that's needed for some reason. Calling this function before usbHsFsExit() isn't mandatory. 161 | /// If multiple UsbHsFsDevice entries are returned for the same physical UMS device, any of them can be used as the input argument for this function. 162 | /// If successful, and `signal_status_event` is true, this will also fire the user-mode status change event returned by usbHsFsGetStatusChangeUserEvent() and, if available, execute the user callback set with usbHsFsSetPopulateCallback(). 163 | /// This function has no effect at all under SX OS. 164 | bool usbHsFsUnmountDevice(const UsbHsFsDevice *device, bool signal_status_event); 165 | 166 | /// Returns a bitmask with the current filesystem mount flags. 167 | /// Can be used even if the USB Mass Storage Host interface hasn't been initialized. 168 | /// This function has no effect at all under SX OS. 169 | u32 usbHsFsGetFileSystemMountFlags(void); 170 | 171 | /// Takes an input bitmask with the desired filesystem mount flags, which will be used for all mount operations. 172 | /// Can be used even if the USB Mass Storage Host interface hasn't been initialized. 173 | /// This function has no effect at all under SX OS. 174 | void usbHsFsSetFileSystemMountFlags(u32 flags); 175 | 176 | #ifdef __cplusplus 177 | } 178 | #endif 179 | 180 | #endif /* __USBHSFS_H__ */ 181 | -------------------------------------------------------------------------------- /source/fatfs/diskio.c: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------*/ 2 | /* Low level disk I/O module skeleton for FatFs (C)ChaN, 2019 */ 3 | /*-----------------------------------------------------------------------*/ 4 | /* If a working storage control module is available, it should be */ 5 | /* attached to the FatFs via a glue function rather than modifying it. */ 6 | /* This is an example of glue functions to attach various exsisting */ 7 | /* storage control modules to the FatFs module with a defined API. */ 8 | /*-----------------------------------------------------------------------*/ 9 | 10 | #include "ff.h" /* Obtains integer types */ 11 | #include "diskio.h" /* Declarations of disk functions */ 12 | 13 | #include "../usbhsfs_utils.h" 14 | #include "../usbhsfs_manager.h" 15 | #include "../usbhsfs_scsi.h" 16 | 17 | /* Reference for needed FATFS impl functions: http://irtos.sourceforge.net/FAT32_ChaN/doc/en/appnote.html#port */ 18 | 19 | /*-----------------------------------------------------------------------*/ 20 | /* Get Drive Status */ 21 | /*-----------------------------------------------------------------------*/ 22 | 23 | DSTATUS ff_disk_status ( 24 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 25 | ) 26 | { 27 | NX_IGNORE_ARG(pdrv); 28 | 29 | /* We take care of this. */ 30 | return RES_OK; 31 | } 32 | 33 | 34 | 35 | /*-----------------------------------------------------------------------*/ 36 | /* Inidialize a Drive */ 37 | /*-----------------------------------------------------------------------*/ 38 | 39 | DSTATUS ff_disk_initialize ( 40 | BYTE pdrv /* Physical drive nmuber to identify the drive */ 41 | ) 42 | { 43 | NX_IGNORE_ARG(pdrv); 44 | 45 | /* We take care of this. */ 46 | return RES_OK; 47 | } 48 | 49 | /*-----------------------------------------------------------------------*/ 50 | /* Read Sector(s) */ 51 | /*-----------------------------------------------------------------------*/ 52 | 53 | DRESULT ff_disk_read ( 54 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 55 | BYTE *buff, /* Data buffer to store read data */ 56 | LBA_t sector, /* Start sector in LBA */ 57 | UINT count /* Number of sectors to read */ 58 | ) 59 | { 60 | UsbHsFsDriveLogicalUnitContext *lun_ctx = NULL; 61 | DRESULT ret = RES_PARERR; 62 | 63 | /* Get LUN context and read logical blocks. */ 64 | lun_ctx = usbHsFsManagerGetLogicalUnitContextForFatFsDriveNumber(pdrv); 65 | if (lun_ctx && usbHsFsScsiReadLogicalUnitBlocks(lun_ctx, buff, sector, count)) ret = RES_OK; 66 | 67 | return ret; 68 | } 69 | 70 | 71 | /*-----------------------------------------------------------------------*/ 72 | /* Write Sector(s) */ 73 | /*-----------------------------------------------------------------------*/ 74 | 75 | DRESULT ff_disk_write ( 76 | BYTE pdrv, /* Physical drive nmuber to identify the drive */ 77 | const BYTE *buff, /* Data to be written */ 78 | LBA_t sector, /* Start sector in LBA */ 79 | UINT count /* Number of sectors to write */ 80 | ) 81 | { 82 | UsbHsFsDriveLogicalUnitContext *lun_ctx = NULL; 83 | DRESULT ret = RES_PARERR; 84 | 85 | /* Get LUN context and write logical blocks. */ 86 | lun_ctx = usbHsFsManagerGetLogicalUnitContextForFatFsDriveNumber(pdrv); 87 | if (lun_ctx && usbHsFsScsiWriteLogicalUnitBlocks(lun_ctx, buff, sector, count)) ret = RES_OK; 88 | 89 | return ret; 90 | } 91 | 92 | 93 | /*-----------------------------------------------------------------------*/ 94 | /* Miscellaneous Functions */ 95 | /*-----------------------------------------------------------------------*/ 96 | 97 | DRESULT ff_disk_ioctl ( 98 | BYTE pdrv, /* Physical drive nmuber (0..) */ 99 | BYTE cmd, /* Control code */ 100 | void *buff /* Buffer to send/receive control data */ 101 | ) 102 | { 103 | UsbHsFsDriveLogicalUnitContext *lun_ctx = NULL; 104 | DRESULT ret = RES_PARERR; 105 | 106 | /* Get LUN context. */ 107 | lun_ctx = usbHsFsManagerGetLogicalUnitContextForFatFsDriveNumber(pdrv); 108 | if (lun_ctx) 109 | { 110 | /* Process control code. */ 111 | switch(cmd) 112 | { 113 | case CTRL_SYNC: 114 | ret = RES_OK; 115 | break; 116 | case GET_SECTOR_COUNT: 117 | *(LBA_t*)buff = lun_ctx->block_count; 118 | ret = RES_OK; 119 | break; 120 | case GET_SECTOR_SIZE: 121 | *(WORD*)buff = lun_ctx->block_length; 122 | ret = RES_OK; 123 | break; 124 | default: 125 | break; 126 | } 127 | } 128 | 129 | return ret; 130 | } 131 | 132 | #if !FF_FS_NORTC /* Get system time */ 133 | DWORD get_fattime(void) 134 | { 135 | Result rc = 0; 136 | u64 timestamp = 0; 137 | struct tm timeinfo = {0}; 138 | DWORD output = FAT_TIMESTAMP(FF_NORTC_YEAR, FF_NORTC_MON, FF_NORTC_MDAY, 0, 0, 0); /* Use FF_NORTC values by default. */ 139 | 140 | /* Try to retrieve time from time services. */ 141 | rc = timeGetCurrentTime(TimeType_LocalSystemClock, ×tamp); 142 | if (R_SUCCEEDED(rc)) 143 | { 144 | localtime_r((time_t*)×tamp, &timeinfo); 145 | output = FAT_TIMESTAMP(timeinfo.tm_year, timeinfo.tm_mon + 1, timeinfo.tm_mday, timeinfo.tm_hour, timeinfo.tm_min, timeinfo.tm_sec); 146 | } 147 | 148 | return output; 149 | } 150 | #endif 151 | -------------------------------------------------------------------------------- /source/fatfs/diskio.h: -------------------------------------------------------------------------------- 1 | /*-----------------------------------------------------------------------/ 2 | / Low level disk interface modlue include file (C)ChaN, 2019 / 3 | /-----------------------------------------------------------------------*/ 4 | 5 | #ifndef _DISKIO_DEFINED 6 | #define _DISKIO_DEFINED 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | /* Status of Disk Functions */ 13 | typedef BYTE DSTATUS; 14 | 15 | /* Results of Disk Functions */ 16 | typedef enum { 17 | RES_OK = 0, /* 0: Successful */ 18 | RES_ERROR, /* 1: R/W Error */ 19 | RES_WRPRT, /* 2: Write Protected */ 20 | RES_NOTRDY, /* 3: Not Ready */ 21 | RES_PARERR /* 4: Invalid Parameter */ 22 | } DRESULT; 23 | 24 | 25 | /*---------------------------------------*/ 26 | /* Prototypes for disk control functions */ 27 | 28 | 29 | DSTATUS ff_disk_initialize (BYTE pdrv); 30 | DSTATUS ff_disk_status (BYTE pdrv); 31 | DRESULT ff_disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count); 32 | DRESULT ff_disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count); 33 | DRESULT ff_disk_ioctl (BYTE pdrv, BYTE cmd, void* buff); 34 | 35 | 36 | /* Disk Status Bits (DSTATUS) */ 37 | 38 | #define STA_NOINIT 0x01 /* Drive not initialized */ 39 | #define STA_NODISK 0x02 /* No medium in the drive */ 40 | #define STA_PROTECT 0x04 /* Write protected */ 41 | 42 | 43 | /* Command code for ff_disk_ioctrl fucntion */ 44 | 45 | /* Generic command (Used by FatFs) */ 46 | #define CTRL_SYNC 0 /* Complete pending write process */ 47 | #define GET_SECTOR_COUNT 1 /* Get media size */ 48 | #define GET_SECTOR_SIZE 2 /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */ 49 | #define GET_BLOCK_SIZE 3 /* Get erase block size */ 50 | #define CTRL_TRIM 4 /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */ 51 | 52 | /* Generic command (Not used by FatFs) */ 53 | #define CTRL_POWER 5 /* Get/Set power status */ 54 | #define CTRL_LOCK 6 /* Lock/Unlock media removal */ 55 | #define CTRL_EJECT 7 /* Eject media */ 56 | #define CTRL_FORMAT 8 /* Create physical format on the media */ 57 | 58 | /* MMC/SDC specific ioctl command */ 59 | #define MMC_GET_TYPE 10 /* Get card type */ 60 | #define MMC_GET_CSD 11 /* Get CSD */ 61 | #define MMC_GET_CID 12 /* Get CID */ 62 | #define MMC_GET_OCR 13 /* Get OCR */ 63 | #define MMC_GET_SDSTAT 14 /* Get SD status */ 64 | #define ISDIO_READ 55 /* Read data form SD iSDIO register */ 65 | #define ISDIO_WRITE 56 /* Write data to SD iSDIO register */ 66 | #define ISDIO_MRITE 57 /* Masked write data to SD iSDIO register */ 67 | 68 | /* ATA/CF specific ioctl command */ 69 | #define ATA_GET_REV 20 /* Get F/W revision */ 70 | #define ATA_GET_MODEL 21 /* Get model name */ 71 | #define ATA_GET_SN 22 /* Get serial number */ 72 | 73 | #ifdef __cplusplus 74 | } 75 | #endif 76 | 77 | #endif 78 | -------------------------------------------------------------------------------- /source/fatfs/ff.h: -------------------------------------------------------------------------------- 1 | /*----------------------------------------------------------------------------/ 2 | / FatFs - Generic FAT Filesystem module R0.15 / 3 | /-----------------------------------------------------------------------------/ 4 | / 5 | / Copyright (C) 2022, ChaN, all right reserved. 6 | / 7 | / FatFs module is an open source software. Redistribution and use of FatFs in 8 | / source and binary forms, with or without modification, are permitted provided 9 | / that the following condition is met: 10 | 11 | / 1. Redistributions of source code must retain the above copyright notice, 12 | / this condition and the following disclaimer. 13 | / 14 | / This software is provided by the copyright holder and contributors "AS IS" 15 | / and any warranties related to this software are DISCLAIMED. 16 | / The copyright owner or contributors be NOT LIABLE for any damages caused 17 | / by use of this software. 18 | / 19 | /----------------------------------------------------------------------------*/ 20 | 21 | 22 | #ifndef FF_DEFINED 23 | #define FF_DEFINED 80286 /* Revision ID */ 24 | 25 | #ifdef __cplusplus 26 | extern "C" { 27 | #endif 28 | 29 | #include "ffconf.h" /* FatFs configuration options */ 30 | 31 | #if FF_DEFINED != FFCONF_DEF 32 | #error Wrong configuration file (ffconf.h). 33 | #endif 34 | 35 | 36 | /* Integer types used for FatFs API */ 37 | 38 | #if defined(_WIN32) /* Windows VC++ (for development only) */ 39 | #define FF_INTDEF 2 40 | #include 41 | typedef unsigned __int64 QWORD; 42 | #include 43 | #define isnan(v) _isnan(v) 44 | #define isinf(v) (!_finite(v)) 45 | 46 | #elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus) /* C99 or later */ 47 | #define FF_INTDEF 2 48 | #include 49 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 50 | typedef unsigned char BYTE; /* char must be 8-bit */ 51 | typedef uint16_t WORD; /* 16-bit unsigned integer */ 52 | typedef uint32_t DWORD; /* 32-bit unsigned integer */ 53 | typedef uint64_t QWORD; /* 64-bit unsigned integer */ 54 | typedef WORD WCHAR; /* UTF-16 character type */ 55 | 56 | #else /* Earlier than C99 */ 57 | #define FF_INTDEF 1 58 | typedef unsigned int UINT; /* int must be 16-bit or 32-bit */ 59 | typedef unsigned char BYTE; /* char must be 8-bit */ 60 | typedef unsigned short WORD; /* 16-bit unsigned integer */ 61 | typedef unsigned long DWORD; /* 32-bit unsigned integer */ 62 | typedef WORD WCHAR; /* UTF-16 character type */ 63 | #endif 64 | 65 | 66 | /* Type of file size and LBA variables */ 67 | 68 | #if FF_FS_EXFAT 69 | #if FF_INTDEF != 2 70 | #error exFAT feature wants C99 or later 71 | #endif 72 | typedef QWORD FSIZE_t; 73 | #if FF_LBA64 74 | typedef QWORD LBA_t; 75 | #else 76 | typedef DWORD LBA_t; 77 | #endif 78 | #else 79 | #if FF_LBA64 80 | #error exFAT needs to be enabled when enable 64-bit LBA 81 | #endif 82 | typedef DWORD FSIZE_t; 83 | typedef DWORD LBA_t; 84 | #endif 85 | 86 | 87 | 88 | /* Type of path name strings on FatFs API (TCHAR) */ 89 | 90 | #if FF_USE_LFN && FF_LFN_UNICODE == 1 /* Unicode in UTF-16 encoding */ 91 | typedef WCHAR TCHAR; 92 | #define _T(x) L ## x 93 | #define _TEXT(x) L ## x 94 | #elif FF_USE_LFN && FF_LFN_UNICODE == 2 /* Unicode in UTF-8 encoding */ 95 | typedef char TCHAR; 96 | #define _T(x) u8 ## x 97 | #define _TEXT(x) u8 ## x 98 | #elif FF_USE_LFN && FF_LFN_UNICODE == 3 /* Unicode in UTF-32 encoding */ 99 | typedef DWORD TCHAR; 100 | #define _T(x) U ## x 101 | #define _TEXT(x) U ## x 102 | #elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3) 103 | #error Wrong FF_LFN_UNICODE setting 104 | #else /* ANSI/OEM code in SBCS/DBCS */ 105 | typedef char TCHAR; 106 | #define _T(x) x 107 | #define _TEXT(x) x 108 | #endif 109 | 110 | 111 | 112 | /* Filesystem object structure (FATFS) */ 113 | 114 | typedef struct { 115 | BYTE fs_type; /* Filesystem type (0:not mounted) */ 116 | BYTE pdrv; /* Volume hosting physical drive */ 117 | #if FF_FS_REENTRANT 118 | BYTE ldrv; /* Logical drive number */ 119 | #endif 120 | BYTE ro_flag; /* Read-only flag */ 121 | BYTE n_fats; /* Number of FATs (1 or 2) */ 122 | BYTE wflag; /* win[] status (b0:dirty) */ 123 | BYTE fsi_flag; /* FSINFO status (b7:disabled, b0:dirty) */ 124 | WORD id; /* Volume mount ID */ 125 | WORD n_rootdir; /* Number of root directory entries (FAT12/16) */ 126 | WORD csize; /* Cluster size [sectors] */ 127 | #if FF_MAX_SS != FF_MIN_SS 128 | WORD ssize; /* Sector size (512, 1024, 2048 or 4096) */ 129 | #endif 130 | #if FF_USE_LFN 131 | WCHAR* lfnbuf; /* LFN working buffer */ 132 | #endif 133 | #if FF_FS_EXFAT 134 | BYTE* dirbuf; /* Directory entry block scratchpad buffer for exFAT */ 135 | #endif 136 | DWORD last_clst; /* Last allocated cluster */ 137 | DWORD free_clst; /* Number of free clusters */ 138 | DWORD cdir; /* Current directory start cluster (0:root) */ 139 | #if FF_FS_EXFAT 140 | DWORD cdc_scl; /* Containing directory start cluster (invalid when cdir is 0) */ 141 | DWORD cdc_size; /* b31-b8:Size of containing directory, b7-b0: Chain status */ 142 | DWORD cdc_ofs; /* Offset in the containing directory (invalid when cdir is 0) */ 143 | #endif 144 | DWORD n_fatent; /* Number of FAT entries (number of clusters + 2) */ 145 | DWORD fsize; /* Number of sectors per FAT */ 146 | LBA_t volbase; /* Volume base sector */ 147 | LBA_t fatbase; /* FAT base sector */ 148 | LBA_t dirbase; /* Root directory base sector (FAT12/16) or cluster (FAT32/exFAT) */ 149 | LBA_t database; /* Data base sector */ 150 | #if FF_FS_EXFAT 151 | LBA_t bitbase; /* Allocation bitmap base sector */ 152 | #endif 153 | LBA_t winsect; /* Current sector appearing in the win[] */ 154 | BYTE win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */ 155 | } FATFS; 156 | 157 | 158 | 159 | /* Object ID and allocation information (FFOBJID) */ 160 | 161 | typedef struct { 162 | FATFS* fs; /* Pointer to the hosting volume of this object */ 163 | WORD id; /* Hosting volume's mount ID */ 164 | BYTE attr; /* Object attribute */ 165 | BYTE stat; /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */ 166 | DWORD sclust; /* Object data start cluster (0:no cluster or root directory) */ 167 | FSIZE_t objsize; /* Object size (valid when sclust != 0) */ 168 | #if FF_FS_EXFAT 169 | DWORD n_cont; /* Size of first fragment - 1 (valid when stat == 3) */ 170 | DWORD n_frag; /* Size of last fragment needs to be written to FAT (valid when not zero) */ 171 | DWORD c_scl; /* Containing directory start cluster (valid when sclust != 0) */ 172 | DWORD c_size; /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */ 173 | DWORD c_ofs; /* Offset in the containing directory (valid when file object and sclust != 0) */ 174 | #endif 175 | #if FF_FS_LOCK 176 | UINT lockid; /* File lock ID origin from 1 (index of file semaphore table Files[]) */ 177 | #endif 178 | } FFOBJID; 179 | 180 | 181 | 182 | /* File object structure (FIL) */ 183 | 184 | typedef struct { 185 | FFOBJID obj; /* Object identifier (must be the 1st member to detect invalid object pointer) */ 186 | BYTE flag; /* File status flags */ 187 | BYTE err; /* Abort flag (error code) */ 188 | FSIZE_t fptr; /* File read/write pointer (Zeroed on file open) */ 189 | DWORD clust; /* Current cluster of fpter (invalid when fptr is 0) */ 190 | LBA_t sect; /* Sector number appearing in buf[] (0:invalid) */ 191 | LBA_t dir_sect; /* Sector number containing the directory entry (not used at exFAT) */ 192 | BYTE* dir_ptr; /* Pointer to the directory entry in the win[] (not used at exFAT) */ 193 | #if FF_USE_FASTSEEK 194 | DWORD* cltbl; /* Pointer to the cluster link map table (nulled on open, set by application) */ 195 | #endif 196 | #if !FF_FS_TINY 197 | BYTE buf[FF_MAX_SS]; /* File private data read/write window */ 198 | #endif 199 | } FIL; 200 | 201 | 202 | 203 | /* Directory object structure (DIR) */ 204 | 205 | typedef struct { 206 | FFOBJID obj; /* Object identifier */ 207 | DWORD dptr; /* Current read/write offset */ 208 | DWORD clust; /* Current cluster */ 209 | LBA_t sect; /* Current sector (0:Read operation has terminated) */ 210 | BYTE* dir; /* Pointer to the directory item in the win[] */ 211 | BYTE fn[12]; /* SFN (in/out) {body[8],ext[3],status[1]} */ 212 | #if FF_USE_LFN 213 | DWORD blk_ofs; /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */ 214 | #endif 215 | #if FF_USE_FIND 216 | const TCHAR* pat; /* Pointer to the name matching pattern */ 217 | #endif 218 | } DIR; 219 | 220 | 221 | 222 | /* File information structure (FILINFO) */ 223 | 224 | typedef struct { 225 | FSIZE_t fsize; /* File size */ 226 | WORD fdate; /* Modified date */ 227 | WORD ftime; /* Modified time */ 228 | BYTE fattrib; /* File attribute */ 229 | #if FF_USE_LFN 230 | TCHAR altname[FF_SFN_BUF + 1];/* Alternative file name */ 231 | TCHAR fname[FF_LFN_BUF + 1]; /* Primary file name */ 232 | #else 233 | TCHAR fname[12 + 1]; /* File name */ 234 | #endif 235 | } FILINFO; 236 | 237 | 238 | 239 | /* File function return code (FRESULT) */ 240 | 241 | typedef enum { 242 | FR_OK = 0, /* (0) Succeeded */ 243 | FR_DISK_ERR, /* (1) A hard error occurred in the low level disk I/O layer */ 244 | FR_INT_ERR, /* (2) Assertion failed */ 245 | FR_NOT_READY, /* (3) The physical drive cannot work */ 246 | FR_NO_FILE, /* (4) Could not find the file */ 247 | FR_NO_PATH, /* (5) Could not find the path */ 248 | FR_INVALID_NAME, /* (6) The path name format is invalid */ 249 | FR_DENIED, /* (7) Access denied due to prohibited access or directory full */ 250 | FR_EXIST, /* (8) Access denied due to prohibited access */ 251 | FR_INVALID_OBJECT, /* (9) The file/directory object is invalid */ 252 | FR_WRITE_PROTECTED, /* (10) The physical drive is write protected */ 253 | FR_INVALID_DRIVE, /* (11) The logical drive number is invalid */ 254 | FR_NOT_ENABLED, /* (12) The volume has no work area */ 255 | FR_NO_FILESYSTEM, /* (13) There is no valid FAT volume */ 256 | FR_TIMEOUT, /* (14) Could not get a grant to access the volume within defined period */ 257 | FR_LOCKED, /* (15) The operation is rejected according to the file sharing policy */ 258 | FR_NOT_ENOUGH_CORE, /* (16) LFN working buffer could not be allocated */ 259 | FR_TOO_MANY_OPEN_FILES, /* (17) Number of open files > FF_FS_LOCK */ 260 | FR_INVALID_PARAMETER /* (18) Given parameter is invalid */ 261 | } FRESULT; 262 | 263 | 264 | 265 | /*--------------------------------------------------------------*/ 266 | /* FatFs Module Application Interface */ 267 | /*--------------------------------------------------------------*/ 268 | 269 | FRESULT ff_open (FIL* fp, const TCHAR* path, BYTE mode); /* Open or create a file */ 270 | FRESULT ff_close (FIL* fp); /* Close an open file object */ 271 | FRESULT ff_read (FIL* fp, void* buff, UINT btr, UINT* br); /* Read data from the file */ 272 | FRESULT ff_write (FIL* fp, const void* buff, UINT btw, UINT* bw); /* Write data to the file */ 273 | FRESULT ff_lseek (FIL* fp, FSIZE_t ofs); /* Move file pointer of the file object */ 274 | FRESULT ff_truncate (FIL* fp); /* Truncate the file */ 275 | FRESULT ff_sync (FIL* fp); /* Flush cached data of the writing file */ 276 | FRESULT ff_opendir (DIR* dp, const TCHAR* path); /* Open a directory */ 277 | FRESULT ff_closedir (DIR* dp); /* Close an open directory */ 278 | FRESULT ff_readdir (DIR* dp, FILINFO* fno); /* Read a directory item */ 279 | FRESULT ff_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern); /* Find first file */ 280 | FRESULT ff_findnext (DIR* dp, FILINFO* fno); /* Find next file */ 281 | FRESULT ff_mkdir (const TCHAR* path); /* Create a sub directory */ 282 | FRESULT ff_unlink (const TCHAR* path); /* Delete an existing file or directory */ 283 | FRESULT _ff_rename (const TCHAR* path_old, const TCHAR* path_new); /* Rename/Move a file or directory */ 284 | FRESULT ff_stat (const TCHAR* path, FILINFO* fno); /* Get file status */ 285 | FRESULT ff_chmod (const TCHAR* path, BYTE attr, BYTE mask); /* Change attribute of a file/dir */ 286 | FRESULT ff_utime (const TCHAR* path, const FILINFO* fno); /* Change timestamp of a file/dir */ 287 | FRESULT ff_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs); /* Get number of free clusters on the drive */ 288 | FRESULT ff_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn); /* Get volume label */ 289 | FRESULT ff_setlabel (const TCHAR* label); /* Set volume label */ 290 | FRESULT ff_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf); /* Forward data to the stream */ 291 | FRESULT ff_expand (FIL* fp, FSIZE_t fsz, BYTE opt); /* Allocate a contiguous block to the file */ 292 | FRESULT ff_mount (FATFS* fs, const TCHAR* path, BYTE opt); /* Mount/Unmount a logical drive */ 293 | FRESULT ff_setcp (WORD cp); /* Set current code page */ 294 | int ff_putc (TCHAR c, FIL* fp); /* Put a character to the file */ 295 | int ff_puts (const TCHAR* str, FIL* cp); /* Put a string to the file */ 296 | int ff_printf (FIL* fp, const TCHAR* str, ...); /* Put a formatted string to the file */ 297 | TCHAR* ff_gets (TCHAR* buff, int len, FIL* fp); /* Get a string from the file */ 298 | 299 | /* Some API fucntions are implemented as macro */ 300 | 301 | #define ff_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize)) 302 | #define ff_error(fp) ((fp)->err) 303 | #define ff_tell(fp) ((fp)->fptr) 304 | #define ff_size(fp) ((fp)->obj.objsize) 305 | #define ff_rewind(fp) ff_lseek((fp), 0) 306 | #define ff_rewinddir(dp) ff_readdir((dp), 0) 307 | #define ff_rmdir(path) ff_unlink(path) 308 | #define ff_unmount(path) ff_mount(0, path, 0) 309 | 310 | 311 | 312 | 313 | /*--------------------------------------------------------------*/ 314 | /* Additional Functions */ 315 | /*--------------------------------------------------------------*/ 316 | 317 | /* RTC function (provided by user) */ 318 | #define FAT_TIMESTAMP(year, mon, mday, hour, min, sec) ({ \ 319 | DWORD fat_year = (DWORD)year, fat_mon = (DWORD)mon, fat_mday = (DWORD)mday, fat_hour = (DWORD)hour, fat_min = (DWORD)min, fat_sec = (DWORD)sec; \ 320 | if (fat_year < 1980 || fat_year > 2107) fat_year = FF_NORTC_YEAR; \ 321 | fat_year -= 1980; \ 322 | if (fat_mon < 1 || fat_mon > 12) fat_mon = FF_NORTC_MON; \ 323 | if (fat_mday < 1 || fat_mday > 31) fat_mday = FF_NORTC_MDAY; \ 324 | if (fat_hour > 23) fat_hour = 0; \ 325 | if (fat_min > 59) fat_min = 0; \ 326 | if (fat_sec > 58) fat_sec = 58; \ 327 | fat_sec /= 2; \ 328 | DWORD timestamp = (((fat_year << 25) & (DWORD)0xFE000000) | ((fat_mon << 21) & (DWORD)0x01E00000) | ((fat_mday << 16) & (DWORD)0x001F0000) | ((fat_hour << 11) & (DWORD)0x0000F800) | ((fat_min << 5) & (DWORD)0x000007E0) | (fat_sec & (DWORD)0x0000001F)); \ 329 | timestamp; \ 330 | }) 331 | 332 | #if !FF_FS_NORTC 333 | DWORD get_fattime (void); /* Get current time */ 334 | #endif 335 | 336 | 337 | /* LFN support functions (defined in ffunicode.c) */ 338 | 339 | #if FF_USE_LFN >= 1 340 | WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */ 341 | WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */ 342 | DWORD ff_wtoupper (DWORD uni); /* Unicode upper-case conversion */ 343 | #endif 344 | 345 | 346 | /* O/S dependent functions (samples available in ffsystem.c) */ 347 | 348 | #if FF_USE_LFN == 3 /* Dynamic memory allocation */ 349 | void* ff_memalloc (UINT msize); /* Allocate memory block */ 350 | void ff_memfree (void* mblock); /* Free memory block */ 351 | #endif 352 | #if FF_FS_REENTRANT /* Sync functions */ 353 | int ff_mutex_create (int vol); /* Create a sync object */ 354 | void ff_mutex_delete (int vol); /* Delete a sync object */ 355 | int ff_mutex_take (int vol); /* Lock sync object */ 356 | void ff_mutex_give (int vol); /* Unlock sync object */ 357 | #endif 358 | 359 | 360 | 361 | 362 | /*--------------------------------------------------------------*/ 363 | /* Flags and Offset Address */ 364 | /*--------------------------------------------------------------*/ 365 | 366 | /* File access mode and open method flags (3rd argument of ff_open) */ 367 | #define FA_READ 0x01 368 | #define FA_WRITE 0x02 369 | #define FA_OPEN_EXISTING 0x00 370 | #define FA_CREATE_NEW 0x04 371 | #define FA_CREATE_ALWAYS 0x08 372 | #define FA_OPEN_ALWAYS 0x10 373 | #define FA_OPEN_APPEND 0x30 374 | 375 | /* Fast seek controls (2nd argument of ff_lseek) */ 376 | #define CREATE_LINKMAP ((FSIZE_t)0 - 1) 377 | 378 | /* Filesystem type (FATFS.fs_type) */ 379 | #define FS_FAT12 1 380 | #define FS_FAT16 2 381 | #define FS_FAT32 3 382 | #define FS_EXFAT 4 383 | 384 | /* File attribute bits for directory entry (FILINFO.fattrib) */ 385 | #define AM_RDO 0x01 /* Read only */ 386 | #define AM_HID 0x02 /* Hidden */ 387 | #define AM_SYS 0x04 /* System */ 388 | #define AM_DIR 0x10 /* Directory */ 389 | #define AM_ARC 0x20 /* Archive */ 390 | 391 | 392 | #ifdef __cplusplus 393 | } 394 | #endif 395 | 396 | #endif /* FF_DEFINED */ 397 | -------------------------------------------------------------------------------- /source/fatfs/ff_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ff_dev.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifndef __FF_DEV_H__ 13 | #define __FF_DEV_H__ 14 | 15 | const devoptab_t *ffdev_get_devoptab(); 16 | 17 | #endif /* __FF_DEV_H__ */ 18 | -------------------------------------------------------------------------------- /source/fatfs/ffconf.h: -------------------------------------------------------------------------------- 1 | #include "../usbhsfs_utils.h" 2 | 3 | /*---------------------------------------------------------------------------/ 4 | / Configurations of FatFs Module 5 | /---------------------------------------------------------------------------*/ 6 | 7 | #define FFCONF_DEF 80286 /* Revision ID */ 8 | 9 | /*---------------------------------------------------------------------------/ 10 | / Function Configurations 11 | /---------------------------------------------------------------------------*/ 12 | 13 | #define FF_FS_MINIMIZE 0 14 | /* This option defines minimization level to remove some basic API functions. 15 | / 16 | / 0: Basic functions are fully enabled. 17 | / 1: ff_stat(), ff_getfree(), ff_unlink(), ff_mkdir(), ff_truncate() and ff_rename() 18 | / are removed. 19 | / 2: ff_opendir(), ff_readdir() and ff_closedir() are removed in addition to 1. 20 | / 3: ff_lseek() function is removed in addition to 2. */ 21 | 22 | 23 | #define FF_USE_FIND 0 24 | /* This option switches filtered directory read functions, ff_findfirst() and 25 | / ff_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */ 26 | 27 | 28 | #define FF_USE_FASTSEEK 0 29 | /* This option switches fast seek function. (0:Disable or 1:Enable) */ 30 | 31 | 32 | #define FF_USE_EXPAND 0 33 | /* This option switches ff_expand function. (0:Disable or 1:Enable) */ 34 | 35 | 36 | #define FF_USE_CHMOD 1 37 | /* This option switches attribute manipulation functions, ff_chmod() and ff_utime(). 38 | / (0:Disable or 1:Enable) */ 39 | 40 | 41 | #define FF_USE_LABEL 0 42 | /* This option switches volume label functions, ff_getlabel() and ff_setlabel(). 43 | / (0:Disable or 1:Enable) */ 44 | 45 | 46 | #define FF_USE_FORWARD 0 47 | /* This option switches ff_forward() function. (0:Disable or 1:Enable) */ 48 | 49 | 50 | #define FF_USE_STRFUNC 0 51 | #define FF_PRINT_LLI 0 52 | #define FF_PRINT_FLOAT 0 53 | #define FF_STRF_ENCODE 3 54 | /* FF_USE_STRFUNC switches string functions, ff_gets(), ff_putc(), ff_puts() and 55 | / ff_printf(). 56 | / 57 | / 0: Disable. FF_PRINT_LLI, FF_PRINT_FLOAT and FF_STRF_ENCODE have no effect. 58 | / 1: Enable without LF-CRLF conversion. 59 | / 2: Enable with LF-CRLF conversion. 60 | / 61 | / FF_PRINT_LLI = 1 makes ff_printf() support long long argument and FF_PRINT_FLOAT = 1/2 62 | / makes ff_printf() support floating point argument. These features want C99 or later. 63 | / When FF_LFN_UNICODE >= 1 with LFN enabled, string functions convert the character 64 | / encoding in it. FF_STRF_ENCODE selects assumption of character encoding ON THE FILE 65 | / to be read/written via those functions. 66 | / 67 | / 0: ANSI/OEM in current CP 68 | / 1: Unicode in UTF-16LE 69 | / 2: Unicode in UTF-16BE 70 | / 3: Unicode in UTF-8 71 | */ 72 | 73 | 74 | /*---------------------------------------------------------------------------/ 75 | / Locale and Namespace Configurations 76 | /---------------------------------------------------------------------------*/ 77 | 78 | #define FF_CODE_PAGE 932 79 | /* This option specifies the OEM code page to be used on the target system. 80 | / Incorrect code page setting can cause a file open failure. 81 | / 82 | / 437 - U.S. 83 | / 720 - Arabic 84 | / 737 - Greek 85 | / 771 - KBL 86 | / 775 - Baltic 87 | / 850 - Latin 1 88 | / 852 - Latin 2 89 | / 855 - Cyrillic 90 | / 857 - Turkish 91 | / 860 - Portuguese 92 | / 861 - Icelandic 93 | / 862 - Hebrew 94 | / 863 - Canadian French 95 | / 864 - Arabic 96 | / 865 - Nordic 97 | / 866 - Russian 98 | / 869 - Greek 2 99 | / 932 - Japanese (DBCS) 100 | / 936 - Simplified Chinese (DBCS) 101 | / 949 - Korean (DBCS) 102 | / 950 - Traditional Chinese (DBCS) 103 | / 0 - Include all code pages above and configured by ff_setcp() 104 | */ 105 | 106 | 107 | #define FF_USE_LFN 3 108 | #define FF_MAX_LFN 255 109 | /* The FF_USE_LFN switches the support for LFN (long file name). 110 | / 111 | / 0: Disable LFN. FF_MAX_LFN has no effect. 112 | / 1: Enable LFN with static working buffer on the BSS. Always NOT thread-safe. 113 | / 2: Enable LFN with dynamic working buffer on the STACK. 114 | / 3: Enable LFN with dynamic working buffer on the HEAP. 115 | / 116 | / To enable the LFN, ffunicode.c needs to be added to the project. The LFN function 117 | / requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and 118 | / additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled. 119 | / The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can 120 | / be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN 121 | / specification. 122 | / When use stack for the working buffer, take care on stack overflow. When use heap 123 | / memory for the working buffer, memory management functions, ff_memalloc() and 124 | / ff_memfree() exemplified in ffsystem.c, need to be added to the project. */ 125 | 126 | 127 | #define FF_LFN_UNICODE 2 128 | /* This option switches the character encoding on the API when LFN is enabled. 129 | / 130 | / 0: ANSI/OEM in current CP (TCHAR = char) 131 | / 1: Unicode in UTF-16 (TCHAR = WCHAR) 132 | / 2: Unicode in UTF-8 (TCHAR = char) 133 | / 3: Unicode in UTF-32 (TCHAR = DWORD) 134 | / 135 | / Also behavior of string I/O functions will be affected by this option. 136 | / When LFN is not enabled, this option has no effect. */ 137 | 138 | 139 | #define FF_LFN_BUF 255 140 | #define FF_SFN_BUF 12 141 | /* This set of options defines size of file name members in the FILINFO structure 142 | / which is used to read out directory items. These values should be suffcient for 143 | / the file names to read. The maximum possible length of the read file name depends 144 | / on character encoding. When LFN is not enabled, these options have no effect. */ 145 | 146 | 147 | /*---------------------------------------------------------------------------/ 148 | / Drive/Volume Configurations 149 | /---------------------------------------------------------------------------*/ 150 | 151 | #define FF_VOLUMES 64 152 | /* Number of volumes (logical drives) to be used. (1-100) */ 153 | 154 | 155 | #define FF_MIN_SS BLKDEV_MIN_BLOCK_SIZE 156 | #define FF_MAX_SS BLKDEV_MAX_BLOCK_SIZE 157 | /* This set of options configures the range of sector size to be supported. (512, 158 | / 1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and 159 | / harddisk, but a larger value may be required for on-board flash memory and some 160 | / type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured 161 | / for variable sector size mode and ff_disk_ioctl() function needs to implement 162 | / GET_SECTOR_SIZE command. */ 163 | 164 | 165 | #define FF_LBA64 1 166 | /* This option switches support for 64-bit LBA. (0:Disable or 1:Enable) 167 | / To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */ 168 | 169 | 170 | #define FF_USE_TRIM 0 171 | /* This option switches support for ATA-TRIM. (0:Disable or 1:Enable) 172 | / To enable Trim function, also CTRL_TRIM command should be implemented to the 173 | / ff_disk_ioctl() function. */ 174 | 175 | 176 | 177 | /*---------------------------------------------------------------------------/ 178 | / System Configurations 179 | /---------------------------------------------------------------------------*/ 180 | 181 | #define FF_FS_TINY 0 182 | /* This option switches tiny buffer configuration. (0:Normal or 1:Tiny) 183 | / At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes. 184 | / Instead of private sector buffer eliminated from the file object, common sector 185 | / buffer in the filesystem object (FATFS) is used for the file data transfer. */ 186 | 187 | 188 | #define FF_FS_EXFAT 1 189 | /* This option switches support for exFAT filesystem. (0:Disable or 1:Enable) 190 | / To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1) 191 | / Note that enabling exFAT discards ANSI C (C89) compatibility. */ 192 | 193 | 194 | #define FF_FS_NORTC 0 195 | #define FF_NORTC_MON 1 196 | #define FF_NORTC_MDAY 1 197 | #define FF_NORTC_YEAR 2023 198 | /* The option FF_FS_NORTC switches timestamp feature. If the system does not have 199 | / an RTC or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable the 200 | / timestamp feature. Every object modified by FatFs will have a fixed timestamp 201 | / defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time. 202 | / To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be 203 | / added to the project to read current time form real-time clock. FF_NORTC_MON, 204 | / FF_NORTC_MDAY and FF_NORTC_YEAR have no effect. */ 205 | 206 | 207 | #define FF_FS_NOFSINFO 0 208 | /* If you need to know correct free space on the FAT32 volume, set bit 0 of this 209 | / option, and ff_getfree() function at the first time after volume mount will force 210 | / a full FAT scan. Bit 1 controls the use of last allocated cluster number. 211 | / 212 | / bit0=0: Use free cluster count in the FSINFO if available. 213 | / bit0=1: Do not trust free cluster count in the FSINFO. 214 | / bit1=0: Use last allocated cluster number in the FSINFO if available. 215 | / bit1=1: Do not trust last allocated cluster number in the FSINFO. 216 | */ 217 | 218 | 219 | #define FF_FS_LOCK 64 220 | /* The option FF_FS_LOCK switches file lock function to control duplicated file open 221 | / and illegal operation to open objects. 222 | / 223 | / 0: Disable file lock function. To avoid volume corruption, application program 224 | / should avoid illegal open, remove and rename to the open objects. 225 | / >0: Enable file lock function. The value defines how many files/sub-directories 226 | / can be opened simultaneously under file lock control. Note that the file 227 | / lock control is independent of re-entrancy. */ 228 | 229 | 230 | #define FF_FS_REENTRANT 0 231 | #define FF_FS_TIMEOUT 1000 232 | /* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs 233 | / module itself. Note that regardless of this option, file access to different 234 | / volume is always re-entrant and volume control functions, ff_mount(), 235 | / are always not re-entrant. Only file/directory access to the same volume is 236 | / under control of this featuer. 237 | / 238 | / 0: Disable re-entrancy. FF_FS_TIMEOUT have no effect. 239 | / 1: Enable re-entrancy. Also user provided synchronization handlers, 240 | / ff_mutex_create(), ff_mutex_delete(), ff_mutex_take() and ff_mutex_give() 241 | / function, must be added to the project. Samples are available in ffsystem.c. 242 | / 243 | / The FF_FS_TIMEOUT defines timeout period in unit of O/S time tick. 244 | */ 245 | 246 | 247 | 248 | /*--- End of configuration options ---*/ 249 | -------------------------------------------------------------------------------- /source/fatfs/ffsystem.c: -------------------------------------------------------------------------------- 1 | /*------------------------------------------------------------------------*/ 2 | /* A Sample Code of User Provided OS Dependent Functions for FatFs */ 3 | /*------------------------------------------------------------------------*/ 4 | 5 | #include "ff.h" 6 | 7 | 8 | #if FF_USE_LFN == 3 /* Use dynamic memory allocation */ 9 | 10 | /*------------------------------------------------------------------------*/ 11 | /* Allocate/Free a Memory Block */ 12 | /*------------------------------------------------------------------------*/ 13 | 14 | #include /* with POSIX API */ 15 | 16 | 17 | void* ff_memalloc ( /* Returns pointer to the allocated memory block (null if not enough core) */ 18 | UINT msize /* Number of bytes to allocate */ 19 | ) 20 | { 21 | return malloc((size_t)msize); /* Allocate a new memory block */ 22 | } 23 | 24 | 25 | void ff_memfree ( 26 | void* mblock /* Pointer to the memory block to free (no effect if null) */ 27 | ) 28 | { 29 | free(mblock); /* Free the memory block */ 30 | } 31 | 32 | #endif 33 | 34 | 35 | 36 | 37 | #if FF_FS_REENTRANT /* Mutal exclusion */ 38 | /*------------------------------------------------------------------------*/ 39 | /* Definitions of Mutex */ 40 | /*------------------------------------------------------------------------*/ 41 | 42 | #define OS_TYPE 0 /* 0:Win32, 1:uITRON4.0, 2:uC/OS-II, 3:FreeRTOS, 4:CMSIS-RTOS */ 43 | 44 | 45 | #if OS_TYPE == 0 /* Win32 */ 46 | #include 47 | static HANDLE Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 48 | 49 | #elif OS_TYPE == 1 /* uITRON */ 50 | #include "itron.h" 51 | #include "kernel.h" 52 | static mtxid Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 53 | 54 | #elif OS_TYPE == 2 /* uc/OS-II */ 55 | #include "includes.h" 56 | static OS_EVENT *Mutex[FF_VOLUMES + 1]; /* Table of mutex pinter */ 57 | 58 | #elif OS_TYPE == 3 /* FreeRTOS */ 59 | #include "FreeRTOS.h" 60 | #include "semphr.h" 61 | static SemaphoreHandle_t Mutex[FF_VOLUMES + 1]; /* Table of mutex handle */ 62 | 63 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 64 | #include "cmsis_os.h" 65 | static osMutexId Mutex[FF_VOLUMES + 1]; /* Table of mutex ID */ 66 | 67 | #endif 68 | 69 | 70 | 71 | /*------------------------------------------------------------------------*/ 72 | /* Create a Mutex */ 73 | /*------------------------------------------------------------------------*/ 74 | /* This function is called in f_mount function to create a new mutex 75 | / or semaphore for the volume. When a 0 is returned, the f_mount function 76 | / fails with FR_INT_ERR. 77 | */ 78 | 79 | int ff_mutex_create ( /* Returns 1:Function succeeded or 0:Could not create the mutex */ 80 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 81 | ) 82 | { 83 | #if OS_TYPE == 0 /* Win32 */ 84 | Mutex[vol] = CreateMutex(NULL, FALSE, NULL); 85 | return (int)(Mutex[vol] != INVALID_HANDLE_VALUE); 86 | 87 | #elif OS_TYPE == 1 /* uITRON */ 88 | T_CMTX cmtx = {TA_TPRI,1}; 89 | 90 | Mutex[vol] = acre_mtx(&cmtx); 91 | return (int)(Mutex[vol] > 0); 92 | 93 | #elif OS_TYPE == 2 /* uC/OS-II */ 94 | OS_ERR err; 95 | 96 | Mutex[vol] = OSMutexCreate(0, &err); 97 | return (int)(err == OS_NO_ERR); 98 | 99 | #elif OS_TYPE == 3 /* FreeRTOS */ 100 | Mutex[vol] = xSemaphoreCreateMutex(); 101 | return (int)(Mutex[vol] != NULL); 102 | 103 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 104 | osMutexDef(cmsis_os_mutex); 105 | 106 | Mutex[vol] = osMutexCreate(osMutex(cmsis_os_mutex)); 107 | return (int)(Mutex[vol] != NULL); 108 | 109 | #endif 110 | } 111 | 112 | 113 | /*------------------------------------------------------------------------*/ 114 | /* Delete a Mutex */ 115 | /*------------------------------------------------------------------------*/ 116 | /* This function is called in f_mount function to delete a mutex or 117 | / semaphore of the volume created with ff_mutex_create function. 118 | */ 119 | 120 | void ff_mutex_delete ( /* Returns 1:Function succeeded or 0:Could not delete due to an error */ 121 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 122 | ) 123 | { 124 | #if OS_TYPE == 0 /* Win32 */ 125 | CloseHandle(Mutex[vol]); 126 | 127 | #elif OS_TYPE == 1 /* uITRON */ 128 | del_mtx(Mutex[vol]); 129 | 130 | #elif OS_TYPE == 2 /* uC/OS-II */ 131 | OS_ERR err; 132 | 133 | OSMutexDel(Mutex[vol], OS_DEL_ALWAYS, &err); 134 | 135 | #elif OS_TYPE == 3 /* FreeRTOS */ 136 | vSemaphoreDelete(Mutex[vol]); 137 | 138 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 139 | osMutexDelete(Mutex[vol]); 140 | 141 | #endif 142 | } 143 | 144 | 145 | /*------------------------------------------------------------------------*/ 146 | /* Request a Grant to Access the Volume */ 147 | /*------------------------------------------------------------------------*/ 148 | /* This function is called on enter file functions to lock the volume. 149 | / When a 0 is returned, the file function fails with FR_TIMEOUT. 150 | */ 151 | 152 | int ff_mutex_take ( /* Returns 1:Succeeded or 0:Timeout */ 153 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 154 | ) 155 | { 156 | #if OS_TYPE == 0 /* Win32 */ 157 | return (int)(WaitForSingleObject(Mutex[vol], FF_FS_TIMEOUT) == WAIT_OBJECT_0); 158 | 159 | #elif OS_TYPE == 1 /* uITRON */ 160 | return (int)(tloc_mtx(Mutex[vol], FF_FS_TIMEOUT) == E_OK); 161 | 162 | #elif OS_TYPE == 2 /* uC/OS-II */ 163 | OS_ERR err; 164 | 165 | OSMutexPend(Mutex[vol], FF_FS_TIMEOUT, &err)); 166 | return (int)(err == OS_NO_ERR); 167 | 168 | #elif OS_TYPE == 3 /* FreeRTOS */ 169 | return (int)(xSemaphoreTake(Mutex[vol], FF_FS_TIMEOUT) == pdTRUE); 170 | 171 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 172 | return (int)(osMutexWait(Mutex[vol], FF_FS_TIMEOUT) == osOK); 173 | 174 | #endif 175 | } 176 | 177 | 178 | 179 | /*------------------------------------------------------------------------*/ 180 | /* Release a Grant to Access the Volume */ 181 | /*------------------------------------------------------------------------*/ 182 | /* This function is called on leave file functions to unlock the volume. 183 | */ 184 | 185 | void ff_mutex_give ( 186 | int vol /* Mutex ID: Volume mutex (0 to FF_VOLUMES - 1) or system mutex (FF_VOLUMES) */ 187 | ) 188 | { 189 | #if OS_TYPE == 0 /* Win32 */ 190 | ReleaseMutex(Mutex[vol]); 191 | 192 | #elif OS_TYPE == 1 /* uITRON */ 193 | unl_mtx(Mutex[vol]); 194 | 195 | #elif OS_TYPE == 2 /* uC/OS-II */ 196 | OSMutexPost(Mutex[vol]); 197 | 198 | #elif OS_TYPE == 3 /* FreeRTOS */ 199 | xSemaphoreGive(Mutex[vol]); 200 | 201 | #elif OS_TYPE == 4 /* CMSIS-RTOS */ 202 | osMutexRelease(Mutex[vol]); 203 | 204 | #endif 205 | } 206 | 207 | #endif /* FF_FS_REENTRANT */ 208 | -------------------------------------------------------------------------------- /source/lwext4/ext.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ext.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #include "ext.h" 10 | 11 | #include "../usbhsfs_drive.h" 12 | 13 | #define EXT2_FINCOM_SUPPORTED (EXT4_FINCOM_FILETYPE | EXT4_FINCOM_META_BG) 14 | #define EXT2_FINCOM_UNSUPPORTED ~EXT2_FINCOM_SUPPORTED 15 | 16 | #define EXT2_FRO_SUPPORTED (EXT4_FRO_COM_SPARSE_SUPER | EXT4_FRO_COM_LARGE_FILE | EXT4_FRO_COM_BTREE_DIR) 17 | #define EXT2_FRO_UNSUPPORTED ~EXT2_FRO_SUPPORTED 18 | 19 | #define EXT3_FINCOM_SUPPORTED (EXT2_FINCOM_SUPPORTED | EXT4_FINCOM_RECOVER) 20 | #define EXT3_FINCOM_UNSUPPORTED ~EXT3_FINCOM_SUPPORTED 21 | 22 | #define EXT3_FRO_SUPPORTED EXT2_FRO_SUPPORTED 23 | #define EXT3_FRO_UNSUPPORTED ~EXT3_FRO_SUPPORTED 24 | 25 | /* Function prototypes. */ 26 | 27 | static void ext_get_version(ext_vd *vd); 28 | 29 | bool ext_mount(ext_vd *vd) 30 | { 31 | UsbHsFsDriveLogicalUnitContext *lun_ctx = NULL; 32 | char mount_point[CONFIG_EXT4_MAX_MP_NAME + 3] = {0}; 33 | struct ext4_sblock *sblock = NULL; 34 | bool ret = false, bdev_reg = false, vol_mounted = false, read_only = false; 35 | int res = 0; 36 | 37 | if (!vd || !vd->bdev || !vd->bdev->bdif || !vd->bdev->bdif->ph_bbuf || !(lun_ctx = (UsbHsFsDriveLogicalUnitContext*)vd->bdev->bdif->p_user) || !vd->dev_name[0]) 38 | { 39 | USBHSFS_LOG_MSG("Invalid parameters!"); 40 | return false; 41 | } 42 | 43 | /* Update read only flag. */ 44 | read_only = ((vd->flags & UsbHsFsMountFlags_ReadOnly) || lun_ctx->write_protect); 45 | 46 | /* Register EXT block device. */ 47 | res = ext4_device_register(vd->bdev, vd->dev_name); 48 | if (res) 49 | { 50 | USBHSFS_LOG_MSG("Failed to register EXT block device \"%s\"! (%d).", vd->dev_name, res); 51 | goto end; 52 | } 53 | 54 | bdev_reg = true; 55 | 56 | /* Generate mount point name. */ 57 | sprintf(mount_point, "/%s/", vd->dev_name); 58 | 59 | /* Mount EXT volume. */ 60 | res = ext4_mount(vd->dev_name, mount_point, read_only); 61 | if (res) 62 | { 63 | USBHSFS_LOG_MSG("Failed to mount EXT volume \"%s\"! (%d).", mount_point, res); 64 | goto end; 65 | } 66 | 67 | vol_mounted = true; 68 | 69 | /* Update EXT superblock pointer. */ 70 | sblock = &(vd->bdev->fs->sb); 71 | 72 | /* Perform EXT journal operations if needed. */ 73 | if (!read_only && ext4_sb_feature_com(sblock, EXT4_FCOM_HAS_JOURNAL)) 74 | { 75 | /* Replay EXT journal depending on the mount flags. */ 76 | if ((vd->flags & UsbHsFsMountFlags_ReplayJournal) && (res = ext4_recover(mount_point))) 77 | { 78 | USBHSFS_LOG_MSG("Failed to replay EXT journal from volume \"%s\"! (%d).", mount_point, res); 79 | goto end; 80 | } 81 | 82 | /* Start EXT journaling. */ 83 | res = ext4_journal_start(mount_point); 84 | if (res) 85 | { 86 | USBHSFS_LOG_MSG("Failed to start journaling on EXT volume \"%s\"! (%d).", mount_point, res); 87 | goto end; 88 | } 89 | } 90 | 91 | /* Get EXT version. */ 92 | ext_get_version(vd); 93 | 94 | /* Update return value. */ 95 | ret = true; 96 | 97 | end: 98 | if (!ret) 99 | { 100 | if (vol_mounted) ext4_umount(mount_point); 101 | 102 | if (bdev_reg) ext4_device_unregister(vd->dev_name); 103 | } 104 | 105 | return ret; 106 | } 107 | 108 | void ext_umount(ext_vd *vd) 109 | { 110 | if (!vd || !vd->bdev || !vd->bdev->bdif || !vd->bdev->bdif->ph_bbuf || !vd->dev_name[0]) return; 111 | 112 | char mount_point[CONFIG_EXT4_MAX_MP_NAME + 3] = {0}; 113 | int res = 0; 114 | 115 | /* Generate mount point name. */ 116 | sprintf(mount_point, "/%s/", vd->dev_name); 117 | 118 | /* Stop EXT journaling. */ 119 | res = ext4_journal_stop(mount_point); 120 | if (res) USBHSFS_LOG_MSG("Failed to stop EXT journaling for volume \"%s\"! (%d).", mount_point, res); 121 | 122 | /* Unmount EXT volume. */ 123 | res = ext4_umount(mount_point); 124 | if (res) USBHSFS_LOG_MSG("Failed to unmount EXT volume \"%s\"! (%d).", mount_point, res); 125 | 126 | /* Unregister EXT block device. */ 127 | /* Do not check for errors in this call - it always returns ENOENT. */ 128 | res = ext4_device_unregister(vd->dev_name); 129 | //if (res) USBHSFS_LOG_MSG("Failed to unregister EXT block device \"%s\"! (%d).", vd->dev_name, res); 130 | } 131 | 132 | static void ext_get_version(ext_vd *vd) 133 | { 134 | u32 fincom = 0, fro = 0; 135 | struct ext4_sblock *sblock = &(vd->bdev->fs->sb); 136 | 137 | /* Get features_incompatible. */ 138 | fincom = ext4_get32(sblock, features_incompatible); 139 | 140 | /* Get features_read_only. */ 141 | fro = ext4_get32(sblock, features_read_only); 142 | 143 | /* Check EXT4 features. */ 144 | if ((fincom & EXT3_FINCOM_UNSUPPORTED) || (fro & EXT3_FRO_UNSUPPORTED)) vd->version = UsbHsFsDeviceFileSystemType_EXT4; 145 | 146 | /* Check EXT3 features. */ 147 | if (!(fincom & EXT3_FINCOM_UNSUPPORTED) && !(fro & EXT3_FRO_UNSUPPORTED)) vd->version = UsbHsFsDeviceFileSystemType_EXT3; 148 | 149 | /* Check EXT2 features. */ 150 | if (!(fincom & EXT2_FINCOM_UNSUPPORTED) && !(fro & EXT2_FRO_UNSUPPORTED)) vd->version = UsbHsFsDeviceFileSystemType_EXT2; 151 | } 152 | -------------------------------------------------------------------------------- /source/lwext4/ext.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ext.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __EXT_H__ 12 | #define __EXT_H__ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "../usbhsfs_utils.h" 22 | 23 | #include "ext_disk_io.h" 24 | 25 | /// EXT volume descriptor. 26 | typedef struct _ext_vd { 27 | struct ext4_blockdev *bdev; ///< EXT block device handle. 28 | char dev_name[CONFIG_EXT4_MAX_MP_NAME]; ///< Block device mount name. 29 | u32 flags; ///< EXT mount flags. 30 | s64 id; ///< Filesystem ID. 31 | u16 uid; ///< User ID for entry creation. 32 | u16 gid; ///< Group ID for entry creation. 33 | u16 fmask; ///< Unix style permission mask for file creation. 34 | u16 dmask; ///< Unix style permission mask for directory creation. 35 | u8 version; ///< UsbHsFsDeviceFileSystemType_EXT* value to identify the EXT version. 36 | } ext_vd; 37 | 38 | /// Mounts an EXT volume using the provided volume descriptor. 39 | bool ext_mount(ext_vd *vd); 40 | 41 | /// Unmounts the EXT volume represented by the provided volume descriptor. 42 | void ext_umount(ext_vd *vd); 43 | 44 | #endif /* __EXT_H__ */ 45 | -------------------------------------------------------------------------------- /source/lwext4/ext_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ext_dev.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __EXT_DEV_H__ 12 | #define __EXT_DEV_H__ 13 | 14 | const devoptab_t *extdev_get_devoptab(); 15 | 16 | #endif /* __EXT_DEV_H__ */ 17 | -------------------------------------------------------------------------------- /source/lwext4/ext_disk_io.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ext_disk_io.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #include "ext.h" 10 | 11 | #include "../usbhsfs_scsi.h" 12 | 13 | /* Function prototypes. */ 14 | 15 | static int ext_blockdev_open(struct ext4_blockdev *bdev); 16 | static int ext_blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, uint32_t blk_cnt); 17 | static int ext_blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf, uint64_t blk_id, uint32_t blk_cnt); 18 | static int ext_blockdev_close(struct ext4_blockdev *bdev); 19 | static int ext_blockdev_lock(struct ext4_blockdev *bdev); 20 | static int ext_blockdev_unlock(struct ext4_blockdev *bdev); 21 | 22 | /* Global variables. */ 23 | 24 | static const struct ext4_blockdev_iface ext_blockdev_usbhsfs_iface = { 25 | .open = ext_blockdev_open, 26 | .bread = ext_blockdev_bread, 27 | .bwrite = ext_blockdev_bwrite, 28 | .close = ext_blockdev_close, 29 | .lock = ext_blockdev_lock, 30 | .unlock = ext_blockdev_unlock, 31 | .ph_bsize = 0, 32 | .ph_bcnt = 0, 33 | .ph_bbuf = NULL, 34 | .ph_refctr = 0, 35 | .bread_ctr = 0, 36 | .bwrite_ctr = 0, 37 | .p_user = NULL 38 | }; 39 | 40 | struct ext4_blockdev *ext_disk_io_alloc_blockdev(void *p_user, u64 part_lba, u64 part_size) 41 | { 42 | UsbHsFsDriveLogicalUnitContext *lun_ctx = (UsbHsFsDriveLogicalUnitContext*)p_user; 43 | struct ext4_blockdev *bdev = NULL; 44 | bool success = false; 45 | 46 | /* Allocate memory for ext4_blockdev object. */ 47 | bdev = calloc(1, sizeof(struct ext4_blockdev)); 48 | if (!bdev) 49 | { 50 | USBHSFS_LOG_MSG("Failed to allocate memory for ext4_blockdev object!"); 51 | goto end; 52 | } 53 | 54 | /* Allocate memory for ext4_blockdev_iface object. */ 55 | bdev->bdif = calloc(1, sizeof(struct ext4_blockdev_iface)); 56 | if (!bdev->bdif) 57 | { 58 | USBHSFS_LOG_MSG("Failed to allocate memory for ext4_blockdev_iface object!"); 59 | goto end; 60 | } 61 | 62 | /* Copy ext4_blockdev_iface object data. */ 63 | memcpy(bdev->bdif, &ext_blockdev_usbhsfs_iface, sizeof(struct ext4_blockdev_iface)); 64 | 65 | /* Allocate memory for block size buffer. */ 66 | bdev->bdif->ph_bbuf = calloc(1, lun_ctx->block_length); 67 | if (!bdev->bdif->ph_bbuf) 68 | { 69 | USBHSFS_LOG_MSG("Failed to allocate 0x%X bytes for block size buffer!", lun_ctx->block_length); 70 | goto end; 71 | } 72 | 73 | /* Fill ext4_blockdev object. */ 74 | bdev->part_offset = (part_lba * (u64)lun_ctx->block_length); 75 | bdev->part_size = (part_size * (u64)lun_ctx->block_length); 76 | 77 | /* Fill ext4_blockdev_iface object. */ 78 | bdev->bdif->ph_bsize = lun_ctx->block_length; 79 | bdev->bdif->ph_bcnt = part_size; 80 | bdev->bdif->p_user = lun_ctx; 81 | 82 | /* Update flag. */ 83 | success = true; 84 | 85 | end: 86 | if (!success && bdev) 87 | { 88 | ext_disk_io_free_blockdev(bdev); 89 | bdev = NULL; 90 | } 91 | 92 | return bdev; 93 | } 94 | 95 | void ext_disk_io_free_blockdev(struct ext4_blockdev *bdev) 96 | { 97 | if (!bdev) return; 98 | 99 | if (bdev->bdif) 100 | { 101 | if (bdev->bdif->ph_bbuf) free(bdev->bdif->ph_bbuf); 102 | free(bdev->bdif); 103 | } 104 | 105 | free(bdev); 106 | } 107 | 108 | static int ext_blockdev_open(struct ext4_blockdev *bdev) 109 | { 110 | NX_IGNORE_ARG(bdev); 111 | 112 | /* Low level block device initialization is handled by us. */ 113 | return 0; 114 | } 115 | 116 | static int ext_blockdev_bread(struct ext4_blockdev *bdev, void *buf, uint64_t blk_id, uint32_t blk_cnt) 117 | { 118 | /* Get LUN context and read sectors. */ 119 | UsbHsFsDriveLogicalUnitContext *lun_ctx = (UsbHsFsDriveLogicalUnitContext*)bdev->bdif->p_user; 120 | return (usbHsFsScsiReadLogicalUnitBlocks(lun_ctx, buf, blk_id, blk_cnt) ? 0 : EIO); 121 | } 122 | 123 | static int ext_blockdev_bwrite(struct ext4_blockdev *bdev, const void *buf, uint64_t blk_id, uint32_t blk_cnt) 124 | { 125 | /* Get LUN context and write sectors. */ 126 | UsbHsFsDriveLogicalUnitContext *lun_ctx = (UsbHsFsDriveLogicalUnitContext*)bdev->bdif->p_user; 127 | return (usbHsFsScsiWriteLogicalUnitBlocks(lun_ctx, buf, blk_id, blk_cnt) ? 0 : EIO); 128 | } 129 | 130 | static int ext_blockdev_close(struct ext4_blockdev *bdev) 131 | { 132 | NX_IGNORE_ARG(bdev); 133 | 134 | /* Low level block device deinitialization is handled by us. */ 135 | return 0; 136 | } 137 | 138 | static int ext_blockdev_lock(struct ext4_blockdev *bdev) 139 | { 140 | NX_IGNORE_ARG(bdev); 141 | 142 | /* Mutex locking is handled by us. */ 143 | return 0; 144 | } 145 | 146 | static int ext_blockdev_unlock(struct ext4_blockdev *bdev) 147 | { 148 | NX_IGNORE_ARG(bdev); 149 | 150 | /* Mutex unlocking is handled by us. */ 151 | return 0; 152 | } 153 | -------------------------------------------------------------------------------- /source/lwext4/ext_disk_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ext_disk_io.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __EXT_DISK_IO_H__ 12 | #define __EXT_DISK_IO_H__ 13 | 14 | /// Returns a pointer to a dynamically allocated ext4_blockdev object using the provided data. 15 | struct ext4_blockdev *ext_disk_io_alloc_blockdev(void *p_user, u64 part_lba, u64 part_size); 16 | 17 | /// Frees a previously allocated ext4_blockdev object. 18 | void ext_disk_io_free_blockdev(struct ext4_blockdev *bdev); 19 | 20 | #endif /* __EXT_DISK_IO_H__ */ 21 | -------------------------------------------------------------------------------- /source/ntfs-3g/ntfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ntfs.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Rhys Koedijk. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | * 9 | * Based on work from libntfs-wii (https://github.com/rhyskoedijk/libntfs-wii). 10 | */ 11 | 12 | #include "ntfs.h" 13 | 14 | /* Type definitions. */ 15 | 16 | /// NTFS path. 17 | typedef struct _ntfs_path { 18 | const char *path; ///< Volume path (e.g. '/foo/bar/file.txt'). 19 | const char *dir; ///< Directory path (e.g. '/foo/bar'). 20 | const char *name; ///< Filename (e.g. 'file.txt'). 21 | char buf[MAX_PATH_LENGTH]; ///< Internal buffer containing the path string. 22 | } ntfs_path; 23 | 24 | /* Function prototypes. */ 25 | 26 | static ntfs_inode *ntfs_inode_open_from_path_reparse(ntfs_vd *vd, const char *path, int reparse_depth); 27 | 28 | static void ntfs_split_path(const char *path, ntfs_path *p); 29 | 30 | #ifdef DEBUG 31 | int ntfs_log_handler_usbhsfs(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args) 32 | { 33 | (void)data; 34 | 35 | int ret = 0; 36 | size_t formatted_str_len = 0; 37 | char *formatted_str = NULL; 38 | 39 | /* Get formatted string length. */ 40 | formatted_str_len = vsnprintf(NULL, 0, format, args); 41 | if (!formatted_str_len) return ret; 42 | 43 | /* Allocate buffer for the formatted string. */ 44 | formatted_str = calloc(++formatted_str_len, sizeof(char)); 45 | if (!formatted_str) return ret; 46 | 47 | /* Generate formatted string and save it to the logfile. */ 48 | ret = (int)vsnprintf(formatted_str, formatted_str_len, format, args); 49 | if (ret) 50 | { 51 | /* Remove CRLFs and dots - we take care of them. */ 52 | if (formatted_str[formatted_str_len - 1] == '\n') formatted_str[--formatted_str_len] = '\0'; 53 | if (formatted_str[formatted_str_len - 1] == '\r') formatted_str[--formatted_str_len] = '\0'; 54 | if (formatted_str[formatted_str_len - 1] == '.') formatted_str[--formatted_str_len] = '\0'; 55 | 56 | /* Log message. */ 57 | usbHsFsLogWriteFormattedStringToLogFile(file, line, function, "%s (level %d).", formatted_str, level); 58 | } 59 | 60 | /* Free allocated buffer. */ 61 | free(formatted_str); 62 | 63 | return ret; 64 | } 65 | #endif /* DEBUG */ 66 | 67 | ntfs_inode *ntfs_inode_open_from_path(ntfs_vd *vd, const char *path) 68 | { 69 | return ntfs_inode_open_from_path_reparse(vd, path, 1); 70 | } 71 | 72 | ntfs_inode *ntfs_inode_create(ntfs_vd *vd, const char *path, mode_t type, const char *target) 73 | { 74 | ntfs_path full_path = {0}; 75 | ntfs_inode *dir_ni = NULL, *ni = NULL; 76 | ntfschar *uname = NULL, *utarget = NULL; 77 | int uname_len = 0, utarget_len = 0; 78 | 79 | /* Safety check. */ 80 | if (!vd || !vd->vol || !path || !*path || (type == S_IFLNK && (!target || !*target))) 81 | { 82 | errno = EINVAL; 83 | goto end; 84 | } 85 | 86 | /* Split entry path. */ 87 | ntfs_split_path(path, &full_path); 88 | 89 | /* Make sure we have a valid entry name to work with. */ 90 | if (full_path.name[0] == '\0') 91 | { 92 | errno = EINVAL; 93 | goto end; 94 | } 95 | 96 | /* Open the parent directory the desired entry will be created in. */ 97 | dir_ni = ntfs_inode_open_from_path(vd, full_path.dir); 98 | if (!dir_ni) goto end; 99 | 100 | /* Convert the entry name string from our current locale (UTF-8) into UTF-16LE. */ 101 | uname_len = ntfs_mbstoucs(full_path.name, &uname); 102 | if (uname_len <= 0) 103 | { 104 | errno = EINVAL; 105 | goto end; 106 | } 107 | 108 | /* Create the new entry. */ 109 | switch(type) 110 | { 111 | case S_IFDIR: /* Directory. */ 112 | case S_IFREG: /* File. */ 113 | USBHSFS_LOG_MSG("Creating inode in directory \"%s\" named \"%s\".", full_path.dir, full_path.name); 114 | ni = ntfs_create(dir_ni, 0, uname, uname_len, type); 115 | break; 116 | case S_IFLNK: /* Symbolic link. */ 117 | /* Convert the target link path string from our current locale (UTF-8) into UTF-16LE. */ 118 | utarget_len = ntfs_mbstoucs(target, &utarget); 119 | if (utarget_len <= 0) 120 | { 121 | errno = EINVAL; 122 | goto end; 123 | } 124 | 125 | USBHSFS_LOG_MSG("Creating symlink in directory \"%s\" named \"%s\" targetting \"%s\".", full_path.dir, full_path.name, target); 126 | ni = ntfs_create_symlink(dir_ni, 0, uname, uname_len, utarget, utarget_len); 127 | break; 128 | default: /* Invalid entry. */ 129 | errno = EINVAL; 130 | break; 131 | } 132 | 133 | end: 134 | if (utarget) free(utarget); 135 | 136 | if (uname) free(uname); 137 | 138 | if (dir_ni) ntfs_inode_close(dir_ni); 139 | 140 | return ni; 141 | } 142 | 143 | int ntfs_inode_link(ntfs_vd *vd, const char *old_path, const char *new_path) 144 | { 145 | ntfs_path full_old_path = {0}, full_new_path = {0}; 146 | ntfs_inode *ni = NULL, *dir_ni = NULL; 147 | ntfschar *uname = NULL; 148 | int ret = -1, uname_len = 0; 149 | 150 | /* Safety check. */ 151 | if (!vd || !vd->vol || !old_path || !*old_path || !new_path || !*new_path) 152 | { 153 | errno = EINVAL; 154 | goto end; 155 | } 156 | 157 | /* Split entry paths. */ 158 | ntfs_split_path(old_path, &full_old_path); 159 | ntfs_split_path(new_path, &full_new_path); 160 | 161 | /* Make sure we have valid old and new entry names to work with. */ 162 | if (full_old_path.name[0] == '\0' || full_new_path.name[0] == '\0') 163 | { 164 | errno = EINVAL; 165 | goto end; 166 | } 167 | 168 | /* Open the entry we will create a symlink for. */ 169 | ni = ntfs_inode_open_from_path(vd, full_old_path.path); 170 | if (!ni) goto end; 171 | 172 | /* Open new parent directory. */ 173 | dir_ni = ntfs_inode_open_from_path(vd, full_new_path.dir); 174 | if (!dir_ni) goto end; 175 | 176 | /* Convert the entry name string from our current locale (UTF-8) into UTF-16LE. */ 177 | uname_len = ntfs_mbstoucs(full_new_path.name, &uname); 178 | if (uname_len <= 0) 179 | { 180 | errno = EINVAL; 181 | goto end; 182 | } 183 | 184 | /* Link the entry to its new parent directory. */ 185 | USBHSFS_LOG_MSG("Linking inode \"%s\" to \"%s\".", full_old_path.path, full_new_path.path); 186 | ret = ntfs_link(ni, dir_ni, uname, uname_len); 187 | 188 | end: 189 | if (uname) free(uname); 190 | 191 | if (dir_ni) ntfs_inode_close(dir_ni); 192 | 193 | if (ni) ntfs_inode_close(ni); 194 | 195 | return ret; 196 | } 197 | 198 | int ntfs_inode_unlink(ntfs_vd *vd, const char *path) 199 | { 200 | ntfs_path full_path = {0}; 201 | ntfs_inode *ni = NULL, *dir_ni = NULL; 202 | ntfschar *uname = NULL; 203 | int ret = -1, uname_len = 0; 204 | 205 | /* Safety check. */ 206 | if (!vd || !vd->vol || !path || !*path) 207 | { 208 | errno = EINVAL; 209 | goto end; 210 | } 211 | 212 | /* Split entry path. */ 213 | ntfs_split_path(path, &full_path); 214 | 215 | /* Make sure we have a valid entry name to work with. */ 216 | if (full_path.name[0] == '\0') 217 | { 218 | errno = EINVAL; 219 | goto end; 220 | } 221 | 222 | /* Open entry. */ 223 | ni = ntfs_inode_open_from_path(vd, full_path.path); 224 | if (!ni) goto end; 225 | 226 | /* Open parent directory. */ 227 | dir_ni = ntfs_inode_open_from_path(vd, full_path.dir); 228 | if (!dir_ni) goto end; 229 | 230 | /* Convert the entry name string from our current locale (UTF-8) into UTF-16LE. */ 231 | uname_len = ntfs_mbstoucs(full_path.name, &uname); 232 | if (uname_len <= 0) 233 | { 234 | errno = EINVAL; 235 | goto end; 236 | } 237 | 238 | USBHSFS_LOG_MSG("Unlinking inode \"%s\" from \"%s\".", full_path.name, full_path.dir); 239 | 240 | /* Unlink entry from its parent. */ 241 | /* 'ni' and 'dir_ni' are always closed by ntfs_delete(), even if it fails. */ 242 | ret = ntfs_delete(vd->vol, full_path.path, ni, dir_ni, uname, uname_len); 243 | 244 | ni = NULL; 245 | dir_ni = NULL; 246 | 247 | end: 248 | if (uname) free(uname); 249 | 250 | if (dir_ni) ntfs_inode_close(dir_ni); 251 | 252 | if (ni) ntfs_inode_close(ni); 253 | 254 | return ret; 255 | } 256 | 257 | void ntfs_inode_update_times_filtered(ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask) 258 | { 259 | if (!vd || !ni) return; 260 | 261 | /* Run the access time update strategy against the volume settings first. */ 262 | if (!vd->update_access_times) mask &= ~NTFS_UPDATE_ATIME; 263 | 264 | /* Update entry times. */ 265 | if (mask) 266 | { 267 | USBHSFS_LOG_MSG("Updating access times for inode %lu (mask 0x%X).", ni->mft_no, mask); 268 | ntfs_inode_update_times(ni, mask); 269 | } 270 | } 271 | 272 | static ntfs_inode *ntfs_inode_open_from_path_reparse(ntfs_vd *vd, const char *path, int reparse_depth) 273 | { 274 | ntfs_inode *ni = NULL; 275 | char *target = NULL; 276 | 277 | /* Safety check. */ 278 | if (!vd || !vd->vol || !path || !*path || reparse_depth <= 0 || reparse_depth > NTFS_MAX_SYMLINK_DEPTH) 279 | { 280 | errno = EINVAL; 281 | goto end; 282 | } 283 | 284 | USBHSFS_LOG_MSG("Opening requested inode \"%s\" (reparse depth %d).", path, reparse_depth); 285 | 286 | /* Open requested inode. */ 287 | ni = ntfs_pathname_to_inode(vd->vol, NULL, path); 288 | if (!ni) 289 | { 290 | USBHSFS_LOG_MSG("Failed to open requested inode \"%s\" (errno %d).", path, errno); 291 | goto end; 292 | } 293 | 294 | USBHSFS_LOG_MSG("Successfully opened inode from path \"%s\" (mft_no %lu).", path, ni->mft_no); 295 | 296 | /* If the entry was found and it has reparse data, then resolve the true entry. */ 297 | /* This effectively follows directory junctions and symbolic links until the target entry is found. */ 298 | if ((ni->flags & FILE_ATTR_REPARSE_POINT) && ntfs_possible_symlink(ni)) 299 | { 300 | /* Get the target path of this entry. */ 301 | target = ntfs_make_symlink(ni, path); 302 | if (!target) goto end; 303 | 304 | /* Close this entry (we are no longer interested in it). */ 305 | ntfs_inode_close(ni); 306 | 307 | /* Open the target entry. */ 308 | USBHSFS_LOG_MSG("Following inode symlink \"%s\" -> \"%s\".", path, target); 309 | ni = ntfs_inode_open_from_path_reparse(vd, target, ++reparse_depth); 310 | 311 | /* Clean up. */ 312 | free(target); 313 | } 314 | 315 | end: 316 | return ni; 317 | } 318 | 319 | /* This function doesn't perform checks on the provided path because it is guaranteed to be valid. */ 320 | /* Check ntfsdev_fixpath(). */ 321 | static void ntfs_split_path(const char *path, ntfs_path *p) 322 | { 323 | USBHSFS_LOG_MSG("Input path: \"%s\".", path); 324 | 325 | /* Setup NTFS path. */ 326 | memset(p, 0, sizeof(ntfs_path)); 327 | p->path = path; 328 | 329 | /* Copy the path to internal buffer so we can modify it. */ 330 | strcpy(p->buf, p->path); 331 | 332 | /* Split the path into separate directory and filename parts. */ 333 | /* e.g. "/dir/file.txt" => dir: "/dir", name: "file.txt". */ 334 | char *buf_sep = strrchr(p->buf, PATH_SEP); 335 | 336 | /* If there's just a single path separator at the start of the string, set the directory string to a path separator. */ 337 | /* Otherwise, just use the directory string as-is. */ 338 | p->dir = (buf_sep == p->buf ? "/" : p->buf); 339 | 340 | /* Remove the path separator we found and update the entry name pointer. */ 341 | *buf_sep = '\0'; 342 | p->name = (buf_sep + 1); 343 | 344 | USBHSFS_LOG_MSG("Output strings -> Path: \"%s\" | Directory: \"%s\" | Name: \"%s\".", p->path, p->dir, p->name); 345 | } 346 | -------------------------------------------------------------------------------- /source/ntfs-3g/ntfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ntfs.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Rhys Koedijk. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | * 9 | * Based on work from libntfs-wii (https://github.com/rhyskoedijk/libntfs-wii). 10 | */ 11 | 12 | #pragma once 13 | 14 | #ifndef __NTFS_H__ 15 | #define __NTFS_H__ 16 | 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | 29 | #include "../usbhsfs_utils.h" 30 | 31 | #include "ntfs_disk_io.h" 32 | 33 | /// NTFS errno values. 34 | #define ENOPART 3000 /* No partition was found. */ 35 | #define EINVALPART 3001 /* Specified partition is invalid or not supported. */ 36 | #define EDIRTY 3002 /* Volume is dirty and NTFS_RECOVER was not specified during mount. */ 37 | #define EHIBERNATED 3003 /* Volume is hibernated and NTFS_IGNORE_HIBERFILE was not specified during mount. */ 38 | 39 | #define NTFS_MAX_SYMLINK_DEPTH 10 /* Maximum search depth when resolving symbolic links. */ 40 | 41 | /// NTFS volume descriptor. 42 | typedef struct _ntfs_vd { 43 | struct _ntfs_dd *dd; ///< NTFS device descriptor. 44 | struct ntfs_device *dev; ///< NTFS device handle. 45 | ntfs_volume *vol; ///< NTFS volume handle. 46 | u32 flags; ///< NTFS mount flags. 47 | s64 id; ///< Filesystem ID. 48 | u16 uid; ///< User ID for entry creation. 49 | u16 gid; ///< Group ID for entry creation. 50 | u16 fmask; ///< Unix style permission mask for file creation. 51 | u16 dmask; ///< Unix style permission mask for directory creation. 52 | bool update_access_times; ///< True if file/directory access times should be updated during I/O operations. 53 | bool ignore_read_only_attr; ///< True if read-only file attributes should be ignored (allows writing to read-only files). 54 | } ntfs_vd; 55 | 56 | #ifdef DEBUG 57 | int ntfs_log_handler_usbhsfs(const char *function, const char *file, int line, u32 level, void *data, const char *format, va_list args); 58 | #endif 59 | 60 | ntfs_inode *ntfs_inode_open_from_path(ntfs_vd *vd, const char *path); 61 | 62 | ntfs_inode *ntfs_inode_create(ntfs_vd *vd, const char *path, mode_t type, const char *target); 63 | 64 | int ntfs_inode_link(ntfs_vd *vd, const char *old_path, const char *new_path); 65 | int ntfs_inode_unlink(ntfs_vd *vd, const char *path); 66 | 67 | void ntfs_inode_update_times_filtered(ntfs_vd *vd, ntfs_inode *ni, ntfs_time_update_flags mask); 68 | 69 | #endif /* __NTFS_H__ */ 70 | -------------------------------------------------------------------------------- /source/ntfs-3g/ntfs_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ntfs_dev.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Rhys Koedijk. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | * 9 | * Based on work from libntfs-wii (https://github.com/rhyskoedijk/libntfs-wii). 10 | */ 11 | 12 | #pragma once 13 | 14 | #ifndef __NTFS_DEV_H__ 15 | #define __NTFS_DEV_H__ 16 | 17 | const devoptab_t *ntfsdev_get_devoptab(); 18 | 19 | #endif /* __NTFS_DEV_H__ */ 20 | -------------------------------------------------------------------------------- /source/ntfs-3g/ntfs_disk_io.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ntfs_disk_io.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Rhys Koedijk. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | * 9 | * Based on work from libntfs-wii (https://github.com/rhyskoedijk/libntfs-wii). 10 | */ 11 | 12 | #pragma once 13 | 14 | #ifndef __NTFS_DISK_IO_H__ 15 | #define __NTFS_DISK_IO_H__ 16 | 17 | /// NTFS device descriptor. 18 | typedef struct _ntfs_dd { 19 | void *lun_ctx; ///< Logical unit context. 20 | NTFS_BOOT_SECTOR vbr; ///< Volume Boot Record (VBR) data. This is the first sector of the filesystem. 21 | u64 sector_start; ///< LBA of partition start. 22 | u64 sector_offset; ///< LBA offset to true partition start (as described by boot sector). 23 | u16 sector_size; ///< Device sector size (in bytes). 24 | u64 sector_count; ///< Total number of sectors in partition. 25 | u64 pos; ///< Current position within the partition (in bytes). 26 | u64 len; ///< Total length of partition (in bytes). 27 | ino_t ino; ///< Device identifier (serial number). 28 | } ntfs_dd; 29 | 30 | /// Returns a pointer to the generic ntfs_device_operations object. 31 | struct ntfs_device_operations *ntfs_disk_io_get_dops(void); 32 | 33 | #endif /* __NTFS_DISK_IO_H__ */ 34 | -------------------------------------------------------------------------------- /source/sxos/service_guard.h: -------------------------------------------------------------------------------- 1 | /* 2 | * service_guard.h 3 | * 4 | * Copyright (c) 2018-2021, Switchbrew and libnx contributors. 5 | * Copyright (c) 2020-2023, DarkMatterCore . 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifndef __SERVICE_GUARD_H__ 13 | #define __SERVICE_GUARD_H__ 14 | 15 | typedef struct ServiceGuard { 16 | Mutex mutex; 17 | u32 refCount; 18 | } ServiceGuard; 19 | 20 | NX_INLINE bool serviceGuardBeginInit(ServiceGuard* g) 21 | { 22 | mutexLock(&g->mutex); 23 | return (g->refCount++) == 0; 24 | } 25 | 26 | NX_INLINE Result serviceGuardEndInit(ServiceGuard* g, Result rc, void (*cleanupFunc)(void)) 27 | { 28 | if (R_FAILED(rc)) { 29 | cleanupFunc(); 30 | --g->refCount; 31 | } 32 | mutexUnlock(&g->mutex); 33 | return rc; 34 | } 35 | 36 | NX_INLINE void serviceGuardExit(ServiceGuard* g, void (*cleanupFunc)(void)) 37 | { 38 | mutexLock(&g->mutex); 39 | if (g->refCount && (--g->refCount) == 0) 40 | cleanupFunc(); 41 | mutexUnlock(&g->mutex); 42 | } 43 | 44 | #define NX_GENERATE_SERVICE_GUARD_PARAMS(name, _paramdecl, _parampass) \ 45 | \ 46 | static ServiceGuard g_##name##Guard = {0}; \ 47 | NX_INLINE Result _##name##Initialize _paramdecl; \ 48 | static void _##name##Cleanup(void); \ 49 | \ 50 | Result name##Initialize _paramdecl \ 51 | { \ 52 | Result rc = 0; \ 53 | if (serviceGuardBeginInit(&g_##name##Guard)) \ 54 | rc = _##name##Initialize _parampass; \ 55 | return serviceGuardEndInit(&g_##name##Guard, rc, _##name##Cleanup); \ 56 | } \ 57 | \ 58 | void name##Exit(void) \ 59 | { \ 60 | serviceGuardExit(&g_##name##Guard, _##name##Cleanup); \ 61 | } 62 | 63 | #define NX_GENERATE_SERVICE_GUARD(name) NX_GENERATE_SERVICE_GUARD_PARAMS(name, (void), ()) 64 | 65 | #endif /* __SERVICE_GUARD_H__ */ 66 | -------------------------------------------------------------------------------- /source/sxos/usbfs.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Blake Warner. 6 | * Copyright (c) 2018, Team Xecuter. 7 | * 8 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #include 30 | 31 | #include "usbfs.h" 32 | #include "service_guard.h" 33 | 34 | enum UsbFsServiceCmd { 35 | UsbFs_Cmd_GetMountStatus = 0, 36 | UsbFs_Cmd_OpenFile = 1, 37 | UsbFs_Cmd_CloseFile = 2, 38 | UsbFs_Cmd_ReadFile = 3, 39 | UsbFs_Cmd_IsReady = 4, 40 | UsbFs_Cmd_OpenDir = 5, 41 | UsbFs_Cmd_CloseDir = 6, 42 | UsbFs_Cmd_ReadDir = 7, 43 | UsbFs_Cmd_CreateDir = 8, 44 | UsbFs_Cmd_SeekFile = 9, 45 | UsbFs_Cmd_ReadRaw = 10, 46 | UsbFs_Cmd_WriteFile = 11, 47 | UsbFs_Cmd_SyncFile = 12, 48 | UsbFs_Cmd_DeleteDir = 13, 49 | UsbFs_Cmd_DeleteFile = 14, 50 | UsbFs_Cmd_TruncateFile = 15, 51 | UsbFs_Cmd_StatFile = 16, 52 | UsbFs_Cmd_StatPath = 17, 53 | UsbFs_Cmd_StatFilesystem = 18, 54 | }; 55 | 56 | static Service g_usbFsSrv = {0}; 57 | 58 | NX_GENERATE_SERVICE_GUARD(usbFs); 59 | 60 | Result usbFsGetMountStatus(u64 *status) 61 | { 62 | return serviceDispatchOut(&g_usbFsSrv, UsbFs_Cmd_GetMountStatus, *status); 63 | } 64 | 65 | Result usbFsOpenFile(u64 *fileid, const char *filepath, u64 mode) 66 | { 67 | return serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_OpenFile, mode, *fileid, \ 68 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 69 | .buffers = { { filepath, strlen(filepath) + 1 } }); 70 | } 71 | 72 | Result usbFsCloseFile(u64 fileid) 73 | { 74 | return serviceDispatchIn(&g_usbFsSrv, UsbFs_Cmd_CloseFile, fileid); 75 | } 76 | 77 | Result usbFsReadFile(u64 fileid, void *buffer, size_t size, size_t *retsize) 78 | { 79 | return serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_ReadFile, fileid, *retsize, \ 80 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, \ 81 | .buffers = { { buffer, size } }); 82 | } 83 | 84 | Result usbFsIsReady() 85 | { 86 | return serviceDispatch(&g_usbFsSrv, UsbFs_Cmd_IsReady); 87 | } 88 | 89 | Result usbFsOpenDir(u64 *dirid, const char *dirpath) 90 | { 91 | return serviceDispatchOut(&g_usbFsSrv, UsbFs_Cmd_OpenDir, *dirid, \ 92 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 93 | .buffers = { { dirpath, strlen(dirpath) + 1 } }); 94 | } 95 | 96 | Result usbFsCloseDir(u64 dirid) 97 | { 98 | return serviceDispatchIn(&g_usbFsSrv, UsbFs_Cmd_CloseDir, dirid); 99 | } 100 | 101 | Result usbFsReadDir(u64 dirid, u64 *type, u64 *size, char *name, size_t namemax) 102 | { 103 | Result rc = 0; 104 | 105 | struct { 106 | u64 type; 107 | u64 size; 108 | } out; 109 | 110 | rc = serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_ReadDir, dirid, out, \ 111 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, \ 112 | .buffers = { { name, namemax } }); 113 | 114 | if (R_SUCCEEDED(rc)) 115 | { 116 | *type = out.type; 117 | *size = out.size; 118 | } 119 | 120 | return rc; 121 | } 122 | 123 | Result usbFsCreateDir(const char *dirpath) 124 | { 125 | return serviceDispatch(&g_usbFsSrv, UsbFs_Cmd_CreateDir, \ 126 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 127 | .buffers = { { dirpath, strlen(dirpath) + 1 } }); 128 | } 129 | 130 | Result usbFsSeekFile(u64 fileid, u64 pos, u64 whence, u64 *retpos) 131 | { 132 | struct { 133 | u64 fileid; 134 | u64 pos; 135 | u64 whence; 136 | } in = { fileid, pos, whence }; 137 | 138 | return serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_SeekFile, in, *retpos); 139 | } 140 | 141 | Result usbFsReadRaw(u64 sector, u64 sectorcount, void *buffer) 142 | { 143 | struct { 144 | u64 sector; 145 | u64 sectorcount; 146 | } in = { sector, sectorcount }; 147 | 148 | return serviceDispatchIn(&g_usbFsSrv, UsbFs_Cmd_ReadRaw, in, \ 149 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_Out }, \ 150 | .buffers = { { buffer, 0x200ULL * sectorcount } }); 151 | } 152 | 153 | Result usbFsWriteFile(u64 fileid, const void *buffer, size_t size, size_t *retsize) 154 | { 155 | return serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_WriteFile, fileid, *retsize, \ 156 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 157 | .buffers = { { buffer, size } }); 158 | } 159 | 160 | Result usbFsSyncFile(u64 fileid) 161 | { 162 | return serviceDispatchIn(&g_usbFsSrv, UsbFs_Cmd_SyncFile, fileid); 163 | } 164 | 165 | Result usbFsDeleteDir(const char *dirpath) 166 | { 167 | return serviceDispatch(&g_usbFsSrv, UsbFs_Cmd_DeleteDir, \ 168 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 169 | .buffers = { { dirpath, strlen(dirpath) + 1 } }); 170 | } 171 | 172 | Result usbFsDeleteFile(const char *filepath) 173 | { 174 | return serviceDispatch(&g_usbFsSrv, UsbFs_Cmd_DeleteFile, \ 175 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 176 | .buffers = { { filepath, strlen(filepath) + 1 } }); 177 | } 178 | 179 | Result usbFsTruncateFile(u64 fileid, u64 size) 180 | { 181 | struct { 182 | u64 fileid; 183 | u64 size; 184 | } in = { fileid, size }; 185 | 186 | return serviceDispatchIn(&g_usbFsSrv, UsbFs_Cmd_TruncateFile, in); 187 | } 188 | 189 | Result usbFsStatFile(u64 fileid, u64 *size, u64 *mode) 190 | { 191 | Result rc = 0; 192 | 193 | struct { 194 | u64 size; 195 | u64 mode; 196 | } out; 197 | 198 | rc = serviceDispatchInOut(&g_usbFsSrv, UsbFs_Cmd_StatFile, fileid, out); 199 | if (R_SUCCEEDED(rc)) 200 | { 201 | *size = out.size; 202 | *mode = out.mode; 203 | } 204 | 205 | return rc; 206 | } 207 | 208 | Result usbFsStatPath(const char *path, u64 *size, u64 *mode) 209 | { 210 | Result rc = 0; 211 | 212 | struct { 213 | u64 size; 214 | u64 mode; 215 | } out; 216 | 217 | rc = serviceDispatchOut(&g_usbFsSrv, UsbFs_Cmd_StatPath, out, \ 218 | .buffer_attrs = { SfBufferAttr_HipcMapAlias | SfBufferAttr_In }, \ 219 | .buffers = { { path, strlen(path) + 1 } }); 220 | 221 | if (R_SUCCEEDED(rc)) 222 | { 223 | *size = out.size; 224 | *mode = out.mode; 225 | } 226 | 227 | return rc; 228 | } 229 | 230 | Result usbFsStatFilesystem(u64 *totalsize, u64 *freesize) 231 | { 232 | Result rc = 0; 233 | 234 | struct { 235 | u64 totalsize; 236 | u64 freesize; 237 | } out; 238 | 239 | rc = serviceDispatchOut(&g_usbFsSrv, UsbFs_Cmd_StatFilesystem, out); 240 | if (R_SUCCEEDED(rc)) 241 | { 242 | *totalsize = out.totalsize; 243 | *freesize = out.freesize; 244 | } 245 | 246 | return rc; 247 | } 248 | 249 | NX_INLINE Result _usbFsInitialize(void) 250 | { 251 | return smGetService(&g_usbFsSrv, "usbfs"); 252 | } 253 | 254 | static void _usbFsCleanup(void) 255 | { 256 | serviceClose(&g_usbFsSrv); 257 | } 258 | -------------------------------------------------------------------------------- /source/sxos/usbfs.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, Blake Warner. 6 | * Copyright (c) 2018, Team Xecuter. 7 | * 8 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 9 | * 10 | * Permission is hereby granted, free of charge, to any person obtaining a copy 11 | * of this software and associated documentation files (the "Software"), to deal 12 | * in the Software without restriction, including without limitation the rights 13 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 14 | * copies of the Software, and to permit persons to whom the Software is 15 | * furnished to do so, subject to the following conditions: 16 | * 17 | * The above copyright notice and this permission notice shall be included in all 18 | * copies or substantial portions of the Software. 19 | * 20 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 21 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 22 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 23 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 24 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 25 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 26 | * SOFTWARE. 27 | */ 28 | 29 | #pragma once 30 | 31 | #ifndef __USBFS_H__ 32 | #define __USBFS_H__ 33 | 34 | #include 35 | 36 | #define USBFS_MOUNT_NAME "usbhdd" 37 | 38 | #define USBFS_UNMOUNTED 0 39 | #define USBFS_MOUNTED 1 40 | #define USBFS_UNSUPPORTED_FS 2 41 | 42 | Result usbFsInitialize(void); 43 | void usbFsExit(void); 44 | 45 | Result usbFsGetMountStatus(u64 *status); 46 | Result usbFsOpenFile(u64 *fileid, const char *filepath, u64 mode); 47 | Result usbFsCloseFile(u64 fileid); 48 | Result usbFsReadFile(u64 fileid, void *buffer, size_t size, size_t *retsize); 49 | Result usbFsIsReady(); 50 | Result usbFsOpenDir(u64 *dirid, const char *dirpath); 51 | Result usbFsCloseDir(u64 dirid); 52 | Result usbFsReadDir(u64 dirid, u64 *type, u64 *size, char *name, size_t namemax); 53 | Result usbFsCreateDir(const char *dirpath); 54 | Result usbFsSeekFile(u64 fileid, u64 pos, u64 whence, u64 *retpos); 55 | Result usbFsReadRaw(u64 sector, u64 sectorcount, void *buffer); 56 | Result usbFsWriteFile(u64 fileid, const void *buffer, size_t size, size_t *retsize); 57 | Result usbFsSyncFile(u64 fileid); 58 | Result usbFsDeleteDir(const char *dirpath); 59 | Result usbFsDeleteFile(const char *filepath); 60 | Result usbFsTruncateFile(u64 fileid, u64 size); 61 | Result usbFsStatFile(u64 fileid, u64 *size, u64 *mode); 62 | Result usbFsStatPath(const char *path, u64 *size, u64 *mode); 63 | Result usbFsStatFilesystem(u64 *totalsize, u64 *freesize); 64 | 65 | #endif /* __USBFS_H__ */ 66 | -------------------------------------------------------------------------------- /source/sxos/usbfs_dev.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbfs_dev.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2018, Team Xecuter. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | * 9 | * Permission is hereby granted, free of charge, to any person obtaining a copy 10 | * of this software and associated documentation files (the "Software"), to deal 11 | * in the Software without restriction, including without limitation the rights 12 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 13 | * copies of the Software, and to permit persons to whom the Software is 14 | * furnished to do so, subject to the following conditions: 15 | * 16 | * The above copyright notice and this permission notice shall be included in all 17 | * copies or substantial portions of the Software. 18 | * 19 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 20 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 21 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 22 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 23 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 24 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 25 | * SOFTWARE. 26 | */ 27 | 28 | #ifndef __USBFS_DEV_H__ 29 | #define __USBFS_DEV_H__ 30 | 31 | #include "usbfs.h" 32 | 33 | /// Register "usbhdd" devoptab device. 34 | bool usbfsdev_register(void); 35 | 36 | /// Unregister "usbhdd" devoptab device. 37 | void usbfsdev_unregister(void); 38 | 39 | #endif /* __USBFS_DEV_H__ */ 40 | -------------------------------------------------------------------------------- /source/usb_common.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usb_common.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __USB_COMMON_H__ 12 | #define __USB_COMMON_H__ 13 | 14 | #define USB_SUBCLASS_SCSI_TRANSPARENT_CMD_SET 0x06 15 | 16 | #define USB_PROTOCOL_BULK_ONLY_TRANSPORT 0x50 17 | #define USB_PROTOCOL_USB_ATTACHED_SCSI 0x62 18 | 19 | #define USB_XFER_BUF_ALIGNMENT 0x1000 /* 4 KiB. */ 20 | #define USB_XFER_BUF_SIZE 0x800000 /* 8 MiB. */ 21 | 22 | #define USB_FEATURE_ENDPOINT_HALT 0x00 23 | 24 | #define USB_POSTBUFFER_TIMEOUT (u64)10000000000 /* 10 seconds. Expressed in nanoseconds. */ 25 | 26 | #define USB_DT_PIPE_USAGE 0x24 27 | 28 | #define USB_DT_STRING_MAXLEN 0x7E 29 | 30 | #define USB_LANGID_ENUS 0x0409 31 | 32 | #define UMS_MAX_LUN 16 /* Max returned value is actually a zero-based index to the highest LUN. */ 33 | 34 | #define MOUNT_NAME_LENGTH 32 35 | #define MAX_PATH_LENGTH (FS_MAX_PATH + 1) 36 | 37 | #define BLKDEV_MIN_BLOCK_SIZE 512 38 | #define BLKDEV_MAX_BLOCK_SIZE 4096 39 | 40 | /// Structs imported from libusb, with some adjustments. 41 | 42 | struct _usb_string_descriptor { 43 | u8 bLength; 44 | u8 bDescriptorType; ///< Must match USB_DT_STRING. 45 | u16 wData[USB_DT_STRING_MAXLEN]; 46 | }; 47 | 48 | enum usb_pipe_usage_id { 49 | USB_PIPE_USAGE_ID_CMD = 0x01, ///< Command pipe. 50 | USB_PIPE_USAGE_ID_STS = 0x02, ///< Status pipe. 51 | USB_PIPE_USAGE_ID_DATA_IN = 0x03, ///< Data In pipe. 52 | USB_PIPE_USAGE_ID_DATA_OUT = 0x04 ///< Data Out pipe. 53 | }; 54 | 55 | struct usb_pipe_usage_descriptor { 56 | u8 bLength; 57 | u8 bDescriptorType; ///< Must match USB_DT_PIPE_USAGE. 58 | u8 bPipeID; ///< usb_pipe_usage_id. 59 | u8 Reserved; 60 | }; 61 | 62 | enum usb_request_type { 63 | USB_REQUEST_TYPE_STANDARD = (0x00 << 5), 64 | USB_REQUEST_TYPE_CLASS = (0x01 << 5), 65 | USB_REQUEST_TYPE_VENDOR = (0x02 << 5), 66 | USB_REQUEST_TYPE_RESERVED = (0x03 << 5), 67 | }; 68 | 69 | enum usb_request_recipient { 70 | USB_RECIPIENT_DEVICE = 0x00, 71 | USB_RECIPIENT_INTERFACE = 0x01, 72 | USB_RECIPIENT_ENDPOINT = 0x02, 73 | USB_RECIPIENT_OTHER = 0x03, 74 | }; 75 | 76 | enum usb_request_bot { 77 | USB_REQUEST_BOT_GET_MAX_LUN = 0xFE, 78 | USB_REQUEST_BOT_RESET = 0xFF 79 | }; 80 | 81 | #endif /* __USB_COMMON_H__ */ 82 | -------------------------------------------------------------------------------- /source/usbhsfs_drive.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_drive.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * Copyright (c) 2020-2021, Rhys Koedijk. 7 | * 8 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 9 | */ 10 | 11 | #pragma once 12 | 13 | #ifndef __USBHSFS_DRIVE_H__ 14 | #define __USBHSFS_DRIVE_H__ 15 | 16 | #include 17 | #include "fatfs/ff.h" 18 | 19 | #ifdef GPL_BUILD 20 | #include "ntfs-3g/ntfs.h" 21 | #include "lwext4/ext.h" 22 | #endif 23 | 24 | /// Used by filesystem contexts to determine which FS object to use. 25 | typedef enum { 26 | UsbHsFsDriveLogicalUnitFileSystemType_Invalid = 0, ///< Invalid boot signature. 27 | UsbHsFsDriveLogicalUnitFileSystemType_Unsupported = 1, ///< Valid boot signature, unsupported FS. 28 | UsbHsFsDriveLogicalUnitFileSystemType_FAT = 2, ///< FAT filesystem (FAT12, FAT16, FAT32, exFAT). 29 | UsbHsFsDriveLogicalUnitFileSystemType_NTFS = 3, ///< NTFS filesystem. 30 | UsbHsFsDriveLogicalUnitFileSystemType_EXT = 4 ///< EXT* filesystem (EXT2, EXT3, EXT4). 31 | } UsbHsFsDriveLogicalUnitFileSystemType; 32 | 33 | /// Used to handle filesystems from LUNs. 34 | typedef struct { 35 | void *lun_ctx; ///< Pointer to the LUN context this filesystem belongs to. 36 | u32 fs_idx; ///< Filesystem index within the fs_ctx array from the LUN context. 37 | u8 fs_type; ///< UsbHsFsDriveLogicalUnitFileSystemType. 38 | u32 flags; ///< UsbHsFsMountFlags bitmask used at mount time. 39 | FATFS *fatfs; ///< Pointer to a dynamically allocated FatFs object. Only used if fs_type == UsbHsFsFileSystemType_FAT. 40 | #ifdef GPL_BUILD 41 | ntfs_vd *ntfs; ///< Pointer to a dynamically allocated ntfs_vd object. Only used if fs_type == UsbHsFsFileSystemType_NTFS. 42 | ext_vd *ext; ///< Pointer to a dynamically allocated ext_vd object. Only used if fs_type == UsbHsFsFileSystemType_EXT. 43 | #endif 44 | 45 | /// TODO: add more FS objects here after implementing support for other filesystems. 46 | 47 | u32 device_id; ///< ID used as part of the mount name. 48 | char *name; ///< Pointer to the dynamically allocated mount name string, without a trailing colon (:). 49 | char *cwd; ///< Pointer to the dynamically allocated current working directory string. 50 | devoptab_t *device; ///< Pointer to the dynamically allocated devoptab virtual device interface. Used to provide a way to use libcstd I/O calls on the mounted filesystem. 51 | } UsbHsFsDriveLogicalUnitFileSystemContext; 52 | 53 | /// Used to handle LUNs from drives. 54 | typedef struct { 55 | void *drive_ctx; ///< Pointer to the drive context this LUN belongs to. 56 | s32 usb_if_id; ///< USB interface ID. Placed here for convenience. 57 | bool uasp; ///< Set to true if USB Attached SCSI Protocol is being used with this drive. Placed here for convenience. 58 | u8 lun; ///< Drive LUN index (zero-based, up to 15). Used to send SCSI commands. 59 | bool removable; ///< Set to true if this LUN is removable. Retrieved via SCSI Inquiry command. 60 | bool eject_supported; ///< Set to true if ejection via Prevent/Allow Medium Removal + Start Stop Unit is supported. 61 | bool write_protect; ///< Set to true if the Write Protect bit is set. 62 | bool fua_supported; ///< Set to true if the Force Unit Access feature is supported. 63 | char vendor_id[0x9]; ///< Vendor identification string. Retrieved via SCSI Inquiry command. May be empty. 64 | char product_id[0x11]; ///< Product identification string. Retrieved via SCSI Inquiry command. May be empty. 65 | char serial_number[0x40]; ///< Serial number string. Retrieved via SCSI Inquiry command. May be empty. 66 | bool long_lba; ///< Set to true if Read Capacity (16) was used to retrieve the LUN capacity. 67 | u64 block_count; ///< Logical block count. Retrieved via SCSI Read Capacity command. Must be non-zero. 68 | u32 block_length; ///< Logical block length (bytes). Retrieved via SCSI Read Capacity command. Must be non-zero. 69 | u64 capacity; ///< LUN capacity (block count times block length). 70 | u32 fs_count; ///< Number of mounted filesystems stored in this LUN. 71 | UsbHsFsDriveLogicalUnitFileSystemContext **fs_ctx; ///< Dynamically allocated pointer array of fs_count filesystem contexts. 72 | } UsbHsFsDriveLogicalUnitContext; 73 | 74 | /// Used to handle drives. 75 | typedef struct { 76 | Mutex mutex; ///< Drive mutex. 77 | u8 *xfer_buf; ///< Dedicated transfer buffer for this drive. 78 | s32 usb_if_id; ///< USB interface ID. Exactly the same as usb_if_session.ID / usb_if_session.inf.inf.ID. Placed here for convenience. 79 | bool uasp; ///< Set to true if USB Attached SCSI Protocol is being used with this drive. 80 | UsbHsClientIfSession usb_if_session; ///< Interface session. 81 | UsbHsClientEpSession usb_in_ep_session[2]; ///< Input endpoint sessions (device to host). BOT: 0 = Data In & Status, 1 = Unused. UASP: 0 = Status, 1 = Data In. 82 | UsbHsClientEpSession usb_out_ep_session[2]; ///< Output endpoint sessions (host to device). BOT: 0 = Command & Data Out, 1 = Unused. UASP: 0 = Command, 1 = Data Out. 83 | u16 vid; ///< Vendor ID. Retrieved from the device descriptor. Placed here for convenience. 84 | u16 pid; ///< Product ID. Retrieved from the device descriptor. Placed here for convenience. 85 | char *manufacturer; ///< Dynamically allocated, UTF-8 encoded manufacturer string. May be NULL if not provided by the USB device descriptor. 86 | char *product_name; ///< Dynamically allocated, UTF-8 encoded manufacturer string. May be NULL if not provided by the USB device descriptor. 87 | char *serial_number; ///< Dynamically allocated, UTF-8 encoded manufacturer string. May be NULL if not provided by the USB device descriptor. 88 | u8 max_lun; ///< Max LUNs supported by this drive. Must be at least 1. 89 | u8 lun_count; ///< Initialized LUN count. May differ from the max LUN count. 90 | UsbHsFsDriveLogicalUnitContext **lun_ctx; ///< Dynamically allocated pointer array of lun_count LUN contexts. 91 | } UsbHsFsDriveContext; 92 | 93 | /// None of these functions are thread safe - make sure to (un)lock mutexes elsewhere. 94 | 95 | /// Initializes a drive context using the provided UsbHsInterface object. 96 | bool usbHsFsDriveInitializeContext(UsbHsFsDriveContext *drive_ctx, UsbHsInterface *usb_if); 97 | 98 | /// Destroys the provided drive context. 99 | void usbHsFsDriveDestroyContext(UsbHsFsDriveContext *drive_ctx, bool stop_lun); 100 | 101 | /// Wrapper for usbHsFsRequestClearEndpointHaltFeature() that clears a possible STALL status from all endpoints. 102 | void usbHsFsDriveClearStallStatus(UsbHsFsDriveContext *drive_ctx); 103 | 104 | /// Checks if the provided drive context is valid. 105 | NX_INLINE bool usbHsFsDriveIsValidContext(UsbHsFsDriveContext *drive_ctx) 106 | { 107 | return (drive_ctx && drive_ctx->xfer_buf && usbHsIfIsActive(&(drive_ctx->usb_if_session)) && \ 108 | serviceIsActive(&(drive_ctx->usb_in_ep_session[0].s)) && serviceIsActive(&(drive_ctx->usb_out_ep_session[0].s)) && \ 109 | (!drive_ctx->uasp || (serviceIsActive(&(drive_ctx->usb_in_ep_session[1].s)) && serviceIsActive(&(drive_ctx->usb_out_ep_session[1].s))))); 110 | } 111 | 112 | /// Checks if the provided LUN context is valid. 113 | NX_INLINE bool usbHsFsDriveIsValidLogicalUnitContext(UsbHsFsDriveLogicalUnitContext *lun_ctx) 114 | { 115 | return (lun_ctx && usbHsFsDriveIsValidContext((UsbHsFsDriveContext*)lun_ctx->drive_ctx) && lun_ctx->lun < UMS_MAX_LUN && lun_ctx->block_count && lun_ctx->block_length && lun_ctx->capacity); 116 | } 117 | 118 | /// Checks if the provided filesystem context is valid. 119 | /// TODO: update this after adding support for more filesystems. 120 | NX_INLINE bool usbHsFsDriveIsValidLogicalUnitFileSystemContext(UsbHsFsDriveLogicalUnitFileSystemContext *fs_ctx) 121 | { 122 | bool ctx_valid = (fs_ctx && usbHsFsDriveIsValidLogicalUnitContext((UsbHsFsDriveLogicalUnitContext*)fs_ctx->lun_ctx) && fs_ctx->fs_type > UsbHsFsDriveLogicalUnitFileSystemType_Unsupported && \ 123 | fs_ctx->name && fs_ctx->cwd && fs_ctx->device); 124 | bool fs_valid = false; 125 | 126 | if (ctx_valid) 127 | { 128 | switch(fs_ctx->fs_type) 129 | { 130 | case UsbHsFsDriveLogicalUnitFileSystemType_FAT: 131 | fs_valid = (fs_ctx->fatfs != NULL); 132 | break; 133 | #ifdef GPL_BUILD 134 | case UsbHsFsDriveLogicalUnitFileSystemType_NTFS: 135 | fs_valid = (fs_ctx->ntfs != NULL); 136 | break; 137 | case UsbHsFsDriveLogicalUnitFileSystemType_EXT: 138 | fs_valid = (fs_ctx->ext != NULL); 139 | break; 140 | #endif 141 | default: 142 | break; 143 | } 144 | } 145 | 146 | return (ctx_valid && fs_valid); 147 | } 148 | 149 | #endif /* __USBHSFS_DRIVE_H__ */ 150 | -------------------------------------------------------------------------------- /source/usbhsfs_log.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_log.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #ifdef DEBUG 10 | 11 | #include "usbhsfs_utils.h" 12 | 13 | #define LOG_FILE_NAME "/" LIB_TITLE ".log" 14 | #define LOG_BUF_SIZE 0x400000 /* 4 MiB. */ 15 | #define LOG_FORCE_FLUSH 0 /* Forces a log buffer flush each time the logfile is written to. */ 16 | 17 | /* Global variables. */ 18 | 19 | static Mutex g_logMutex = 0; 20 | 21 | static FsFileSystem *g_sdCardFileSystem = NULL; 22 | 23 | static FsFile g_logFile = {0}; 24 | static s64 g_logFileOffset = 0; 25 | 26 | static char *g_logBuffer = NULL; 27 | static size_t g_logBufferLength = 0; 28 | 29 | static const char *g_utf8Bom = "\xEF\xBB\xBF"; 30 | static const char *g_lineBreak = "\r\n"; 31 | 32 | static const char *g_logStrFormat = "[%d-%02d-%02d %02d:%02d:%02d.%09lu] %s:%d:%s -> "; 33 | static const char *g_logSessionSeparator = "________________________________________________________________\r\n"; 34 | 35 | /* Function prototypes. */ 36 | 37 | static void _usbHsFsLogWriteStringToLogFile(const char *src); 38 | static void _usbHsFsLogWriteFormattedStringToLogFile(const char *file_name, int line, const char *func_name, const char *fmt, va_list args); 39 | 40 | static void _usbHsFsLogFlushLogFile(void); 41 | 42 | static bool usbHsFsLogAllocateLogBuffer(void); 43 | static bool usbHsFsLogOpenLogFile(void); 44 | 45 | static void usbHsFsLogGenerateHexStringFromData(char *dst, size_t dst_size, const void *src, size_t src_size); 46 | 47 | void usbHsFsLogWriteStringToLogFile(const char *src) 48 | { 49 | SCOPED_LOCK(&g_logMutex) _usbHsFsLogWriteStringToLogFile(src); 50 | } 51 | 52 | __attribute__((format(printf, 4, 5))) void usbHsFsLogWriteFormattedStringToLogFile(const char *file_name, int line, const char *func_name, const char *fmt, ...) 53 | { 54 | va_list args; 55 | va_start(args, fmt); 56 | SCOPED_LOCK(&g_logMutex) _usbHsFsLogWriteFormattedStringToLogFile(file_name, line, func_name, fmt, args); 57 | va_end(args); 58 | } 59 | 60 | __attribute__((format(printf, 6, 7))) void usbHsFsLogWriteBinaryDataToLogFile(const void *data, size_t data_size, const char *file_name, int line, const char *func_name, const char *fmt, ...) 61 | { 62 | if (!data || !data_size || !func_name || !*func_name || !fmt || !*fmt) return; 63 | 64 | va_list args; 65 | size_t data_str_size = ((data_size * 2) + 3); 66 | char *data_str = NULL; 67 | 68 | /* Allocate memory for the hex string representation of the provided binary data. */ 69 | data_str = calloc(data_str_size, sizeof(char)); 70 | if (!data_str) goto end; 71 | 72 | /* Generate hex string representation. */ 73 | usbHsFsLogGenerateHexStringFromData(data_str, data_str_size, data, data_size); 74 | strcat(data_str, g_lineBreak); 75 | 76 | SCOPED_LOCK(&g_logMutex) 77 | { 78 | /* Write formatted string. */ 79 | va_start(args, fmt); 80 | _usbHsFsLogWriteFormattedStringToLogFile(file_name, line, func_name, fmt, args); 81 | va_end(args); 82 | 83 | /* Write hex string representation. */ 84 | _usbHsFsLogWriteStringToLogFile(data_str); 85 | } 86 | 87 | end: 88 | if (data_str) free(data_str); 89 | } 90 | 91 | void usbHsFsLogFlushLogFile(void) 92 | { 93 | SCOPED_LOCK(&g_logMutex) _usbHsFsLogFlushLogFile(); 94 | } 95 | 96 | void usbHsFsLogCloseLogFile(void) 97 | { 98 | SCOPED_LOCK(&g_logMutex) 99 | { 100 | /* Flush log buffer. */ 101 | _usbHsFsLogFlushLogFile(); 102 | 103 | /* Close logfile. */ 104 | if (serviceIsActive(&(g_logFile.s))) 105 | { 106 | fsFileClose(&g_logFile); 107 | memset(&g_logFile, 0, sizeof(FsFile)); 108 | 109 | /* Commit SD card filesystem changes. */ 110 | if (g_sdCardFileSystem) fsFsCommit(g_sdCardFileSystem); 111 | } 112 | 113 | /* Free log buffer. */ 114 | if (g_logBuffer) 115 | { 116 | free(g_logBuffer); 117 | g_logBuffer = NULL; 118 | } 119 | 120 | /* Reset logfile offset. */ 121 | g_logFileOffset = 0; 122 | } 123 | } 124 | 125 | static void _usbHsFsLogWriteStringToLogFile(const char *src) 126 | { 127 | /* Make sure we have allocated memory for the log buffer and opened the logfile. */ 128 | if (!src || !*src || !usbHsFsLogAllocateLogBuffer() || !usbHsFsLogOpenLogFile()) return; 129 | 130 | Result rc = 0; 131 | size_t src_len = strlen(src), tmp_len = 0; 132 | 133 | /* Check if the formatted string length is lower than the log buffer size. */ 134 | if (src_len < LOG_BUF_SIZE) 135 | { 136 | /* Flush log buffer contents (if needed). */ 137 | if ((g_logBufferLength + src_len) >= LOG_BUF_SIZE) 138 | { 139 | _usbHsFsLogFlushLogFile(); 140 | if (g_logBufferLength) return; 141 | } 142 | 143 | /* Copy string into the log buffer. */ 144 | strcpy(g_logBuffer + g_logBufferLength, src); 145 | g_logBufferLength += src_len; 146 | } else { 147 | /* Flush log buffer. */ 148 | _usbHsFsLogFlushLogFile(); 149 | if (g_logBufferLength) return; 150 | 151 | /* Write string data until it no longer exceeds the log buffer size. */ 152 | while(src_len >= LOG_BUF_SIZE) 153 | { 154 | rc = fsFileWrite(&g_logFile, g_logFileOffset, src + tmp_len, LOG_BUF_SIZE, FsWriteOption_Flush); 155 | if (R_FAILED(rc)) return; 156 | 157 | g_logFileOffset += LOG_BUF_SIZE; 158 | tmp_len += LOG_BUF_SIZE; 159 | src_len -= LOG_BUF_SIZE; 160 | } 161 | 162 | /* Copy any remaining data from the string into the log buffer. */ 163 | if (src_len) 164 | { 165 | strcpy(g_logBuffer, src + tmp_len); 166 | g_logBufferLength = src_len; 167 | } 168 | } 169 | 170 | #if LOG_FORCE_FLUSH == 1 171 | /* Flush log buffer. */ 172 | _usbHsFsLogFlushLogFile(); 173 | #endif 174 | } 175 | 176 | static void _usbHsFsLogWriteFormattedStringToLogFile(const char *file_name, int line, const char *func_name, const char *fmt, va_list args) 177 | { 178 | /* Make sure we have allocated memory for the log buffer and opened the logfile. */ 179 | if (!file_name || !*file_name || !func_name || !*func_name || !fmt || !*fmt || !usbHsFsLogAllocateLogBuffer() || !usbHsFsLogOpenLogFile()) return; 180 | 181 | Result rc = 0; 182 | 183 | int str1_len = 0, str2_len = 0; 184 | size_t log_str_len = 0; 185 | 186 | char *tmp_str = NULL; 187 | size_t tmp_len = 0; 188 | 189 | struct tm ts = {0}; 190 | struct timespec now = {0}; 191 | 192 | /* Get current time with nanosecond precision. */ 193 | clock_gettime(CLOCK_REALTIME, &now); 194 | 195 | /* Get local time. */ 196 | localtime_r(&(now.tv_sec), &ts); 197 | ts.tm_year += 1900; 198 | ts.tm_mon++; 199 | 200 | /* Get formatted string length. */ 201 | str1_len = snprintf(NULL, 0, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, file_name, line, func_name); 202 | if (str1_len <= 0) return; 203 | 204 | str2_len = vsnprintf(NULL, 0, fmt, args); 205 | if (str2_len <= 0) return; 206 | 207 | log_str_len = (size_t)(str1_len + str2_len + 2); 208 | 209 | /* Check if the formatted string length is less than the log buffer size. */ 210 | if (log_str_len < LOG_BUF_SIZE) 211 | { 212 | /* Flush log buffer contents (if needed). */ 213 | if ((g_logBufferLength + log_str_len) >= LOG_BUF_SIZE) 214 | { 215 | _usbHsFsLogFlushLogFile(); 216 | if (g_logBufferLength) return; 217 | } 218 | 219 | /* Nice and easy string formatting using the log buffer. */ 220 | sprintf(g_logBuffer + g_logBufferLength, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, file_name, line, func_name); 221 | vsprintf(g_logBuffer + g_logBufferLength + (size_t)str1_len, fmt, args); 222 | strcat(g_logBuffer, g_lineBreak); 223 | 224 | /* Update log buffer length. */ 225 | g_logBufferLength += log_str_len; 226 | } else { 227 | /* Flush log buffer. */ 228 | _usbHsFsLogFlushLogFile(); 229 | if (g_logBufferLength) return; 230 | 231 | /* Allocate memory for a temporary buffer. This will hold the formatted string. */ 232 | tmp_str = calloc(log_str_len + 1, sizeof(char)); 233 | if (!tmp_str) return; 234 | 235 | /* Generate formatted string. */ 236 | sprintf(tmp_str, g_logStrFormat, ts.tm_year, ts.tm_mon, ts.tm_mday, ts.tm_hour, ts.tm_min, ts.tm_sec, now.tv_nsec, file_name, line, func_name); 237 | vsprintf(tmp_str + (size_t)str1_len, fmt, args); 238 | strcat(tmp_str, g_lineBreak); 239 | 240 | /* Write formatted string data until it no longer exceeds the log buffer size. */ 241 | while(log_str_len >= LOG_BUF_SIZE) 242 | { 243 | rc = fsFileWrite(&g_logFile, g_logFileOffset, tmp_str + tmp_len, LOG_BUF_SIZE, FsWriteOption_Flush); 244 | if (R_FAILED(rc)) goto end; 245 | 246 | g_logFileOffset += LOG_BUF_SIZE; 247 | tmp_len += LOG_BUF_SIZE; 248 | log_str_len -= LOG_BUF_SIZE; 249 | } 250 | 251 | /* Copy any remaining data from the formatted string into the log buffer. */ 252 | if (log_str_len) 253 | { 254 | strcpy(g_logBuffer, tmp_str + tmp_len); 255 | g_logBufferLength = log_str_len; 256 | } 257 | } 258 | 259 | #if LOG_FORCE_FLUSH == 1 260 | /* Flush log buffer. */ 261 | _usbHsFsLogFlushLogFile(); 262 | #endif 263 | 264 | end: 265 | if (tmp_str) free(tmp_str); 266 | } 267 | 268 | static void _usbHsFsLogFlushLogFile(void) 269 | { 270 | if (!serviceIsActive(&(g_logFile.s)) || !g_logBuffer || !g_logBufferLength) return; 271 | 272 | /* Write log buffer contents and flush the written data right away. */ 273 | Result rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logBuffer, g_logBufferLength, FsWriteOption_Flush); 274 | if (R_SUCCEEDED(rc)) 275 | { 276 | /* Update global variables. */ 277 | g_logFileOffset += (s64)g_logBufferLength; 278 | *g_logBuffer = '\0'; 279 | g_logBufferLength = 0; 280 | } 281 | } 282 | 283 | static bool usbHsFsLogAllocateLogBuffer(void) 284 | { 285 | if (g_logBuffer) return true; 286 | g_logBuffer = memalign(LOG_BUF_SIZE, LOG_BUF_SIZE); 287 | return (g_logBuffer != NULL); 288 | } 289 | 290 | static bool usbHsFsLogOpenLogFile(void) 291 | { 292 | if (serviceIsActive(&(g_logFile.s))) return true; 293 | 294 | Result rc = 0; 295 | 296 | /* Get SD card FsFileSystem object. */ 297 | g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"); 298 | if (!g_sdCardFileSystem) return false; 299 | 300 | /* Create file. This will fail if the logfile exists, so we don't check its return value. */ 301 | fsFsCreateFile(g_sdCardFileSystem, LOG_FILE_NAME, 0, 0); 302 | 303 | /* Open file. */ 304 | rc = fsFsOpenFile(g_sdCardFileSystem, LOG_FILE_NAME, FsOpenMode_Write | FsOpenMode_Append, &g_logFile); 305 | if (R_SUCCEEDED(rc)) 306 | { 307 | /* Get file size. */ 308 | rc = fsFileGetSize(&g_logFile, &g_logFileOffset); 309 | if (R_SUCCEEDED(rc)) 310 | { 311 | size_t len = 0; 312 | 313 | if (!g_logFileOffset) 314 | { 315 | /* Write UTF-8 BOM if the logfile is empty. */ 316 | len = strlen(g_utf8Bom); 317 | rc = fsFileWrite(&g_logFile, g_logFileOffset, g_utf8Bom, len, FsWriteOption_Flush); 318 | } else { 319 | /* Write session separator if the logfile isn't empty. */ 320 | len = strlen(g_logSessionSeparator); 321 | rc = fsFileWrite(&g_logFile, g_logFileOffset, g_logSessionSeparator, len, FsWriteOption_Flush); 322 | } 323 | 324 | if (R_SUCCEEDED(rc)) g_logFileOffset += (s64)len; 325 | } 326 | } 327 | 328 | /* Close file if we successfully opened it, but an error occurred afterwards. */ 329 | if (R_FAILED(rc) && serviceIsActive(&(g_logFile.s))) 330 | { 331 | fsFileClose(&g_logFile); 332 | memset(&g_logFile, 0, sizeof(FsFile)); 333 | } 334 | 335 | return R_SUCCEEDED(rc); 336 | } 337 | 338 | static void usbHsFsLogGenerateHexStringFromData(char *dst, size_t dst_size, const void *src, size_t src_size) 339 | { 340 | if (!src || !src_size || !dst || dst_size < ((src_size * 2) + 1)) return; 341 | 342 | size_t i, j; 343 | const u8 *src_u8 = (const u8*)src; 344 | 345 | for(i = 0, j = 0; i < src_size; i++) 346 | { 347 | char h_nib = ((src_u8[i] >> 4) & 0xF); 348 | char l_nib = (src_u8[i] & 0xF); 349 | 350 | dst[j++] = (h_nib + (h_nib < 0xA ? 0x30 : 0x37)); 351 | dst[j++] = (l_nib + (l_nib < 0xA ? 0x30 : 0x37)); 352 | } 353 | 354 | dst[j] = '\0'; 355 | } 356 | 357 | #endif /* DEBUG */ 358 | -------------------------------------------------------------------------------- /source/usbhsfs_log.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_log.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __USBHSFS_LOG_H__ 12 | #define __USBHSFS_LOG_H__ 13 | 14 | #ifdef DEBUG 15 | 16 | /// Helper macros. 17 | #define USBHSFS_LOG_MSG(fmt, ...) usbHsFsLogWriteFormattedStringToLogFile(__FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__) 18 | #define USBHSFS_LOG_DATA(data, data_size, fmt, ...) usbHsFsLogWriteBinaryDataToLogFile(data, data_size, __FILE__, __LINE__, __func__, fmt, ##__VA_ARGS__) 19 | 20 | /// Writes the provided string to the logfile. 21 | void usbHsFsLogWriteStringToLogFile(const char *src); 22 | 23 | /// Writes a formatted log string to the logfile. 24 | __attribute__((format(printf, 4, 5))) void usbHsFsLogWriteFormattedStringToLogFile(const char *file_name, int line, const char *func_name, const char *fmt, ...); 25 | 26 | /// Writes a formatted log string + a hex string representation of the provided binary data to the logfile. 27 | __attribute__((format(printf, 6, 7))) void usbHsFsLogWriteBinaryDataToLogFile(const void *data, size_t data_size, const char *file_name, int line, const char *func_name, const char *fmt, ...); 28 | 29 | /// Forces a flush operation on the logfile. 30 | void usbHsFsLogFlushLogFile(void); 31 | 32 | /// Closes the logfile. 33 | void usbHsFsLogCloseLogFile(void); 34 | 35 | #else /* DEBUG */ 36 | 37 | #define USBHSFS_LOG_MSG(fmt, ...) do {} while(0) 38 | #define USBHSFS_LOG_DATA(data, data_size, fmt, ...) do {} while(0) 39 | 40 | #endif /* DEBUG */ 41 | 42 | #endif /* __USBHSFS_LOG_H__ */ 43 | -------------------------------------------------------------------------------- /source/usbhsfs_manager.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_manager.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifndef __USBHSFS_MANAGER_H__ 13 | #define __USBHSFS_MANAGER_H__ 14 | 15 | #include "usbhsfs_drive.h" 16 | 17 | /// Locks the drive manager mutex to prevent the background thread from updating drive contexts while working with them, then tries to find a match for the provided drive context in the pointer array. 18 | /// If a match is found, the drive context mutex is locked. The drive manager mutex is unlocked right before this function returns. 19 | /// This function is thread-safe. 20 | bool usbHsFsManagerIsDriveContextPointerValid(UsbHsFsDriveContext *drive_ctx); 21 | 22 | /// Locks the drive manager mutex to prevent the background thread from updating drive contexts while working with them. 23 | /// Then looks for a filesystem context with a FatFs object that holds a physical drive number matching the provided one. If a match is found, its parent LUN context is returned. 24 | /// Otherwise, this function returns NULL. The drive manager mutex is unlocked right before this function returns. 25 | /// This function is thread-safe. 26 | UsbHsFsDriveLogicalUnitContext *usbHsFsManagerGetLogicalUnitContextForFatFsDriveNumber(u8 pdrv); 27 | 28 | #endif /* __USBHSFS_MANAGER_H__ */ 29 | -------------------------------------------------------------------------------- /source/usbhsfs_mount.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_mount.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifndef __USBHSFS_MOUNT_H__ 13 | #define __USBHSFS_MOUNT_H__ 14 | 15 | #include "usbhsfs_drive.h" 16 | 17 | extern __thread char __usbhsfs_dev_path_buf[MAX_PATH_LENGTH]; 18 | 19 | /// None of these functions are thread safe - make sure to (un)lock mutexes elsewhere. 20 | 21 | /// Initializes filesystem contexts for the provided LUN context. 22 | /// If this function succeeds, at least one filesystem will have been both mounted and registered as a devoptab virtual device. 23 | bool usbHsFsMountInitializeLogicalUnitFileSystemContexts(UsbHsFsDriveLogicalUnitContext *lun_ctx); 24 | 25 | /// Destroys the provided filesystem context, unregistering the devoptab virtual device and unmounting the filesystem in the process. 26 | void usbHsFsMountDestroyLogicalUnitFileSystemContext(UsbHsFsDriveLogicalUnitFileSystemContext *fs_ctx); 27 | 28 | /// Returns the total number of registered devoptab virtual devices. 29 | u32 usbHsFsMountGetDevoptabDeviceCount(void); 30 | 31 | /// Sets the devoptab device from the provided filesystem context as the default devoptab device. 32 | /// Called by the chdir() function from devoptab interfaces. 33 | bool usbHsFsMountSetDefaultDevoptabDevice(UsbHsFsDriveLogicalUnitFileSystemContext *fs_ctx); 34 | 35 | /// Returns a bitmask with the current filesystem mount flags. 36 | u32 usbHsFsMountGetFileSystemMountFlags(void); 37 | 38 | /// Takes an input bitmask with the desired filesystem mount flags, which will be used for all mount operations. 39 | void usbHsFsMountSetFileSystemMountFlags(u32 flags); 40 | 41 | #endif /* __USBHSFS_MOUNT_H__ */ 42 | -------------------------------------------------------------------------------- /source/usbhsfs_request.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_request.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __USBHSFS_REQUEST_H__ 12 | #define __USBHSFS_REQUEST_H__ 13 | 14 | /// None of these functions are thread safe - make sure to (un)lock mutexes elsewhere. 15 | 16 | /// Returns a pointer to a dynamic, memory-aligned buffer suitable for USB transfers. 17 | void *usbHsFsRequestAllocateXferBuffer(void); 18 | 19 | /// Performs a get max logical units class-specific request. 20 | Result usbHsFsRequestGetMaxLogicalUnits(UsbHsClientIfSession *usb_if_session, u8 *out); 21 | 22 | /// Performs a bulk-only mass storage reset class-specific request. 23 | Result usbHsFsRequestMassStorageReset(UsbHsClientIfSession *usb_if_session); 24 | 25 | /// Performs a GET_ENDPOINT request on the device pointed to by the provided interface session to retrieve the full configuration descriptor for the provided zero-based index. 26 | /// The provided index must be lower than the bNumConfigurations value from the device descriptor in the provided interface session. 27 | /// If the call succeeds, both 'out_buf' and 'out_buf_size' pointers will be updated. 28 | /// The pointer to the dynamically allocated buffer stored in 'out_buf' must be freed by the user. 29 | Result usbHsFsRequestGetConfigurationDescriptor(UsbHsClientIfSession *usb_if_session, u8 idx, u8 **out_buf, u32 *out_buf_size); 30 | 31 | /// Performs a GET_ENDPOINT request on the device pointed to by the provided interface session to retrieve the string descriptor for the provided index and language ID. 32 | /// If the call succeeds, both 'out_buf' and 'out_buf_size' pointers will be updated. 33 | /// The pointer to the dynamically allocated buffer stored in 'out_buf' must be freed by the user. 34 | Result usbHsFsRequestGetStringDescriptor(UsbHsClientIfSession *usb_if_session, u8 idx, u16 lang_id, u16 **out_buf, u32 *out_buf_size); 35 | 36 | /// Performs a GET_STATUS request on the provided endpoint. 37 | /// If the call succeeds, the current STALL status from the endpoint is saved to 'out'. 38 | Result usbHsFsRequestGetEndpointStatus(UsbHsClientIfSession *usb_if_session, UsbHsClientEpSession *usb_ep_session, bool *out); 39 | 40 | /// Performs a CLEAR_FEATURE request on the provided endpoint to clear a STALL status. 41 | Result usbHsFsRequestClearEndpointHaltFeature(UsbHsClientIfSession *usb_if_session, UsbHsClientEpSession *usb_ep_session); 42 | 43 | /// Performs a SET_INTERFACE request on the device pointed to by the provided interface session. 44 | Result usbHsFsRequestSetInterface(UsbHsClientIfSession *usb_if_session); 45 | 46 | /// Performs a synchronous data transfer on the provided endpoint, using a hardcoded timeout value (USB_POSTBUFFER_TIMEOUT). 47 | /// Uses usbHsEpPostBufferAsync() + eventWait() + usbHsEpGetXferReport() internally. 48 | Result usbHsFsRequestEndpointDataXfer(UsbHsClientEpSession *usb_ep_session, void *buf, u32 size, u32 *xfer_size); 49 | 50 | /// Performs a data transfer on the provided endpoint. 51 | /// If an error occurs, a STALL status check is performed on the target endpoint. If present, the STALL status is cleared and the transfer is retried one more time (if retry == true). 52 | /// This is essentially a nice wrapper for usbHsFsRequestEndpointDataXfer(), which can be used in data transfer stages and CSW transfers from SCSI commands. 53 | Result usbHsFsRequestPostBuffer(UsbHsClientIfSession *usb_if_session, UsbHsClientEpSession *usb_ep_session, void *buf, u32 size, u32 *xfer_size, bool retry); 54 | 55 | #endif /* __USBHSFS_REQUEST_H__ */ 56 | -------------------------------------------------------------------------------- /source/usbhsfs_scsi.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_scsi.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * Copyright (c) 2020-2021, XorTroll. 6 | * 7 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 8 | */ 9 | 10 | #pragma once 11 | 12 | #ifndef __USBHSFS_SCSI_H__ 13 | #define __USBHSFS_SCSI_H__ 14 | 15 | #include "usbhsfs_manager.h" 16 | 17 | /// None of these functions are thread safe - make sure to (un)lock mutexes elsewhere. 18 | 19 | /// Starts the LUN represented by the provided LUN context using SCSI commands and fills the LUN context. 20 | bool usbHsFsScsiStartDriveLogicalUnit(UsbHsFsDriveLogicalUnitContext *lun_ctx); 21 | 22 | /// Stops the LUN represented by the provided LUN context using SCSI commands, as long as it's removable (returns right away if it isn't). 23 | void usbHsFsScsiStopDriveLogicalUnit(UsbHsFsDriveLogicalUnitContext *lun_ctx); 24 | 25 | /// Reads logical blocks from a LUN using the provided LUN context. Suitable for filesystem libraries. 26 | /// In order to speed up transfers, this function performs no checks on the provided arguments. 27 | bool usbHsFsScsiReadLogicalUnitBlocks(UsbHsFsDriveLogicalUnitContext *lun_ctx, void *buf, u64 block_addr, u32 block_count); 28 | 29 | /// Writes logical blocks to a LUN using the provided LUN context. Suitable for filesystem libraries. 30 | /// In order to speed up transfers, this function performs no checks on the provided arguments. 31 | bool usbHsFsScsiWriteLogicalUnitBlocks(UsbHsFsDriveLogicalUnitContext *lun_ctx, const void *buf, u64 block_addr, u32 block_count); 32 | 33 | #endif /* __USBHSFS_SCSI_H__ */ 34 | -------------------------------------------------------------------------------- /source/usbhsfs_utils.c: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_utils.c 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #include "usbhsfs_utils.h" 10 | 11 | /* Global variables. */ 12 | 13 | static u32 g_atmosphereVersion = 0; 14 | static Mutex g_atmosphereVersionMutex = 0; 15 | 16 | /* Atmosphère-related constants. */ 17 | 18 | static const u32 g_smAtmosphereHasService = 65100; 19 | static const SplConfigItem SplConfigItem_ExosphereApiVersion = (SplConfigItem)65000; 20 | static const u32 g_atmosphereTipcVersion = MAKEHOSVERSION(0, 19, 0); 21 | 22 | /* Function prototypes. */ 23 | 24 | static bool usbHsFsUtilsCheckRunningServiceByName(const char *name); 25 | static Result usbHsFsUtilsAtmosphereHasService(bool *out, SmServiceName name); 26 | static Result usbHsFsUtilsGetExosphereApiVersion(u32 *out); 27 | 28 | void usbHsFsUtilsTrimString(char *str) 29 | { 30 | size_t strsize = 0; 31 | char *start = NULL, *end = NULL; 32 | 33 | if (!str || !(strsize = strlen(str))) return; 34 | 35 | start = str; 36 | end = (start + strsize); 37 | 38 | while(--end >= start) 39 | { 40 | if (!isspace((unsigned char)*end)) break; 41 | } 42 | 43 | *(++end) = '\0'; 44 | 45 | while(isspace((unsigned char)*start)) start++; 46 | 47 | if (start != str) memmove(str, start, end - start + 1); 48 | } 49 | 50 | bool usbHsFsUtilsIsAsciiString(const char *str, size_t strsize) 51 | { 52 | if (!str || !*str) return false; 53 | 54 | /* Retrieve string length if it wasn't provided. */ 55 | if (!strsize) strsize = strlen(str); 56 | 57 | for(size_t i = 0; i < strsize; i++) 58 | { 59 | char cp = str[i]; 60 | if (cp < 0x20 || cp > 0x7E) return false; 61 | } 62 | 63 | return true; 64 | } 65 | 66 | bool usbHsFsUtilsIsFspUsbRunning(void) 67 | { 68 | return usbHsFsUtilsCheckRunningServiceByName("fsp-usb"); 69 | } 70 | 71 | bool usbHsFsUtilsSXOSCustomFirmwareCheck(void) 72 | { 73 | return (usbHsFsUtilsCheckRunningServiceByName("tx") && !usbHsFsUtilsCheckRunningServiceByName("rnx")); 74 | } 75 | 76 | static bool usbHsFsUtilsCheckRunningServiceByName(const char *name) 77 | { 78 | if (!name || !*name) 79 | { 80 | USBHSFS_LOG_MSG("Invalid parameters!"); 81 | return false; 82 | } 83 | 84 | bool ret = false; 85 | 86 | SCOPED_LOCK(&g_atmosphereVersionMutex) 87 | { 88 | Result rc = usbHsFsUtilsAtmosphereHasService(&ret, smEncodeName(name)); 89 | if (R_FAILED(rc)) USBHSFS_LOG_MSG("usbHsFsUtilsAtmosphereHasService failed for \"%s\"! (0x%X).", name, rc); 90 | } 91 | 92 | return ret; 93 | } 94 | 95 | /* SM API extension available in Atmosphère and Atmosphère-based CFWs. */ 96 | static Result usbHsFsUtilsAtmosphereHasService(bool *out, SmServiceName name) 97 | { 98 | if (!out || !name.name[0]) return MAKERESULT(Module_Libnx, LibnxError_BadInput); 99 | 100 | u8 tmp = 0; 101 | Result rc = 0; 102 | 103 | /* Get Exosphère API version. */ 104 | if (!g_atmosphereVersion) 105 | { 106 | rc = usbHsFsUtilsGetExosphereApiVersion(&g_atmosphereVersion); 107 | if (R_FAILED(rc)) USBHSFS_LOG_MSG("usbHsFsUtilsGetExosphereApiVersion failed! (0x%X).", rc); 108 | } 109 | 110 | /* Check if service is running. */ 111 | /* Dispatch IPC request using CMIF or TIPC serialization depending on our current environment. */ 112 | if (hosversionAtLeast(12, 0, 0) || g_atmosphereVersion >= g_atmosphereTipcVersion) 113 | { 114 | rc = tipcDispatchInOut(smGetServiceSessionTipc(), g_smAtmosphereHasService, name, tmp); 115 | } else { 116 | rc = serviceDispatchInOut(smGetServiceSession(), g_smAtmosphereHasService, name, tmp); 117 | } 118 | 119 | if (R_SUCCEEDED(rc)) *out = (tmp != 0); 120 | 121 | return rc; 122 | } 123 | 124 | /* SMC config item available in Atmosphère and Atmosphère-based CFWs. */ 125 | static Result usbHsFsUtilsGetExosphereApiVersion(u32 *out) 126 | { 127 | if (!out) return MAKERESULT(Module_Libnx, LibnxError_BadInput); 128 | 129 | Result rc = 0; 130 | u64 cfg = 0; 131 | u32 version = 0; 132 | 133 | /* Initialize spl service. */ 134 | rc = splInitialize(); 135 | if (R_FAILED(rc)) 136 | { 137 | USBHSFS_LOG_MSG("splInitialize failed! (0x%X).", rc); 138 | return rc; 139 | } 140 | 141 | /* Get Exosphère API version config item. */ 142 | rc = splGetConfig(SplConfigItem_ExosphereApiVersion, &cfg); 143 | 144 | /* Close spl service. */ 145 | splExit(); 146 | 147 | if (R_SUCCEEDED(rc)) 148 | { 149 | *out = version = (u32)((cfg >> 40) & 0xFFFFFF); 150 | USBHSFS_LOG_MSG("Exosphère API version: %u.%u.%u.", HOSVER_MAJOR(version), HOSVER_MINOR(version), HOSVER_MICRO(version)); 151 | } else { 152 | USBHSFS_LOG_MSG("splGetConfig failed! (0x%X).", rc); 153 | } 154 | 155 | return rc; 156 | } 157 | -------------------------------------------------------------------------------- /source/usbhsfs_utils.h: -------------------------------------------------------------------------------- 1 | /* 2 | * usbhsfs_utils.h 3 | * 4 | * Copyright (c) 2020-2023, DarkMatterCore . 5 | * 6 | * This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs). 7 | */ 8 | 9 | #pragma once 10 | 11 | #ifndef __USBHSFS_UTILS_H__ 12 | #define __USBHSFS_UTILS_H__ 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | #include "usb_common.h" 26 | #include "usbhsfs.h" 27 | #include "usbhsfs_log.h" 28 | 29 | #define ALIGN_DOWN(x, y) ((x) & ~((y) - 1)) 30 | 31 | #define SCOPED_LOCK(mtx) for(UsbHsFsUtilsScopedLock scoped_lock __attribute__((__cleanup__(usbHsFsUtilsUnlockScope))) = usbHsFsUtilsLockScope(mtx); scoped_lock.cond; scoped_lock.cond = 0) 32 | 33 | #define LIB_ASSERT(name, size) static_assert(sizeof(name) == (size), "Bad size for " #name "! Expected " #size ".") 34 | 35 | /// Used by scoped locks. 36 | typedef struct { 37 | Mutex *mtx; 38 | bool lock; 39 | int cond; 40 | } UsbHsFsUtilsScopedLock; 41 | 42 | /// Trims whitespace characters from the provided string. 43 | void usbHsFsUtilsTrimString(char *str); 44 | 45 | /// Returns true if the provided string only holds ASCII codepoints. 46 | /// If strsize == 0, strlen() will be used to retrieve the string length. 47 | bool usbHsFsUtilsIsAsciiString(const char *str, size_t strsize); 48 | 49 | /// Returns true if the fsp-usb service is running in the background. 50 | bool usbHsFsUtilsIsFspUsbRunning(void); 51 | 52 | /// Returns true if we're running under SX OS. 53 | bool usbHsFsUtilsSXOSCustomFirmwareCheck(void); 54 | 55 | /// Simple wrapper to sleep the current thread for a specific number of full seconds. 56 | NX_INLINE void usbHsFsUtilsSleep(u64 seconds) 57 | { 58 | if (seconds) svcSleepThread(seconds * (u64)1000000000); 59 | } 60 | 61 | /// Wrappers used in scoped locks. 62 | NX_INLINE UsbHsFsUtilsScopedLock usbHsFsUtilsLockScope(Mutex *mtx) 63 | { 64 | UsbHsFsUtilsScopedLock scoped_lock = { mtx, !mutexIsLockedByCurrentThread(mtx), 1 }; 65 | if (scoped_lock.lock) mutexLock(scoped_lock.mtx); 66 | return scoped_lock; 67 | } 68 | 69 | NX_INLINE void usbHsFsUtilsUnlockScope(UsbHsFsUtilsScopedLock *scoped_lock) 70 | { 71 | if (scoped_lock->lock) mutexUnlock(scoped_lock->mtx); 72 | } 73 | 74 | #endif /* __USBHSFS_UTILS_H__ */ 75 | --------------------------------------------------------------------------------