#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #include #include #include static inline size_t splice_len(off_t len) { return (size_t) (len > INT_MAX ? INT_MAX : len); } int main(int argc, char *argv[]) { bool defer = false, keep = false; if (argc > 1 && strcmp(argv[1], "--defer") == 0) { defer = true; argv[1] = argv[0]; --argc, ++argv; } if (argc > 1 && strcmp(argv[1], "--keep") == 0) { keep = true; argv[1] = argv[0]; --argc, ++argv; } if (argc > 2 || keep && argc < 2) { fprintf(stderr, "usage: %s [--defer] { [] | --keep }\n", argv[0]); return EX_USAGE; } const char *path; int fd; if (keep) { if ((fd = open(argv[1], O_RDWR | O_CREAT | O_EXCL, 0666)) < 0) { error(EX_NOINPUT, errno, "%s", argv[1]); } } else { if (argc > 1) { path = argv[1]; } else if (!(path = getenv("TMPDIR"))) { path = "/tmp"; } if ((fd = open(path, O_RDWR | O_TMPFILE | O_EXCL, 0000)) < 0) { error(EX_NOINPUT, errno, "%s", path); } } bool eof = false, punch = !keep; off_t n_in = 0, n_out = 0, max = 0, mask = 0; for (;;) { off_t n_buf = n_in - n_out; if (n_buf == max) { struct statfs f; if (fstatfs(fd, &f) < 0) { error(EX_NOINPUT, errno, "%s", path); } max = f.f_bavail / 2 * f.f_bsize; mask = ~((off_t) f.f_bsize - 1); } struct pollfd pfds[2] = { }; nfds_t nfds = 0; if (!eof && n_buf < max) { pfds[nfds].fd = STDIN_FILENO; pfds[nfds].events = POLLIN; ++nfds; } if (n_buf > 0 && (!defer || eof)) { pfds[nfds].fd = STDOUT_FILENO; pfds[nfds].events = POLLOUT; ++nfds; } if (nfds == 0) { if (eof) { break; } sleep(1); continue; } if (poll(pfds, nfds, -1) > 0) { for (nfds_t i = 0; i < nfds; ++i) { if (pfds[i].revents) { if (pfds[i].fd == STDIN_FILENO) { ssize_t n = splice(STDIN_FILENO, NULL, fd, &n_in, splice_len(max - n_buf), SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE); if (n <= 0) { if (n == 0) { eof = true; } else if (errno != EAGAIN) { error(EX_IOERR, errno, "%s: splice", ""); } } else { n_buf += n; } } else if (pfds[i].fd == STDOUT_FILENO) { ssize_t n = splice(fd, NULL, STDOUT_FILENO, NULL, splice_len(n_buf), SPLICE_F_MOVE | SPLICE_F_NONBLOCK | SPLICE_F_MORE); if (n <= 0) { if (n < 0 && errno != EAGAIN) { error(EX_IOERR, errno, "%s: splice", ""); } } else if (((n_out += n) & mask) && punch && fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 0, n_out & mask) < 0) { if (errno != EOPNOTSUPP && errno != ENOSYS) { error(EX_IOERR, errno, "%s: fallocate", path); } fprintf(stderr, "WARNING: temp file in %s does not support FALLOC_FL_PUNCH_HOLE\n", path); punch = false; } } } } } else if (errno != EINTR) { error(EX_OSERR, errno, "poll"); } } return EX_OK; }