1// See LICENSE for license details.
2
3#include "file.h"
4#include "atomic.h"
5#include "mmap.h"
6#include "frontend.h"
7#include "syscall.h"
8#include "pk.h"
9#include <string.h>
10#include <errno.h>
11
12#define MAX_FDS 128
13static file_t* fds[MAX_FDS];
14#define MAX_FILES 128
15file_t files[MAX_FILES] = {[0 ... MAX_FILES-1] = {-1,0}};
16
17void file_incref(file_t* f)
18{
19  long prev = atomic_add(&f->refcnt, 1);
20  kassert(prev > 0);
21}
22
23void file_decref(file_t* f)
24{
25  if (atomic_add(&f->refcnt, -1) == 2)
26  {
27    int kfd = f->kfd;
28    mb();
29    atomic_set(&f->refcnt, 0);
30
31    frontend_syscall(SYS_close, kfd, 0, 0, 0, 0, 0, 0);
32  }
33}
34
35static file_t* file_get_free()
36{
37  for (file_t* f = files; f < files + MAX_FILES; f++)
38    if (atomic_read(&f->refcnt) == 0 && atomic_cas(&f->refcnt, 0, 2) == 0)
39      return f;
40  return NULL;
41}
42
43int file_dup(file_t* f)
44{
45  for (int i = 0; i < MAX_FDS; i++)
46  {
47    if (atomic_cas(&fds[i], 0, f) == 0)
48    {
49      file_incref(f);
50      return i;
51    }
52  }
53  return -1;
54}
55
56void file_init()
57{
58  // create stdin, stdout, stderr and FDs 0-2
59  for (int i = 0; i < 3; i++) {
60    file_t* f = file_get_free();
61    f->kfd = i;
62    file_dup(f);
63  }
64}
65
66file_t* file_get(int fd)
67{
68  file_t* f;
69  if (fd < 0 || fd >= MAX_FDS || (f = atomic_read(&fds[fd])) == NULL)
70    return 0;
71
72  long old_cnt;
73  do {
74    old_cnt = atomic_read(&f->refcnt);
75    if (old_cnt == 0)
76      return 0;
77  } while (atomic_cas(&f->refcnt, old_cnt, old_cnt+1) != old_cnt);
78
79  return f;
80}
81
82file_t* file_open(const char* fn, int flags, int mode)
83{
84  return file_openat(AT_FDCWD, fn, flags, mode);
85}
86
87file_t* file_openat(int dirfd, const char* fn, int flags, int mode)
88{
89  file_t* f = file_get_free();
90  if (f == NULL)
91    return ERR_PTR(-ENOMEM);
92
93  size_t fn_size = strlen(fn)+1;
94  long ret = frontend_syscall(SYS_openat, dirfd, va2pa(fn), fn_size, flags, mode, 0, 0);
95  if (ret >= 0)
96  {
97    f->kfd = ret;
98    return f;
99  }
100  else
101  {
102    file_decref(f);
103    return ERR_PTR(ret);
104  }
105}
106
107int fd_close(int fd)
108{
109  file_t* f = file_get(fd);
110  if (!f)
111    return -1;
112  file_t* old = atomic_cas(&fds[fd], f, 0);
113  file_decref(f);
114  if (old != f)
115    return -1;
116  file_decref(f);
117  return 0;
118}
119
120ssize_t file_read(file_t* f, void* buf, size_t size)
121{
122  populate_mapping(buf, size, PROT_WRITE);
123  return frontend_syscall(SYS_read, f->kfd, va2pa(buf), size, 0, 0, 0, 0);
124}
125
126ssize_t file_pread(file_t* f, void* buf, size_t size, off_t offset)
127{
128  populate_mapping(buf, size, PROT_WRITE);
129  return frontend_syscall(SYS_pread, f->kfd, va2pa(buf), size, offset, 0, 0, 0);
130}
131
132ssize_t file_write(file_t* f, const void* buf, size_t size)
133{
134  populate_mapping(buf, size, PROT_READ);
135  return frontend_syscall(SYS_write, f->kfd, va2pa(buf), size, 0, 0, 0, 0);
136}
137
138ssize_t file_pwrite(file_t* f, const void* buf, size_t size, off_t offset)
139{
140  populate_mapping(buf, size, PROT_READ);
141  return frontend_syscall(SYS_pwrite, f->kfd, va2pa(buf), size, offset, 0, 0, 0);
142}
143
144int file_stat(file_t* f, struct stat* s)
145{
146  struct frontend_stat buf;
147  long ret = frontend_syscall(SYS_fstat, f->kfd, va2pa(&buf), 0, 0, 0, 0, 0);
148  copy_stat(s, &buf);
149  return ret;
150}
151
152int file_truncate(file_t* f, off_t len)
153{
154  return frontend_syscall(SYS_ftruncate, f->kfd, len, 0, 0, 0, 0, 0);
155}
156
157ssize_t file_lseek(file_t* f, size_t ptr, int dir)
158{
159  return frontend_syscall(SYS_lseek, f->kfd, ptr, dir, 0, 0, 0, 0);
160}
161