cloudabi_file.c revision 285931
1285307Sed/*-
2285307Sed * Copyright (c) 2015 Nuxi, https://nuxi.nl/
3285307Sed *
4285307Sed * Redistribution and use in source and binary forms, with or without
5285307Sed * modification, are permitted provided that the following conditions
6285307Sed * are met:
7285307Sed * 1. Redistributions of source code must retain the above copyright
8285307Sed *    notice, this list of conditions and the following disclaimer.
9285307Sed * 2. Redistributions in binary form must reproduce the above copyright
10285307Sed *    notice, this list of conditions and the following disclaimer in the
11285307Sed *    documentation and/or other materials provided with the distribution.
12285307Sed *
13285307Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14285307Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15285307Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16285307Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
17285307Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18285307Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19285307Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20285307Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21285307Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22285307Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23285307Sed * SUCH DAMAGE.
24285307Sed */
25285307Sed
26285307Sed#include <sys/cdefs.h>
27285307Sed__FBSDID("$FreeBSD: head/sys/compat/cloudabi/cloudabi_file.c 285931 2015-07-28 06:50:47Z ed $");
28285307Sed
29285596Sed#include <sys/param.h>
30285930Sed#include <sys/capsicum.h>
31285596Sed#include <sys/fcntl.h>
32285834Sed#include <sys/kernel.h>
33285834Sed#include <sys/malloc.h>
34285834Sed#include <sys/namei.h>
35285930Sed#include <sys/proc.h>
36285930Sed#include <sys/stat.h>
37285596Sed#include <sys/syscallsubr.h>
38285596Sed
39285307Sed#include <compat/cloudabi/cloudabi_proto.h>
40285596Sed#include <compat/cloudabi/cloudabi_syscalldefs.h>
41285930Sed#include <compat/cloudabi/cloudabi_util.h>
42285307Sed
43285834Sedstatic MALLOC_DEFINE(M_CLOUDABI_PATH, "cloudabipath", "CloudABI pathnames");
44285834Sed
45285834Sed/*
46285834Sed * Copying pathnames from userspace to kernelspace.
47285834Sed *
48285834Sed * Unlike most operating systems, CloudABI doesn't use null-terminated
49285834Sed * pathname strings. Processes always pass pathnames to the kernel by
50285834Sed * providing a base pointer and a length. This has a couple of reasons:
51285834Sed *
52285834Sed * - It makes it easier to use CloudABI in combination with programming
53285834Sed *   languages other than C, that may use non-null terminated strings.
54285834Sed * - It allows for calling system calls on individual components of the
55285834Sed *   pathname without modifying the input string.
56285834Sed *
57285834Sed * The function below copies in pathname strings and null-terminates it.
58285834Sed * It also ensure that the string itself does not contain any null
59285834Sed * bytes.
60285834Sed *
61285834Sed * TODO(ed): Add an abstraction to vfs_lookup.c that allows us to pass
62285834Sed *           in unterminated pathname strings, so we can do away with
63285834Sed *           the copying.
64285834Sed */
65285834Sed
66285834Sedstatic int
67285834Sedcopyin_path(const char *uaddr, size_t len, char **result)
68285834Sed{
69285834Sed	char *buf;
70285834Sed	int error;
71285834Sed
72285834Sed	if (len >= PATH_MAX)
73285834Sed		return (ENAMETOOLONG);
74285834Sed	buf = malloc(len + 1, M_CLOUDABI_PATH, M_WAITOK);
75285834Sed	error = copyin(uaddr, buf, len);
76285834Sed	if (error != 0) {
77285834Sed		free(buf, M_CLOUDABI_PATH);
78285834Sed		return (error);
79285834Sed	}
80285834Sed	if (memchr(buf, '\0', len) != NULL) {
81285834Sed		free(buf, M_CLOUDABI_PATH);
82285834Sed		return (EINVAL);
83285834Sed	}
84285834Sed	buf[len] = '\0';
85285834Sed	*result = buf;
86285834Sed	return (0);
87285834Sed}
88285834Sed
89285834Sedstatic void
90285834Sedcloudabi_freestr(char *buf)
91285834Sed{
92285834Sed
93285834Sed	free(buf, M_CLOUDABI_PATH);
94285834Sed}
95285834Sed
96285307Sedint
97285307Sedcloudabi_sys_file_advise(struct thread *td,
98285307Sed    struct cloudabi_sys_file_advise_args *uap)
99285307Sed{
100285596Sed	int advice;
101285307Sed
102285596Sed	switch (uap->advice) {
103285596Sed	case CLOUDABI_ADVICE_DONTNEED:
104285596Sed		advice = POSIX_FADV_DONTNEED;
105285596Sed		break;
106285596Sed	case CLOUDABI_ADVICE_NOREUSE:
107285596Sed		advice = POSIX_FADV_NOREUSE;
108285596Sed		break;
109285596Sed	case CLOUDABI_ADVICE_NORMAL:
110285596Sed		advice = POSIX_FADV_NORMAL;
111285596Sed		break;
112285596Sed	case CLOUDABI_ADVICE_RANDOM:
113285596Sed		advice = POSIX_FADV_RANDOM;
114285596Sed		break;
115285596Sed	case CLOUDABI_ADVICE_SEQUENTIAL:
116285596Sed		advice = POSIX_FADV_SEQUENTIAL;
117285596Sed		break;
118285596Sed	case CLOUDABI_ADVICE_WILLNEED:
119285596Sed		advice = POSIX_FADV_WILLNEED;
120285596Sed		break;
121285596Sed	default:
122285596Sed		return (EINVAL);
123285596Sed	}
124285596Sed
125285596Sed	return (kern_posix_fadvise(td, uap->fd, uap->offset, uap->len, advice));
126285307Sed}
127285307Sed
128285307Sedint
129285307Sedcloudabi_sys_file_allocate(struct thread *td,
130285307Sed    struct cloudabi_sys_file_allocate_args *uap)
131285307Sed{
132285307Sed
133285596Sed	return (kern_posix_fallocate(td, uap->fd, uap->offset, uap->len));
134285307Sed}
135285307Sed
136285307Sedint
137285307Sedcloudabi_sys_file_create(struct thread *td,
138285307Sed    struct cloudabi_sys_file_create_args *uap)
139285307Sed{
140285931Sed	char *path;
141285931Sed	int error;
142285307Sed
143285931Sed	error = copyin_path(uap->path, uap->pathlen, &path);
144285931Sed	if (error != 0)
145285931Sed		return (error);
146285931Sed
147285931Sed	/*
148285931Sed	 * CloudABI processes cannot interact with UNIX credentials and
149285931Sed	 * permissions. Depend on the umask that is set prior to
150285931Sed	 * execution to restrict the file permissions.
151285931Sed	 */
152285931Sed	switch (uap->type) {
153285931Sed	case CLOUDABI_FILETYPE_DIRECTORY:
154285931Sed		error = kern_mkdirat(td, uap->fd, path, UIO_SYSSPACE, 0777);
155285931Sed		break;
156285931Sed	case CLOUDABI_FILETYPE_FIFO:
157285931Sed		error = kern_mkfifoat(td, uap->fd, path, UIO_SYSSPACE, 0666);
158285931Sed		break;
159285931Sed	default:
160285931Sed		error = EINVAL;
161285931Sed		break;
162285931Sed	}
163285931Sed	cloudabi_freestr(path);
164285931Sed	return (error);
165285307Sed}
166285307Sed
167285307Sedint
168285307Sedcloudabi_sys_file_link(struct thread *td,
169285307Sed    struct cloudabi_sys_file_link_args *uap)
170285307Sed{
171285834Sed	char *path1, *path2;
172285834Sed	int error;
173285307Sed
174285834Sed	error = copyin_path(uap->path1, uap->path1len, &path1);
175285834Sed	if (error != 0)
176285834Sed		return (error);
177285834Sed	error = copyin_path(uap->path2, uap->path2len, &path2);
178285834Sed	if (error != 0) {
179285834Sed		cloudabi_freestr(path1);
180285834Sed		return (error);
181285834Sed	}
182285834Sed
183285834Sed	error = kern_linkat(td, uap->fd1, uap->fd2, path1, path2,
184285834Sed	    UIO_SYSSPACE, (uap->fd1 & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ?
185285834Sed	    FOLLOW : NOFOLLOW);
186285834Sed	cloudabi_freestr(path1);
187285834Sed	cloudabi_freestr(path2);
188285834Sed	return (error);
189285307Sed}
190285307Sed
191285307Sedint
192285307Sedcloudabi_sys_file_open(struct thread *td,
193285307Sed    struct cloudabi_sys_file_open_args *uap)
194285307Sed{
195285307Sed
196285307Sed	/* Not implemented. */
197285307Sed	return (ENOSYS);
198285307Sed}
199285307Sed
200285307Sedint
201285307Sedcloudabi_sys_file_readdir(struct thread *td,
202285307Sed    struct cloudabi_sys_file_readdir_args *uap)
203285307Sed{
204285307Sed
205285307Sed	/* Not implemented. */
206285307Sed	return (ENOSYS);
207285307Sed}
208285307Sed
209285307Sedint
210285307Sedcloudabi_sys_file_readlink(struct thread *td,
211285307Sed    struct cloudabi_sys_file_readlink_args *uap)
212285307Sed{
213285834Sed	char *path;
214285834Sed	int error;
215285307Sed
216285834Sed	error = copyin_path(uap->path, uap->pathlen, &path);
217285834Sed	if (error != 0)
218285834Sed		return (error);
219285834Sed
220285834Sed	error = kern_readlinkat(td, uap->fd, path, UIO_SYSSPACE,
221285834Sed	    uap->buf, UIO_USERSPACE, uap->bufsize);
222285834Sed	cloudabi_freestr(path);
223285834Sed	return (error);
224285307Sed}
225285307Sed
226285307Sedint
227285307Sedcloudabi_sys_file_rename(struct thread *td,
228285307Sed    struct cloudabi_sys_file_rename_args *uap)
229285307Sed{
230285834Sed	char *old, *new;
231285834Sed	int error;
232285307Sed
233285834Sed	error = copyin_path(uap->old, uap->oldlen, &old);
234285834Sed	if (error != 0)
235285834Sed		return (error);
236285834Sed	error = copyin_path(uap->new, uap->newlen, &new);
237285834Sed	if (error != 0) {
238285834Sed		cloudabi_freestr(old);
239285834Sed		return (error);
240285834Sed	}
241285834Sed
242285834Sed	error = kern_renameat(td, uap->oldfd, old, uap->newfd, new,
243285834Sed	    UIO_SYSSPACE);
244285834Sed	cloudabi_freestr(old);
245285834Sed	cloudabi_freestr(new);
246285834Sed	return (error);
247285307Sed}
248285307Sed
249285930Sed/* Converts a FreeBSD stat structure to a CloudABI stat structure. */
250285930Sedstatic void
251285930Sedconvert_stat(const struct stat *sb, cloudabi_filestat_t *csb)
252285930Sed{
253285930Sed	cloudabi_filestat_t res = {
254285930Sed		.st_dev		= sb->st_dev,
255285930Sed		.st_ino		= sb->st_ino,
256285930Sed		.st_nlink	= sb->st_nlink,
257285930Sed		.st_size	= sb->st_size,
258285930Sed	};
259285930Sed
260285930Sed	cloudabi_convert_timespec(&sb->st_atim, &res.st_atim);
261285930Sed	cloudabi_convert_timespec(&sb->st_mtim, &res.st_mtim);
262285930Sed	cloudabi_convert_timespec(&sb->st_ctim, &res.st_ctim);
263285930Sed	*csb = res;
264285930Sed}
265285930Sed
266285307Sedint
267285307Sedcloudabi_sys_file_stat_fget(struct thread *td,
268285307Sed    struct cloudabi_sys_file_stat_fget_args *uap)
269285307Sed{
270285930Sed	struct stat sb;
271285930Sed	cloudabi_filestat_t csb;
272285930Sed	struct file *fp;
273285930Sed	cap_rights_t rights;
274285930Sed	cloudabi_filetype_t filetype;
275285930Sed	int error;
276285307Sed
277285930Sed	/* Fetch file descriptor attributes. */
278285930Sed	error = fget(td, uap->fd, cap_rights_init(&rights, CAP_FSTAT), &fp);
279285930Sed	if (error != 0)
280285930Sed		return (error);
281285930Sed	error = fo_stat(fp, &sb, td->td_ucred, td);
282285930Sed	if (error != 0) {
283285930Sed		fdrop(fp, td);
284285930Sed		return (error);
285285930Sed	}
286285930Sed	filetype = cloudabi_convert_filetype(fp);
287285930Sed	fdrop(fp, td);
288285930Sed
289285930Sed	/* Convert attributes to CloudABI's format. */
290285930Sed	convert_stat(&sb, &csb);
291285930Sed	csb.st_filetype = filetype;
292285930Sed	return (copyout(&csb, uap->buf, sizeof(csb)));
293285307Sed}
294285307Sed
295285307Sedint
296285307Sedcloudabi_sys_file_stat_fput(struct thread *td,
297285307Sed    struct cloudabi_sys_file_stat_fput_args *uap)
298285307Sed{
299285307Sed
300285307Sed	/* Not implemented. */
301285307Sed	return (ENOSYS);
302285307Sed}
303285307Sed
304285307Sedint
305285307Sedcloudabi_sys_file_stat_get(struct thread *td,
306285307Sed    struct cloudabi_sys_file_stat_get_args *uap)
307285307Sed{
308285930Sed	struct stat sb;
309285930Sed	cloudabi_filestat_t csb;
310285930Sed	char *path;
311285930Sed	int error;
312285307Sed
313285930Sed	error = copyin_path(uap->path, uap->pathlen, &path);
314285930Sed	if (error != 0)
315285930Sed		return (error);
316285930Sed
317285930Sed	error = kern_statat(td,
318285930Sed	    (uap->fd & CLOUDABI_LOOKUP_SYMLINK_FOLLOW) != 0 ? 0 :
319285930Sed	    AT_SYMLINK_NOFOLLOW, uap->fd, path, UIO_SYSSPACE, &sb, NULL);
320285930Sed	cloudabi_freestr(path);
321285930Sed	if (error != 0)
322285930Sed		return (error);
323285930Sed
324285930Sed	/* Convert results and return them. */
325285930Sed	convert_stat(&sb, &csb);
326285930Sed	if (S_ISBLK(sb.st_mode))
327285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_BLOCK_DEVICE;
328285930Sed	else if (S_ISCHR(sb.st_mode))
329285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_CHARACTER_DEVICE;
330285930Sed	else if (S_ISDIR(sb.st_mode))
331285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_DIRECTORY;
332285930Sed	else if (S_ISFIFO(sb.st_mode))
333285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_FIFO;
334285930Sed	else if (S_ISREG(sb.st_mode))
335285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_REGULAR_FILE;
336285930Sed	else if (S_ISSOCK(sb.st_mode)) {
337285930Sed		/* Inaccurate, but the best that we can do. */
338285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_SOCKET_STREAM;
339285930Sed	} else if (S_ISLNK(sb.st_mode))
340285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_SYMBOLIC_LINK;
341285930Sed	else
342285930Sed		csb.st_filetype = CLOUDABI_FILETYPE_UNKNOWN;
343285930Sed	return (copyout(&csb, uap->buf, sizeof(csb)));
344285307Sed}
345285307Sed
346285307Sedint
347285307Sedcloudabi_sys_file_stat_put(struct thread *td,
348285307Sed    struct cloudabi_sys_file_stat_put_args *uap)
349285307Sed{
350285307Sed
351285307Sed	/* Not implemented. */
352285307Sed	return (ENOSYS);
353285307Sed}
354285307Sed
355285307Sedint
356285307Sedcloudabi_sys_file_symlink(struct thread *td,
357285307Sed    struct cloudabi_sys_file_symlink_args *uap)
358285307Sed{
359285834Sed	char *path1, *path2;
360285834Sed	int error;
361285307Sed
362285834Sed	error = copyin_path(uap->path1, uap->path1len, &path1);
363285834Sed	if (error != 0)
364285834Sed		return (error);
365285834Sed	error = copyin_path(uap->path2, uap->path2len, &path2);
366285834Sed	if (error != 0) {
367285834Sed		cloudabi_freestr(path1);
368285834Sed		return (error);
369285834Sed	}
370285834Sed
371285834Sed	error = kern_symlinkat(td, path1, uap->fd, path2, UIO_SYSSPACE);
372285834Sed	cloudabi_freestr(path1);
373285834Sed	cloudabi_freestr(path2);
374285834Sed	return (error);
375285307Sed}
376285307Sed
377285307Sedint
378285307Sedcloudabi_sys_file_unlink(struct thread *td,
379285307Sed    struct cloudabi_sys_file_unlink_args *uap)
380285307Sed{
381285834Sed	char *path;
382285834Sed	int error;
383285307Sed
384285834Sed	error = copyin_path(uap->path, uap->pathlen, &path);
385285834Sed	if (error != 0)
386285834Sed		return (error);
387285834Sed
388285834Sed	if (uap->flag & CLOUDABI_UNLINK_REMOVEDIR)
389285834Sed		error = kern_rmdirat(td, uap->fd, path, UIO_SYSSPACE);
390285834Sed	else
391285834Sed		error = kern_unlinkat(td, uap->fd, path, UIO_SYSSPACE, 0);
392285834Sed	cloudabi_freestr(path);
393285834Sed	return (error);
394285307Sed}
395