diff --git a/nx/include/switch/services/fs.h b/nx/include/switch/services/fs.h index 18ee3f02..8d143ff9 100644 --- a/nx/include/switch/services/fs.h +++ b/nx/include/switch/services/fs.h @@ -83,6 +83,15 @@ typedef struct u8 unk_x38[0x28]; ///< Unknown. Usually zeros? } PACKED FsSaveDataInfo; +typedef struct +{ + u64 created; ///< POSIX timestamp. + u64 modified; ///< POSIX timestamp. + u64 accessed; ///< POSIX timestamp. + u8 is_valid; ///< 0x1 when the timestamps are set. + u8 padding[7]; +} PACKED FsTimeStampRaw; + typedef enum { ENTRYTYPE_DIR = 0, ENTRYTYPE_FILE = 1 @@ -211,11 +220,12 @@ Result fsFsOpenDirectory(FsFileSystem* fs, const char* path, int flags, FsDir* o Result fsFsCommit(FsFileSystem* fs); Result fsFsGetFreeSpace(FsFileSystem* fs, const char* path, u64* out); Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out); -Result fsFsCleanDirectoryRecursively(FsFileSystem* fs, const char* path); -Result fsFsQueryEntry(FsFileSystem* fs, void *out, size_t out_size, const void *in, size_t in_size, const char* path, FsFileSystemQueryType query_type); +Result fsFsGetFileTimeStampRaw(FsFileSystem* fs, const char* path, FsTimeStampRaw *out);/// 3.0.0+ +Result fsFsCleanDirectoryRecursively(FsFileSystem* fs, const char* path);/// 3.0.0+ +Result fsFsQueryEntry(FsFileSystem* fs, void *out, size_t out_size, const void *in, size_t in_size, const char* path, FsFileSystemQueryType query_type);/// 4.0.0+ void fsFsClose(FsFileSystem* fs); -/// Uses fsFsQueryEntry to set the archive bit on the specified absolute directory path. +/// Uses \ref fsFsQueryEntry to set the archive bit on the specified absolute directory path. /// This will cause HOS to treat the directory as if it were a file containing the directory's concatenated contents. Result fsFsSetArchiveBit(FsFileSystem* fs, const char *path); diff --git a/nx/source/runtime/devices/fs_dev.c b/nx/source/runtime/devices/fs_dev.c index aed1ec3e..deced87d 100644 --- a/nx/source/runtime/devices/fs_dev.c +++ b/nx/source/runtime/devices/fs_dev.c @@ -55,6 +55,7 @@ typedef struct FsFile fd; int flags; /*! Flags used in open(2) */ u64 offset; /*! Current file offset */ + FsTimeStampRaw timestamps; } fsdev_file_t; /*! fsdev devoptab */ @@ -581,6 +582,10 @@ fsdev_open(struct _reent *r, file->fd = fd; file->flags = (flags & (O_ACCMODE|O_APPEND|O_SYNC)); file->offset = 0; + + memset(&file->timestamps, 0, sizeof(file->timestamps)); + rc = fsFsGetFileTimeStampRaw(&device->fs, fs_path, &file->timestamps);//Result can be ignored since output is only set on success, etc. + return 0; } @@ -924,6 +929,14 @@ fsdev_fstat(struct _reent *r, st->st_size = (off_t)size; st->st_nlink = 1; st->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH; + + if(file->timestamps.is_valid) + { + st->st_ctime = file->timestamps.created; + st->st_mtime = file->timestamps.modified; + st->st_atime = file->timestamps.accessed; + } + return 0; } @@ -948,8 +961,10 @@ fsdev_stat(struct _reent *r, FsFile fd; FsDir fdir; Result rc; + int ret=0; char fs_path[FS_MAX_PATH]; fsdev_fsdevice *device = NULL; + FsTimeStampRaw timestamps = {0}; FsEntryType type; if(fsdev_getfspath(r, file, &device, fs_path)==-1) @@ -974,10 +989,21 @@ fsdev_stat(struct _reent *r, if(R_SUCCEEDED(rc = fsFsOpenFile(&device->fs, fs_path, FS_OPEN_READ, &fd))) { fsdev_file_t tmpfd = { .fd = fd }; - rc = fsdev_fstat(r, &tmpfd, st); + ret = fsdev_fstat(r, &tmpfd, st); fsFileClose(&fd); - return rc; + if(ret==0) + { + rc = fsFsGetFileTimeStampRaw(&device->fs, fs_path, ×tamps); + if(R_SUCCEEDED(rc) && timestamps.is_valid) + { + st->st_ctime = timestamps.created; + st->st_mtime = timestamps.modified; + st->st_atime = timestamps.accessed; + } + } + + return ret; } } else diff --git a/nx/source/services/fs.c b/nx/source/services/fs.c index d84e1eff..8619c4f9 100644 --- a/nx/source/services/fs.c +++ b/nx/source/services/fs.c @@ -975,6 +975,9 @@ Result fsFsGetTotalSpace(FsFileSystem* fs, const char* path, u64* out) { } Result fsFsCleanDirectoryRecursively(FsFileSystem* fs, const char* path) { + if (!kernelAbove300()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + IpcCommand c; ipcInitialize(&c); ipcAddSendStatic(&c, path, FS_MAX_PATH, 0); @@ -1007,6 +1010,48 @@ Result fsFsCleanDirectoryRecursively(FsFileSystem* fs, const char* path) { return rc; } +Result fsFsGetFileTimeStampRaw(FsFileSystem* fs, const char* path, FsTimeStampRaw *out) { + if (!kernelAbove300()) + return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer); + + char send_path[FS_MAX_PATH] = {0}; + strncpy(send_path, path, sizeof(send_path)-1); + + IpcCommand c; + ipcInitialize(&c); + ipcAddSendStatic(&c, send_path, sizeof(send_path), 0); + + struct { + u64 magic; + u64 cmd_id; + } *raw; + + raw = serviceIpcPrepareHeader(&fs->s, &c, sizeof(*raw)); + + raw->magic = SFCI_MAGIC; + raw->cmd_id = 14; + + Result rc = serviceIpcDispatch(&fs->s); + + if (R_SUCCEEDED(rc)) { + IpcParsedCommand r; + struct { + u64 magic; + u64 result; + FsTimeStampRaw out; + } *resp; + + serviceIpcParse(&fs->s, &r, sizeof(*resp)); + resp = r.Raw; + + rc = resp->result; + + if (R_SUCCEEDED(rc) && out) *out = resp->out; + } + + return rc; +} + Result fsFsQueryEntry(FsFileSystem* fs, void *out, size_t out_size, const void *in, size_t in_size, const char* path, FsFileSystemQueryType query_type) { if (!kernelAbove400()) return MAKERESULT(Module_Libnx, LibnxError_IncompatSysVer);