/* * 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 * Copyright (c) 2005 James Forshaw * Copyright (c) 2005 John Kelley * Copyright (c) 2005 Jim Paris * Copyright (c) 2021 Francisco J Trujillo * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #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 */