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