/* $Id: fileio-sg-at-posix.cpp 77635 2019-03-10 14:51:26Z vboxsync $ */ /** @file * IPRT - File I/O, RTFileSgReadAt & RTFileSgWriteAt, posixy. */ /* * Copyright (C) 2006-2019 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; * you can redistribute it and/or modify it under the terms of the GNU * General Public License (GPL) as published by the Free Software * Foundation, in version 2 as it comes in the "COPYING" file of the * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. * * The contents of this file may alternatively be used under the terms * of the Common Development and Distribution License Version 1.0 * (CDDL) only, as it comes in the "COPYING.CDDL" file of the * VirtualBox OSE distribution, in which case the provisions of the * CDDL are applicable instead of those of the GPL. * * You may elect to license modified versions of this file under the * terms and conditions of either the GPL or the CDDL or both. */ /********************************************************************************************************************************* * Header Files * *********************************************************************************************************************************/ /* * Determin whether we've got preadv and pwritev. */ #include #ifdef RT_OS_LINUX /* Linux has these since glibc 2.10 and Linux 2.6.30: */ # include # ifdef __GLIBC_PREREQ # if __GLIBC_PREREQ(2,10) # define HAVE_PREADV_AND_PWRITEV 1 #else # endif # endif #elif defined(RT_OS_FREEBSD) /* FreeBSD has these since 6.0: */ # include # ifdef __FreeBSD_version # if __FreeBSD_version >= 600000 # define HAVE_PREADV_AND_PWRITEV 1 # endif # endif #endif #ifndef HAVE_PREADV_AND_PWRITEV # include "../../generic/fileio-sg-at-generic.cpp" #else /* HAVE_PREADV_AND_PWRITEV - rest of the file */ # include # include # include # include # include # if defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) || defined(RT_OS_NETBSD) || defined(RT_OS_OPENBSD) # include # endif # include "internal/iprt.h" # include # include # include # include # ifndef UIO_MAXIOV # ifdef IOV_MAX # define UIO_MAXIOV IOV_MAX # else # error "UIO_MAXIOV and IOV_MAX are undefined" # endif # endif /* These assumptions simplifies things a lot here. */ AssertCompileMembersSameSizeAndOffset(struct iovec, iov_base, RTSGSEG, pvSeg); AssertCompileMembersSameSizeAndOffset(struct iovec, iov_len, RTSGSEG, cbSeg); RTDECL(int) RTFileSgReadAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToRead, size_t *pcbRead) { /* * Make sure we set pcbRead. */ if (pcbRead) *pcbRead = 0; /* * Special case: Zero read == seek. */ if (cbToRead == 0) return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL); /* * We can use the segment array directly if we're at the start of the * current S/G segment and cbToRead matches the remainder exactly. */ size_t cbTotalRead = 0; size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf); AssertMsgReturn(cbSgBufLeft >= cbToRead, ("%#zx vs %#zx\n", cbSgBufLeft, cbToRead), VERR_INVALID_PARAMETER); if (cbToRead == cbSgBufLeft) while (RTSgBufIsAtStartOfSegment(pSgBuf)) { size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg; ssize_t cbThisRead = preadv(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg], RT_MIN(cSegsLeft, UIO_MAXIOV), off); if (cbThisRead >= 0) { AssertStmt((size_t)cbThisRead <= cbToRead, cbThisRead = cbToRead); RTSgBufAdvance(pSgBuf, cbThisRead); cbTotalRead += cbThisRead; cbToRead -= cbThisRead; if (cbToRead == 0) { if (pcbRead) *pcbRead = cbTotalRead; return VINF_SUCCESS; } if ( pcbRead && ( cSegsLeft <= UIO_MAXIOV || cbThisRead == 0 /* typically EOF */ )) { *pcbRead = cbTotalRead; return VINF_SUCCESS; } if (cbThisRead == 0) return VERR_EOF; off += cbThisRead; } else if (cbTotalRead > 0 && pcbRead) { *pcbRead = cbTotalRead; return VINF_SUCCESS; } else return RTErrConvertFromErrno(errno); } /* * Unaligned start or not reading the whole buffer. For reasons of * simplicity, we work the input segment by segment like the generic code. */ int rc = VINF_SUCCESS; while (cbToRead > 0) { size_t cbSeg; void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToRead, &cbSeg); size_t cbThisRead = cbSeg; rc = RTFileReadAt(hFile, off, pvSeg, cbSeg, pcbRead ? &cbThisRead : NULL); if (RT_SUCCESS(rc)) { RTSgBufAdvance(pSgBuf, cbThisRead); cbTotalRead += cbThisRead; } else break; if ((size_t)cbThisRead < cbSeg) { AssertStmt(pcbRead, rc = VERR_INTERNAL_ERROR_2); break; } Assert(cbSeg == cbThisRead); cbToRead -= cbSeg; off += cbSeg; } if (pcbRead) *pcbRead = cbTotalRead; return rc; } RTDECL(int) RTFileSgWriteAt(RTFILE hFile, RTFOFF off, PRTSGBUF pSgBuf, size_t cbToWrite, size_t *pcbWritten) { /* * Make sure we set pcbWritten. */ if (pcbWritten) *pcbWritten = 0; /* * Special case: Zero write == seek. */ if (cbToWrite == 0) return RTFileSeek(hFile, off, RTFILE_SEEK_BEGIN, NULL); /* * We can use the segment array directly if we're at the start of the * current S/G segment and cbToWrite matches the remainder exactly. */ size_t cbTotalWritten = 0; size_t const cbSgBufLeft = RTSgBufCalcLengthLeft(pSgBuf); AssertMsgReturn(cbSgBufLeft >= cbToWrite, ("%#zx vs %#zx\n", cbSgBufLeft, cbToWrite), VERR_INVALID_PARAMETER); if (cbToWrite == cbSgBufLeft) while (RTSgBufIsAtStartOfSegment(pSgBuf)) { size_t const cSegsLeft = pSgBuf->cSegs - pSgBuf->idxSeg; ssize_t cbThisWritten = pwritev(RTFileToNative(hFile), (const struct iovec *)&pSgBuf->paSegs[pSgBuf->idxSeg], RT_MIN(cSegsLeft, UIO_MAXIOV), off); if (cbThisWritten >= 0) { AssertStmt((size_t)cbThisWritten <= cbToWrite, cbThisWritten = cbToWrite); RTSgBufAdvance(pSgBuf, cbThisWritten); cbTotalWritten += cbThisWritten; cbToWrite -= cbThisWritten; if (cbToWrite == 0) { if (pcbWritten) *pcbWritten = cbTotalWritten; return VINF_SUCCESS; } if ( pcbWritten && ( cSegsLeft <= UIO_MAXIOV || cbThisWritten == 0 /* non-file, full buffer/whatever */ )) { *pcbWritten = cbTotalWritten; return VINF_SUCCESS; } if (cbThisWritten == 0) return VERR_TRY_AGAIN; off += cbThisWritten; } else if (cbTotalWritten > 0 && pcbWritten) { *pcbWritten = cbTotalWritten; return VINF_SUCCESS; } else return RTErrConvertFromErrno(errno); } /* * Unaligned start or not writing the whole buffer. For reasons of * simplicity, we work the input segment by segment like the generic code. */ int rc = VINF_SUCCESS; while (cbToWrite > 0) { size_t cbSeg; void *pvSeg = RTSgBufGetCurrentSegment(pSgBuf, cbToWrite, &cbSeg); size_t cbThisWritten = cbSeg; rc = RTFileWriteAt(hFile, off, pvSeg, cbSeg, pcbWritten ? &cbThisWritten : NULL); if (RT_SUCCESS(rc)) { RTSgBufAdvance(pSgBuf, cbThisWritten); cbTotalWritten += cbThisWritten; } else break; if ((size_t)cbThisWritten < cbSeg) { AssertStmt(pcbWritten, rc = VERR_INTERNAL_ERROR_2); break; } Assert(cbSeg == cbThisWritten); cbToWrite -= cbSeg; off += cbSeg; } if (pcbWritten) *pcbWritten = cbTotalWritten; return rc; } #endif /* HAVE_PREADV_AND_PWRITEV */