ssize_t __libc_pread64 (int fd, void *buf, size_t nbyte, off64_t offset) { /* Since we must not change the file pointer preserve the value so that we can restore it later. */ int save_errno; ssize_t result; off64_t old_offset = __libc_lseek64 (fd, 0, SEEK_CUR); if (old_offset == (off64_t) -1) return -1; /* Set to wanted position. */ if (__libc_lseek64 (fd, offset, SEEK_SET) == (off64_t) -1) return -1; /* Write out the data. */ result = __libc_read (fd, buf, nbyte); /* Now we have to restore the position. If this fails we have to return this as an error. But if the writing also failed we return this error. */ save_errno = errno; if (__libc_lseek64 (fd, old_offset, SEEK_SET) == (off64_t) -1) { if (result == -1) __set_errno (save_errno); return -1; } __set_errno (save_errno); return result; }
COPY_FILE_RANGE_DECL ssize_t COPY_FILE_RANGE (int infd, __off64_t *pinoff, int outfd, __off64_t *poutoff, size_t length, unsigned int flags) { if (flags != 0) { __set_errno (EINVAL); return -1; } { struct stat64 instat; struct stat64 outstat; if (fstat64 (infd, &instat) != 0 || fstat64 (outfd, &outstat) != 0) return -1; if (S_ISDIR (instat.st_mode) || S_ISDIR (outstat.st_mode)) { __set_errno (EISDIR); return -1; } if (!S_ISREG (instat.st_mode) || !S_ISREG (outstat.st_mode)) { /* We need a regular input file so that the we can seek backwards in case of a write failure. */ __set_errno (EINVAL); return -1; } if (instat.st_dev != outstat.st_dev) { /* Cross-device copies are not supported. */ __set_errno (EXDEV); return -1; } } /* The output descriptor must not have O_APPEND set. */ { int flags = __fcntl (outfd, F_GETFL); if (flags & O_APPEND) { __set_errno (EBADF); return -1; } } /* Avoid an overflow in the result. */ if (length > SSIZE_MAX) length = SSIZE_MAX; /* Main copying loop. The buffer size is arbitrary and is a trade-off between stack size consumption, cache usage, and amortization of system call overhead. */ size_t copied = 0; char buf[8192]; while (length > 0) { size_t to_read = length; if (to_read > sizeof (buf)) to_read = sizeof (buf); /* Fill the buffer. */ ssize_t read_count; if (pinoff == NULL) read_count = read (infd, buf, to_read); else read_count = __libc_pread64 (infd, buf, to_read, *pinoff); if (read_count == 0) /* End of file reached prematurely. */ return copied; if (read_count < 0) { if (copied > 0) /* Report the number of bytes copied so far. */ return copied; return -1; } if (pinoff != NULL) *pinoff += read_count; /* Write the buffer part which was read to the destination. */ char *end = buf + read_count; for (char *p = buf; p < end; ) { ssize_t write_count; if (poutoff == NULL) write_count = write (outfd, p, end - p); else write_count = __libc_pwrite64 (outfd, p, end - p, *poutoff); if (write_count < 0) { /* Adjust the input read position to match what we have written, so that the caller can pick up after the error. */ size_t written = p - buf; /* NB: This needs to be signed so that we can form the negative value below. */ ssize_t overread = read_count - written; if (pinoff == NULL) { if (overread > 0) { /* We are on an error recovery path, so we cannot deal with failure here. */ int save_errno = errno; (void) __libc_lseek64 (infd, -overread, SEEK_CUR); __set_errno (save_errno); } } else /* pinoff != NULL */ *pinoff -= overread; if (copied + written > 0) /* Report the number of bytes copied so far. */ return copied + written; return -1; } p += write_count; if (poutoff != NULL) *poutoff += write_count; } /* Write loop. */ copied += read_count; length -= read_count; } return copied; }
/* Seek to OFFSET on FD, starting from WHENCE. */ off_t __libc_lseek (int fd, off_t offset, int whence) { return __libc_lseek64 (fd, (off64_t) offset, whence); }