a009ce9dd690aa050c99556b007fc01ed23fa6e9
[samba.git] / source3 / lib / recvfile.c
1 /*
2  Unix SMB/Netbios implementation.
3  Version 3.2.x
4  recvfile implementations.
5  Copyright (C) Jeremy Allison 2007.
6
7  This program is free software; you can redistribute it and/or modify
8  it under the terms of the GNU General Public License as published by
9  the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version.
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details.
15
16  You should have received a copy of the GNU General Public License
17  along with this program; if not, see <http://www.gnu.org/licenses/>.
18 */
19
20 /*
21  * This file handles the OS dependent recvfile implementations.
22  * The API is such that it returns -1 on error, else returns the
23  * number of bytes written.
24  */
25
26 #include "includes.h"
27
28 /* Do this on our own in TRANSFER_BUF_SIZE chunks.
29  * It's safe to make direct syscalls to lseek/write here
30  * as we're below the Samba vfs layer.
31  *
32  * If tofd is -1 we just drain the incoming socket of count
33  * bytes without writing to the outgoing fd.
34  * If a write fails we do the same (to cope with disk full)
35  * errors.
36  *
37  * Returns -1 on short reads from fromfd (read error)
38  * and sets errno.
39  *
40  * Returns number of bytes written to 'tofd'
41  * or thrown away if 'tofd == -1'.
42  * return != count then sets errno.
43  * Returns count if complete success.
44  */
45
46 #ifndef TRANSFER_BUF_SIZE
47 #define TRANSFER_BUF_SIZE (128*1024)
48 #endif
49
50 static ssize_t default_sys_recvfile(int fromfd,
51                         int tofd,
52                         SMB_OFF_T offset,
53                         size_t count)
54 {
55         int saved_errno = 0;
56         size_t total = 0;
57         size_t bufsize = MIN(TRANSFER_BUF_SIZE,count);
58         size_t total_written = 0;
59         char *buffer = NULL;
60
61         if (count == 0) {
62                 return 0;
63         }
64
65         if (tofd != -1 && offset != (SMB_OFF_T)-1) {
66                 if (sys_lseek(tofd, offset, SEEK_SET) == -1) {
67                         if (errno != ESPIPE) {
68                                 return -1;
69                         }
70                 }
71         }
72
73         buffer = SMB_MALLOC_ARRAY(char, bufsize);
74         if (buffer == NULL) {
75                 return -1;
76         }
77
78         while (total < count) {
79                 size_t num_written = 0;
80                 ssize_t read_ret;
81                 size_t toread = MIN(bufsize,count - total);
82
83                 /* Read from socket - ignore EINTR. */
84                 read_ret = sys_read(fromfd, buffer, toread);
85                 if (read_ret <= 0) {
86                         /* EOF or socket error. */
87                         free(buffer);
88                         return -1;
89                 }
90
91                 num_written = 0;
92
93                 while (num_written < read_ret) {
94                         ssize_t write_ret;
95
96                         if (tofd == -1) {
97                                 write_ret = read_ret;
98                         } else {
99                                 /* Write to file - ignore EINTR. */
100                                 write_ret = sys_write(tofd,
101                                                 buffer + num_written,
102                                                 read_ret - num_written);
103
104                                 if (write_ret <= 0) {
105                                         /* write error - stop writing. */
106                                         tofd = -1;
107                                         saved_errno = errno;
108                                         continue;
109                                 }
110                         }
111
112                         num_written += (size_t)write_ret;
113                         total_written += (size_t)write_ret;
114                 }
115
116                 total += read_ret;
117         }
118
119         free(buffer);
120         if (saved_errno) {
121                 /* Return the correct write error. */
122                 errno = saved_errno;
123         }
124         return (ssize_t)total_written;
125 }
126
127 #if defined(HAVE_SPLICE_SYSCALL)
128
129 #ifdef JRA_SPLICE_TEST
130 #include <linux/unistd.h>
131 #include <sys/syscall.h>
132
133 #define __NR_splice             313
134 _syscall6( long, splice,
135                 int, fromfd,
136                 loff_t *, fromoffset,
137                 int, tofd,
138                 loff_t *, tooffset,
139                 size_t, count,
140                 unsigned int, flags);
141 #endif
142
143 /*
144  * Try and use the Linux system call to do this.
145  * Remember we only return -1 if the socket read
146  * failed. Else we return the number of bytes
147  * actually written. We always read count bytes
148  * from the network in the case of return != -1.
149  */
150
151 ssize_t sys_recvfile(int fromfd,
152                         int tofd,
153                         SMB_OFF_T offset,
154                         size_t count)
155 {
156         size_t total_written = 0;
157
158         if (count == 0) {
159                 return 0;
160         }
161
162         while (total_written < count) {
163                 ssize_t ret = splice(fromfd,
164                                         NULL,
165                                         tofd,
166                                         &offset,
167                                         count,
168                                         0);
169                 if (ret == -1) {
170                         if (errno != EINTR) {
171                                 break;
172                         }
173                         continue;
174                 }
175                 total_written += ret;
176                 count -= ret;
177         }
178
179         if (total_written < count) {
180                 int saved_errno = errno;
181                 if (drain_socket(fromfd, count-total_written) !=
182                                 count-total_written) {
183                         /* socket is dead. */
184                         return -1;
185                 }
186                 errno = saved_errno;
187         }
188
189         return total_written;
190 }
191 #else
192
193 /*****************************************************************
194  No recvfile system call - use the default 128 chunk implementation.
195 *****************************************************************/
196
197 ssize_t sys_recvfile(int fromfd,
198                         int tofd,
199                         SMB_OFF_T offset,
200                         size_t count)
201 {
202         return default_sys_recvfile(fromfd, tofd, offset, count);
203 }
204 #endif
205
206 /*****************************************************************
207  Throw away "count" bytes from the client socket.
208 *****************************************************************/
209
210 ssize_t drain_socket(int sockfd, size_t count)
211 {
212         return default_sys_recvfile(sockfd, -1, (SMB_OFF_T)-1, count);
213 }