cloudabi_file.c revision 285930
1/*-
2 * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23 * SUCH DAMAGE.
24 */
25
26#include <sys/cdefs.h>
27__FBSDID("$FreeBSD: head/sys/compat/cloudabi/cloudabi_file.c 285930 2015-07-28 06:36:49Z ed $");
28
29#include <sys/param.h>
30#include <sys/capsicum.h>
31#include <sys/fcntl.h>
32#include <sys/kernel.h>
33#include <sys/malloc.h>
34#include <sys/namei.h>
35#include <sys/proc.h>
36#include <sys/stat.h>
37#include <sys/syscallsubr.h>
38
39#include <compat/cloudabi/cloudabi_proto.h>
40#include <compat/cloudabi/cloudabi_syscalldefs.h>
41#include <compat/cloudabi/cloudabi_util.h>
42
43static MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames");
44
45/*
46 * Copying pathnames from userspace to kernelspace.
47 *
48 * Unlike most operating systems, CloudABI doesn't use null-terminated
49 * pathname strings. Processes always pass pathnames to the kernel by
50 * providing a base pointer and a length. This has a couple of reasons:
51 *
52 * - It makes it easier to use CloudABI in combination with programming
53 *   languages other than C, that may use non-null terminated strings.
54 * - It allows for calling system calls on individual components of the
55 *   pathname without modifying the input string.
56 *
57 * The function below copies in pathname strings and null-terminates it.
58 * It also ensure that the string itself does not contain any null
59 * bytes.
60 *
61 * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass
62 *           in unterminated pathname strings, so we can do away with
63 *           the copying.
64 */
65
66static int
67copyin_path(const char *uaddr, size_t len, char **result)
68{
69	char *buf;
70	int error;
71
72	if (len >= PATH_MAX)
73		return (ENAMETOOLONG);
74	buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK);
75	error = copyin(uaddr, buf, len);
76	if (error != 0) {
77		free(buf, M_CLOUDABI_PATH);
78		return (error);
79	}
80	if (memchr(buf, '\0', len) != NULL) {
81		free(buf, M_CLOUDABI_PATH);
82		return (EINVAL);
83	}
84	buf[len] = '\0';
85	*result = buf;
86	return (0);
87}
88
89static void
90cloudabi_freestr(char *buf)
91{
92
93	free(buf, M_CLOUDABI_PATH);
94}
95
96int
97cloudabi_sys_file_advise(struct thread *td,
98    struct cloudabi_sys_file_advise_args *uap)
99{
100	int advice;
101
102	switch (uap->advice) {
103	case CLOUDABI_ADVICE_DONTNEED:
104		advice = POSIX_FADV_DONTNEED;
105		break;
106	case CLOUDABI_ADVICE_NOREUSE:
107		advice = POSIX_FADV_NOREUSE;
108		break;
109	case CLOUDABI_ADVICE_NORMAL:
110		advice = POSIX_FADV_NORMAL;
111		break;
112	case CLOUDABI_ADVICE_RANDOM:
113		advice = POSIX_FADV_RANDOM;
114		break;
115	case CLOUDABI_ADVICE_SEQUENTIAL:
116		advice = POSIX_FADV_SEQUENTIAL;
117		break;
118	case CLOUDABI_ADVICE_WILLNEED:
119		advice = POSIX_FADV_WILLNEED;
120		break;
121	default:
122		return (EINVAL);
123	}
124
125	return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice));
126}
127
128int
129cloudabi_sys_file_allocate(struct thread *td,
130    struct cloudabi_sys_file_allocate_args *uap)
131{
132
133	return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len));
134}
135
136int
137cloudabi_sys_file_create(struct thread *td,
138    struct cloudabi_sys_file_create_args *uap)
139{
140
141	/* Not implemented. */
142	return (ENOSYS);
143}
144
145int
146cloudabi_sys_file_link(struct thread *td,
147    struct cloudabi_sys_file_link_args *uap)
148{
149	char *path1, *path2;
150	int error;
151
152	error = copyin_path(uap->path1, uap->path1len, &path1);
153	if (error != 0)
154		return (error);
155	error = copyin_path(uap->path2, uap->path2len, &path2);
156	if (error != 0) {
157		cloudabi_freestr(path1);
158		return (error);
159	}
160
161	error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2,
162	    UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ?
163	    FOLLOW : NOFOLLOW);
164	cloudabi_freestr(path1);
165	cloudabi_freestr(path2);
166	return (error);
167}
168
169int
170cloudabi_sys_file_open(struct thread *td,
171    struct cloudabi_sys_file_open_args *uap)
172{
173
174	/* Not implemented. */
175	return (ENOSYS);
176}
177
178int
179cloudabi_sys_file_readdir(struct thread *td,
180    struct cloudabi_sys_file_readdir_args *uap)
181{
182
183	/* Not implemented. */
184	return (ENOSYS);
185}
186
187int
188cloudabi_sys_file_readlink(struct thread *td,
189    struct cloudabi_sys_file_readlink_args *uap)
190{
191	char *path;
192	int error;
193
194	error = copyin_path(uap->path, uap->pathlen, &path);
195	if (error != 0)
196		return (error);
197
198	error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
199	    uap->buf, UIO_USERSPACE, uap->bufsize);
200	cloudabi_freestr(path);
201	return (error);
202}
203
204int
205cloudabi_sys_file_rename(struct thread *td,
206    struct cloudabi_sys_file_rename_args *uap)
207{
208	char *old, *new;
209	int error;
210
211	error = copyin_path(uap->old, uap->oldlen, &old);
212	if (error != 0)
213		return (error);
214	error = copyin_path(uap->new, uap->newlen, &new);
215	if (error != 0) {
216		cloudabi_freestr(old);
217		return (error);
218	}
219
220	error = kern_renameat(td, uap->oldfd, old, uap->newfd, new,
221	    UIO_SYSSPACE);
222	cloudabi_freestr(old);
223	cloudabi_freestr(new);
224	return (error);
225}
226
227/* Converts a FreeBSD stat structure to a CloudABI stat structure. */
228static void
229convert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
230{
231	cloudabi_filestat_t res = {
232		.st_dev		= sb->st_dev,
233		.st_ino		= sb->st_ino,
234		.st_nlink	= sb->st_nlink,
235		.st_size	= sb->st_size,
236	};
237
238	cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
239	cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
240	cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
241	*csb = res;
242}
243
244int
245cloudabi_sys_file_stat_fget(struct thread *td,
246    struct cloudabi_sys_file_stat_fget_args *uap)
247{
248	struct stat sb;
249	cloudabi_filestat_t csb;
250	struct file *fp;
251	cap_rights_t rights;
252	cloudabi_filetype_t filetype;
253	int error;
254
255	/* Fetch file descriptor attributes. */
256	error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp);
257	if (error != 0)
258		return (error);
259	error = fo_stat(fp, &sb, td->td_ucred, td);
260	if (error != 0) {
261		fdrop(fp, td);
262		return (error);
263	}
264	filetype = cloudabi_convert_filetype(fp);
265	fdrop(fp, td);
266
267	/* Convert attributes to CloudABI's format. */
268	convert_stat(&sb, &csb);
269	csb.st_filetype = filetype;
270	return (copyout(&csb, uap->buf, sizeof(csb)));
271}
272
273int
274cloudabi_sys_file_stat_fput(struct thread *td,
275    struct cloudabi_sys_file_stat_fput_args *uap)
276{
277
278	/* Not implemented. */
279	return (ENOSYS);
280}
281
282int
283cloudabi_sys_file_stat_get(struct thread *td,
284    struct cloudabi_sys_file_stat_get_args *uap)
285{
286	struct stat sb;
287	cloudabi_filestat_t csb;
288	char *path;
289	int error;
290
291	error = copyin_path(uap->path, uap->pathlen, &path);
292	if (error != 0)
293		return (error);
294
295	error = kern_statat(td,
296	    (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 :
297	    AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL);
298	cloudabi_freestr(path);
299	if (error != 0)
300		return (error);
301
302	/* Convert results and return them. */
303	convert_stat(&sb, &csb);
304	if (S_ISBLK(sb.st_mode))
305		csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE;
306	else if (S_ISCHR(sb.st_mode))
307		csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
308	else if (S_ISDIR(sb.st_mode))
309		csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY;
310	else if (S_ISFIFO(sb.st_mode))
311		csb.st_filetype = CLOUDABI_FILETYPE_FIFO;
312	else if (S_ISREG(sb.st_mode))
313		csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE;
314	else if (S_ISSOCK(sb.st_mode)) {
315		/* Inaccurate, but the best that we can do. */
316		csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM;
317	} else if (S_ISLNK(sb.st_mode))
318		csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
319	else
320		csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN;
321	return (copyout(&csb, uap->buf, sizeof(csb)));
322}
323
324int
325cloudabi_sys_file_stat_put(struct thread *td,
326    struct cloudabi_sys_file_stat_put_args *uap)
327{
328
329	/* Not implemented. */
330	return (ENOSYS);
331}
332
333int
334cloudabi_sys_file_symlink(struct thread *td,
335    struct cloudabi_sys_file_symlink_args *uap)
336{
337	char *path1, *path2;
338	int error;
339
340	error = copyin_path(uap->path1, uap->path1len, &path1);
341	if (error != 0)
342		return (error);
343	error = copyin_path(uap->path2, uap->path2len, &path2);
344	if (error != 0) {
345		cloudabi_freestr(path1);
346		return (error);
347	}
348
349	error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE);
350	cloudabi_freestr(path1);
351	cloudabi_freestr(path2);
352	return (error);
353}
354
355int
356cloudabi_sys_file_unlink(struct thread *td,
357    struct cloudabi_sys_file_unlink_args *uap)
358{
359	char *path;
360	int error;
361
362	error = copyin_path(uap->path, uap->pathlen, &path);
363	if (error != 0)
364		return (error);
365
366	if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR)
367		error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE);
368	else
369		error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0);
370	cloudabi_freestr(path);
371	return (error);
372}
373