tar: fix --to-command wrt short writes
[oweals/busybox.git] / libbb / copyfd.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 #include "libbb.h"
11
12 /* Used by NOFORK applets (e.g. cat) - must not use xmalloc.
13  * size < 0 means "ignore write errors", used by tar --to-command
14  * size = 0 means "copy till EOF"
15  */
16 static off_t bb_full_fd_action(int src_fd, int dst_fd, off_t size)
17 {
18         int status = -1;
19         off_t total = 0;
20 #if CONFIG_FEATURE_COPYBUF_KB <= 4
21         char buffer[CONFIG_FEATURE_COPYBUF_KB * 1024];
22         enum { buffer_size = sizeof(buffer) };
23 #else
24         char *buffer;
25         int buffer_size;
26         bool continue_on_write_error = 0;
27
28         if (size < 0) {
29                 size = -size;
30                 continue_on_write_error = 1;
31         }
32
33         if (size > 0 && size <= 4 * 1024)
34                 goto use_small_buf;
35         /* We want page-aligned buffer, just in case kernel is clever
36          * and can do page-aligned io more efficiently */
37         buffer = mmap(NULL, CONFIG_FEATURE_COPYBUF_KB * 1024,
38                         PROT_READ | PROT_WRITE,
39                         MAP_PRIVATE | MAP_ANON,
40                         /* ignored: */ -1, 0);
41         buffer_size = CONFIG_FEATURE_COPYBUF_KB * 1024;
42         if (buffer == MAP_FAILED) {
43  use_small_buf:
44                 buffer = alloca(4 * 1024);
45                 buffer_size = 4 * 1024;
46         }
47 #endif
48
49         if (src_fd < 0)
50                 goto out;
51
52         if (!size) {
53                 size = buffer_size;
54                 status = 1; /* copy until eof */
55         }
56
57         while (1) {
58                 ssize_t rd;
59
60                 rd = safe_read(src_fd, buffer, size > buffer_size ? buffer_size : size);
61
62                 if (!rd) { /* eof - all done */
63                         status = 0;
64                         break;
65                 }
66                 if (rd < 0) {
67                         bb_perror_msg(bb_msg_read_error);
68                         break;
69                 }
70                 /* dst_fd == -1 is a fake, else... */
71                 if (dst_fd >= 0) {
72                         ssize_t wr = full_write(dst_fd, buffer, rd);
73                         if (wr < rd) {
74                                 if (!continue_on_write_error) {
75                                         bb_perror_msg(bb_msg_write_error);
76                                         break;
77                                 }
78                                 dst_fd = -1;
79                         }
80                 }
81                 total += rd;
82                 if (status < 0) { /* if we aren't copying till EOF... */
83                         size -= rd;
84                         if (!size) {
85                                 /* 'size' bytes copied - all done */
86                                 status = 0;
87                                 break;
88                         }
89                 }
90         }
91  out:
92
93 #if CONFIG_FEATURE_COPYBUF_KB > 4
94         if (buffer_size != 4 * 1024)
95                 munmap(buffer, buffer_size);
96 #endif
97         return status ? -1 : total;
98 }
99
100
101 #if 0
102 void FAST_FUNC complain_copyfd_and_die(off_t sz)
103 {
104         if (sz != -1)
105                 bb_error_msg_and_die("short read");
106         /* if sz == -1, bb_copyfd_XX already complained */
107         xfunc_die();
108 }
109 #endif
110
111 off_t FAST_FUNC bb_copyfd_size(int fd1, int fd2, off_t size)
112 {
113         if (size) {
114                 return bb_full_fd_action(fd1, fd2, size);
115         }
116         return 0;
117 }
118
119 void FAST_FUNC bb_copyfd_exact_size(int fd1, int fd2, off_t size)
120 {
121         off_t sz = bb_copyfd_size(fd1, fd2, size);
122         if (sz == (size >= 0 ? size : -size))
123                 return;
124         if (sz != -1)
125                 bb_error_msg_and_die("short read");
126         /* if sz == -1, bb_copyfd_XX already complained */
127         xfunc_die();
128 }
129
130 off_t FAST_FUNC bb_copyfd_eof(int fd1, int fd2)
131 {
132         return bb_full_fd_action(fd1, fd2, 0);
133 }