romfs_dev: Backport proper multimount system from libnx with some tweaks

This commit is contained in:
fincs 2020-07-03 00:23:08 +02:00
parent 1c7914e2f4
commit da529aed21
No known key found for this signature in database
GPG Key ID: 62C7609ADA219C60
3 changed files with 160 additions and 209 deletions

View File

@ -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");
}

View File

@ -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;
}

View File

@ -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)