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