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