/* Write NBYTES of BUF to FD at given position OFFSET without changing the file position. Return the number written, or -1. */ ssize_t __libc_pwrite (int fd, const void *buf, size_t nbytes, off_t offset) { return __libc_pwrite64 (fd, buf, nbytes, (off64_t) offset); }
int __posix_fallocate64_l64 (int fd, __off64_t offset, __off64_t len) { struct stat64 st; if (offset < 0 || len < 0) return EINVAL; /* Perform overflow check. The outer cast relies on a GCC extension. */ if ((__off64_t) ((uint64_t) offset + (uint64_t) len) < 0) return EFBIG; /* pwrite64 below will not do the right thing in O_APPEND mode. */ { int flags = __fcntl (fd, F_GETFL, 0); if (flags < 0 || (flags & O_APPEND) != 0) return EBADF; } /* We have to make sure that this is really a regular file. */ if (__fxstat64 (_STAT_VER, fd, &st) != 0) return EBADF; if (S_ISFIFO (st.st_mode)) return ESPIPE; if (! S_ISREG (st.st_mode)) return ENODEV; if (len == 0) { /* This is racy, but there is no good way to satisfy a zero-length allocation request. */ if (st.st_size < offset) { int ret = __ftruncate64 (fd, offset); if (ret != 0) ret = errno; return ret; } return 0; } /* Minimize data transfer for network file systems, by issuing single-byte write requests spaced by the file system block size. (Most local file systems have fallocate support, so this fallback code is not used there.) */ unsigned increment; { struct statfs64 f; if (__fstatfs64 (fd, &f) != 0) return errno; if (f.f_bsize == 0) increment = 512; else if (f.f_bsize < 4096) increment = f.f_bsize; else /* NFS clients do not propagate the block size of the underlying storage and may report a much larger value which would still leave holes after the loop below, so we cap the increment at 4096. */ increment = 4096; } /* Write a null byte to every block. This is racy; we currently lack a better option. Compare-and-swap against a file mapping might address local races, but requires interposition of a signal handler to catch SIGBUS. */ for (offset += (len - 1) % increment; len > 0; offset += increment) { len -= increment; if (offset < st.st_size) { unsigned char c; ssize_t rsize = __libc_pread64 (fd, &c, 1, offset); if (rsize < 0) return errno; /* If there is a non-zero byte, the block must have been allocated already. */ else if (rsize == 1 && c != 0) continue; } if (__libc_pwrite64 (fd, "", 1, offset) != 1) return errno; } return 0; }
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; }