Files
pspsdk/src/libcglue/glue.c

1232 lines
26 KiB
C
Executable File

/*
* PSP Software Development Kit - https://github.com/pspdev
* -----------------------------------------------------------------------
* Licensed under the BSD license, see LICENSE in PSPSDK root for details.
*
* glue.c - Newlib-compatible system calls.
*
* Copyright (c) 2005 Marcus R. Brown <mrbrown@ocgnet.org>
* Copyright (c) 2005 James Forshaw <tyranid@gmail.com>
* Copyright (c) 2005 John Kelley <ps2dev@kelley.ca>
* Copyright (c) 2005 Jim Paris <jim@jtan.com>
* Copyright (c) 2021 Francisco J Trujillo <fjtrujy@gmail.com>
*
*/
#include <stdio.h>
#include <errno.h>
#include <malloc.h>
#include <stdarg.h>
#include <string.h>
#include <time.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <pwd.h>
#include <sys/time.h>
#include <sys/timeb.h>
#include <sys/times.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/statvfs.h>
#include <psptypes.h>
#include <pspiofilemgr.h>
#include <pspmodulemgr.h>
#include <pspsysmem.h>
#include <pspthreadman.h>
#include <psputils.h>
#include <pspsdk.h>
#include <psprtc.h>
#include <psputility.h>
#include "fdman.h"
#define DEFAULT_HEAP_THRESHOLD_SIZE_KB 512
/* If defined it specifies the desired size of the heap, in KB. */
extern unsigned int sce_newlib_heap_kb_size __attribute__((weak));
extern unsigned int sce_newlib_heap_threshold_kb_size __attribute__((weak));
/* Functions from cwd.c */
extern char __cwd[MAXNAMLEN + 1];
int __get_drive(const char *d);
int __path_absolute(const char *in, char *out, int len);
/* Functions from mutexman.c */
extern SceLwMutexWorkarea __sbrk_mutex;
/* Fuctions from errno.c */
int __set_errno(int code);
/* Functions from pipe.c */
int __pipe_read(int fd, void *buf, size_t len);
int __pipe_close(int fd);
int __pipe_write(int fd, const void *buf, size_t len);
int __pipe_nonblocking_read(int fd, void *buf, size_t len);
int __pipe_nonblocking_write(int fd, const void *buf, size_t len);
/* Functions from socket.c */
int __socket_close(int sock);
#ifdef F___dummy_passwd
/* the present working directory variable. */
struct passwd __dummy_passwd = { "psp_user", "xxx", 1000, 1000, "", "", "/", "" };
#else
extern struct passwd __dummy_passwd;
#endif
#ifdef F___psp_heap_blockid
/* UID of the memory block that represents the heap. */
SceUID __psp_heap_blockid;
#else
extern SceUID __psp_heap_blockid;
#endif
#ifdef F___psp_free_heap
/* Free the heap. */
int __psp_free_heap(void)
{
if (__psp_heap_blockid > 0) {
return sceKernelFreePartitionMemory(__psp_heap_blockid);
}
return __psp_heap_blockid;
}
#endif
#ifdef F__open
int _open(const char *buf, int flags, int mode) {
int scefd, fd, sce_flags, is_dir;
char dest[MAXNAMLEN + 1];
if(__path_absolute(buf, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
is_dir = 0;
/* O_RDONLY starts at 0, where PSP_O_RDONLY starts at 1, so remap the read/write
flags by adding 1. */
sce_flags = (flags & O_ACCMODE) + 1;
/* Translate standard open flags into the flags understood by the PSP kernel. */
if (flags & O_APPEND) {
sce_flags |= PSP_O_APPEND;
}
if (flags & O_CREAT) {
sce_flags |= PSP_O_CREAT;
}
if (flags & O_TRUNC) {
sce_flags |= PSP_O_TRUNC;
}
if (flags & O_EXCL) {
sce_flags |= PSP_O_EXCL;
}
if (flags & O_NONBLOCK) {
sce_flags |= PSP_O_NBLOCK;
}
if (flags & O_DIRECTORY) {
sce_flags |= PSP_O_DIR;
is_dir = 1;
}
scefd = is_dir ? sceIoDopen(dest) : sceIoOpen(dest, sce_flags, mode);
if (scefd >= 0) {
fd = __fdman_get_new_descriptor();
if (fd != -1) {
__descriptormap[fd]->descriptor = scefd;
__descriptormap[fd]->type = is_dir ? __DESCRIPTOR_TYPE_FOLDER : __DESCRIPTOR_TYPE_FILE;
__descriptormap[fd]->flags = flags;
__descriptormap[fd]->filename = strdup(dest);
return fd;
}
else {
is_dir ? sceIoDclose(scefd) : sceIoClose(scefd);
errno = ENOMEM;
return -1;
}
}
else {
return __set_errno(scefd);
}
}
#else
int _open(const char *buf, int flags, int mode);
#endif
#ifdef F__close
int _close(int fd) {
int ret = 0;
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_FILE:
case __DESCRIPTOR_TYPE_TTY:
if (__descriptormap[fd]->ref_count == 1) {
ret = __set_errno(sceIoClose(__descriptormap[fd]->descriptor));
}
__fdman_release_descriptor(fd);
return ret;
break;
case __DESCRIPTOR_TYPE_FOLDER:
if (__descriptormap[fd]->ref_count == 1) {
ret = __set_errno(sceIoDclose(__descriptormap[fd]->descriptor));
}
__fdman_release_descriptor(fd);
return ret;
break;
case __DESCRIPTOR_TYPE_PIPE:
return __pipe_close(fd);
break;
case __DESCRIPTOR_TYPE_SOCKET:
return __socket_close(fd);
break;
default:
break;
}
errno = EBADF;
return -1;
}
#endif
#ifdef F__read
int _read(int fd, void *buf, size_t nbytes) {
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_FILE:
case __DESCRIPTOR_TYPE_TTY:
return __set_errno(sceIoRead(__descriptormap[fd]->descriptor, buf, nbytes));
break;
case __DESCRIPTOR_TYPE_PIPE:
if (__descriptormap[fd]->flags & O_NONBLOCK) {
return __pipe_nonblocking_read(fd, buf, nbytes);
}
else {
return __pipe_read(fd, buf, nbytes);
}
break;
case __DESCRIPTOR_TYPE_SOCKET:
return recv(fd, buf, nbytes, 0);
break;
default:
break;
}
errno = EBADF;
return -1;
}
#endif
#ifdef F__write
int _write(int fd, const void *buf, size_t nbytes) {
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_FILE:
case __DESCRIPTOR_TYPE_TTY:
return __set_errno(sceIoWrite(__descriptormap[fd]->descriptor, buf, nbytes));
break;
case __DESCRIPTOR_TYPE_PIPE:
if (__descriptormap[fd]->flags & O_NONBLOCK) {
return __pipe_nonblocking_write(fd, buf, nbytes);
}
else {
return __pipe_write(fd, buf, nbytes);
}
break;
break;
case __DESCRIPTOR_TYPE_SOCKET:
return send(fd, buf, nbytes, 0);
break;
default:
break;
}
errno = EBADF;
return -1;
}
#endif
#ifdef F__stat
static time_t psp_to_posix_time(ScePspDateTime psp_time)
{
struct tm conv_time;
conv_time.tm_year = psp_time.year;
conv_time.tm_mon = psp_time.month;
conv_time.tm_mday = psp_time.day;
conv_time.tm_hour = psp_time.hour;
conv_time.tm_min = psp_time.minute;
conv_time.tm_sec = psp_time.second;
conv_time.tm_isdst = -1;
return mktime(&conv_time);
}
static mode_t io_to_posix_mode(SceMode sceMode)
{
mode_t posixmode = 0;
if (sceMode & FIO_S_IFREG) posixmode |= S_IFREG;
if (sceMode & FIO_S_IFDIR) posixmode |= S_IFDIR;
if (sceMode & FIO_S_IROTH) posixmode |= S_IRUSR|S_IRGRP|S_IROTH;
if (sceMode & FIO_S_IWOTH) posixmode |= S_IWUSR|S_IWGRP|S_IWOTH;
if (sceMode & FIO_S_IXOTH) posixmode |= S_IXUSR|S_IXGRP|S_IXOTH;
return posixmode;
}
static void __fill_stat(struct stat *stat, const SceIoStat *sce_stat)
{
stat->st_dev = 0;
stat->st_ino = 0;
stat->st_mode = io_to_posix_mode(sce_stat->st_mode);
stat->st_nlink = 0;
stat->st_uid = 0;
stat->st_gid = 0;
stat->st_rdev = 0;
stat->st_size = sce_stat->st_size;
stat->st_atime = psp_to_posix_time(sce_stat->sce_st_atime);
stat->st_mtime = psp_to_posix_time(sce_stat->sce_st_mtime);
stat->st_ctime = psp_to_posix_time(sce_stat->sce_st_ctime);
stat->st_blksize = 16*1024;
stat->st_blocks = stat->st_size / 512;
}
int _stat(const char *filename, struct stat *buf)
{
SceIoStat psp_stat;
char dest[MAXNAMLEN + 1];
int ret;
if(__path_absolute(filename, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
memset(buf, '\0', sizeof(struct stat));
ret = sceIoGetstat(dest, &psp_stat);
if (ret < 0) {
return __set_errno(ret);
}
__fill_stat(buf, &psp_stat);
return 0;
}
#else
int _stat(const char *filename, struct stat *buf);
#endif
#ifdef F_lstat
int lstat(const char *filename, struct stat *buf)
{
return stat(filename, buf);
}
#endif
#ifdef F__fstat
int _fstat(int fd, struct stat *buf) {
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_TTY:
memset(buf, '\0', sizeof(struct stat));
buf->st_mode = S_IFCHR;
return 0;
break;
case __DESCRIPTOR_TYPE_FILE:
if (__descriptormap[fd]->filename != NULL) {
return _stat(__descriptormap[fd]->filename, buf);
}
break;
case __DESCRIPTOR_TYPE_PIPE:
case __DESCRIPTOR_TYPE_SOCKET:
default:
break;
}
errno = EBADF;
return -1;
}
#endif
#ifdef F_access
int access(const char *fn, int flags)
{
struct stat s;
if (stat(fn, &s))
return -1;
if (s.st_mode & S_IFDIR)
return 0;
if (flags & W_OK)
{
if (s.st_mode & S_IWRITE)
return 0;
errno = EACCES;
return -1;
}
return 0;
}
#endif
#ifdef F__fcntl
int _fcntl(int fd, int cmd, ...)
{
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch (cmd)
{
case F_DUPFD:
{
return __fdman_get_dup_descriptor(fd);
break;
}
case F_GETFL:
{
return __descriptormap[fd]->flags;
break;
}
case F_SETFL:
{
int newfl;
va_list args;
va_start (args, cmd); /* Initialize the argument list. */
newfl = va_arg(args, int);
va_end (args); /* Clean up. */
__descriptormap[fd]->flags = newfl;
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_FILE:
break;
case __DESCRIPTOR_TYPE_PIPE:
break;
case __DESCRIPTOR_TYPE_SOCKET:
if (newfl & O_NONBLOCK)
{
int one = 1;
return setsockopt(fd, SOL_SOCKET, SO_NONBLOCK, (char *)&one, sizeof(one));
}
else
{
int zero = 0;
return setsockopt(fd, SOL_SOCKET, SO_NONBLOCK, (char *)&zero, sizeof(zero));
}
break;
default:
break;
}
return 0;
break;
}
case F_SETFD:
{
int newfl;
va_list args;
va_start (args, cmd); /* Initialize the argument list. */
newfl = va_arg(args, int);
va_end (args); /* Clean up. */
__descriptormap[fd]->flags = newfl;
return 0;
break;
}
}
errno = EBADF;
return -1;
}
#endif /* F__fcntl */
#ifdef F_getdents
int getdents(int fd, void *dd_buf, int count)
{
struct dirent *dirp;
int rv, read;
SceIoDirent sceiode;
read = 0;
// NEEDED otherwise it will crash!!!
memset(&sceiode, 0, sizeof(SceIoDirent));
rv = sceIoDread(__descriptormap[fd]->descriptor, &sceiode);
if (rv < 0) {
return __set_errno(rv);
} else if (rv == 0) {
return read;
}
read += sizeof(struct dirent);
dirp = (struct dirent *)dd_buf;
dirp->d_fileno = rv;
strncpy(dirp->d_name, sceiode.d_name, MAXNAMLEN);
dirp->d_name[MAXNAMLEN] = 0;
dirp->d_reclen = count;
switch (sceiode.d_stat.st_mode & FIO_S_IFMT) {
case FIO_S_IFLNK: dirp->d_type = DT_LNK; break;
case FIO_S_IFDIR: dirp->d_type = DT_DIR; break;
case FIO_S_IFREG: dirp->d_type = DT_REG; break;
default: dirp->d_type = DT_UNKNOWN; break;
}
return read;
}
#endif
#ifdef F__lseek
static off_t _lseekDir(int fd, off_t offset, int whence)
{
int i;
SceUID uid;
SceIoDirent sceiode;
if (whence != SEEK_SET || __descriptormap[fd]->filename == NULL) {
errno = EINVAL;
return -1;
}
sceIoDclose(__descriptormap[fd]->descriptor);
uid = sceIoDopen(__descriptormap[fd]->filename);
__descriptormap[fd]->descriptor = uid;
for (i = 0; i < offset; i++) {
// NEEDED otherwise it will crash!!!
memset(&sceiode, 0, sizeof(SceIoDirent));
sceIoDread(uid, &sceiode);
}
return offset;
}
off_t _lseek(int fd, off_t offset, int whence)
{
if (!__IS_FD_VALID(fd)) {
errno = EBADF;
return -1;
}
switch(__descriptormap[fd]->type)
{
case __DESCRIPTOR_TYPE_FILE:
/* We don't have to do anything with the whence argument because SEEK_* == PSP_SEEK_*. */
return (off_t) __set_errno(sceIoLseek(__descriptormap[fd]->descriptor, offset, whence));
break;
case __DESCRIPTOR_TYPE_FOLDER:
return _lseekDir(fd, offset, whence);
break;
case __DESCRIPTOR_TYPE_PIPE:
break;
case __DESCRIPTOR_TYPE_SOCKET:
break;
default:
break;
}
errno = EBADF;
return -1;
}
#endif
#ifdef F_chdir
int chdir(const char *path)
{
char dest[MAXNAMLEN + 1];
SceUID uid;
if(__path_absolute(path, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
/* sceIoChdir doesn't give an indication of whether it worked,
so test for existence by attempting to open the dir */
uid = sceIoDopen(dest);
if(uid < 0) {
errno = ENOTDIR;
return -1;
}
sceIoDclose(uid);
sceIoChdir(dest);
strcpy(__cwd, dest);
return 0;
}
#endif
#ifdef F_mkdir
int mkdir(const char *pathname, mode_t mode)
{
char dest[MAXNAMLEN + 1];
if(__path_absolute(pathname, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
return __set_errno(sceIoMkdir(dest, mode));
}
#endif
#ifdef F_rmdir
int rmdir(const char *pathname)
{
char dest[MAXNAMLEN + 1];
if(__path_absolute(pathname, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
return __set_errno(sceIoRmdir(dest));
}
#endif
#ifdef F__link
int _link(const char *old, const char *new) {
errno = ENOSYS;
return -1; /* not supported */
}
#endif
#ifdef F__unlink
int _unlink(const char *path) {
char dest[MAXNAMLEN + 1];
if(__path_absolute(path, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
return __set_errno(sceIoRemove(dest));
}
#endif
#ifdef F__rename
int _rename(const char *old, const char *new)
{
char oldname[MAXNAMLEN + 1];
char newname[MAXNAMLEN + 1];
if(__path_absolute(old, oldname, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
if(__path_absolute(new, newname, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
return __set_errno(sceIoRename(oldname, newname));
}
#endif
#ifdef F__getpid
pid_t _getpid(void)
{
return (pid_t)__set_errno(sceKernelGetThreadId());
}
#endif
#ifdef F__kill
int _kill(pid_t pid, int sig)
{
return __set_errno(sceKernelDeleteThread((SceUID)pid));
}
#endif
#ifdef F__fork
pid_t _fork(void)
{
errno = ENOSYS;
return (pid_t) -1; /* not supported */
}
#endif
#ifdef F__wait
pid_t _wait(int *unused)
{
errno = ENOSYS;
return (pid_t) -1; /* not supported */
}
#endif
#ifdef F__execve
int _execve(const char *name, char *const argv[], char *const env[])
{
errno = ENOSYS;
return (pid_t) -1; /* not supported */
}
#endif
#ifdef F__sbrk
#define TO_KB(value) (value * 1024)
void * _sbrk(ptrdiff_t incr)
{
static void * heap_bottom = NULL;
static void * heap_top = NULL;
static void * heap_ptr = NULL;
sceKernelLockLwMutex(&__sbrk_mutex, 1, 0);
/* Has our heap been initialized? */
if (heap_bottom == NULL) {
/* No, initialize the heap. */
SceSize heap_size = (SceSize) -1;
if (&sce_newlib_heap_kb_size != NULL) {
heap_size = TO_KB(sce_newlib_heap_kb_size);
}
if ((int)heap_size < 0) {
heap_size = sceKernelMaxFreeMemSize();
if(&sce_newlib_heap_threshold_kb_size != NULL)
heap_size -= TO_KB(sce_newlib_heap_threshold_kb_size);
else
heap_size -= TO_KB(DEFAULT_HEAP_THRESHOLD_SIZE_KB);
}
if (heap_size != 0) {
__psp_heap_blockid = sceKernelAllocPartitionMemory(PSP_MEMORY_PARTITION_USER, "heap_block", PSP_SMEM_Low, heap_size, NULL);
if (__psp_heap_blockid > 0) {
heap_bottom = sceKernelGetBlockHeadAddr(__psp_heap_blockid);
heap_ptr = heap_bottom;
heap_top = (unsigned char *) heap_bottom + heap_size;
}
}
}
void * heap_addr = (void *) -1;
void * next_heap_ptr = (void *) ((ptrdiff_t) heap_ptr + incr);
if ((heap_bottom != NULL) && (next_heap_ptr >= heap_bottom) && (next_heap_ptr < heap_top)) {
heap_addr = heap_ptr;
heap_ptr = next_heap_ptr;
}
sceKernelUnlockLwMutex(&__sbrk_mutex, 1);
return heap_addr;
}
#endif
#ifdef F__gettimeofday
int _gettimeofday(struct timeval *tp, struct timezone *tzp)
{
int ret;
struct SceKernelTimeval pspTimeval;
ret = __set_errno(sceKernelLibcGettimeofday(&pspTimeval, tzp));
if (ret < 0)
return ret;
/* Return the actual time since epoch */
tp->tv_sec = pspTimeval.tv_sec;
tp->tv_usec = pspTimeval.tv_usec;
return 0;
}
#endif
#ifdef F__times
clock_t _times(struct tms *buffer)
{
clock_t clk = sceKernelGetSystemTimeWide();
if (buffer != NULL) {
buffer->tms_utime = clk;
buffer->tms_stime = 0;
buffer->tms_cutime = 0;
buffer->tms_cstime = 0;
}
return clk;
}
#endif
#ifdef F_ftime
int ftime(struct timeb *tb)
{
struct timeval tv;
struct timezone tz;
gettimeofday(&tv, &tz);
tb->time = tv.tv_sec;
tb->millitm = tv.tv_usec / 1000;
tb->timezone = tz.tz_minuteswest;
tb->dstflag = tz.tz_dsttime;
return 0;
}
#endif
#ifdef F_clock_getres
int clock_getres(clockid_t clk_id, struct timespec *res)
{
int ret;
struct SceKernelTimeval pspTimeval;
ret = __set_errno(sceKernelLibcGettimeofday(&pspTimeval, NULL));
if (ret < 0)
return ret;
/* Return the actual time since epoch */
res->tv_sec = pspTimeval.tv_sec;
res->tv_nsec = 1000 * pspTimeval.tv_usec;
return 0;
}
#endif
#ifdef F_clock_gettime
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
int ret;
struct SceKernelTimeval pspTimeval;
ret = __set_errno(sceKernelLibcGettimeofday(&pspTimeval, NULL));
if (ret < 0)
return ret;
/* Return the actual time since epoch */
tp->tv_sec = pspTimeval.tv_sec;
tp->tv_nsec = 1000 * pspTimeval.tv_usec;
return 0;
}
#endif
#ifdef F_clock_settime
int clock_settime(clockid_t clk_id, const struct timespec *tp)
{
return 0;
}
#endif
// Some POSIX functions that are missing in NEWLIB
#ifdef F_symlink
int symlink(const char *path1, const char *path2)
{
return link(path1, path2);
}
#endif
#ifdef F_truncate
int truncate(const char *path, off_t length)
{
ssize_t bytes_read;
int fd;
char buff[length];
fd = open(path, O_RDONLY);
if (fd < 0) {
return -1;
}
bytes_read = read(fd, &buff, length);
close(fd);
if (bytes_read < length) {
errno = EFBIG;
return -1;
}
fd = open (path, O_TRUNC|O_WRONLY);
if (fd < 0) {
return -1;
}
write(fd, &buff, length);
close(fd);
return 0;
}
#endif
#ifdef F__isatty
int _isatty(int fd)
{
errno = ENOTTY;
return -1; /* not supported */
}
#endif
#ifdef F_chmod
int chmod(const char *pathname, mode_t mode)
{
SceIoStat psp_stat;
char dest[MAXNAMLEN + 1];
int ret;
if(__path_absolute(pathname, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
ret = sceIoGetstat(dest, &psp_stat);
if (ret < 0) {
return __set_errno(ret);
}
psp_stat.st_mode = (SceMode)mode;
ret = sceIoChstat(dest, &psp_stat, 0x0001);
if (ret < 0) {
return __set_errno(ret);
}
return 0;
}
#endif
#ifdef F_fchmod
int fchmod(int fd, mode_t mode)
{
return chmod(__descriptormap[fd]->filename, mode);
}
#endif
#ifdef F_pathconf
long int pathconf(const char *path, int name) {
errno = ENOSYS;
return -1; /* not supported */
}
#endif
#ifdef F_readlink
ssize_t readlink(const char *path, char *buf, size_t bufsiz) {
errno = ENOSYS;
return -1; /* not supported */
}
#endif
#ifdef F_utime
struct utimbuf {
time_t actime; /* Access time */
time_t modtime; /* Modification time */
};
int utime(const char *pathname, const struct utimbuf *times)
{
SceIoStat psp_stat;
char dest[MAXNAMLEN + 1];
int ret;
if(__path_absolute(pathname, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
ret = sceIoGetstat(dest, &psp_stat);
if (ret < 0) {
return __set_errno(ret);
}
ret = sceRtcSetTime_t(&psp_stat.sce_st_atime, times->actime);
if (ret < 0) {
return __set_errno(ret);
}
ret = sceRtcSetTime_t(&psp_stat.sce_st_mtime, times->modtime);
if (ret < 0) {
return __set_errno(ret);
}
ret = sceIoChstat(dest, &psp_stat, 0x0030);
if (ret < 0) {
return __set_errno(ret);
}
return 0;
}
#endif
#ifdef F_fchown
int fchown(int fd, uid_t owner, gid_t group)
{
errno = ENOSYS;
return -1; /* not supported */
}
#endif
#ifdef F__getentropy
int _getentropy(void *buffer, size_t length) {
size_t i;
SceKernelUtilsMt19937Context ctx;
sceKernelUtilsMt19937Init(&ctx, time(NULL));
uint8_t *source = (uint8_t *)buffer;
for (i = 0; i < length; i++) {
uint8_t rand_val = sceKernelUtilsMt19937UInt(&ctx) & 0xFF;
source[i] = rand_val;
}
return 0;
}
#endif
#ifdef F_fsync
int fsync(int fd) {
char devname[10]= { 0 };
int dr;
dr = __get_drive(__descriptormap[fd]->filename);
if (dr <= 0 || dr >= 10) {
errno = ENODEV;
return -1;
}
strncpy(devname, __descriptormap[fd]->filename, dr);
devname[dr] = '\0';
return __set_errno(sceIoSync(devname, 0));
}
#endif
#ifdef F_getuid
uid_t getuid(void) {
return __dummy_passwd.pw_uid;
}
#endif
#ifdef F_geteuid
uid_t geteuid(void) {
return __dummy_passwd.pw_uid;
}
#endif
#ifdef F_getpwuid
struct passwd *getpwuid(uid_t uid) {
/* There's no support for users */
return &__dummy_passwd;
}
#endif
#ifdef F_getpwnam
struct passwd *getpwnam(const char *name) {
/* There's no support for users */
return &__dummy_passwd;
}
#endif
#ifdef F_basename
char* basename (char *path)
{
char *p;
if( path == NULL || *path == '\0' )
return ".";
p = path + strlen(path) - 1;
while( *p == '/' ) {
if( p == path )
return path;
*p-- = '\0';
}
while( p >= path && *p != '/' )
p--;
return p + 1;
}
#endif /* F_basename */
#ifdef F_statvfs
int statvfs (const char *__path, struct statvfs *__buf)
{
SceDevInf inf;
SceDevctlCmd cmd;
char dest[MAXNAMLEN + 1];
if(__path_absolute(__path, dest, MAXNAMLEN) < 0) {
errno = ENAMETOOLONG;
return -1;
}
cmd.dev_inf = &inf;
memset(&inf, 0, sizeof(SceDevInf));
sceIoDevctl(__path, SCE_PR_GETDEV, &cmd, sizeof(SceDevctlCmd), NULL, 0);
memset(__buf, 0, sizeof(struct statvfs));
__buf->f_bsize = (inf.sectorSize * inf.sectorCount);
__buf->f_frsize = (inf.sectorSize * inf.sectorCount);
__buf->f_blocks = inf.maxClusters;
__buf->f_bfree = inf.freeClusters;
__buf->f_bavail = inf.freeClusters;
__buf->f_namemax = MAXNAMLEN;
return 0;
}
#endif /* F_statvfs */
/* ATFILE functions */
#ifdef F_openat
int openat(int dirfd, const char *pathname, int flags, ...)
{
// TODO: Do better implementation following https://linux.die.net/man/2/openat
// for now use the same as open
// Extract mode from variable arguments
va_list args;
va_start(args, flags);
// Get the mode argument
int mode = va_arg(args, int);
// Clean up the va_list
va_end(args);
return _open(pathname, flags, mode);
}
#endif /* F_openat */
#ifdef F_renameat
int renameat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
{
// TODO: Do better implementation following https://linux.die.net/man/2/renameat
// for now use the same as rename
return rename(oldpath, newpath);
}
#endif /* F_renameat */
#ifdef F_fchmodat
int fchmodat(int dirfd, const char *pathname, mode_t mode, int flags)
{
// TODO: Do better implementation following https://linux.die.net/man/2/fchmodat
// for now use the same as chmod
return chmod(pathname, mode);
}
#endif /* F_fchmodat */
#ifdef F_fstatat
int fstatat(int dirfd, const char *pathname, struct stat *buf, int flags)
{
// TODO: Do better implementation following https://linux.die.net/man/2/fstatat
// for now use the same as stat
return _stat(pathname, buf);
}
#endif /* F_fstatat */
#ifdef F_mkdirat
int mkdirat(int dirfd, const char *pathname, mode_t mode)
{
// TODO: Do better implementation following https://linux.die.net/man/2/mkdirat
// for now use the same as mkdir
return mkdir(pathname, mode);
}
#endif /* F_mkdirat */
#ifdef F_faccessat
int faccessat(int dirfd, const char *pathname, int mode, int flags)
{
// TODO: Do better implementation following https://linux.die.net/man/2/faccessat
// for now use the same as access
return access(pathname, mode);
}
#endif /* F_faccessat */
#ifdef F_fchownat
int fchownat(int dirfd, const char *pathname, uid_t owner, gid_t group, int flags)
{
// TODO: Do better implementation following https://linux.die.net/man/2/fchownat
// for now use the same as chown
return chown(pathname, owner, group);
}
#endif /* F_fchownat */
#ifdef F_linkat
int linkat(int olddirfd, const char *oldpath, int newdirfd, const char *newpath, int flags) {
// TODO: Do better implementation following https://linux.die.net/man/2/linkat
// for now use the same as link
return link(oldpath, newpath);
}
#endif /* F_linkat */
#ifdef F_readlinkat
int readlinkat(int dirfd, const char *pathname, char *buf, size_t bufsiz)
{
// TODO: Do better implementation following https://linux.die.net/man/2/linkat
// for now use the same as readlink
return readlink(pathname, buf, bufsiz);
}
#endif /* F_readlinkat */
#ifdef F_unlinkat
int unlinkat(int dirfd, const char *pathname, int flags)
{
// If flags contains AT_REMOVEDIR, then the path refers to a directory.
// Otherwise, the path refers to a file.
if (flags & AT_REMOVEDIR) {
return rmdir(pathname);
}
else {
return unlink(pathname);
}
}
#endif /* F_unlinkat */
#ifdef F_realpath
char *realpath(const char *path, char *resolved_path)
{
if (path == NULL) {
errno = EINVAL;
return NULL;
}
if (strlen(path) > PATH_MAX) {
errno = ENAMETOOLONG;
return NULL;
}
/* check if file or directory exist */
struct stat st;
if (stat(path, &st) < 0) {
errno = ENOENT;
return NULL;
}
// if resolved_path arg is NULL, use malloc instead
if (resolved_path == NULL) {
resolved_path = (char *)malloc(PATH_MAX * sizeof(char));
if (resolved_path == NULL) {
errno = ENOMEM;
return NULL;
}
}
__path_absolute(path, resolved_path, PATH_MAX);
return resolved_path;
}
#endif /* F_realpath */
#ifdef F_gethostname
int gethostname (char *__name, size_t __len) {
char nickname[_SC_HOST_NAME_MAX];
memset(nickname, 0, _SC_HOST_NAME_MAX);
if (sceUtilityGetSystemParamString(PSP_SYSTEMPARAM_ID_STRING_NICKNAME, nickname, _SC_HOST_NAME_MAX) != PSP_SYSTEMPARAM_RETVAL_FAIL) {
strlcpy(__name, nickname, __len);
return 0;
}
return __set_errno(EINVAL);
}
#endif /* F_gethostname */