+ if (program == NULL || strlen(program) == 0) {
+ return new FileFilter(fd, fd, -1);
+ }
+
+ pid_t pid;
+ int wrapped_fd = spawn_filter(fd, program, &pid);
+ return new FileFilter(fd, wrapped_fd, pid);
+}
+
+int FileFilter::wait()
+{
+ // No filter program was launched implies no need to wait.
+ if (pid == -1)
+ return 0;
+
+ // The raw file descriptor was held open to track the output file size, but
+ // is not needed any longer.
+ close(fd_raw);
+
+ int status;
+ if (waitpid(pid, &status, 0) < 0) {
+ fprintf(stderr, "Error waiting for filter process: %m\n");
+ return -1;
+ }
+
+ if (!WIFEXITED(status) || WEXITSTATUS(status) != 0) {
+ fprintf(stderr, "Filter process error: %d\n", WEXITSTATUS(status));
+ }
+
+ return status;
+}
+
+/* Launch a child process which can act as a filter (compress, encrypt, etc.)
+ * on the TAR output. The file descriptor to which output should be written
+ * must be specified; the return value is the file descriptor which will be
+ * attached to the standard input of the filter program. */
+int FileFilter::spawn_filter(int fd_out, const char *program, pid_t *filter_pid)
+{
+ int fds[2];
+ pid_t pid;
+
+ /* Create a pipe for communicating with the filter process. */
+ if (pipe(fds) < 0) {
+ fatal("Unable to create pipe for filter");
+ }
+
+ /* Create a child process which can exec() the filter program. */
+ pid = fork();
+ if (pid < 0)
+ fatal("Unable to fork filter process");
+
+ if (pid > 0) {
+ /* Parent process */
+ close(fds[0]);
+ cloexec(fds[1]);
+ if (filter_pid != NULL)
+ *filter_pid = pid;
+ } else {
+ /* Child process. Rearrange file descriptors. stdin is fds[0], stdout
+ * is fd_out, stderr is unchanged. */
+ close(fds[1]);
+
+ if (dup2(fds[0], 0) < 0)
+ exit(1);
+ close(fds[0]);
+
+ if (dup2(fd_out, 1) < 0)
+ exit(1);
+ close(fd_out);
+
+ /* Exec the filter program. */
+ execlp("/bin/sh", "/bin/sh", "-c", program, NULL);
+
+ /* Should not reach here except for error cases. */
+ fprintf(stderr, "Could not exec filter: %m\n");
+ exit(1);
+ }
+
+ return fds[1];
+}
+
+void Tarfile::tar_write(const char *data, size_t len)
+{
+ size += len;
+
+ while (len > 0) {
+ int res = write(filter->get_wrapped_fd(), data, len);
+
+ if (res < 0) {
+ if (errno == EINTR)
+ continue;
+ fprintf(stderr, "Write error: %m\n");
+ fatal("Write error");
+ }