VirtualBox

source: vbox/trunk/src/VBox/Runtime/r3/posix/fileio-sg-posix.cpp@ 77239

Last change on this file since 77239 was 77239, checked in by vboxsync, 6 years ago

IPRT: Implemented RTFileSgWriteAt and RTfileSgReadAt for linux and freebsd. Clearified RTFileWriteAt and RTFileSgWriteAt specs wrt RTFILE_O_APPEND. bugref:9172

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 8.7 KB
Line 
1/* $Id: fileio-sg-posix.cpp 77239 2019-02-10 14:59:28Z vboxsync $ */
2/** @file
3 * IPRT - File I/O, RTFileSgReadAt & RTFileSgWriteAt, posixy.
4 */
5
6/*
7 * Copyright (C) 2006-2019 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 *
17 * The contents of this file may alternatively be used under the terms
18 * of the Common Development and Distribution License Version 1.0
19 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the
20 * VirtualBox OSE distribution, in which case the provisions of the
21 * CDDL are applicable instead of those of the GPL.
22 *
23 * You may elect to license modified versions of this file under the
24 * terms and conditions of either the GPL or the CDDL or both.
25 */
26
27
28/*
29 * Determin whether we've got preadv and pwritev.
30 */
31#include <iprt/cdefs.h>
32#ifdef RT_OS_LINUX
33/* Linux has these since glibc 2.10 and Linux 2.6.30: */
34# include <features.h>
35# ifdef __GLIBC_PREREQ
36# if __GLIBC_PREREQ(2,10)
37# define HAVE_PREADV_AND_PWRITEV 1
38#else
39# endif
40# endif
41
42#elif defined(RT_OS_FREEBSD)
43/* FreeBSD has these since 6.0: */
44# include <osreldate.h>
45# ifdef __FreeBSD_version
46# if __FreeBSD_version >= 600000
47# define HAVE_PREADV_AND_PWRITEV 1
48# endif
49# endif
50
51#endif
52
53#ifndef HAVE_PREADV_AND_PWRITEV
54# include "../../generic/fileio-sg-generic.cpp"
55#else /* HAVE_PREADV_AND_PWRITEV */
56
57/*********************************************************************************************************************************
58* Header Files *
59*********************************************************************************************************************************/
60# include <errno.h>
61# include <sys/types.h>
62# include <sys/uio.h>
63# include <unistd.h>
64
65# include "internal/iprt.h"
66# include <iprt/file.h>
67
68# include <iprt/assert.h>
69# include <iprt/err.h>
70# include <iprt/log.h>
71
72# ifndef UIO_MAXIOV
73# error "UIO_MAXIOV is undefined"
74# endif
75
76
77/* These assumptions simplifies things a lot here. */
78AssertCompileMembersSameSizeAndOffset(struct iovec, iov_base, RTSGSEG, pvSeg);
79AssertCompileMembersSameSizeAndOffset(struct iovec, iov_len, RTSGSEG, cbSeg);
80
81
82RTDECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead)
83{
84 /*
85 * Make sure we set pcbRead.
86 */
87 if (pcbRead)
88 *pcbRead = 0;
89
90 /*
91 * Special case: Zero read == seek.
92 */
93 if (cbToRead == 0)
94 return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
95
96 /*
97 * We can use the segment array directly if we're at the start of the
98 * current S/G segment and cbToRead matches the remainder exactly.
99 */
100 size_t cbTotalRead = 0;
101
102 size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
103 AssertMsgReturn(cbSgBufLeft >= cbToRead, ("%#zx vs %#zx\n", cbSgBufLeft, cbToRead), VERR_INVALID_PARAMETER);
104
105 if (cbToRead == cbSgBufLeft)
106 while (RTSgBufIsAtStartOfSegment(pSgBuf))
107 {
108 size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
109 ssize_t cbThisRead = preadv(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
110 RT_MIN(cSegsLeft, UIO_MAXIOV), off);
111 if (cbThisRead >= 0)
112 {
113 AssertStmt((size_t)cbThisRead <= cbToRead, cbThisRead = cbToRead);
114
115 RTSgBufAdvance(pSgBuf, cbThisRead);
116 cbTotalRead += cbThisRead;
117 cbToRead -= cbThisRead;
118 if (cbToRead == 0)
119 {
120 if (pcbRead)
121 *pcbRead = cbTotalRead;
122 return VINF_SUCCESS;
123 }
124
125 if ( pcbRead
126 && ( cSegsLeft <= UIO_MAXIOV
127 || cbThisRead == 0 /* typically EOF */ ))
128 {
129 *pcbRead = cbTotalRead;
130 return VINF_SUCCESS;
131 }
132 if (cbThisRead == 0)
133 return VERR_EOF;
134
135 off += cbThisRead;
136 }
137 else if (cbTotalRead > 0 && pcbRead)
138 {
139 *pcbRead = cbTotalRead;
140 return VINF_SUCCESS;
141 }
142 else
143 return RTErrConvertFromErrno(errno);
144 }
145
146 /*
147 * Unaligned start or not reading the whole buffer. For reasons of
148 * simplicity, we work the input segment by segment like the generic code.
149 */
150 int rc = VINF_SUCCESS;
151 while (cbToRead > 0)
152 {
153 size_t cbSeg;
154 void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToRead, &cbSeg);
155 size_t cbThisRead = cbSeg;
156 rc = RTFileReadAt(hFile, off, pvSeg, cbSeg, pcbRead ? &cbThisRead : NULL);
157 if (RT_SUCCESS(rc))
158 {
159 RTSgBufAdvance(pSgBuf, cbThisRead);
160 cbTotalRead += cbThisRead;
161 }
162 else
163 break;
164 if ((size_t)cbThisRead < cbSeg)
165 {
166 AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2);
167 break;
168 }
169
170 Assert(cbSeg == cbThisRead);
171 cbToRead -= cbSeg;
172 off += cbSeg;
173 }
174 if (pcbRead)
175 *pcbRead = cbTotalRead;
176 return rc;
177}
178
179
180RTDECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten)
181{
182 /*
183 * Make sure we set pcbWritten.
184 */
185 if (pcbWritten)
186 *pcbWritten = 0;
187
188 /*
189 * Special case: Zero write == seek.
190 */
191 if (cbToWrite == 0)
192 return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL);
193
194 /*
195 * We can use the segment array directly if we're at the start of the
196 * current S/G segment and cbToWrite matches the remainder exactly.
197 */
198 size_t cbTotalWritten = 0;
199
200 size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf);
201 AssertMsgReturn(cbSgBufLeft >= cbToWrite, ("%#zx vs %#zx\n", cbSgBufLeft, cbToWrite), VERR_INVALID_PARAMETER);
202
203 if (cbToWrite == cbSgBufLeft)
204 while (RTSgBufIsAtStartOfSegment(pSgBuf))
205 {
206 size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg;
207 ssize_t cbThisWritten = pwritev(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg],
208 RT_MIN(cSegsLeft, UIO_MAXIOV), off);
209 if (cbThisWritten >= 0)
210 {
211 AssertStmt((size_t)cbThisWritten <= cbToWrite, cbThisWritten = cbToWrite);
212
213 RTSgBufAdvance(pSgBuf, cbThisWritten);
214 cbTotalWritten += cbThisWritten;
215 cbToWrite -= cbThisWritten;
216 if (cbToWrite == 0)
217 {
218 if (pcbWritten)
219 *pcbWritten = cbTotalWritten;
220 return VINF_SUCCESS;
221 }
222
223 if ( pcbWritten
224 && ( cSegsLeft <= UIO_MAXIOV
225 || cbThisWritten == 0 /* non-file, full buffer/whatever */ ))
226 {
227 *pcbWritten = cbTotalWritten;
228 return VINF_SUCCESS;
229 }
230 if (cbThisWritten == 0)
231 return VERR_TRY_AGAIN;
232
233 off += cbThisWritten;
234 }
235 else if (cbTotalWritten > 0 && pcbWritten)
236 {
237 *pcbWritten = cbTotalWritten;
238 return VINF_SUCCESS;
239 }
240 else
241 return RTErrConvertFromErrno(errno);
242 }
243
244 /*
245 * Unaligned start or not writing the whole buffer. For reasons of
246 * simplicity, we work the input segment by segment like the generic code.
247 */
248 int rc = VINF_SUCCESS;
249 while (cbToWrite > 0)
250 {
251 size_t cbSeg;
252 void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToWrite, &cbSeg);
253 size_t cbThisWritten = cbSeg;
254 rc = RTFileWriteAt(hFile, off, pvSeg, cbSeg, pcbWritten ? &cbThisWritten : NULL);
255 if (RT_SUCCESS(rc))
256 {
257 RTSgBufAdvance(pSgBuf, cbThisWritten);
258 cbTotalWritten += cbThisWritten;
259 }
260 else
261 break;
262 if ((size_t)cbThisWritten < cbSeg)
263 {
264 AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2);
265 break;
266 }
267
268 Assert(cbSeg == cbThisWritten);
269 cbToWrite -= cbSeg;
270 off += cbSeg;
271 }
272 if (pcbWritten)
273 *pcbWritten = cbTotalWritten;
274 return rc;
275}
276
277#endif /* HAVE_PREADV_AND_PWRITEV */
278
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette