blob: c788315d6f363c6b34a505117ddd5dea8f0c9dcb [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include <dirent.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <sys/stat.h>
#include "unistd.h"
// checkfile, checkfileat, and checkfd let us error out if the object
// doesn't exist, which allows the stubs to be a little more 'real'
static int seterr(int err) {
if (err) {
errno = err;
return -1;
}
return 0;
}
static int checkfile(const char* path, int err) {
struct stat s;
if (stat(path, &s)) {
return -1;
}
return seterr(err);
}
static int checkfileat(int fd, const char* path, int flags, int err) {
struct stat s;
if (fstatat(fd, path, &s, flags)) {
return -1;
}
return seterr(err);
}
static int checkfd(int fd, int err) {
fdio_t* io;
if ((io = fd_to_io(fd)) == NULL) {
errno = EBADF;
return -1;
}
fdio_release(io);
return seterr(err);
}
static int check2fds(int fd1, int fd2, int err) {
fdio_t* io;
if ((io = fd_to_io(fd1)) == NULL) {
errno = EBADF;
return -1;
}
fdio_release(io);
if ((io = fd_to_io(fd2)) == NULL) {
errno = EBADF;
return -1;
}
fdio_release(io);
return seterr(err);
}
static int checkfilefd(const char* path, int fd, int err) {
struct stat s;
if (stat(path, &s)) {
return -1;
}
fdio_t* io;
if ((io = fd_to_io(fd)) == NULL) {
errno = EBADF;
return -1;
}
fdio_release(io);
return seterr(err);
}
static int checksocket(int fd, int sock_err, int err) {
fdio_t* io = fd_to_io(fd);
if (io == NULL) {
errno = EBADF;
return -1;
}
int32_t is_socket = *fdio_get_ioflag(io) & IOFLAG_SOCKET;
fdio_release(io);
if (!is_socket) {
errno = sock_err;
return -1;
}
return seterr(err);
}
static int checkdir(DIR* dir, int err) {
if (dirfd(dir) < 0) {
errno = EBADF;
return -1;
}
return seterr(err);
}
// not supported by any filesystems yet
__EXPORT
int symlink(const char* existing, const char* newpath) {
errno = ENOSYS;
return -1;
}
__EXPORT
ssize_t readlink(const char* __restrict path, char* __restrict buf, size_t bufsize) {
// EINVAL = not symlink
return checkfile(path, EINVAL);
}
// creating things we don't have plumbing for yet
__EXPORT
int mkfifo(const char *path, mode_t mode) {
errno = ENOSYS;
return -1;
}
__EXPORT
int mknod(const char* path, mode_t mode, dev_t dev) {
errno = ENOSYS;
return -1;
}
// no permissions support yet
__EXPORT
int chown(const char *path, uid_t owner, gid_t group) {
return checkfile(path, ENOSYS);
}
__EXPORT
int fchown(int fd, uid_t owner, gid_t group) {
return checkfd(fd, ENOSYS);
}
__EXPORT
int lchown(const char *path, uid_t owner, gid_t group) {
return checkfile(path, ENOSYS);
}
// no permissions support, but treat rwx bits as don't care rather than error
__EXPORT
int chmod(const char *path, mode_t mode) {
return checkfile(path, (mode & (~0777)) ? ENOSYS : 0);
}
__EXPORT
int fchmod(int fd, mode_t mode) {
return checkfd(fd, (mode & (~0777)) ? ENOSYS : 0);
}
__EXPORT
int fchmodat(int fd, const char* path, mode_t mode, int flags) {
if (flags & ~AT_SYMLINK_NOFOLLOW) {
errno = EINVAL;
return -1;
}
return checkfileat(fd, path, flags, (mode & (~0777)) ? ENOSYS : 0);
}
__EXPORT
int access(const char* path, int mode) {
return checkfile(path, 0);
}
__EXPORT
void sync(void) {
}
// at the moment our unlink works on all fs objects
__EXPORT
int rmdir(const char* path) {
return unlink(path);
}
// tty stubbing.
__EXPORT
int ttyname_r(int fd, char* name, size_t size) {
if (!isatty(fd)) {
return ENOTTY;
}
return checkfd(fd, ENOSYS);
}
__EXPORT
int sendmmsg(int fd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags) {
return checksocket(fd, ENOTSOCK, ENOSYS);
}
__EXPORT
int recvmmsg(int fd, struct mmsghdr* msgvec, unsigned int vlen, unsigned int flags, struct timespec* timeout) {
return checksocket(fd, ENOTSOCK, ENOSYS);
}
__EXPORT
int sockatmark(int fd) {
// ENOTTY is sic.
return checksocket(fd, ENOTTY, ENOSYS);
}
__EXPORT
int fchownat(int fd, const char* path, uid_t uid, gid_t gid, int flag) {
return checkfd(fd, ENOSYS);
}
__EXPORT
int linkat(int fd1, const char* existing, int fd2, const char* newpath, int flag) {
return check2fds(fd1, fd2, ENOSYS);
}
__EXPORT
int symlinkat(const char* existing, int fd, const char* newpath) {
return checkfilefd(existing, fd, ENOSYS);
}
__EXPORT
ssize_t readlinkat(int fd, const char* __restrict path, char* __restrict buf, size_t bufsize) {
return checkfilefd(path, fd, ENOSYS);
}
__EXPORT
void seekdir(DIR* dir, long loc) {
}
__EXPORT
long telldir(DIR* dir) {
return checkdir(dir, ENOSYS);
}
__EXPORT
int posix_fadvise(int fd, off_t base, off_t len, int advice) {
return checkfd(fd, ENOSYS);
}
__EXPORT
int posix_fallocate(int fd, off_t base, off_t len) {
return checkfd(fd, ENOSYS);
}
__EXPORT
int readdir_r(DIR* dir, struct dirent* entry, struct dirent** result) {
return checkdir(dir, ENOSYS);
}