diff --git a/nx/include/switch/runtime/devices/romfs_dev.h b/nx/include/switch/runtime/devices/romfs_dev.h index 63a0786ff..9efa12a38 100644 --- a/nx/include/switch/runtime/devices/romfs_dev.h +++ b/nx/include/switch/runtime/devices/romfs_dev.h @@ -120,3 +120,115 @@ static inline Result romfsExit(void) { return romfsUnmount("romfs"); } + +/// Opaque handle to a RomFS mount +typedef struct romfs_mount romfs_mount; + +/// Object for interacting with a romfs_file +typedef struct +{ + romfs_mount *mount; ///< The RomFS mount the file is associated with. + romfs_file *file; ///< Detail about the actual RomFS file. + + u64 offset; ///< The starting offset in RomFS for file data. + u64 pos; ///< Current read position into the file. + + int err; +} romfs_fileobj; + +/** + * @brief Finds a RomFS mount by the given name, the default mount name is "romfs" + * @param name The name of the mount to search for + * @param mount A pointer to a romfs_mount pointer to fill out with the mount information + */ +Result romfsFindMount(const char *name, romfs_mount **mount); + +/** + * @brief Finds a file in RomFS automatically determining the mount + * @param path The path to the file + * @param file A pointer to a romfs_fileobj structure to fill out + * @remark The path structure should follow <mount name>:<path> (i.e. romfs:/data/file.txt) + * If no mount name is provided the default of "romfs" will be used. + */ +Result romfsFindFile(const char *path, romfs_fileobj *file); + +/** + * @brief Finds a file in a specific RomFS mount + * @param mount The mount to search in + * @param path The path to the file + * @param file A pointer to a romfs_fileobj structure to fill out + * @remark The mount name prefix is not required and is ignored if provided + */ +Result romfsFindFileInMount(romfs_mount *mount, const char *path, romfs_fileobj *file); + +/** + * @brief Wrapper function for turning a romfs_file into a romfs_fileobj to be operated on, this + * is useful for creating readable files from a romfs_direntry + * @param mount The mount the file came from + * @param file The file information + */ +romfs_fileobj romfsFileObj(romfs_mount *mount, romfs_file *file); + +/** + * @brief Reads data from the specified RomFS file. + * @param file The RomFS file to read from. + * @param buffer The buffer to read data into. + * @param size The number of bytes to read. + * @param offset The offset in bytes from the beginning of the file to start reading from. + * @param nread A pointer in which the number of total bytes read will be written to. + * @remark The file's position pointer is not updated by this function. + */ +Result romfsReadFile(romfs_fileobj *file, void *buffer, u64 size, u64 offset, u64 *nread); + +typedef struct +{ + romfs_mount *mount; ///< The RomFS mount associated with the directory. + romfs_dir *dir; ///< Information about the directory being searched. + int state; ///< Current iteration count or error code + u32 childDir; ///< Next child directory of the directory. + u32 childFile; ///< Next child file of the directory. +} romfs_diriter; + +typedef enum +{ + RomfsDirEntryType_File = 0, + RomfsDirEntryType_Dir +} RomfsDirEntryType; + +typedef struct +{ + RomfsDirEntryType type; ///< Type of this entry. + union + { + romfs_file *file; ///< Entry information if type is RomfsDirEntryType_File. + romfs_dir *dir; ///< Entry information if type is RomfsDirEntryType_Dir. + }; + + const char *name; ///< Basename of the entry, not null-terminated, UTF-8 coded. + u32 name_len; ///< Length in bytes of the basename. +} romfs_direntry; + +/** + * @brief Initialises the directory iterator for seaching at the given path, automatically determines mount. + * @param path The directory path to search. + * @param iter The directory iterator to fill out with found information. + * @remark The path structure should follow <mount name>:<path> (i.e. romfs:/data/folder). + * If no mount name is provided the default of "romfs" will be used. + */ +Result romfsDirOpen(const char *path, romfs_diriter *iter); + +/** + * @brief Initialises the directory iterator for seaching at the given path in the supplied mount. + * @param mount The RomFS mount to search in. + * @param path The directory path to search. + * @param iter The directory iterator to fill out with found information. + * @remark The mount does not need to be specified in the path and is ignored if provided. + */ +Result romfsDirOpenWithMount(romfs_mount *mount, const char *path, romfs_diriter *iter); + +/** + * @brief Gets the next entry in the directory, returns false on error or when no more entries are found. + * @param iter The directory iterator. + * @param entry The entry to fill out with information. + */ +bool romfsDirNext(romfs_diriter *iter, romfs_direntry *entry); diff --git a/nx/source/runtime/devices/romfs_dev.c b/nx/source/runtime/devices/romfs_dev.c index baad1d775..93aadb7c8 100644 --- a/nx/source/runtime/devices/romfs_dev.c +++ b/nx/source/runtime/devices/romfs_dev.c @@ -22,6 +22,8 @@ typedef enum { RomfsSource_FsStorage, } RomfsSource; +#define ROMFS_MOUNT_NAME_LIMIT 32 + typedef struct romfs_mount { devoptab_t device; @@ -36,7 +38,7 @@ typedef struct romfs_mount romfs_dir *cwd; u32 *dirHashTable, *fileHashTable; void *dirTable, *fileTable; - char name[32]; + char name[ROMFS_MOUNT_NAME_LIMIT]; } romfs_mount; extern int __system_argc; @@ -138,22 +140,6 @@ static int romfs_dirreset(struct _reent *r, DIR_ITER *dirState); static int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat); static int romfs_dirclose(struct _reent *r, DIR_ITER *dirState); -typedef struct -{ - romfs_mount *mount; - romfs_file *file; - u64 offset, pos; -} romfs_fileobj; - -typedef struct -{ - romfs_mount *mount; - romfs_dir* dir; - u32 state; - u32 childDir; - u32 childFile; -} romfs_diriter; - static const devoptab_t romFS_devoptab = { .structSize = sizeof(romfs_fileobj), @@ -205,7 +191,7 @@ static void _romfsInit(void) } } -static romfs_mount *romfsFindMount(const char *name) +static romfs_mount *_romfsFindMount(const char *name) { u32 i; u32 total = sizeof(romfs_mounts) / sizeof(romfs_mount); @@ -236,7 +222,7 @@ __attribute__((weak)) const char* __romfs_path = NULL; static romfs_mount* romfs_alloc(void) { - return romfsFindMount(NULL); + return _romfsFindMount(NULL); } static void romfs_free(romfs_mount *mount) @@ -445,9 +431,9 @@ static void romfsInitMtime(romfs_mount *mount) Result romfsUnmount(const char *name) { romfs_mount *mount; - char tmpname[34]; + char tmpname[ROMFS_MOUNT_NAME_LIMIT + 2]; // +2 for : and \0 - mount = romfsFindMount(name); + mount = _romfsFindMount(name); if (mount == NULL) return MAKERESULT(Module_Libnx, LibnxError_NotFound); @@ -616,46 +602,266 @@ static ino_t file_inode(romfs_mount *mount, romfs_file *file) //----------------------------------------------------------------------------- -int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +Result romfsFindMount(const char *name, romfs_mount **mount) { - romfs_fileobj* fileobj = (romfs_fileobj*)fileStruct; + if (name == NULL || mount == NULL) + { + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } - fileobj->mount = (romfs_mount*)r->deviceData; + *mount = _romfsFindMount(name); + if (*mount == NULL) + { + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + } - if ((flags & O_ACCMODE) != O_RDONLY) + return 0; +} + +Result romfsFindFile(const char *path, romfs_fileobj *file) +{ + // Get the mount name from the path if there is one, otherwise fallback on "romfs" + char mount_name[ROMFS_MOUNT_NAME_LIMIT] = "romfs"; + + char *colon_pos = strchr(path, ':'); + if (colon_pos) { - r->_errno = EROFS; - return -1; + size_t len = (size_t) (colon_pos - path); + len = (len > ROMFS_MOUNT_NAME_LIMIT) ? ROMFS_MOUNT_NAME_LIMIT : len; + + memset(mount_name, 0, sizeof(mount_name)); + memcpy(mount_name, path, len); } - romfs_dir* curDir = NULL; - r->_errno = navigateToDir(fileobj->mount, &curDir, &path, false); - if (r->_errno != 0) - return -1; + // Search for the mount by name + romfs_mount *mount = _romfsFindMount(mount_name); + if (mount == NULL) + { + file->err = ENOENT; + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + } - romfs_file* file = NULL; - int ret = searchForFile(fileobj->mount, curDir, (uint8_t*)path, strlen(path), &file); - if (ret != 0) + return romfsFindFileInMount(mount, path, file); +} + +Result romfsFindFileInMount(romfs_mount *mount, const char *path, romfs_fileobj *file) +{ + // In the event the user has retrieved a handle to a RomFS mount but then unmounted + // it, the mount will no longer be setup so we return with a fail + if (!mount->setup) { - if(ret == ENOENT && (flags & O_CREAT)) - r->_errno = EROFS; - else - r->_errno = ret; - return -1; + file->err = EACCES; + return MAKERESULT(Module_Libnx, LibnxError_BadInput); } - else if((flags & O_CREAT) && (flags & O_EXCL)) + + // Get the directory containing the file, navigateToDir already strips the mount name for us + romfs_dir *dir = 0; + file->err = navigateToDir(mount, &dir, &path, false); + if (file->err != 0) { - r->_errno = EEXIST; - return -1; + if (file->err == ENOENT) + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + // Get the file information from the directory + romfs_file *info = 0; + file->err = searchForFile(mount, dir, (uint8_t *) path, strlen(path), &info); + if (file->err != 0) + { + if (file->err == ENOENT) + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + + return MAKERESULT(Module_Libnx, LibnxError_BadInput); } - fileobj->file = file; - fileobj->offset = fileobj->mount->header.fileDataOff + file->dataOff; - fileobj->pos = 0; + // Fill out the fileobj information + file->mount = mount; + file->file = info; + file->offset = mount->header.fileDataOff + info->dataOff; + file->pos = 0; return 0; } +romfs_fileobj romfsFileObj(romfs_mount *mount, romfs_file *file) +{ + romfs_fileobj result = { 0 }; + result.mount = mount; + result.file = file; + result.offset = mount->header.fileDataOff + file->dataOff; + + return result; +} + +Result romfsReadFile(romfs_fileobj *file, void *buffer, u64 size, u64 offset, u64 *nread) +{ + if (nread == NULL || file == NULL || buffer == NULL) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + if (offset >= file->file->dataSize) + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + + // Truncate read if past end of file + u64 end = offset + size; + if (end > file->file->dataSize) end = file->file->dataSize; + + size = end - offset; + + ssize_t bytes_read = _romfs_read(file->mount, file->offset + offset, buffer, size); + if (bytes_read < 0) + { + return MAKERESULT(Module_Libnx, LibnxError_IoError); + } + + *nread = bytes_read; + return 0; +} + +Result romfsDirOpen(const char *path, romfs_diriter *iter) +{ + char mount_name[ROMFS_MOUNT_NAME_LIMIT] = "romfs"; + char *colon_pos = strchr(path, ':'); + if (colon_pos) + { + size_t len = (size_t) (colon_pos - path); + len = (len > ROMFS_MOUNT_NAME_LIMIT) ? ROMFS_MOUNT_NAME_LIMIT : len; + + memset(mount_name, 0, sizeof(mount_name)); + memcpy(mount_name, path, len); + } + + romfs_mount *mount = _romfsFindMount(mount_name); + if (mount == NULL) + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + + return romfsDirOpenWithMount(mount, path, iter); +} + +Result romfsDirOpenWithMount(romfs_mount *mount, const char *path, romfs_diriter *iter) +{ + romfs_dir *dir = 0; + iter->state = navigateToDir(mount, &dir, &path, true); + if (iter->state != 0) + { + if (iter->state == ENOENT) + return MAKERESULT(Module_Libnx, LibnxError_NotFound); + + return MAKERESULT(Module_Libnx, LibnxError_BadInput); + } + + iter->mount = mount; + iter->dir = dir; + iter->childDir = dir->childDir; + iter->childFile = dir->childFile; + + return 0; +} + +bool romfsDirNext(romfs_diriter *iter, romfs_direntry *entry) +{ + int err = EFAULT; + + iter->state += 1; + + if (iter->state == 1) + { + // relative '.' + entry->type = RomfsDirEntryType_Dir; + entry->dir = iter->dir; + entry->name = "."; + entry->name_len = 1; + + err = 0; + } + else if (iter->state == 2) + { + // relative '..' + romfs_dir *dir = romFS_dir(iter->mount, iter->dir->parent); + if (dir != NULL) + { + entry->type = RomfsDirEntryType_Dir; + entry->dir = dir; + entry->name = ".."; + entry->name_len = 2; + + err = 0; + } + } + else if (iter->childDir != romFS_none) + { + romfs_dir *dir = romFS_dir(iter->mount, iter->childDir); + if (dir != NULL) + { + entry->type = RomfsDirEntryType_Dir; + entry->dir = dir; + entry->name = (const char *) dir->name; + entry->name_len = dir->nameLen; + + iter->childDir = dir->sibling; + + err = 0; + } + } + else if (iter->childFile != romFS_none) + { + romfs_file *file = romFS_file(iter->mount, iter->childFile); + if (file != NULL) + { + entry->type = RomfsDirEntryType_File; + entry->file = file; + entry->name = (const char *) file->name; + entry->name_len = file->nameLen; + + iter->childFile = file->sibling; + + err = 0; + } + } + else + { + err = ENOENT; + } + + if (err != 0) + { + iter->state = err; + } + + return (err == 0); +} + +//----------------------------------------------------------------------------- + +int romfs_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) +{ + int ret = -1; + + romfs_fileobj *fileobj = (romfs_fileobj *) fileStruct; + romfs_mount *mount = (romfs_mount *) r->deviceData; + + if ((flags & O_ACCMODE) != O_RDONLY) + { + r->_errno = EROFS; + } + else if (R_FAILED(romfsFindFileInMount(mount, path, fileobj))) + { + int err = fileobj->err; + r->_errno = (err == ENOENT && (flags & O_CREAT)) ? EROFS : err; + } + else if ((flags & O_CREAT) && (flags & O_EXCL)) + { + r->_errno = EEXIST; + } + else + { + ret = 0; + } + + return ret; +} + int romfs_close(struct _reent *r, void *fd) { return 0; @@ -664,22 +870,16 @@ int romfs_close(struct _reent *r, void *fd) ssize_t romfs_read(struct _reent *r, void *fd, char *ptr, size_t len) { romfs_fileobj* file = (romfs_fileobj*)fd; - u64 endPos = file->pos + len; - /* check if past end-of-file */ - if(file->pos >= file->file->dataSize) + // past end of file + if (file->pos >= file->file->dataSize) return 0; - /* truncate the read to end-of-file */ - if(endPos > file->file->dataSize) - endPos = file->file->dataSize; - len = endPos - file->pos; - - ssize_t adv = _romfs_read(file->mount, file->offset + file->pos, ptr, len); - if(adv >= 0) + u64 nread = 0; + if (R_SUCCEEDED(romfsReadFile(file, ptr, len, file->pos, &nread))) { - file->pos += adv; - return adv; + file->pos += nread; + return nread; } r->_errno = EIO; @@ -822,18 +1022,14 @@ int romfs_chdir(struct _reent *r, const char *path) 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*)r->deviceData; + romfs_diriter *iter = (romfs_diriter *) dirState->dirStruct; + romfs_mount *mount = (romfs_mount *) r->deviceData; - r->_errno = navigateToDir(iter->mount, &curDir, &path, true); - if(r->_errno != 0) + if (R_FAILED(romfsDirOpenWithMount(mount, path, iter))) + { + r->_errno = iter->state; return NULL; - - iter->dir = curDir; - iter->state = 0; - iter->childDir = curDir->childDir; - iter->childFile = curDir->childFile; + } return dirState; } @@ -853,93 +1049,32 @@ int romfs_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct s { romfs_diriter* iter = (romfs_diriter*)(dirState->dirStruct); - if(iter->state == 0) - { - /* '.' entry */ - memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(iter->mount, iter->dir); - filestat->st_mode = romFS_dir_mode; + // Clear to zero + memset(filestat, 0, sizeof(*filestat)); + memset(filename, 0, NAME_MAX); - strcpy(filename, "."); - iter->state = 1; - return 0; - } - else if(iter->state == 1) + romfs_direntry entry; + if (!romfsDirNext(iter, &entry)) { - /* '..' entry */ - romfs_dir* dir = romFS_dir(iter->mount, iter->dir->parent); - if(!dir) - { - r->_errno = EFAULT; - return -1; - } - - memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(iter->mount, dir); - filestat->st_mode = romFS_dir_mode; - - strcpy(filename, ".."); - iter->state = 2; - return 0; + r->_errno = iter->state; + return -1; } - - if(iter->childDir != romFS_none) + else if (entry.name_len >= NAME_MAX) { - romfs_dir* dir = romFS_dir(iter->mount, iter->childDir); - if(!dir) - { - r->_errno = EFAULT; - return -1; - } - - iter->childDir = dir->sibling; - - memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = dir_inode(iter->mount, dir); - filestat->st_mode = romFS_dir_mode; - - memset(filename, 0, NAME_MAX); - - if(dir->nameLen >= NAME_MAX) - { - r->_errno = ENAMETOOLONG; - return -1; - } - - strncpy(filename, (char*)dir->name, dir->nameLen); - - return 0; + r->_errno = iter->state = ENAMETOOLONG; + return -1; } - else if(iter->childFile != romFS_none) - { - romfs_file* file = romFS_file(iter->mount, iter->childFile); - if(!file) - { - r->_errno = EFAULT; - return -1; - } - iter->childFile = file->sibling; + // Copy name into filename + strncpy(filename, entry.name, entry.name_len); - memset(filestat, 0, sizeof(*filestat)); - filestat->st_ino = file_inode(iter->mount, file); - filestat->st_mode = romFS_file_mode; + // Fill out filestat information + bool is_dir = (entry.type == RomfsDirEntryType_Dir); - memset(filename, 0, NAME_MAX); + filestat->st_ino = is_dir ? dir_inode(iter->mount, entry.dir) : file_inode(iter->mount, entry.file); + filestat->st_mode = is_dir ? romFS_dir_mode : romFS_file_mode; - if(file->nameLen >= NAME_MAX) - { - r->_errno = ENAMETOOLONG; - return -1; - } - - strncpy(filename, (char*)file->name, file->nameLen); - - return 0; - } - - r->_errno = ENOENT; - return -1; + return 0; } int romfs_dirclose(struct _reent *r, DIR_ITER *dirState)