1 /*        $NetBSD: rumpuser_file.c,v 1.5 2024/10/16 09:09:39 ozaki-r 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.5 2024/10/16 09:09:39 ozaki-r 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 #ifdef HAVE_PREADV
254                     KLOCK_WRAP(nn = preadv(fd, iov, iovlen, off));
255 #else
256                     int nlocks;
257 
258                     rumpkern_unsched(&nlocks, NULL);
259                     if (lseek(fd, off, SEEK_SET) == off) {
260                               nn = readv(fd, iov, iovlen);
261                     } else {
262                               nn = -1;
263                     }
264                     rumpkern_sched(nlocks, NULL);
265 #endif
266           }
267 
268           if (nn == -1) {
269                     rv = errno;
270           } else {
271                     *retp = (size_t)nn;
272                     rv = 0;
273           }
274 
275           ET(rv);
276 }
277 
278 int
rumpuser_iovwrite(int fd,const struct rumpuser_iovec * ruiov,size_t iovlen,int64_t roff,size_t * retp)279 rumpuser_iovwrite(int fd, const struct rumpuser_iovec *ruiov, size_t iovlen,
280           int64_t roff, size_t *retp)
281 {
282           const struct iovec *iov = (const struct iovec *)ruiov;
283           off_t off = (off_t)roff;
284           ssize_t nn;
285           int rv;
286 
287           if (off == RUMPUSER_IOV_NOSEEK) {
288                     KLOCK_WRAP(nn = writev(fd, iov, iovlen));
289           } else {
290 #ifdef HAVE_PWRITEV
291                     KLOCK_WRAP(nn = pwritev(fd, iov, iovlen, off));
292 #else
293                     int nlocks;
294 
295                     rumpkern_unsched(&nlocks, NULL);
296                     if (lseek(fd, off, SEEK_SET) == off) {
297                               nn = writev(fd, iov, iovlen);
298                     } else {
299                               nn = -1;
300                     }
301                     rumpkern_sched(nlocks, NULL);
302 #endif
303           }
304 
305           if (nn == -1) {
306                     rv = errno;
307           } else {
308                     *retp = (size_t)nn;
309                     rv = 0;
310           }
311 
312           ET(rv);
313 }
314 
315 int
rumpuser_syncfd(int fd,int flags,uint64_t start,uint64_t len)316 rumpuser_syncfd(int fd, int flags, uint64_t start, uint64_t len)
317 {
318           int rv = 0;
319 
320           /*
321            * For now, assume fd is regular file and does not care
322            * about read syncing
323            */
324           if ((flags & RUMPUSER_SYNCFD_BOTH) == 0) {
325                     rv = EINVAL;
326                     goto out;
327           }
328           if ((flags & RUMPUSER_SYNCFD_WRITE) == 0) {
329                     rv = 0;
330                     goto out;
331           }
332 
333 #if defined(HAVE_FSYNC_RANGE)
334           {
335           int fsflags = FDATASYNC;
336 
337           if (fsflags & RUMPUSER_SYNCFD_SYNC)
338                     fsflags |= FDISKSYNC;
339           if (fsync_range(fd, fsflags, start, len) == -1)
340                     rv = errno;
341           }
342 #else
343           /* el-simplo */
344           if (fsync(fd) == -1)
345                     rv = errno;
346 #endif
347 
348  out:
349           ET(rv);
350 }
351