From da529aed21ebed625446ddf17afa88b35510820f Mon Sep 17 00:00:00 2001 From: fincs Date: Fri, 3 Jul 2020 00:23:08 +0200 Subject: [PATCH] romfs_dev: Backport proper multimount system from libnx with some tweaks --- libctru/include/3ds/romfs.h | 42 +++-- libctru/source/os-versionbin.c | 9 +- libctru/source/romfs_dev.c | 318 ++++++++++++++------------------- 3 files changed, 160 insertions(+), 209 deletions(-) diff --git a/libctru/include/3ds/romfs.h b/libctru/include/3ds/romfs.h index 7deec55..f08ad15 100644 --- a/libctru/include/3ds/romfs.h +++ b/libctru/include/3ds/romfs.h @@ -45,36 +45,40 @@ typedef struct u16 name[]; ///< Name. (UTF-16) } romfs_file; -struct romfs_mount; - /** * @brief Mounts the Application's RomFS. - * @param mount Output mount handle + * @param name Device mount name. + * @remark This function is intended to be used to access one's own RomFS. + * If the application is running as 3DSX, it mounts the embedded RomFS section inside the 3DSX. + * If on the other hand it's an NCCH, it behaves identically to \ref romfsMountFromCurrentProcess. */ -Result romfsMount(struct romfs_mount **mount); -static inline Result romfsInit(void) -{ - return romfsMount(NULL); -} +Result romfsMountSelf(const char *name); /** * @brief Mounts RomFS from an open file. - * @param file Handle of the RomFS file. + * @param fd FSFILE handle of the RomFS image. * @param offset Offset of the RomFS within the file. - * @param mount Output mount handle + * @param name Device mount name. */ -Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **mount); -static inline Result romfsInitFromFile(Handle file, u32 offset) -{ - return romfsMountFromFile(file, offset, NULL); -} +Result romfsMountFromFile(Handle fd, u32 offset, const char *name); -/// Bind the RomFS mount -Result romfsBind(struct romfs_mount *mount); +/** + * @brief Mounts RomFS using the current process host program RomFS. + * @param name Device mount name. + */ +Result romfsMountFromCurrentProcess(const char *name); /// Unmounts the RomFS device. -Result romfsUnmount(struct romfs_mount *mount); +Result romfsUnmount(const char *name); + +/// Wrapper for \ref romfsMountSelf with the default "romfs" device name. +static inline Result romfsInit(void) +{ + return romfsMountSelf("romfs"); +} + +/// Wrapper for \ref romfsUnmount with the default "romfs" device name. static inline Result romfsExit(void) { - return romfsUnmount(NULL); + return romfsUnmount("romfs"); } diff --git a/libctru/source/os-versionbin.c b/libctru/source/os-versionbin.c index 5856aa7..3cdc9a3 100644 --- a/libctru/source/os-versionbin.c +++ b/libctru/source/os-versionbin.c @@ -41,15 +41,14 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_ Result ret = 0; Handle filehandle = 0; FILE *f = NULL; - struct romfs_mount *mount; ret = FSUSER_OpenFileDirectly(&filehandle, archiveId, archivePath, fileLowPath, FS_OPEN_READ, 0x0); if(R_FAILED(ret))return ret; - ret = romfsMountFromFile(filehandle, 0x0, &mount); + ret = romfsMountFromFile(filehandle, 0x0, "ver"); if(R_FAILED(ret))return ret; - f = fopen("romfs:/version.bin", "r"); + f = fopen("ver:/version.bin", "r"); if(f==NULL) { ret = errno; @@ -60,7 +59,7 @@ static Result __read_versionbin(FS_ArchiveID archiveId, FS_Path archivePath, FS_ fclose(f); } - romfsUnmount(mount); + romfsUnmount("ver"); return ret; } @@ -121,7 +120,7 @@ Result osGetSystemVersionDataString(OS_VersionBin *nver_versionbin, OS_VersionBi ret = osGetSystemVersionData(nver_versionbin, cver_versionbin); if(R_FAILED(ret))return ret; - snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c\n", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region); + snprintf(sysverstr, sysverstr_maxsize, "%u.%u.%u-%u%c", cver_versionbin->mainver, cver_versionbin->minor, cver_versionbin->build, nver_versionbin->mainver, nver_versionbin->region); return 0; } diff --git a/libctru/source/romfs_dev.c b/libctru/source/romfs_dev.c index 0ae5a63..1354119 100644 --- a/libctru/source/romfs_dev.c +++ b/libctru/source/romfs_dev.c @@ -20,6 +20,8 @@ typedef struct romfs_mount { + devoptab_t device; + char name[32]; Handle fd; time_t mtime; u32 offset; @@ -84,9 +86,8 @@ typedef struct u32 childFile; } romfs_diriter; -static devoptab_t romFS_devoptab = +static const devoptab_t romFS_devoptab = { - .name = "romfs", .structSize = sizeof(romfs_fileobj), .open_r = romfs_open, .close_r = romfs_close, @@ -101,7 +102,6 @@ static devoptab_t romFS_devoptab = .dirreset_r = romfs_dirreset, .dirnext_r = romfs_dirnext, .dirclose_r = romfs_dirclose, - .deviceData = 0, }; //----------------------------------------------------------------------------- @@ -124,9 +124,6 @@ typedef struct u32 fsOffset; } _3DSX_Header; -static Result romfsMountCommon(romfs_mount *mount); -static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath); - __attribute__((weak)) const char* __romfs_path = NULL; static romfs_mount *romfs_mount_list = NULL; @@ -151,16 +148,23 @@ static void romfs_remove(romfs_mount *mount) static romfs_mount* romfs_alloc(void) { - romfs_mount *mount = (romfs_mount*)calloc(1, sizeof(romfs_mount)); + romfs_mount *mount = (romfs_mount*)malloc(sizeof(romfs_mount)); - if(mount) + if (mount) + { + memset(mount, 0, sizeof(*mount)); + memcpy(&mount->device, &romFS_devoptab, sizeof(romFS_devoptab)); + mount->device.name = mount->name; + mount->device.deviceData = mount; romfs_insert(mount); + } return mount; } static void romfs_free(romfs_mount *mount) { + FSFILE_Close(mount->fd); romfs_remove(mount); free(mount->fileTable); free(mount->fileHashTable); @@ -169,221 +173,165 @@ static void romfs_free(romfs_mount *mount) free(mount); } -Result romfsMount(struct romfs_mount **p) +Result romfsMountFromFile(Handle fd, u32 offset, const char *name) { romfs_mount *mount = romfs_alloc(); - if(mount == NULL) - return 99; - - if (envIsHomebrew()) + if (mount == NULL) { - // RomFS appended to a 3DSX file - const char* filename = __romfs_path; - if (__system_argc > 0 && __system_argv[0]) - filename = __system_argv[0]; - if (!filename) - { - romfs_free(mount); - return 1; - } - - if (strncmp(filename, "sdmc:/", 6) == 0) - filename += 5; - else if (strncmp(filename, "3dslink:/", 9) == 0) - { - strncpy(__ctru_dev_path_buf, "/3ds", PATH_MAX); - strncat(__ctru_dev_path_buf, filename+8, PATH_MAX); - __ctru_dev_path_buf[PATH_MAX] = 0; - filename = __ctru_dev_path_buf; - } - else - { - romfs_free(mount); - return 2; - } - - ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)filename, PATH_MAX); - if (units < 0) - { - romfs_free(mount); - return 3; - } - if (units > PATH_MAX) - { - romfs_free(mount); - return 4; - } - __ctru_dev_utf16_buf[units] = 0; - - FS_Path archPath = { PATH_EMPTY, 1, "" }; - FS_Path filePath = { PATH_UTF16, (units+1)*2, __ctru_dev_utf16_buf }; - - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); - if (R_FAILED(rc)) - { - romfs_free(mount); - return rc; - } - - romfsInitMtime(mount, ARCHIVE_SDMC, archPath, filePath); - - _3DSX_Header hdr; - if (!_romfs_read_chk(mount, 0, &hdr, sizeof(hdr))) goto _fail0; - if (hdr.magic != _3DSX_MAGIC) goto _fail0; - if (hdr.headerSize < sizeof(hdr)) goto _fail0; - mount->offset = hdr.fsOffset; - if (!mount->offset) goto _fail0; - } - else - { - // Regular RomFS - u8 zeros[0xC] = {0}; - - FS_Path archPath = { PATH_EMPTY, 1, "" }; - FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros }; - - Result rc = FSUSER_OpenFileDirectly(&mount->fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); - if (R_FAILED(rc)) - { - romfs_free(mount); - return rc; - } - - romfsInitMtime(mount, ARCHIVE_ROMFS, archPath, filePath); + FSFILE_Close(fd); + return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY); } - Result ret = romfsMountCommon(mount); - if(R_SUCCEEDED(ret) && p) - *p = mount; - - return ret; - -_fail0: - FSFILE_Close(mount->fd); - romfs_free(mount); - return 10; -} - -Result romfsMountFromFile(Handle file, u32 offset, struct romfs_mount **p) -{ - romfs_mount *mount = romfs_alloc(); - if(mount == NULL) - return 99; - - mount->fd = file; + mount->fd = fd; mount->offset = offset; + mount->mtime = time(NULL); + strncpy(mount->name, name, sizeof(mount->name)-1); - Result ret = romfsMountCommon(mount); - if(R_SUCCEEDED(ret) && p) - *p = mount; - - return ret; -} - -Result romfsMountCommon(romfs_mount *mount) -{ if (_romfs_read(mount, 0, &mount->header, sizeof(mount->header)) != sizeof(mount->header)) - goto fail; + goto fail_io; mount->dirHashTable = (u32*)malloc(mount->header.dirHashTableSize); if (!mount->dirHashTable) - goto fail; + goto fail_oom; if (!_romfs_read_chk(mount, mount->header.dirHashTableOff, mount->dirHashTable, mount->header.dirHashTableSize)) - goto fail; + goto fail_io; mount->dirTable = malloc(mount->header.dirTableSize); if (!mount->dirTable) - goto fail; + goto fail_oom; if (!_romfs_read_chk(mount, mount->header.dirTableOff, mount->dirTable, mount->header.dirTableSize)) - goto fail; + goto fail_io; mount->fileHashTable = (u32*)malloc(mount->header.fileHashTableSize); if (!mount->fileHashTable) - goto fail; + goto fail_oom; if (!_romfs_read_chk(mount, mount->header.fileHashTableOff, mount->fileHashTable, mount->header.fileHashTableSize)) - goto fail; + goto fail_io; mount->fileTable = malloc(mount->header.fileTableSize); if (!mount->fileTable) - goto fail; + goto fail_oom; if (!_romfs_read_chk(mount, mount->header.fileTableOff, mount->fileTable, mount->header.fileTableSize)) - goto fail; + goto fail_io; mount->cwd = romFS_root(mount); - // add device if this is the first one - if(mount->next == NULL && AddDevice(&romFS_devoptab) < 0) - goto fail; + if (AddDevice(&mount->device) < 0) + goto fail_oom; return 0; -fail: - FSFILE_Close(mount->fd); +fail_oom: romfs_free(mount); - return 10; + return MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_ROMFS, RD_OUT_OF_MEMORY); + +fail_io: + romfs_free(mount); + return MAKERESULT(RL_FATAL, RS_INVALIDSTATE, RM_ROMFS, RD_NOT_FOUND); } -static void romfsInitMtime(romfs_mount *mount, FS_ArchiveID archId, FS_Path archPath, FS_Path filePath) +Result romfsMountSelf(const char* name) { - u64 mtime; - FS_Archive arch; - Result rc; + // If we are not 3DSX, we need to mount this process' real RomFS + if (!envIsHomebrew()) + return romfsMountFromCurrentProcess(name); - mount->mtime = time(NULL); - rc = FSUSER_OpenArchive(&arch, archId, archPath); - if (R_FAILED(rc)) - return; + // Otherwise, we use the embedded RomFS inside the 3DSX file + // Retrieve the filename of our 3DSX file + const char* filename = __romfs_path; + if (__system_argc > 0 && __system_argv[0]) + filename = __system_argv[0]; + if (!filename) + return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_NO_DATA); - rc = FSUSER_ControlArchive(arch, ARCHIVE_ACTION_GET_TIMESTAMP, - (void*)filePath.data, filePath.size, - &mtime, sizeof(mtime)); - FSUSER_CloseArchive(arch); - if (R_FAILED(rc)) - return; - - /* convert from milliseconds to seconds */ - mtime /= 1000; - /* convert from 2000-based timestamp to UNIX timestamp */ - mtime += 946684800; - mount->mtime = mtime; -} - -Result romfsBind(struct romfs_mount *mount) -{ - for(romfs_mount **it = &romfs_mount_list; *it; it = &(*it)->next) + // Adjust the path name + if (strncmp(filename, "sdmc:/", 6) == 0) + filename += 5; + else if (strncmp(filename, "3dslink:/", 9) == 0) { - if(*it == mount) - { - *it = mount->next; - romfs_insert(mount); - return 0; - } - } - - return 99; -} - -Result romfsUnmount(struct romfs_mount *mount) -{ - if(mount) - { - // unmount specific - FSFILE_Close(mount->fd); - romfs_free(mount); + strncpy(__ctru_dev_path_buf, "/3ds", PATH_MAX); + strncat(__ctru_dev_path_buf, filename+8, PATH_MAX); + __ctru_dev_path_buf[PATH_MAX] = 0; + filename = __ctru_dev_path_buf; } else + return MAKERESULT(RL_USAGE, RS_NOTSUPPORTED, RM_ROMFS, RD_NOT_IMPLEMENTED); + + // Convert the path to UTF-16 + ssize_t units = utf8_to_utf16(__ctru_dev_utf16_buf, (const uint8_t*)filename, PATH_MAX); + if (units < 0 || units > PATH_MAX) + return MAKERESULT(RL_USAGE, RS_INVALIDARG, RM_ROMFS, RD_OUT_OF_RANGE); + __ctru_dev_utf16_buf[units] = 0; + + // Open the file directly + Handle fd = 0; + FS_Path archPath = { PATH_EMPTY, 1, "" }; + FS_Path filePath = { PATH_UTF16, (units+1)*2, __ctru_dev_utf16_buf }; + Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_SDMC, archPath, filePath, FS_OPEN_READ, 0); + if (R_FAILED(rc)) + return rc; + + // Read and parse the header + _3DSX_Header hdr; + u32 bytesRead = 0; + rc = FSFILE_Read(fd, &bytesRead, 0, &hdr, sizeof(hdr)); + if (R_FAILED(rc)) { - // unmount everything - while(romfs_mount_list) - { - FSFILE_Close(romfs_mount_list->fd); - romfs_free(romfs_mount_list); - } + FSFILE_Close(fd); + return rc; } - // if no more mounts, remove device - if(romfs_mount_list == NULL) - RemoveDevice("romfs:"); + // Validate the 3DSX header + if (bytesRead != sizeof(hdr) || hdr.magic != _3DSX_MAGIC || hdr.headerSize < sizeof(hdr)) + { + FSFILE_Close(fd); + return MAKERESULT(RL_FATAL, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND); + } + + // Mount the file + return romfsMountFromFile(fd, hdr.fsOffset, name); +} + +Result romfsMountFromCurrentProcess(const char *name) +{ + // Set up FS_Path structures + u8 zeros[0xC] = {0}; + FS_Path archPath = { PATH_EMPTY, 1, "" }; + FS_Path filePath = { PATH_BINARY, sizeof(zeros), zeros }; + + // Open the RomFS file and mount it + Handle fd = 0; + Result rc = FSUSER_OpenFileDirectly(&fd, ARCHIVE_ROMFS, archPath, filePath, FS_OPEN_READ, 0); + if (R_SUCCEEDED(rc)) + rc = romfsMountFromFile(fd, 0, name); + + return rc; +} + +Result romfsUnmount(const char* name) +{ + // Find the mount + romfs_mount* mount = romfs_mount_list; + while (mount) + { + if (strncmp(mount->name, name, sizeof(mount->name)) == 0) + break; + mount = mount->next; + } + + if (mount == NULL) + return MAKERESULT(RL_STATUS, RS_NOTFOUND, RM_ROMFS, RD_NOT_FOUND); + + // Remove device + char tmpname[34]; + unsigned len = strlen(name); + memcpy(tmpname, mount->name, len); + tmpname[len] = ':'; + tmpname[len+1] = 0; + RemoveDevice(tmpname); + + // Free the mount + romfs_free(mount); return 0; } @@ -567,7 +515,7 @@ int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, { romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct; - fileobj->mount = romfs_mount_list; + fileobj->mount = (romfs_mount*)r->deviceData; if ((flags & O_ACCMODE) != O_RDONLY) { @@ -696,7 +644,7 @@ int romfs_fstat(struct _reent *r, void *fd, struct stat *st) int romfs_stat(struct _reent *r, const char *path, struct stat *st) { - romfs_mount* mount = romfs_mount_list; + romfs_mount* mount = (romfs_mount*)r->deviceData; romfs_dir* curDir = NULL; r->_errno = navigateToDir(mount, &curDir, &path, false); if(r->_errno != 0) @@ -740,7 +688,7 @@ int romfs_stat(struct _reent *r, const char *path, struct stat *st) int romfs_chdir(struct _reent *r, const char *path) { - romfs_mount* mount = romfs_mount_list; + romfs_mount* mount = (romfs_mount*)r->deviceData; romfs_dir* curDir = NULL; r->_errno = navigateToDir(mount, &curDir, &path, true); if (r->_errno != 0) @@ -754,7 +702,7 @@ DIR_ITER* romfs_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) { romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); romfs_dir* curDir = NULL; - iter->mount = romfs_mount_list; + iter->mount = (romfs_mount*)r->deviceData; r->_errno = navigateToDir(iter->mount, &curDir, &path, true); if(r->_errno != 0)