446 lines
12 KiB
C
Raw Normal View History

2023-09-05 02:09:05 +01:00
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdarg.h>
#include <errno.h>
#include <dirent.h>
#include <threads.h>
#include <usbhsfs.h>
static UEvent *g_statusChangeEvent = NULL, g_exitEvent = {0};
static u32 g_usbDeviceCount = 0;
static UsbHsFsDevice *g_usbDevices = NULL;
void usbMscFileSystemTest(UsbHsFsDevice *device)
{
if (!device) return;
char path[FS_MAX_PATH] = {0}, tmp[0x40] = {0}, new_path[FS_MAX_PATH] = {0}, *ptr = NULL;
const char *test_str = "Hello world!";
size_t test_str_len = strlen(test_str);
FILE *fd = NULL, *ums_fd = NULL;
struct stat st = {0};
DIR *dp = NULL;
struct dirent *dt = NULL;
struct statvfs fsinfo = {0};
bool copy_failed = false;
int ret = -1, val = 0;
u8 *buf = NULL;
size_t blksize = 0x800000;
sprintf(path, "%s/test_dir", device->name);
/* Create directory. */
printf("\t\t- Create directory (\"%s\"): ", path);
consoleUpdate(NULL);
if (!mkdir(path, 0))
{
printf("OK!\n");
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Write data to file. */
strcat(path, "/" APP_TITLE ".txt");
printf("\t\t- Write data to file (\"%s\") (\"%s\"): ", path, test_str);
consoleUpdate(NULL);
fd = fopen(path, "w");
if (fd)
{
val = fprintf(fd, test_str);
if (val == (int)test_str_len)
{
printf("OK!\n");
} else {
printf("FAILED! (%d, %d).\n", errno, val);
}
fclose(fd);
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Read data from file. */
printf("\t\t- Read data from file (\"%s\"): ", path);
consoleUpdate(NULL);
fd = fopen(path, "r");
if (fd)
{
if (fgets(tmp, test_str_len + 1, fd) != NULL)
{
printf("OK! (\"%s\").\n", tmp);
} else {
printf("FAILED! (%d).\n", errno);
}
fclose(fd);
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* File stats. */
printf("\t\t- File stats (\"%s\"): ", path);
consoleUpdate(NULL);
if (!stat(path, &st))
{
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);
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Rename file. */
ptr = strrchr(path, '/');
sprintf(new_path, "%.*s/test.txt", (int)(ptr - path), path);
printf("\t\t- Rename file (\"%s\" -> \"%s\"): ", path, new_path);
consoleUpdate(NULL);
if (!rename(path, new_path))
{
printf("OK!\n");
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Change directory. */
*ptr = '\0';
printf("\t\t- Change directory (\"%s\"): ", path);
consoleUpdate(NULL);
ret = chdir(path);
if (!ret)
{
printf("OK!\n");
/* Directory listing. */
printf("\t\t- Directory listing (\".\"): ");
consoleUpdate(NULL);
dp = opendir("."); /* Open current directory. */
if (dp)
{
printf("OK!\n");
consoleUpdate(NULL);
while((dt = readdir(dp)))
{
printf("\t\t\t- [%c] ./%s\n", (dt->d_type & DT_DIR) ? 'D' : 'F', dt->d_name);
consoleUpdate(NULL);
}
closedir(dp);
} else {
printf("FAILED! (%d).\n", errno);
consoleUpdate(NULL);
}
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Delete file. */
printf("\t\t- Delete file (\"%s\"): ", new_path);
consoleUpdate(NULL);
if (!unlink(new_path))
{
printf("OK!\n");
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Delete directory. */
printf("\t\t- Delete directory (\"%s\"): ", path);
consoleUpdate(NULL);
if (!rmdir(path))
{
printf("OK!\n");
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* Filesystem stats. */
printf("\t\t- Filesystem stats: ");
consoleUpdate(NULL);
if (!statvfs(".", &fsinfo))
{
u64 fsid = (u64)fsinfo.f_fsid;
u64 total_size = ((u64)fsinfo.f_blocks * (u64)fsinfo.f_frsize);
u64 free_space = ((u64)fsinfo.f_bfree * (u64)fsinfo.f_frsize);
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);
} else {
printf("FAILED! (%d).\n", errno);
}
consoleUpdate(NULL);
/* File copy. */
sprintf(path, "sdmc:/test.file");
sprintf(new_path, "%s/test.file", device->name);
printf("\t\t- File copy (\"%s\" -> \"%s\"): ", path, new_path);
consoleUpdate(NULL);
fd = fopen(path, "rb");
ums_fd = fopen(new_path, "wb");
buf = malloc(blksize);
if (fd && ums_fd && buf)
{
printf("OK!\n");
consoleUpdate(NULL);
fseek(fd, 0, SEEK_END);
size_t file_size = ftell(fd);
rewind(fd);
printf("\t\t\t- File size (\"%s\"): 0x%lX bytes. Please wait.\n", path, file_size);
consoleUpdate(NULL);
time_t start = time(NULL), now = start;
for(size_t off = 0; off < file_size; off += blksize)
{
if (blksize > (file_size - off)) blksize = (file_size - off);
fread(buf, 1, blksize, fd);
fwrite(buf, 1, blksize, ums_fd);
}
now = time(NULL);
printf("\t\t\t- Process completed in %lu seconds.\n", now - start);
consoleUpdate(NULL);
} else {
printf("FAILED! (%d).\n", errno);
consoleUpdate(NULL);
copy_failed = true;
}
if (buf) free(buf);
if (ums_fd)
{
fclose(ums_fd);
if (copy_failed) unlink(new_path);
}
if (fd) fclose(fd);
printf("\n");
consoleUpdate(NULL);
}
int usbMscThreadFunc(void *arg)
{
(void)arg;
Result rc = 0;
int idx = 0;
u32 listed_device_count = 0;
/* Generate waiters for our user events. */
Waiter status_change_event_waiter = waiterForUEvent(g_statusChangeEvent);
Waiter exit_event_waiter = waiterForUEvent(&g_exitEvent);
while(true)
{
/* Wait until an event is triggered. */
rc = waitMulti(&idx, -1, status_change_event_waiter, exit_event_waiter);
if (R_FAILED(rc)) continue;
/* Free mounted devices buffer. */
if (g_usbDevices)
{
free(g_usbDevices);
g_usbDevices = NULL;
}
/* Exit event triggered. */
if (idx == 1)
{
printf("Exit event triggered!\n");
break;
}
/* Get mounted device count. */
g_usbDeviceCount = usbHsFsGetMountedDeviceCount();
printf("USB Mass Storage status change event triggered!\nMounted USB Mass Storage device count: %u.\n\n", g_usbDeviceCount);
consoleUpdate(NULL);
if (!g_usbDeviceCount) continue;
/* Allocate mounted devices buffer. */
g_usbDevices = calloc(g_usbDeviceCount, sizeof(UsbHsFsDevice));
if (!g_usbDevices)
{
printf("Failed to allocate memory for mounted USB Mass Storage devices buffer!\n\n");
consoleUpdate(NULL);
continue;
}
/* List mounted devices. */
if (!(listed_device_count = usbHsFsListMountedDevices(g_usbDevices, g_usbDeviceCount)))
{
printf("Failed to list mounted USB Mass Storage devices!\n\n");
consoleUpdate(NULL);
continue;
}
/* Print info from mounted devices. */
for(u32 i = 0; i < listed_device_count; i++)
{
UsbHsFsDevice *device = &(g_usbDevices[i]);
printf("Device #%u:\n" \
"\t- USB interface ID: %d.\n" \
"\t- Logical Unit Number: %u.\n" \
"\t- Filesystem index: %u.\n" \
"\t- Write protected: %s.\n" \
"\t- Vendor ID: 0x%04X.\n" \
"\t- Product ID: 0x%04X.\n" \
"\t- Manufacturer: \"%s\".\n" \
"\t- Product Name: \"%s\".\n" \
"\t- Serial Number: \"%s\".\n" \
"\t- Logical Unit Capacity: 0x%lX bytes.\n" \
"\t- Mount name: \"%s\".\n" \
"\t- Filesystem type: %s.\n" \
"\t- Mount flags: 0x%08X.\n" \
"\t- Filesystem tests:\n", \
i + 1, \
device->usb_if_id, \
device->lun, \
device->fs_idx, \
device->write_protect ? "yes" : "no", \
device->vid, \
device->pid, \
device->manufacturer, \
device->product_name, \
device->serial_number, \
device->capacity, \
device->name, \
LIBUSBHSFS_FS_TYPE_STR(device->fs_type), \
device->flags);
consoleUpdate(NULL);
/* Perform filesystem tests on current device. */
usbMscFileSystemTest(device);
}
/* Unmount devices. */
for(u32 i = 0; i < listed_device_count; i++)
{
UsbHsFsDevice *device = &(g_usbDevices[i]);
usbHsFsUnmountDevice(device, false);
}
printf("%u device(s) safely unmounted. You may now disconnect them from the console.\n\n", listed_device_count);
consoleUpdate(NULL);
}
/* Exit thread. */
return 0;
}
int main(int argc, char **argv)
{
(void)argc;
(void)argv;
Result rc = 0;
int ret = 0;
thrd_t thread = {0};
PadState pad = {0};
/* Initialize console output. */
consoleInit(NULL);
/* Configure our supported input layout: a single player with full controller styles. */
padConfigureInput(1, HidNpadStyleSet_NpadFullCtrl);
/* Initialize the default gamepad (which reads handheld mode inputs as well as the first connected controller. */
padInitializeDefault(&pad);
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);
consoleUpdate(NULL);
/* Initialize USB Mass Storage Host interface. */
rc = usbHsFsInitialize(0);
if (R_FAILED(rc))
{
printf("usbHsFsInitialize() failed! (0x%08X).\n", rc);
ret = - 1;
goto end1;
}
/* Get USB Mass Storage status change event. */
g_statusChangeEvent = usbHsFsGetStatusChangeUserEvent();
/* Create usermode thread exit event. */
ueventCreate(&g_exitEvent, true);
/* Create thread. */
thrd_create(&thread, usbMscThreadFunc, NULL);
while(appletMainLoop())
{
padUpdate(&pad);
u64 keys_down = padGetButtonsDown(&pad);
if (keys_down & HidNpadButton_Plus)
{
/* Signal background thread. */
ueventSignal(&g_exitEvent);
/* Wait for the background thread to exit on its own. */
thrd_join(thread, NULL);
/* Break out of this loop. */
break;
}
}
/* Deinitialize USB Mass Storage Host interface. */
usbHsFsExit();
end1:
/* Update console output. */
consoleUpdate(NULL);
/* Wait some time (3 seconds). */
svcSleepThread(3000000000ULL);
/* Deinitialize console output. */
consoleExit(NULL);
return ret;
}