1 /*	$NetBSD: rumpuser_file.c,v 1.4 2014/11/04 21:08:12 pooka Exp $	*/
2 
3 /*
4  * Copyright (c) 2007-2010 Antti Kantee.  All Rights Reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
16  * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
18  * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
21  * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  */
27 
28 /* NOTE this code will move to a new driver in the next hypercall revision */
29 
30 #include "rumpuser_port.h"
31 
32 #if !defined(lint)
33 __RCSID("$NetBSD: rumpuser_file.c,v 1.4 2014/11/04 21:08:12 pooka Exp $");
34 #endif /* !lint */
35 
36 #include <sys/ioctl.h>
37 #include <sys/mman.h>
38 #include <sys/uio.h>
39 #include <sys/stat.h>
40 #include <sys/time.h>
41 #include <sys/types.h>
42 
43 #if defined(HAVE_SYS_DISK_H)
44 #include <sys/disk.h>
45 #endif
46 #if defined(HAVE_SYS_DISKLABEL_H)
47 #include <sys/disklabel.h>
48 #endif
49 #if defined(HAVE_SYS_DKIO_H)
50 #include <sys/dkio.h>
51 #endif
52 
53 #if defined(HAVE_SYS_SYSCTL_H)
54 #include <sys/sysctl.h>
55 #endif
56 
57 #include <assert.h>
58 #include <errno.h>
59 #include <fcntl.h>
60 #include <netdb.h>
61 #include <stdarg.h>
62 #include <stdint.h>
63 #include <stdio.h>
64 #include <stdlib.h>
65 #include <string.h>
66 #include <unistd.h>
67 
68 #include <rump/rumpuser.h>
69 
70 #include "rumpuser_int.h"
71 
72 int
rumpuser_getfileinfo(const char * path,uint64_t * sizep,int * ftp)73 rumpuser_getfileinfo(const char *path, uint64_t *sizep, int *ftp)
74 {
75 	struct stat sb;
76 	uint64_t size = 0;
77 	int needsdev = 0, rv = 0, ft = 0;
78 	int fd = -1;
79 
80 	if (stat(path, &sb) == -1) {
81 		rv = errno;
82 		goto out;
83 	}
84 
85 	switch (sb.st_mode & S_IFMT) {
86 	case S_IFDIR:
87 		ft = RUMPUSER_FT_DIR;
88 		break;
89 	case S_IFREG:
90 		ft = RUMPUSER_FT_REG;
91 		break;
92 	case S_IFBLK:
93 		ft = RUMPUSER_FT_BLK;
94 		needsdev = 1;
95 		break;
96 	case S_IFCHR:
97 		ft = RUMPUSER_FT_CHR;
98 		needsdev = 1;
99 		break;
100 	default:
101 		ft = RUMPUSER_FT_OTHER;
102 		break;
103 	}
104 
105 	if (!needsdev) {
106 		size = sb.st_size;
107 	} else if (sizep) {
108 		/*
109 		 * Welcome to the jungle.  Of course querying the kernel
110 		 * for a device partition size is supposed to be far from
111 		 * trivial.  On NetBSD we use ioctl.  On $other platform
112 		 * we have a problem.  We try "the lseek trick" and just
113 		 * fail if that fails.  Platform specific code can later
114 		 * be written here if appropriate.
115 		 *
116 		 * On NetBSD we hope and pray that for block devices nobody
117 		 * else is holding them open, because otherwise the kernel
118 		 * will not permit us to open it.  Thankfully, this is
119 		 * usually called only in bootstrap and then we can
120 		 * forget about it.
121 		 */
122 
123 		fd = open(path, O_RDONLY);
124 		if (fd == -1) {
125 			rv = errno;
126 			goto out;
127 		}
128 
129 #if (!defined(DIOCGDINFO) || !defined(DISKPART)) && !defined(DIOCGWEDGEINFO)
130 		{
131 		off_t off = lseek(fd, 0, SEEK_END);
132 		if (off != 0) {
133 			size = off;
134 			goto out;
135 		}
136 		fprintf(stderr, "error: device size query not implemented on "
137 		    "this platform\n");
138 		rv = EOPNOTSUPP;
139 		goto out;
140 		}
141 #else
142 
143 #if defined(DIOCGDINFO) && defined(DISKPART)
144 		{
145 		struct disklabel lab;
146 		struct partition *parta;
147 		if (ioctl(fd, DIOCGDINFO, &lab) == 0) {
148 			parta = &lab.d_partitions[DISKPART(sb.st_rdev)];
149 			size = (uint64_t)lab.d_secsize * parta->p_size;
150 			goto out;
151 		}
152 		}
153 #endif
154 
155 #if defined(DIOCGWEDGEINFO)
156 		{
157 		struct dkwedge_info dkw;
158 		if (ioctl(fd, DIOCGWEDGEINFO, &dkw) == 0) {
159 			/*
160 			 * XXX: should use DIOCGDISKINFO to query
161 			 * sector size, but that requires proplib,
162 			 * so just don't bother for now.  it's nice
163 			 * that something as difficult as figuring out
164 			 * a partition's size has been made so easy.
165 			 */
166 			size = dkw.dkw_size << DEV_BSHIFT;
167 			goto out;
168 		}
169 		}
170 #endif
171 		rv = errno;
172 #endif
173 	}
174 
175  out:
176 	if (rv == 0 && sizep)
177 		*sizep = size;
178 	if (rv == 0 && ftp)
179 		*ftp = ft;
180 	if (fd != -1)
181 		close(fd);
182 
183 	ET(rv);
184 }
185 
186 int
rumpuser_open(const char * path,int ruflags,int * fdp)187 rumpuser_open(const char *path, int ruflags, int *fdp)
188 {
189 	int fd, flags, rv;
190 
191 	switch (ruflags & RUMPUSER_OPEN_ACCMODE) {
192 	case RUMPUSER_OPEN_RDONLY:
193 		flags = O_RDONLY;
194 		break;
195 	case RUMPUSER_OPEN_WRONLY:
196 		flags = O_WRONLY;
197 		break;
198 	case RUMPUSER_OPEN_RDWR:
199 		flags = O_RDWR;
200 		break;
201 	default:
202 		rv = EINVAL;
203 		goto out;
204 	}
205 
206 #define TESTSET(_ru_, _h_) if (ruflags & _ru_) flags |= _h_;
207 	TESTSET(RUMPUSER_OPEN_CREATE, O_CREAT);
208 	TESTSET(RUMPUSER_OPEN_EXCL, O_EXCL);
209 #undef TESTSET
210 
211 	KLOCK_WRAP(fd = open(path, flags, 0644));
212 	if (fd == -1) {
213 		rv = errno;
214 	} else {
215 		*fdp = fd;
216 		rv = 0;
217 	}
218 
219  out:
220 	ET(rv);
221 }
222 
223 int
rumpuser_close(int fd)224 rumpuser_close(int fd)
225 {
226 	int nlocks;
227 
228 	rumpkern_unsched(&nlocks, NULL);
229 	fsync(fd);
230 	close(fd);
231 	rumpkern_sched(nlocks, NULL);
232 
233 	ET(0);
234 }
235 
236 /*
237  * Assume "struct rumpuser_iovec" and "struct iovec" are the same.
238  * If you encounter POSIX platforms where they aren't, add some
239  * translation for iovlen > 1.
240  */
241 int
rumpuser_iovread(int fd,struct rumpuser_iovec * ruiov,size_t iovlen,int64_t roff,size_t * retp)242 rumpuser_iovread(int fd, struct rumpuser_iovec *ruiov, size_t iovlen,
243 	int64_t roff, size_t *retp)
244 {
245 	struct iovec *iov = (struct iovec *)ruiov;
246 	off_t off = (off_t)roff;
247 	ssize_t nn;
248 	int rv;
249 
250 	if (off == RUMPUSER_IOV_NOSEEK) {
251 		KLOCK_WRAP(nn = readv(fd, iov, iovlen));
252 	} else {
253 		int nlocks;
254 
255 		rumpkern_unsched(&nlocks, NULL);
256 		if (lseek(fd, off, SEEK_SET) == off) {
257 			nn = readv(fd, iov, iovlen);
258 		} else {
259 			nn = -1;
260 		}
261 		rumpkern_sched(nlocks, NULL);
262 	}
263 
264 	if (nn == -1) {
265 		rv = errno;
266 	} else {
267 		*retp = (size_t)nn;
268 		rv = 0;
269 	}
270 
271 	ET(rv);
272 }
273 
274 int
rumpuser_iovwrite(int fd,const struct rumpuser_iovec * ruiov,size_t iovlen,int64_t roff,size_t * retp)275 rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
276 	int64_t roff, size_t *retp)
277 {
278 	const struct iovec *iov = (const struct iovec *)ruiov;
279 	off_t off = (off_t)roff;
280 	ssize_t nn;
281 	int rv;
282 
283 	if (off == RUMPUSER_IOV_NOSEEK) {
284 		KLOCK_WRAP(nn = writev(fd, iov, iovlen));
285 	} else {
286 		int nlocks;
287 
288 		rumpkern_unsched(&nlocks, NULL);
289 		if (lseek(fd, off, SEEK_SET) == off) {
290 			nn = writev(fd, iov, iovlen);
291 		} else {
292 			nn = -1;
293 		}
294 		rumpkern_sched(nlocks, NULL);
295 	}
296 
297 	if (nn == -1) {
298 		rv = errno;
299 	} else {
300 		*retp = (size_t)nn;
301 		rv = 0;
302 	}
303 
304 	ET(rv);
305 }
306 
307 int
rumpuser_syncfd(int fd,int flags,uint64_t start,uint64_t len)308 rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
309 {
310 	int rv = 0;
311 
312 	/*
313 	 * For now, assume fd is regular file and does not care
314 	 * about read syncing
315 	 */
316 	if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
317 		rv = EINVAL;
318 		goto out;
319 	}
320 	if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
321 		rv = 0;
322 		goto out;
323 	}
324 
325 #if defined(HAVE_FSYNC_RANGE)
326 	{
327 	int fsflags = FDATASYNC;
328 
329 	if (fsflags & RUMPUSER_SYNCFD_SYNC)
330 		fsflags |= FDISKSYNC;
331 	if (fsync_range(fd, fsflags, start, len) == -1)
332 		rv = errno;
333 	}
334 #else
335 	/* el-simplo */
336 	if (fsync(fd) == -1)
337 		rv = errno;
338 #endif
339 
340  out:
341 	ET(rv);
342 }
343