2023-09-05 02:09:05 +01:00

911 lines
26 KiB
C

/*
* ext_dev.c
*
* Copyright (c) 2020-2023, DarkMatterCore <pabloacurielz@gmail.com>.
*
* This file is part of libusbhsfs (https://github.com/DarkMatterCore/libusbhsfs).
*
* Loosely based on fs_dev.c from libnx, et al.
*/
#include <sys/param.h>
#include <fcntl.h>
#include "../usbhsfs_manager.h"
#include "../usbhsfs_mount.h"
/* Helper macros. */
#define ext_end goto end
#define ext_ended_with_error (_errno != 0)
#define ext_set_error(x) r->_errno = _errno = (x)
#define ext_set_error_and_exit(x) \
do { \
ext_set_error((x)); \
ext_end; \
} while(0)
#define ext_declare_error_state int _errno = 0
#define ext_declare_file_state ext4_file *file = (ext4_file*)fd
#define ext_declare_dir_state ext4_dir *dir = (ext4_dir*)dirState->dirStruct
#define ext_declare_fs_ctx UsbHsFsDriveLogicalUnitFileSystemContext *fs_ctx = (UsbHsFsDriveLogicalUnitFileSystemContext*)r->deviceData
#define ext_declare_lun_ctx UsbHsFsDriveLogicalUnitContext *lun_ctx = (UsbHsFsDriveLogicalUnitContext*)fs_ctx->lun_ctx
#define ext_declare_drive_ctx UsbHsFsDriveContext *drive_ctx = (UsbHsFsDriveContext*)lun_ctx->drive_ctx
#define ext_declare_vol_state ext_vd *vd = fs_ctx->ext
#define ext_lock_drive_ctx ext_declare_fs_ctx; \
ext_declare_lun_ctx; \
ext_declare_drive_ctx; \
bool drive_ctx_valid = usbHsFsManagerIsDriveContextPointerValid(drive_ctx); \
if (!drive_ctx_valid) ext_set_error_and_exit(ENODEV)
#define ext_unlock_drive_ctx if (drive_ctx_valid) mutexUnlock(&(drive_ctx->mutex))
#define ext_return(x) return (ext_ended_with_error ? -1 : (x))
#define ext_return_ptr(x) return (ext_ended_with_error ? NULL : (x))
#define ext_return_bool return (ext_ended_with_error ? false : true)
/* Function prototypes. */
static int extdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode);
static int extdev_close(struct _reent *r, void *fd);
static ssize_t extdev_write(struct _reent *r, void *fd, const char *ptr, size_t len);
static ssize_t extdev_read(struct _reent *r, void *fd, char *ptr, size_t len);
static off_t extdev_seek(struct _reent *r, void *fd, off_t pos, int dir);
static int extdev_fstat(struct _reent *r, void *fd, struct stat *st);
static int extdev_stat(struct _reent *r, const char *file, struct stat *st);
static int extdev_link(struct _reent *r, const char *existing, const char *newLink);
static int extdev_unlink(struct _reent *r, const char *name);
static int extdev_chdir(struct _reent *r, const char *name);
static int extdev_rename(struct _reent *r, const char *oldName, const char *newName);
static int extdev_mkdir(struct _reent *r, const char *path, int mode);
static DIR_ITER* extdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path);
static int extdev_dirreset(struct _reent *r, DIR_ITER *dirState);
static int extdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
static int extdev_dirclose(struct _reent *r, DIR_ITER *dirState);
static int extdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf);
static int extdev_ftruncate(struct _reent *r, void *fd, off_t len);
static int extdev_fsync(struct _reent *r, void *fd);
static int extdev_chmod(struct _reent *r, const char *path, mode_t mode);
static int extdev_fchmod(struct _reent *r, void *fd, mode_t mode);
static int extdev_rmdir(struct _reent *r, const char *name);
static int extdev_utimes(struct _reent *r, const char *filename, const struct timeval times[2]);
static bool extdev_fixpath(struct _reent *r, const char *path, UsbHsFsDriveLogicalUnitFileSystemContext **fs_ctx, char *outpath);
static void extdev_fill_stat(const struct ext4_inode *inode, u32 st_dev, u32 st_ino, u32 st_blksize, struct stat *st);
static int ext_trans_start(struct ext4_fs *ext_fs);
static int ext_trans_stop(struct ext4_fs *ext_fs);
static void ext_trans_abort(struct ext4_fs *ext_fs);
/* Global variables. */
static const devoptab_t extdev_devoptab = {
.name = NULL,
.structSize = sizeof(ext4_file),
.open_r = extdev_open,
.close_r = extdev_close,
.write_r = extdev_write,
.read_r = extdev_read,
.seek_r = extdev_seek,
.fstat_r = extdev_fstat,
.stat_r = extdev_stat,
.link_r = extdev_link,
.unlink_r = extdev_unlink,
.chdir_r = extdev_chdir,
.rename_r = extdev_rename,
.mkdir_r = extdev_mkdir,
.dirStateSize = sizeof(ext4_dir),
.diropen_r = extdev_diropen,
.dirreset_r = extdev_dirreset,
.dirnext_r = extdev_dirnext,
.dirclose_r = extdev_dirclose,
.statvfs_r = extdev_statvfs,
.ftruncate_r = extdev_ftruncate,
.fsync_r = extdev_fsync, ///< Not supported by lwext4.
.deviceData = NULL,
.chmod_r = extdev_chmod,
.fchmod_r = extdev_fchmod,
.rmdir_r = extdev_rmdir,
.lstat_r = extdev_stat, ///< We'll just alias lstat() to stat().
.utimes_r = extdev_utimes
};
const devoptab_t *extdev_get_devoptab()
{
return &extdev_devoptab;
}
static int extdev_open(struct _reent *r, void *fd, const char *path, int flags, int mode)
{
(void)mode;
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file) ext_set_error_and_exit(EINVAL);
/* Fix input path. */
if (!extdev_fixpath(r, path, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Opening file \"%s\" (\"%s\") with flags 0x%X.", path, __usbhsfs_dev_path_buf, flags);
/* Reset file descriptor. */
memset(file, 0, sizeof(ext4_file));
/* Open file. */
ret = ext4_fopen2(file, __usbhsfs_dev_path_buf, flags);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_close(struct _reent *r, void *fd)
{
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file) ext_set_error_and_exit(EINVAL);
USBHSFS_LOG_MSG("Closing file %u.", file->inode);
/* Close file. */
ret = ext4_fclose(file);
if (ret) ext_set_error_and_exit(ret);
/* Reset file descriptor. */
memset(file, 0, sizeof(ext4_file));
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static ssize_t extdev_write(struct _reent *r, void *fd, const char *ptr, size_t len)
{
size_t bw = 0;
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file || !ptr || !len) ext_set_error_and_exit(EINVAL);
/* Check if the append flag is enabled. */
if ((file->flags & O_APPEND) && ext4_ftell(file) != ext4_fsize(file))
{
/* Seek to EOF. */
ret = ext4_fseek(file, 0, SEEK_END);
if (ret) ext_set_error_and_exit(ret);
}
USBHSFS_LOG_MSG("Writing 0x%lX byte(s) to file %u at offset 0x%lX.", len, file->inode, ext4_ftell(file));
/* Write file data. */
ret = ext4_fwrite(file, ptr, len, &bw);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return((ssize_t)bw);
}
static ssize_t extdev_read(struct _reent *r, void *fd, char *ptr, size_t len)
{
size_t br = 0;
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file || !ptr || !len) ext_set_error_and_exit(EINVAL);
USBHSFS_LOG_MSG("Reading 0x%lX byte(s) from file %u at offset 0x%lX.", len, file->inode, ext4_ftell(file));
/* Read file data. */
ret = ext4_fread(file, ptr, len, &br);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return((ssize_t)br);
}
static off_t extdev_seek(struct _reent *r, void *fd, off_t pos, int dir)
{
off_t offset = 0;
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file) ext_set_error_and_exit(EINVAL);
USBHSFS_LOG_MSG("Seeking 0x%lX byte(s) from current position in file %u.", pos, file->inode);
/* Perform file seek. */
ret = ext4_fseek(file, (s64)pos, (u32)dir);
if (ret) ext_set_error_and_exit(ret);
/* Update current offset. */
offset = (off_t)ext4_ftell(file);
end:
ext_unlock_drive_ctx;
ext_return(offset);
}
static int extdev_fstat(struct _reent *r, void *fd, struct stat *st)
{
struct ext4_inode_ref inode_ref = {0};
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
ext_declare_vol_state;
/* Sanity check. */
if (!file || !st) ext_set_error_and_exit(EINVAL);
/* Get inode reference. */
ret = ext4_fs_get_inode_ref(vd->bdev->fs, file->inode, &inode_ref);
if (ret) ext_set_error_and_exit(ret);
/* Fill stat info. */
extdev_fill_stat(inode_ref.inode, fs_ctx->device_id, file->inode, vd->bdev->lg_bsize, st);
/* Put back inode reference. */
ret = ext4_fs_put_inode_ref(&inode_ref);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_stat(struct _reent *r, const char *file, struct stat *st)
{
u32 inode_num = 0;
struct ext4_inode inode = {0};
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
ext_declare_vol_state;
/* Sanity check. */
if (!st) ext_set_error_and_exit(EINVAL);
/* Fix input path. */
if (!extdev_fixpath(r, file, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Getting stats for \"%s\" (\"%s\").", file, __usbhsfs_dev_path_buf);
/* Get inode. */
ret = ext4_raw_inode_fill(__usbhsfs_dev_path_buf, &inode_num, &inode);
if (ret) ext_set_error_and_exit(ret);
/* Fill stat info. */
extdev_fill_stat(&inode, fs_ctx->device_id, inode_num, vd->bdev->lg_bsize, st);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_link(struct _reent *r, const char *existing, const char *newLink)
{
char existing_path[MAX_PATH_LENGTH] = {0};
char *new_path = __usbhsfs_dev_path_buf;
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input paths. */
if (!extdev_fixpath(r, existing, &fs_ctx, existing_path) || !extdev_fixpath(r, newLink, &fs_ctx, new_path)) ext_end;
USBHSFS_LOG_MSG("Linking \"%s\" (\"%s\") to \"%s\" (\"%s\").", existing, existing_path, newLink, new_path);
/* Create hard link. */
ret = ext4_flink(existing_path, new_path);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_unlink(struct _reent *r, const char *name)
{
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input path. */
if (!extdev_fixpath(r, name, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Deleting \"%s\" (\"%s\").", name, __usbhsfs_dev_path_buf);
/* Delete file. */
ret = ext4_fremove(__usbhsfs_dev_path_buf);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_chdir(struct _reent *r, const char *name)
{
ext4_dir dir = {0};
size_t cwd_len = 0;
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input path. */
if (!extdev_fixpath(r, name, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Changing current directory to \"%s\" (\"%s\").", name, __usbhsfs_dev_path_buf);
/* Open directory. */
ret = ext4_dir_open(&dir, __usbhsfs_dev_path_buf);
if (ret) ext_set_error_and_exit(ret);
/* Close directory. */
ext4_dir_close(&dir);
/* Update current working directory. */
sprintf(fs_ctx->cwd, "%s", strchr(__usbhsfs_dev_path_buf + 1, '/'));
cwd_len = strlen(fs_ctx->cwd);
if (fs_ctx->cwd[cwd_len - 1] != '/')
{
fs_ctx->cwd[cwd_len] = '/';
fs_ctx->cwd[cwd_len + 1] = '\0';
}
/* Set default devoptab device. */
usbHsFsMountSetDefaultDevoptabDevice(fs_ctx);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_rename(struct _reent *r, const char *oldName, const char *newName)
{
char old_path[MAX_PATH_LENGTH] = {0};
char *new_path = __usbhsfs_dev_path_buf;
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input paths. */
if (!extdev_fixpath(r, oldName, &fs_ctx, old_path) || !extdev_fixpath(r, newName, &fs_ctx, new_path)) ext_end;
USBHSFS_LOG_MSG("Renaming \"%s\" (\"%s\") to \"%s\" (\"%s\").", oldName, old_path, newName, new_path);
/* Rename entry. */
ret = ext4_frename(old_path, new_path);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_mkdir(struct _reent *r, const char *path, int mode)
{
(void)mode;
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input path. */
if (!extdev_fixpath(r, path, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Creating directory \"%s\" (\"%s\").", path, __usbhsfs_dev_path_buf);
/* Create directory. */
ret = ext4_dir_mk(__usbhsfs_dev_path_buf);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static DIR_ITER *extdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path)
{
int res = -1;
DIR_ITER *ret = NULL;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!dirState) ext_set_error_and_exit(EINVAL);
ext_declare_dir_state;
/* Fix input path. */
if (!extdev_fixpath(r, path, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Opening directory \"%s\" (\"%s\").", path, __usbhsfs_dev_path_buf);
/* Reset directory state. */
memset(dir, 0, sizeof(ext4_dir));
/* Open directory. */
res = ext4_dir_open(dir, __usbhsfs_dev_path_buf);
if (res) ext_set_error_and_exit(res);
/* Update return value. */
ret = dirState;
end:
ext_unlock_drive_ctx;
ext_return_ptr(ret);
}
static int extdev_dirreset(struct _reent *r, DIR_ITER *dirState)
{
ext_declare_error_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!dirState) ext_set_error_and_exit(EINVAL);
ext_declare_dir_state;
USBHSFS_LOG_MSG("Resetting state from directory %u.", dir->f.inode);
/* Reset directory state. */
ext4_dir_entry_rewind(dir);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat)
{
const ext4_direntry *entry = NULL;
struct ext4_inode_ref inode_ref = {0};
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
ext_declare_vol_state;
/* Sanity check. */
if (!dirState || !filename || !filestat) ext_set_error_and_exit(EINVAL);
ext_declare_dir_state;
USBHSFS_LOG_MSG("Getting info from next entry in directory %u.", dir->f.inode);
while(true)
{
/* Read directory. */
entry = ext4_dir_entry_next(dir);
if (!entry) break;
/* Filter entry types. */
if (entry->inode_type != EXT4_DE_REG_FILE && entry->inode_type != EXT4_DE_DIR && entry->inode_type != EXT4_DE_SYMLINK) continue;
/* Filter dot directory entries. */
if (entry->inode_type == EXT4_DE_DIR && (!strcmp((char*)entry->name, ".") || !strcmp((char*)entry->name, ".."))) continue;
/* Jackpot. */
break;
}
if (!entry) ext_set_error_and_exit(ENOENT); /* ENOENT signals EOD. */
/* Get inode reference. */
ret = ext4_fs_get_inode_ref(vd->bdev->fs, entry->inode, &inode_ref);
if (ret) ext_set_error_and_exit(ret);
/* Fill stat info. */
extdev_fill_stat(inode_ref.inode, fs_ctx->device_id, entry->inode, vd->bdev->lg_bsize, filestat);
/* Put back inode reference. */
ret = ext4_fs_put_inode_ref(&inode_ref);
if (ret) ext_set_error_and_exit(ret);
/* Copy filename. */
strcpy(filename, (char*)entry->name);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_dirclose(struct _reent *r, DIR_ITER *dirState)
{
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!dirState) ext_set_error_and_exit(EINVAL);
ext_declare_dir_state;
USBHSFS_LOG_MSG("Closing directory %u.", dir->f.inode);
/* Close directory. */
ret = ext4_dir_close(dir);
if (ret) ext_set_error_and_exit(ret);
/* Reset directory state. */
memset(dir, 0, sizeof(ext4_dir));
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf)
{
(void)path;
char mount_point[CONFIG_EXT4_MAX_MP_NAME + 3] = {0};
struct ext4_mount_stats mount_stats = {0};
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
ext_declare_vol_state;
/* Sanity check. */
if (!buf) ext_set_error_and_exit(EINVAL);
/* Generate lwext4 mount point. */
sprintf(mount_point, "/%s/", vd->dev_name);
USBHSFS_LOG_MSG("Getting filesystem stats for \"%s\" (\"%s\").", path, mount_point);
/* Get volume information. */
ret = ext4_mount_point_stats(mount_point, &mount_stats);
if (ret) ext_set_error_and_exit(ret);
/* Fill filesystem stats. */
memset(buf, 0, sizeof(struct statvfs));
buf->f_bsize = mount_stats.block_size;
buf->f_frsize = mount_stats.block_size;
buf->f_blocks = mount_stats.blocks_count;
buf->f_bfree = mount_stats.free_blocks_count;
buf->f_bavail = mount_stats.free_blocks_count;
buf->f_files = mount_stats.inodes_count;
buf->f_ffree = mount_stats.free_inodes_count;
buf->f_favail = mount_stats.free_inodes_count;
buf->f_fsid = fs_ctx->device_id;
buf->f_flag = ST_NOSUID;
buf->f_namemax = EXT4_DIRECTORY_FILENAME_LEN;
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_ftruncate(struct _reent *r, void *fd, off_t len)
{
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
/* Sanity check. */
if (!file || len < 0) ext_set_error_and_exit(EINVAL);
USBHSFS_LOG_MSG("Truncating file %u to 0x%lX bytes.", file->inode, len);
/* Truncate file. */
ret = ext4_ftruncate(file, (u64)len);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_fsync(struct _reent *r, void *fd)
{
(void)fd;
/* Not supported by lwext4. */
r->_errno = ENOSYS;
return -1;
}
static int extdev_chmod(struct _reent *r, const char *path, mode_t mode)
{
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input path. */
if (!extdev_fixpath(r, path, &fs_ctx, NULL)) ext_end;
USBHSFS_LOG_MSG("Changing permissions for \"%s\" (\"%s\") to %o.", path, __usbhsfs_dev_path_buf, mode);
/* Change permissions. */
ret = ext4_mode_set(__usbhsfs_dev_path_buf, (u32)mode);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_fchmod(struct _reent *r, void *fd, mode_t mode)
{
struct ext4_fs *ext_fs = NULL;
struct ext4_sblock *sblock = NULL;
struct ext4_inode_ref inode_ref = {0};
u32 orig_mode = 0;
int ret = -1;
ext_declare_error_state;
ext_declare_file_state;
ext_lock_drive_ctx;
ext_declare_vol_state;
ext_fs = vd->bdev->fs;
sblock = &(vd->bdev->fs->sb);
/* Sanity check. */
if (!file) ext_set_error_and_exit(EINVAL);
/* Start journal transfer. */
ret = ext_trans_start(ext_fs);
if (ret) ext_set_error_and_exit(ret);
/* Get inode reference. */
ret = ext4_fs_get_inode_ref(ext_fs, file->inode, &inode_ref);
if (ret)
{
ext_trans_abort(ext_fs);
ext_set_error_and_exit(ret);
}
USBHSFS_LOG_MSG("Changing permissions for file %u to %o.", file->inode, mode);
/* Change permissions. */
orig_mode = ext4_inode_get_mode(sblock, inode_ref.inode);
orig_mode &= ~0xFFF;
orig_mode |= ((u32)mode & 0xFFF);
ext4_inode_set_mode(sblock, inode_ref.inode, orig_mode);
inode_ref.dirty = true;
/* Put back inode reference. */
ret = ext4_fs_put_inode_ref(&inode_ref);
if (ret)
{
ext_trans_abort(ext_fs);
ext_set_error_and_exit(ret);
}
/* Stop journal transfer. */
ret = ext_trans_stop(ext_fs);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static int extdev_rmdir(struct _reent *r, const char *name)
{
/* Exactly the same as extdev_unlink(). */
return extdev_unlink(r, name);
}
static int extdev_utimes(struct _reent *r, const char *filename, const struct timeval times[2])
{
struct timespec ts_times[2] = {0};
int ret = -1;
ext_declare_error_state;
ext_lock_drive_ctx;
/* Fix input path. */
if (!extdev_fixpath(r, filename, &fs_ctx, NULL)) ext_end;
/* Check if we should use the current time. */
if (!times)
{
/* Get current time. */
clock_gettime(CLOCK_REALTIME, &(ts_times[0]));
memcpy(&(ts_times[1]), &(ts_times[0]), sizeof(struct timespec));
} else {
/* Convert provided timeval values to timespec values. */
TIMEVAL_TO_TIMESPEC(&(times[0]), &(ts_times[0]));
TIMEVAL_TO_TIMESPEC(&(times[1]), &(ts_times[1]));
}
USBHSFS_LOG_MSG("Setting last access and modification times for \"%s\" (\"%s\") to 0x%lX and 0x%lX, respectively.", filename, __usbhsfs_dev_path_buf, ts_times[0].tv_sec, ts_times[1].tv_sec);
/* Set access time. */
ret = ext4_atime_set(__usbhsfs_dev_path_buf, (u32)ts_times[0].tv_sec);
if (ret) ext_set_error_and_exit(ret);
/* Set modification time. */
ret = ext4_mtime_set(__usbhsfs_dev_path_buf, (u32)ts_times[1].tv_sec);
if (ret) ext_set_error(ret);
end:
ext_unlock_drive_ctx;
ext_return(0);
}
static bool extdev_fixpath(struct _reent *r, const char *path, UsbHsFsDriveLogicalUnitFileSystemContext **fs_ctx, char *outpath)
{
ext_vd *vd = NULL;
const u8 *p = (const u8*)path;
ssize_t units = 0;
u32 code = 0;
size_t len = 0;
char mount_point[CONFIG_EXT4_MAX_MP_NAME + 3] = {0}, *outptr = (outpath ? outpath : __usbhsfs_dev_path_buf), *cwd = NULL;
ext_declare_error_state;
if (!r || !path || !*path || !fs_ctx || !*fs_ctx || !(vd = (*fs_ctx)->ext) || !(cwd = (*fs_ctx)->cwd)) ext_set_error_and_exit(EINVAL);
USBHSFS_LOG_MSG("Input path: \"%s\".", path);
/* Generate lwext4 mount point. */
sprintf(mount_point, "/%s", vd->dev_name);
/* Move the path pointer to the start of the actual path. */
do {
units = decode_utf8(&code, p);
if (units < 0) ext_set_error_and_exit(EILSEQ);
p += units;
} while(code >= ' ' && code != ':');
/* We found a colon; p points to the actual path. */
if (code == ':') path = (const char*)p;
/* Make sure there are no more colons and that the remainder of the string is valid UTF-8. */
p = (const u8*)path;
do {
units = decode_utf8(&code, p);
if (units < 0) ext_set_error_and_exit(EILSEQ);
if (code == ':') ext_set_error_and_exit(EINVAL);
p += units;
} while(code >= ' ');
/* Verify fixed path length. */
len = (strlen(mount_point) + strlen(path));
if (path[0] != '/') len += strlen(cwd);
if (len >= MAX_PATH_LENGTH) ext_set_error_and_exit(ENAMETOOLONG);
/* Generate fixed path. */
if (path[0] == '/')
{
sprintf(outptr, "%s%s", mount_point, path);
} else {
sprintf(outptr, "%s%s%s", mount_point, cwd, path);
}
USBHSFS_LOG_MSG("Fixed path: \"%s\".", outptr);
end:
ext_return_bool;
}
static void extdev_fill_stat(const struct ext4_inode *inode, u32 st_dev, u32 st_ino, u32 st_blksize, struct stat *st)
{
/* Clear stat struct. */
memset(st, 0, sizeof(struct stat));
/* Fill stat struct. */
st->st_dev = st_dev;
st->st_ino = st_ino;
st->st_mode = inode->mode;
st->st_nlink = inode->links_count;
st->st_uid = inode->uid;
st->st_gid = inode->gid;
if (inode->mode & (EXT4_INODE_MODE_FILE | EXT4_INODE_MODE_SOFTLINK)) st->st_size = ((((u64)inode->size_hi << 0x20) & 0xFFFFFFFF00000000ULL) | (u64)inode->size_lo);
st->st_blksize = st_blksize;
st->st_blocks = inode->blocks_count_lo;
st->st_atim.tv_sec = inode->access_time;
st->st_atim.tv_nsec = inode->atime_extra;
st->st_mtim.tv_sec = inode->modification_time;
st->st_mtim.tv_nsec = inode->mtime_extra;
st->st_ctim.tv_sec = inode->crtime;
st->st_ctim.tv_nsec = inode->crtime_extra;
}
static int ext_trans_start(struct ext4_fs *ext_fs)
{
struct jbd_journal *journal = NULL;
struct jbd_trans *trans = NULL;
int ret = 0;
if (ext_fs->jbd_journal && !ext_fs->curr_trans)
{
journal = ext_fs->jbd_journal;
trans = jbd_journal_new_trans(journal);
if (!trans)
{
ret = ENOMEM;
goto end;
}
ext_fs->curr_trans = trans;
}
end:
return ret;
}
static int ext_trans_stop(struct ext4_fs *ext_fs)
{
struct jbd_journal *journal = NULL;
struct jbd_trans *trans = NULL;
int ret = 0;
if (ext_fs->jbd_journal && ext_fs->curr_trans)
{
journal = ext_fs->jbd_journal;
trans = ext_fs->curr_trans;
ret = jbd_journal_commit_trans(journal, trans);
ext_fs->curr_trans = NULL;
}
return ret;
}
static void ext_trans_abort(struct ext4_fs *ext_fs)
{
struct jbd_journal *journal = NULL;
struct jbd_trans *trans = NULL;
if (ext_fs->jbd_journal && ext_fs->curr_trans)
{
journal = ext_fs->jbd_journal;
trans = ext_fs->curr_trans;
jbd_journal_free_trans(journal, trans, true);
ext_fs->curr_trans = NULL;
}
}