1/*
2 * Copyright 2018, Data61
3 * Commonwealth Scientific and Industrial Research Organisation (CSIRO)
4 * ABN 41 687 119 230.
5 *
6 * This software may be distributed and modified according to the terms of
7 * the BSD 2-Clause license. Note that NO WARRANTY is provided.
8 * See "LICENSE_BSD2.txt" for details.
9 *
10 * @TAG(DATA61_BSD)
11 */
12
13#include <stdint.h>
14#include <string.h>
15#include <fcntl.h>
16#include <errno.h>
17#include <sys/uio.h>
18#include <utils/util.h>
19#include <muslcsys/vsyscall.h>
20#include "fsclient.h"
21
22static void *ext_buf;
23static int (*ext_open)(const char *name, int flags);
24static ssize_t (*ext_read)(int fd, size_t size);
25static int64_t (*ext_seek)(int fd, int64_t offset, int whence);
26static int (*ext_close)(int fd);
27
28static long fileserver_open(va_list ap) {
29    const char *pathname = va_arg(ap, const char *);
30    int flags = va_arg(ap, int);
31    return ext_open(pathname, flags);
32}
33
34static long fileserver_openat(va_list ap) {
35    int dirfd = va_arg(ap, int);
36    const char *pathname = va_arg(ap, const char *);
37    int flags = va_arg(ap, int);
38
39    if (dirfd != AT_FDCWD) {
40        ZF_LOGE("Openat only supports relative path to the current working directory\n");
41        return -EINVAL;
42    }
43    return ext_open(pathname, flags);
44}
45
46static long fileserver_close(va_list ap) {
47    int fd = va_arg(ap, int);
48    return ext_close(fd);
49}
50
51static long fileserver_read(va_list ap) {
52    int fd = va_arg(ap, int);
53    void *buf = va_arg(ap, void*);
54    size_t count = va_arg(ap, size_t);
55    ssize_t total = 0;
56    size_t remain = count;
57    while (total < count) {
58        ssize_t result = ext_read(fd, remain);
59        if (result <= 0) {
60            return total;
61        }
62        memcpy(buf + total, ext_buf, result);
63        total += result;
64        remain -= result;
65    }
66    return total;
67}
68
69static long fileserver_readv(va_list ap) {
70    int fd = va_arg(ap, int);
71    struct iovec *iov = va_arg(ap, struct iovec*);
72    int iovcnt = va_arg(ap, int);
73    ssize_t total = 0;
74    int i;
75    for (i = 0; i < iovcnt; i++) {
76        long iov_offset = 0;
77        while (iov_offset < iov[i].iov_len) {
78            long read = ext_read(fd, iov[i].iov_len - iov_offset);
79            if (read <= 0) {
80                return total;
81            }
82            memcpy(iov[i].iov_base + iov_offset, ext_buf, read);
83            iov_offset += read;
84            total += read;
85        }
86    }
87    return total;
88}
89
90static long fileserver_lseek(va_list ap) {
91    int fd = va_arg(ap, int);
92    off_t offset = va_arg(ap, off_t);
93    int whence = va_arg(ap, int);
94
95    return ext_seek(fd, offset, whence);
96}
97
98static long fileserver_llseek(va_list ap) {
99    int fd = va_arg(ap, int);
100    uint32_t offset_high = va_arg(ap, uint32_t);
101    uint32_t offset_low = va_arg(ap, uint32_t);
102    off_t *result = va_arg(ap, off_t*);
103    int whence = va_arg(ap, int);
104
105    *result = ext_seek(fd, (((uint64_t)offset_high) << 32) | offset_low, whence);
106    return 0;
107}
108
109void install_fileserver(file_server_interface_t fs_interface) {
110    ext_buf = fs_interface.ext_buf;
111    ext_open = fs_interface.ext_open;
112    ext_read = fs_interface.ext_read;
113    ext_seek = fs_interface.ext_seek;
114    ext_close = fs_interface.ext_close;
115#ifdef __NR_open
116    muslcsys_install_syscall(__NR_open, fileserver_open);
117#endif
118#ifdef __NR_openat
119    muslcsys_install_syscall(__NR_openat, fileserver_openat);
120#endif
121    muslcsys_install_syscall(__NR_close, fileserver_close);
122    muslcsys_install_syscall(__NR_read, fileserver_read);
123    muslcsys_install_syscall(__NR_readv, fileserver_readv);
124    muslcsys_install_syscall(__NR_lseek, fileserver_lseek);
125#ifdef __NR__llseek
126    muslcsys_install_syscall(__NR__llseek, fileserver_llseek);
127#endif
128}
129