a8b710914323aee64e518ab6cf661143c40b5e39
[monolithium.git] / crt / src / fcntl.c
1 /*
2  * fcntl.c
3  *
4  * Copyright (C) 2017 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <monolithium.h>
21 #include <stdlib.h>
22 #include <string.h>
23
24 #define __DONT_DEFINE_OPEN__
25 #include <fcntl.h>
26
27 #define MAX_OPEN_FILES 4096
28
29 static handle_t mutex;
30 static struct
31 {
32     handle_t handle;
33     int flags;
34     int descriptor_flags;
35     off_t position;
36     size_t size;
37 } descriptors[MAX_OPEN_FILES];
38
39 handle_t __crt_get_raw_handle(int fd)
40 {
41     syscall_wait_mutex(mutex, NO_TIMEOUT);
42     handle_t handle = descriptors[fd].handle;
43     syscall_release_mutex(mutex);
44     return handle;
45 }
46
47 static inline int __crt_open_handle_at(int fd, handle_t handle, int flags)
48 {
49     object_type_t type;
50     sysret_t status = syscall_query_handle(handle, HANDLE_INFO_TYPE, &type, sizeof(type));
51
52     if (status != ERR_SUCCESS || (type != OBJECT_FILE && type != OBJECT_PIPE))
53     {
54         errno = EINVAL;
55         return -1;
56     }
57
58     if (type == OBJECT_FILE)
59     {
60         qword_t real_size;
61         status = syscall_query_file(handle, FILE_INFO_SIZE, &real_size, sizeof(real_size));
62         if (status != ERR_SUCCESS)
63         {
64             errno = __crt_translate_error(status);
65             return -1;
66         }
67
68         if (real_size > (qword_t)((size_t)-1))
69         {
70             errno = EFBIG;
71             return -1;
72         }
73
74         descriptors[fd].descriptor_flags = 0;
75         descriptors[fd].size = (size_t)real_size;
76     }
77     else
78     {
79         descriptors[fd].descriptor_flags = FD_PIPE;
80         descriptors[fd].size = 0;
81     }
82
83     descriptors[fd].handle = handle;
84     descriptors[fd].flags = flags;
85     descriptors[fd].position = 0;
86     return fd;
87 }
88
89 static inline int __crt_open_handle(handle_t handle, int flags)
90 {
91     int fd;
92     syscall_wait_mutex(mutex, NO_TIMEOUT);
93
94     for (fd = 0; fd < MAX_OPEN_FILES; fd++)
95     {
96         if (descriptors[fd].handle == INVALID_HANDLE) break;
97     }
98
99     if (fd == MAX_OPEN_FILES)
100     {
101         errno = EMFILE;
102         return -1;
103     }
104
105     fd = __crt_open_handle_at(fd, handle, flags);
106
107     syscall_release_mutex(mutex);
108     return fd;
109 }
110
111 int open(const char *pathname, int flags, mode_t mode)
112 {
113     handle_t handle;
114     char fullpath[PATH_MAX];
115
116     if (*pathname == '/')
117     {
118         if (strlen(pathname) >= PATH_MAX)
119         {
120             errno = ENAMETOOLONG;
121             return -1;
122         }
123
124         strcpy(fullpath, pathname);
125     }
126     else
127     {
128         getwd(fullpath);
129
130         if (strlen(fullpath) + strlen(pathname) >= PATH_MAX)
131         {
132             errno = ENAMETOOLONG;
133             return -1;
134         }
135
136         strcat(fullpath, pathname);
137     }
138
139     if ((flags & 3) == 3)
140     {
141         errno = EINVAL;
142         return -1;
143     }
144
145     dword_t kernel_flags = FILE_MODE_SHARE_READ | FILE_MODE_SHARE_WRITE;
146     dword_t attributes = 0;
147
148     if ((flags & 3) == O_RDONLY) kernel_flags |= FILE_MODE_READ;
149     else if ((flags & 3) == O_RDONLY) kernel_flags |= FILE_MODE_WRITE;
150     else kernel_flags |= FILE_MODE_READ | FILE_MODE_WRITE;
151
152     if (flags & O_CREAT) kernel_flags |= FILE_MODE_CREATE;
153     if (flags & O_TRUNC) kernel_flags |= FILE_MODE_TRUNCATE;
154
155     sysret_t status = syscall_open_file(fullpath, &handle, kernel_flags, attributes);
156     if (status != ERR_SUCCESS)
157     {
158         errno = __crt_translate_error(status);
159         return -1;
160     }
161
162     int fd = __crt_open_handle(handle, flags);
163     if (fd < 0) syscall_close_object(handle);
164     return fd;
165 }
166
167 int creat(const char *pathname, mode_t mode)
168 {
169     return open(pathname, O_CREAT | O_WRONLY | O_TRUNC, mode);
170 }
171
172 int close(int fd)
173 {
174     int ret = -1;
175     syscall_wait_mutex(mutex, NO_TIMEOUT);
176
177     if (fd < 0 || fd >= MAX_OPEN_FILES || descriptors[fd].handle == INVALID_HANDLE)
178     {
179         errno = EBADF;
180         goto cleanup;
181     }
182
183     syscall_close_object(descriptors[fd].handle);
184     descriptors[fd].handle = INVALID_HANDLE;
185     ret = 0;
186
187 cleanup:
188     syscall_release_mutex(mutex);
189     return ret;
190 }
191
192 ssize_t read(int fd, void *buf, size_t count)
193 {
194     syscall_wait_mutex(mutex, NO_TIMEOUT);
195
196     size_t ret;
197     sysret_t status = syscall_read_file(descriptors[fd].handle, buf, descriptors[fd].position, count, &ret);
198
199     descriptors[fd].position += (off_t)ret;
200
201     if (status != ERR_SUCCESS && status != ERR_BEYOND)
202     {
203         errno = __crt_translate_error(status);
204         ret = (off_t)-1;
205     }
206
207     syscall_release_mutex(mutex);
208     return (ssize_t)ret;
209 }
210
211 ssize_t write(int fd, const void *buf, size_t count)
212 {
213     syscall_wait_mutex(mutex, NO_TIMEOUT);
214
215     size_t ret;
216     sysret_t status = syscall_write_file(descriptors[fd].handle, buf, descriptors[fd].position, count, &ret);
217
218     descriptors[fd].position += (off_t)ret;
219
220     if (status != ERR_SUCCESS && status != ERR_BEYOND)
221     {
222         errno = __crt_translate_error(status);
223         ret = (off_t)-1;
224     }
225
226     syscall_release_mutex(mutex);
227     return (ssize_t)ret;
228 }
229
230 off_t lseek(int fd, off_t offset, int whence)
231 {
232     if (whence < SEEK_SET || whence > SEEK_END)
233     {
234         errno = EINVAL;
235         return (off_t)-1;
236     }
237
238     syscall_wait_mutex(mutex, NO_TIMEOUT);
239
240     if (fd < 0 || fd >= MAX_OPEN_FILES || descriptors[fd].handle == INVALID_HANDLE)
241     {
242         errno = EBADF;
243         goto cleanup;
244     }
245
246     if (descriptors[fd].descriptor_flags & FD_PIPE)
247     {
248         errno = ESPIPE;
249         goto cleanup;
250     }
251
252     off_t origin = 0;
253     if (whence == SEEK_CUR) origin = descriptors[fd].position;
254     else if (whence == SEEK_END) origin = descriptors[fd].size;
255
256     off_t ret = origin + offset;
257
258     if ((offset > 0 && ret < origin) || (offset < 0 && ret > origin))
259     {
260         errno = EOVERFLOW;
261         goto cleanup;
262     }
263
264     descriptors[fd].position = ret;
265
266 cleanup:
267     syscall_release_mutex(mutex);
268     return ret;
269 }
270
271 int dup(int oldfd)
272 {
273     syscall_wait_mutex(mutex, NO_TIMEOUT);
274
275     handle_t duplicate;
276     int flags = descriptors[oldfd].flags;
277     sysret_t status = syscall_duplicate_handle(INVALID_HANDLE, descriptors[oldfd].handle, INVALID_HANDLE, &duplicate);
278     syscall_release_mutex(mutex);
279
280     if (status != ERR_SUCCESS)
281     {
282         errno = __crt_translate_error(status);
283         return -1;
284     }
285
286     return __crt_open_handle(duplicate, flags);
287 }
288
289 int dup2(int oldfd, int newfd)
290 {
291     int fd = -1;
292     syscall_wait_mutex(mutex, NO_TIMEOUT);
293
294     handle_t duplicate;
295     sysret_t status = syscall_duplicate_handle(INVALID_HANDLE, descriptors[oldfd].handle, INVALID_HANDLE, &duplicate);
296
297     if (status != ERR_SUCCESS)
298     {
299         errno = __crt_translate_error(status);
300         goto cleanup;
301     }
302
303     fd = __crt_open_handle_at(newfd, duplicate, descriptors[oldfd].flags);
304
305 cleanup:
306     syscall_release_mutex(mutex);
307     return fd;
308 }
309
310 int __crt_initialize_files(process_params_t *params)
311 {
312     sysret_t ret = syscall_create_mutex(NULL, TRUE, &mutex);
313     if (ret != ERR_SUCCESS) return -1;
314
315     int i;
316     for (i = 0; i < MAX_OPEN_FILES; i++) descriptors[i].handle = INVALID_HANDLE;
317
318     if (params->standard_input != INVALID_HANDLE) __crt_open_handle_at(STDIN_FILENO, params->standard_input, O_RDONLY);
319     if (params->standard_input != INVALID_HANDLE) __crt_open_handle_at(STDOUT_FILENO, params->standard_output, O_WRONLY);
320     if (params->standard_input != INVALID_HANDLE) __crt_open_handle_at(STDERR_FILENO, params->standard_error, O_WRONLY);
321     return 0;
322 }