mirror of
https://github.com/Atmosphere-NX/Atmosphere.git
synced 2024-12-02 08:42:15 +00:00
681 lines
20 KiB
C
681 lines
20 KiB
C
/*
|
|
* Copyright (c) 2018-2020 Atmosphère-NX
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <errno.h>
|
|
#include <limits.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <stdio.h>
|
|
#include <time.h>
|
|
#include <fcntl.h>
|
|
#include <sys/iosupport.h>
|
|
#include <sys/param.h>
|
|
#include <unistd.h>
|
|
|
|
#include "../../../fusee/common/fatfs/ff.h"
|
|
|
|
#if FF_VOLUMES != 10
|
|
#error "FatFs misconfigured, expected FF_VOLUMES == 10"
|
|
#endif
|
|
|
|
#include "fs_dev.h"
|
|
/* Quite a bit of code comes from https://github.com/switchbrew/libnx/blob/master/nx/source/runtime/devices/fs_dev.c */
|
|
|
|
static int fsdev_convert_rc(struct _reent *r, FRESULT rc);
|
|
static void fsdev_filinfo_to_st(struct stat *st, const FILINFO *info);
|
|
|
|
static int fsdev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode);
|
|
static int fsdev_close(struct _reent *r, void *fd);
|
|
static ssize_t fsdev_write(struct _reent *r, void *fd, const char *ptr, size_t len);
|
|
static ssize_t fsdev_read(struct _reent *r, void *fd, char *ptr, size_t len);
|
|
static off_t fsdev_seek(struct _reent *r, void *fd, off_t pos, int dir);
|
|
static int fsdev_fstat(struct _reent *r, void *fd, struct stat *st);
|
|
static int fsdev_stat(struct _reent *r, const char *file, struct stat *st);
|
|
static int fsdev_link(struct _reent *r, const char *existing, const char *newLink);
|
|
static int fsdev_unlink(struct _reent *r, const char *name);
|
|
static int fsdev_chdir(struct _reent *r, const char *name);
|
|
static int fsdev_rename(struct _reent *r, const char *oldName, const char *newName);
|
|
static int fsdev_mkdir(struct _reent *r, const char *path, int mode);
|
|
static DIR_ITER* fsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path);
|
|
static int fsdev_dirreset(struct _reent *r, DIR_ITER *dirState);
|
|
static int fsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat);
|
|
static int fsdev_dirclose(struct _reent *r, DIR_ITER *dirState);
|
|
static int fsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf);
|
|
static int fsdev_ftruncate(struct _reent *r, void *fd, off_t len);
|
|
static int fsdev_fsync(struct _reent *r, void *fd);
|
|
static int fsdev_chmod(struct _reent *r, const char *path, mode_t mode);
|
|
static int fsdev_fchmod(struct _reent *r, void *fd, mode_t mode);
|
|
static int fsdev_rmdir(struct _reent *r, const char *name);
|
|
|
|
static devoptab_t g_fsdev_devoptab = {
|
|
.structSize = sizeof(FIL),
|
|
.open_r = fsdev_open,
|
|
.close_r = fsdev_close,
|
|
.write_r = fsdev_write,
|
|
.read_r = fsdev_read,
|
|
.seek_r = fsdev_seek,
|
|
.fstat_r = fsdev_fstat,
|
|
.stat_r = fsdev_stat,
|
|
.link_r = fsdev_link,
|
|
.unlink_r = fsdev_unlink,
|
|
.chdir_r = fsdev_chdir,
|
|
.rename_r = fsdev_rename,
|
|
.mkdir_r = fsdev_mkdir,
|
|
.dirStateSize = sizeof(DIR),
|
|
.diropen_r = fsdev_diropen,
|
|
.dirreset_r = fsdev_dirreset,
|
|
.dirnext_r = fsdev_dirnext,
|
|
.dirclose_r = fsdev_dirclose,
|
|
.statvfs_r = fsdev_statvfs,
|
|
.ftruncate_r = fsdev_ftruncate,
|
|
.fsync_r = fsdev_fsync,
|
|
.deviceData = NULL,
|
|
.chmod_r = fsdev_chmod,
|
|
.fchmod_r = fsdev_fchmod,
|
|
.rmdir_r = fsdev_rmdir,
|
|
};
|
|
|
|
typedef struct fsdev_device_t {
|
|
devoptab_t devoptab;
|
|
device_partition_t devpart;
|
|
FATFS fatfs;
|
|
char name[32+1];
|
|
bool setup, registered;
|
|
} fsdev_device_t;
|
|
|
|
static fsdev_device_t g_fsdev_devices[FF_VOLUMES] = { 0 };
|
|
|
|
/* Required by ff.c */
|
|
/* FF_VOLUMES = 10 */
|
|
#define FKNAM "\xff$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$" /* Workaround for fatfs. */
|
|
|
|
const char *VolumeStr[FF_VOLUMES] = { FKNAM, FKNAM, FKNAM, FKNAM, FKNAM, FKNAM, FKNAM, FKNAM, FKNAM, FKNAM };
|
|
|
|
/* For diskio.c code */
|
|
device_partition_t *g_volume_to_devparts[FF_VOLUMES] = { NULL };
|
|
|
|
static fsdev_device_t *fsdev_find_device(const char *name) {
|
|
for (size_t i = 0; i < FF_VOLUMES; i++) {
|
|
if (g_fsdev_devices[i].setup && strcmp(g_fsdev_devices[i].name, name) == 0) {
|
|
return &g_fsdev_devices[i];
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
int fsdev_mount_device(const char *name, const device_partition_t *devpart, bool initialize_immediately) {
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
FRESULT rc;
|
|
char drname[40];
|
|
|
|
if (device != NULL) {
|
|
errno = EEXIST; /* Device already exists */
|
|
return -1;
|
|
}
|
|
|
|
strcpy(drname, name);
|
|
strcat(drname, ":");
|
|
|
|
if (name[0] == '\0' || strcmp(name, FKNAM) == 0 || devpart == NULL) {
|
|
errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if (strlen(name) > 32) {
|
|
errno = ENAMETOOLONG;
|
|
return -1;
|
|
}
|
|
|
|
/* Find an unused slot. */
|
|
for(size_t i = 0; i < FF_VOLUMES; i++) {
|
|
if (!g_fsdev_devices[i].setup) {
|
|
device = &g_fsdev_devices[i];
|
|
break;
|
|
}
|
|
}
|
|
if (device == NULL) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
}
|
|
|
|
memset(device, 0, sizeof(fsdev_device_t));
|
|
strcpy(device->name, name);
|
|
|
|
device->devoptab = g_fsdev_devoptab;
|
|
device->devpart = *devpart;
|
|
|
|
device->devoptab.name = device->name;
|
|
device->devoptab.deviceData = device;
|
|
|
|
VolumeStr[device - g_fsdev_devices] = device->name;
|
|
g_volume_to_devparts[device - g_fsdev_devices] = &device->devpart;
|
|
|
|
rc = f_mount(&device->fatfs, drname, initialize_immediately ? 1 : 0);
|
|
|
|
if (rc != FR_OK) {
|
|
g_volume_to_devparts[device - g_fsdev_devices] = NULL;
|
|
VolumeStr[device - g_fsdev_devices] = FKNAM;
|
|
return fsdev_convert_rc(NULL, rc);
|
|
}
|
|
|
|
device->setup = true;
|
|
device->registered = false;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fsdev_register_device(const char *name) {
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
if (device == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (device->registered) {
|
|
/* Do nothing if the device is already registered. */
|
|
return 0;
|
|
}
|
|
|
|
if (AddDevice(&device->devoptab) == -1) {
|
|
errno = ENOMEM;
|
|
return -1;
|
|
} else {
|
|
device->registered = true;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int fsdev_unregister_device(const char *name) {
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
char drname[40];
|
|
|
|
if (device == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
if (!device->registered) {
|
|
/* Do nothing if the device is not registered. */
|
|
return 0;
|
|
}
|
|
|
|
strcpy(drname, name);
|
|
strcat(drname, ":");
|
|
|
|
if (RemoveDevice(drname) == -1) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
} else {
|
|
device->registered = false;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
int fsdev_set_default_device(const char *name) {
|
|
int ret = 0;
|
|
int devid;
|
|
char drname[40];
|
|
|
|
strcpy(drname, name);
|
|
strcat(drname, ":");
|
|
devid = FindDevice(drname);
|
|
|
|
if (devid == -1 || fsdev_find_device(name) == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
ret = fsdev_convert_rc(NULL, f_chdrive(drname));
|
|
if (ret == 0) {
|
|
setDefaultDevice(devid);
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fsdev_is_exfat(const char *name) {
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
|
|
if (device != NULL) {
|
|
if (device->registered) {
|
|
return ((device->fatfs.fs_type == FS_EXFAT) ? 1 : 0);
|
|
}
|
|
}
|
|
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
int fsdev_unmount_device(const char *name) {
|
|
int ret;
|
|
char drname[40];
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
|
|
if (device == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
ret = fsdev_unregister_device(name);
|
|
if (ret == -1) {
|
|
return -1;
|
|
}
|
|
|
|
strcpy(drname, name);
|
|
strcat(drname, ":");
|
|
|
|
ret = fsdev_convert_rc(NULL, f_unmount(drname));
|
|
|
|
if (ret == 0) {
|
|
VolumeStr[device - g_fsdev_devices] = FKNAM;
|
|
g_volume_to_devparts[device - g_fsdev_devices] = NULL;
|
|
device->devpart.finalizer(&device->devpart);
|
|
memset(device, 0, sizeof(fsdev_device_t));
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
int fsdev_register_keys(const char *name, unsigned int target_firmware, BisPartition part) {
|
|
fsdev_device_t *device = fsdev_find_device(name);
|
|
|
|
if (device == NULL) {
|
|
errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
derive_bis_key(device->devpart.keys, part, target_firmware);
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fsdev_unmount_all(void) {
|
|
for (size_t i = 0; i < FF_VOLUMES; i++) {
|
|
int ret = fsdev_unmount_device(g_fsdev_devices[i].name);
|
|
if (ret != 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int fsdev_set_attr(const char *file, int attr, int mask) {
|
|
return fsdev_convert_rc(NULL, f_chmod(file, (BYTE)attr, (BYTE)mask));
|
|
}
|
|
|
|
int fsdev_get_attr(const char *file) {
|
|
FILINFO info;
|
|
FRESULT rc = f_stat(file, &info);
|
|
|
|
if (rc == FR_OK) {
|
|
return info.fattrib;
|
|
}
|
|
|
|
return fsdev_convert_rc(NULL, rc);
|
|
}
|
|
|
|
/* Adapted from https://github.com/benemorius/openOBC-devboard/blob/bf0a4a33e22d24e7c299f921d185da27377310e0/lib/fatfs/FatFS.cpp#L173 */
|
|
static int fsdev_convert_rc(struct _reent *r, FRESULT rc) {
|
|
int errornumber;
|
|
switch(rc) {
|
|
case FR_OK: /* (0) Succeeded */
|
|
errornumber = 0;
|
|
break;
|
|
case FR_DISK_ERR: /* (1) A hard error occurred in the low level disk I/O layer */
|
|
errornumber = EIO;
|
|
break;
|
|
case FR_INT_ERR: /* (2) Assertion failed */
|
|
errornumber = EINVAL;
|
|
break;
|
|
case FR_NOT_READY: /* (3) The physical drive cannot work */
|
|
errornumber = EIO;
|
|
break;
|
|
case FR_NO_FILE: /* (4) Could not find the file */
|
|
errornumber = ENOENT;
|
|
break;
|
|
case FR_NO_PATH: /* (5) Could not find the path */
|
|
errornumber = ENOENT;
|
|
break;
|
|
case FR_INVALID_NAME: /* (6) The path name format is invalid */
|
|
errornumber = EINVAL;
|
|
break;
|
|
case FR_DENIED: /* (7) Access denied due to prohibited access or directory full */
|
|
errornumber = EACCES;
|
|
break;
|
|
case FR_EXIST: /* (8) Access denied due to prohibited access */
|
|
errornumber = EEXIST;
|
|
break;
|
|
case FR_INVALID_OBJECT: /* (9) The file/directory object is invalid */
|
|
errornumber = EFAULT;
|
|
break;
|
|
case FR_WRITE_PROTECTED: /* (10) The physical drive is write protected */
|
|
errornumber = EROFS;
|
|
break;
|
|
case FR_INVALID_DRIVE: /* (11) The logical drive number is invalid */
|
|
errornumber = ENODEV;
|
|
break;
|
|
case FR_NOT_ENABLED: /* (12) The volume has no work area */
|
|
errornumber = ENOEXEC;
|
|
break;
|
|
case FR_NO_FILESYSTEM: /* (13) There is no valid FAT volume */
|
|
errornumber = ENFILE;
|
|
break;
|
|
case FR_MKFS_ABORTED: /* (14) The f_mkfs() aborted due to any parameter error */
|
|
errornumber = ENOEXEC;
|
|
break;
|
|
case FR_TIMEOUT: /* (15) Could not get a grant to access the volume within defined period */
|
|
errornumber = EAGAIN;
|
|
break;
|
|
case FR_LOCKED: /* (16) The operation is rejected according to the file sharing policy */
|
|
errornumber = EBUSY;
|
|
break;
|
|
case FR_NOT_ENOUGH_CORE: /* (17) LFN working buffer could not be allocated */
|
|
errornumber = ENOMEM;
|
|
break;
|
|
case FR_TOO_MANY_OPEN_FILES: /* (18) Number of open files > _FS_SHARE */
|
|
errornumber = EMFILE;
|
|
break;
|
|
case FR_INVALID_PARAMETER: /* (19) Given parameter is invalid */
|
|
errornumber = EINVAL;
|
|
break;
|
|
default:
|
|
errornumber = EPERM;
|
|
break;
|
|
}
|
|
|
|
if (r != NULL) {
|
|
r->_errno = errornumber;
|
|
} else {
|
|
errno = errornumber;
|
|
}
|
|
return errornumber == 0 ? 0 : -1;
|
|
}
|
|
|
|
static void fsdev_filinfo_to_st(struct stat *st, const FILINFO *info) {
|
|
/* Date of last modification */
|
|
struct tm date;
|
|
memset(st, 0, sizeof(struct stat));
|
|
date.tm_mday = info->fdate & 31;
|
|
date.tm_mon = ((info->fdate >> 5) & 15) - 1;
|
|
date.tm_year = ((info->fdate >> 9) & 127) - 1980 + 1900;
|
|
|
|
date.tm_sec = (info->ftime << 1) & 63;
|
|
date.tm_min = (info->ftime >> 5) & 63;
|
|
date.tm_hour = (info->ftime >> 11) & 31;
|
|
|
|
date.tm_isdst = 0;
|
|
|
|
st->st_atime = st->st_mtime = st->st_ctime = mktime(&date);
|
|
st->st_size = (off_t)info->fsize;
|
|
st->st_nlink = 1;
|
|
|
|
if (info->fattrib & AM_DIR) {
|
|
st->st_mode = S_IFDIR | S_IRWXU | S_IRWXG | S_IRWXO;
|
|
} else {
|
|
st->st_mode = S_IFREG | S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH;
|
|
}
|
|
|
|
st->st_blksize = 512;
|
|
st->st_blocks = st->st_size / st->st_blksize;
|
|
}
|
|
|
|
static int fsdev_open(struct _reent *r, void *fileStruct, const char *path, int flags, int mode) {
|
|
(void)mode;
|
|
FIL *f = (FIL *)fileStruct;
|
|
|
|
BYTE ff_flags = 0;
|
|
static const struct {
|
|
int posix_flag;
|
|
BYTE ff_flag;
|
|
} flag_mappings[] = {
|
|
{ O_APPEND, FA_OPEN_APPEND },
|
|
{ O_CREAT , FA_OPEN_ALWAYS },
|
|
{ O_EXCL , FA_CREATE_NEW },
|
|
{ O_TRUNC , FA_CREATE_ALWAYS },
|
|
/*{ O_RDONLY, FA_READ }, O_RDONLY is 0*/
|
|
{ O_WRONLY, FA_WRITE },
|
|
{ O_RDWR , FA_READ | FA_WRITE },
|
|
};
|
|
|
|
if ((flags & O_RDWR) & (O_RDONLY | O_WRONLY)) {
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
if ((flags & O_APPEND) && (flags & O_ACCMODE) == O_RDONLY) {
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
for (size_t i = 0; i < sizeof(flag_mappings)/sizeof(flag_mappings[0]); i++) {
|
|
if (flags & flag_mappings[i].posix_flag) {
|
|
ff_flags |= flag_mappings[i].ff_flag;
|
|
}
|
|
}
|
|
if ((flags & O_ACCMODE) == O_RDONLY) {
|
|
ff_flags |= FA_READ;
|
|
}
|
|
|
|
/* Normalize O_CREAT|O_TRUNC */
|
|
if ((ff_flags & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS)) == (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS)) {
|
|
ff_flags &= ~FA_OPEN_ALWAYS;
|
|
}
|
|
|
|
return fsdev_convert_rc(r, f_open(f, path, ff_flags));
|
|
}
|
|
|
|
static int fsdev_close(struct _reent *r, void *fd) {
|
|
FIL *f = (FIL *)fd;
|
|
return fsdev_convert_rc(r, f_close(f));
|
|
}
|
|
|
|
static ssize_t fsdev_write(struct _reent *r, void *fd, const char *ptr, size_t len) {
|
|
FIL *f = (FIL *)fd;
|
|
UINT wr;
|
|
|
|
int ret = fsdev_convert_rc(r, f_write(f, ptr, (UINT)len, &wr));
|
|
return ret == -1 ? -1 : (ssize_t)wr;
|
|
}
|
|
|
|
static ssize_t fsdev_read(struct _reent *r, void *fd, char *ptr, size_t len) {
|
|
FIL *f = (FIL *)fd;
|
|
UINT rd;
|
|
|
|
int ret = fsdev_convert_rc(r, f_read(f, ptr, (UINT)len, &rd));
|
|
return ret == -1 ? -1 : (ssize_t)rd;
|
|
}
|
|
|
|
static off_t fsdev_seek(struct _reent *r, void *fd, off_t pos, int whence) {
|
|
FIL *f = (FIL *)fd;
|
|
FSIZE_t off;
|
|
int ret;
|
|
|
|
switch (whence) {
|
|
case SEEK_SET:
|
|
off = 0;
|
|
break;
|
|
case SEEK_CUR:
|
|
off = f_tell(f);
|
|
break;
|
|
case SEEK_END:
|
|
off = f_size(f);
|
|
break;
|
|
default:
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
if ((FSIZE_t)pos < 0 && (FSIZE_t)pos + off < 0) {
|
|
/* don't allow seek to before the beginning of the file */
|
|
r->_errno = EINVAL;
|
|
return -1;
|
|
}
|
|
|
|
ret = fsdev_convert_rc(r, f_lseek(f, off + (FSIZE_t)pos));
|
|
return ret == -1 ? -1 : (off_t)(off + (FSIZE_t)pos);
|
|
}
|
|
|
|
static int fsdev_fstat(struct _reent *r, void *fd, struct stat *st) {
|
|
(void)fd;
|
|
(void)st;
|
|
|
|
r->_errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int fsdev_stat(struct _reent *r, const char *file, struct stat *st) {
|
|
FILINFO info;
|
|
FRESULT rc = f_stat(file, &info);
|
|
|
|
if (rc == FR_OK) {
|
|
fsdev_filinfo_to_st(st, &info);
|
|
return 0;
|
|
}
|
|
|
|
return fsdev_convert_rc(r, rc);
|
|
}
|
|
|
|
static int fsdev_link(struct _reent *r, const char *existing, const char *newLink) {
|
|
(void)existing;
|
|
(void)newLink;
|
|
r->_errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int fsdev_unlink(struct _reent *r, const char *name) {
|
|
return fsdev_convert_rc(r, f_unlink(name));
|
|
}
|
|
|
|
static int fsdev_chdir(struct _reent *r, const char *name) {
|
|
return fsdev_convert_rc(r, f_chdir(name));
|
|
}
|
|
|
|
static int fsdev_rename(struct _reent *r, const char *oldName, const char *newName) {
|
|
return fsdev_convert_rc(r, f_rename(oldName, newName));
|
|
}
|
|
|
|
static int fsdev_mkdir(struct _reent *r, const char *path, int mode) {
|
|
(void)mode;
|
|
return fsdev_convert_rc(r, f_mkdir(path));
|
|
}
|
|
|
|
static DIR_ITER* fsdev_diropen(struct _reent *r, DIR_ITER *dirState, const char *path) {
|
|
DIR *d = (DIR *)(dirState->dirStruct);
|
|
return fsdev_convert_rc(r, f_opendir(d, path)) == -1 ? NULL : dirState;
|
|
}
|
|
|
|
static int fsdev_dirreset(struct _reent *r, DIR_ITER *dirState) {
|
|
(void)dirState;
|
|
r->_errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int fsdev_dirnext(struct _reent *r, DIR_ITER *dirState, char *filename, struct stat *filestat) {
|
|
FILINFO info;
|
|
DIR *d = (DIR *)(dirState->dirStruct);
|
|
FRESULT rc = f_readdir(d, &info);
|
|
|
|
if (rc == FR_OK) {
|
|
fsdev_filinfo_to_st(filestat, &info);
|
|
|
|
if(info.fname[0] == '\0') {
|
|
/* End of directory */
|
|
r->_errno = ENOENT;
|
|
return -1;
|
|
}
|
|
|
|
strcpy(filename, info.fname);
|
|
return 0;
|
|
} else {
|
|
return fsdev_convert_rc(r, rc);
|
|
}
|
|
}
|
|
|
|
static int fsdev_dirclose(struct _reent *r, DIR_ITER *dirState) {
|
|
DIR *d = (DIR *)(dirState->dirStruct);
|
|
return fsdev_convert_rc(r, f_closedir(d));
|
|
}
|
|
|
|
static int fsdev_statvfs(struct _reent *r, const char *path, struct statvfs *buf) {
|
|
(void)path;
|
|
|
|
DWORD nclust;
|
|
FATFS *fatfs;
|
|
FRESULT rc;
|
|
|
|
rc = f_getfree(path, &nclust, &fatfs);
|
|
|
|
if (rc == FR_OK) {
|
|
#if FF_MAX_SS != FF_MIN_SS
|
|
buf->f_bsize = fatfs->ssize;
|
|
#else
|
|
buf->f_bsize = FF_MAX_SS;
|
|
#endif
|
|
buf->f_frsize = buf->f_bsize;
|
|
buf->f_blocks = (fatfs->n_fatent - 2) * fatfs->csize;
|
|
buf->f_bfree = nclust * fatfs->csize;
|
|
buf->f_bavail = buf->f_bfree;
|
|
buf->f_files = 0;
|
|
buf->f_ffree = 0;
|
|
buf->f_favail = 0;
|
|
buf->f_fsid = 0;
|
|
buf->f_flag = ST_NOSUID;
|
|
buf->f_namemax = FF_LFN_BUF;
|
|
|
|
return 0;
|
|
} else {
|
|
return fsdev_convert_rc(r, rc);
|
|
}
|
|
}
|
|
|
|
static int fsdev_ftruncate(struct _reent *r, void *fd, off_t len) {
|
|
FIL *f = (FIL *)fd;
|
|
FSIZE_t off = f_tell(f); /* No need to validate this */
|
|
FRESULT rc = f_lseek(f, (FSIZE_t)len);
|
|
|
|
off = off > (FSIZE_t)len ? (FSIZE_t)len : off;
|
|
if (rc == FR_OK) {
|
|
rc = f_truncate(f);
|
|
/* Ignore errors while attempting restore the position */
|
|
f_lseek(f, off);
|
|
return fsdev_convert_rc(r, rc);
|
|
} else {
|
|
return fsdev_convert_rc(r, rc);
|
|
}
|
|
}
|
|
|
|
static int fsdev_fsync(struct _reent *r, void *fd) {
|
|
FIL *f = (FIL *)fd;
|
|
return fsdev_convert_rc(r, f_sync(f));
|
|
}
|
|
|
|
static int fsdev_chmod(struct _reent *r, const char *path, mode_t mode) {
|
|
(void)path;
|
|
(void)mode;
|
|
r->_errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int fsdev_fchmod(struct _reent *r, void *fd, mode_t mode) {
|
|
(void)fd;
|
|
(void)mode;
|
|
r->_errno = ENOSYS;
|
|
return -1;
|
|
}
|
|
|
|
static int fsdev_rmdir(struct _reent *r, const char *name) {
|
|
return fsdev_convert_rc(r, f_unlink(name));
|
|
}
|