Line data Source code
1 : /* FreeTDS - Library of routines accessing Sybase and Microsoft databases
2 : * Copyright (C) 2013 Frediano Ziglio
3 : *
4 : * This library is free software; you can redistribute it and/or
5 : * modify it under the terms of the GNU Library General Public
6 : * License as published by the Free Software Foundation; either
7 : * version 2 of the License, or (at your option) any later version.
8 : *
9 : * This library is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : * Library General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU Library General Public
15 : * License along with this library; if not, write to the
16 : * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
17 : * Boston, MA 02111-1307, USA.
18 : */
19 :
20 : /**
21 : * \file
22 : * \brief Handle stream of data
23 : */
24 :
25 : #include <config.h>
26 :
27 : #if HAVE_ERRNO_H
28 : #include <errno.h>
29 : #endif /* HAVE_ERRNO_H */
30 :
31 : #if HAVE_STDLIB_H
32 : #include <stdlib.h>
33 : #endif /* HAVE_STDLIB_H */
34 :
35 : #if HAVE_STRING_H
36 : #include <string.h>
37 : #endif /* HAVE_STRING_H */
38 :
39 : #if HAVE_UNISTD_H
40 : #include <unistd.h>
41 : #endif /* HAVE_UNISTD_H */
42 :
43 : #include <assert.h>
44 :
45 : #include <freetds/tds.h>
46 : #include <freetds/iconv.h>
47 : #include <freetds/stream.h>
48 :
49 : /** \cond HIDDEN_SYMBOLS */
50 : #if ENABLE_EXTRA_CHECKS
51 : # define TEMP_INIT(s) const size_t temp_size = s; char* temp = tds_new(char, temp_size)
52 : # define TEMP_FREE free(temp);
53 : # define TEMP_SIZE temp_size
54 : #else
55 : # define TEMP_INIT(s) char temp[s]
56 : # define TEMP_FREE ;
57 : # define TEMP_SIZE sizeof(temp)
58 : #endif
59 : /** \endcond */
60 :
61 : /**
62 : * Reads and writes from a stream converting characters
63 : * \tds
64 : * \param char_conv conversion structure
65 : * \param direction specify conversion to server or from server
66 : * \param istream input stream
67 : * \param ostream output stream
68 : * \return TDS_SUCCESS of TDS_FAIL
69 : */
70 : TDSRET
71 877298 : tds_convert_stream(TDSSOCKET * tds, TDSICONV * char_conv, TDS_ICONV_DIRECTION direction,
72 : TDSINSTREAM * istream, TDSOUTSTREAM *ostream)
73 : {
74 877298 : TEMP_INIT(4096);
75 : /*
76 : * temp (above) is the "preconversion" buffer, the place where the UCS-2 data
77 : * are parked before converting them to ASCII. It has to have a size,
78 : * and there's no advantage to allocating dynamically.
79 : * This also avoids any memory allocation error.
80 : */
81 : const char *ib;
82 877298 : size_t bufleft = 0;
83 877298 : TDSRET res = TDS_FAIL;
84 :
85 : /* cast away const for message suppression sub-structure */
86 877298 : TDS_ERRNO_MESSAGE_FLAGS *suppress = (TDS_ERRNO_MESSAGE_FLAGS*) &char_conv->suppress;
87 :
88 877298 : memset(suppress, 0, sizeof(char_conv->suppress));
89 1699703 : for (ib = temp; ostream->buf_len; ib = temp + bufleft) {
90 :
91 : char *ob;
92 : int len, conv_errno;
93 : size_t ol;
94 :
95 1436720 : assert(ib >= temp);
96 :
97 : /* read a chunk of data */
98 1436720 : len = istream->read(istream, (char*) ib, TEMP_SIZE - bufleft);
99 1436720 : if (len < 0)
100 : break;
101 1436720 : if (len == 0 && bufleft == 0) {
102 : res = TDS_SUCCESS;
103 : break;
104 : }
105 900366 : bufleft += len;
106 :
107 : /* Convert chunk */
108 900366 : ib = temp; /* always convert from start of buffer */
109 :
110 1047790 : convert_more:
111 974078 : ob = ostream->buffer;
112 974078 : ol = ostream->buf_len;
113 : /* FIXME not for last */
114 974078 : suppress->einval = 1; /* EINVAL matters only on the last chunk. */
115 974078 : suppress->e2big = 1;
116 974078 : ol = tds_iconv(tds, char_conv, direction, (const char **) &ib, &bufleft, &ob, &ol);
117 974078 : conv_errno = errno;
118 :
119 : /* write converted chunk */
120 974078 : len = ostream->write(ostream, ob - ostream->buffer);
121 974078 : if (TDS_UNLIKELY(len < 0))
122 : break;
123 :
124 974078 : if ((size_t) -1 == ol) {
125 228104 : tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: tds_iconv returned errno %d, conv_errno %d\n", errno, conv_errno);
126 228104 : if (conv_errno == E2BIG && ostream->buf_len && bufleft && len)
127 : goto convert_more;
128 154392 : if (conv_errno != EILSEQ) {
129 147090 : tdsdump_log(TDS_DBG_NETWORK, "Error: tds_convert_stream: "
130 : "Gave up converting %u bytes due to error %d.\n",
131 : (unsigned int) bufleft, errno);
132 147090 : tdsdump_dump_buf(TDS_DBG_NETWORK, "Troublesome bytes:", ib, bufleft);
133 : }
134 :
135 154392 : if (TDS_UNLIKELY(ib == temp)) { /* tds_iconv did not convert anything, avoid infinite loop */
136 77961 : tdsdump_log(TDS_DBG_NETWORK, "No conversion possible: some bytes left.\n");
137 77961 : res = TDS_FAIL;
138 77961 : if (conv_errno == EINVAL && tds)
139 740 : tdserror(tds_get_ctx(tds), tds, TDSEICONVAVAIL, 0);
140 77961 : if (conv_errno == E2BIG && tds)
141 36030 : tdserror(tds_get_ctx(tds), tds, TDSEICONVIU, 0);
142 77961 : errno = conv_errno;
143 77961 : break;
144 : }
145 :
146 76431 : if (bufleft)
147 76431 : memmove(temp, ib, bufleft);
148 : }
149 : }
150 :
151 877298 : TEMP_FREE;
152 877298 : return res;
153 : }
154 :
155 : /**
156 : * Reads and writes from a stream to another
157 : * \tds
158 : * \param istream input stream
159 : * \param ostream output stream
160 : * \return TDS_SUCCESS or TDS_FAIL
161 : */
162 : TDSRET
163 3358 : tds_copy_stream(TDSINSTREAM * istream, TDSOUTSTREAM * ostream)
164 : {
165 10820 : while (ostream->buf_len) {
166 : /* read a chunk of data */
167 7462 : int len = istream->read(istream, ostream->buffer, ostream->buf_len);
168 7462 : if (len == 0)
169 : return TDS_SUCCESS;
170 4104 : if (TDS_UNLIKELY(len < 0))
171 : break;
172 :
173 : /* write chunk */
174 4104 : len = ostream->write(ostream, len);
175 4104 : if (TDS_UNLIKELY(len < 0))
176 : break;
177 : }
178 : return TDS_FAIL;
179 : }
180 :
181 : /**
182 : * Reads data from network for input stream
183 : */
184 : static int
185 1033124 : tds_datain_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
186 : {
187 1033124 : TDSDATAINSTREAM *s = (TDSDATAINSTREAM *) stream;
188 1033124 : if (len > s->wire_size)
189 1032190 : len = s->wire_size;
190 1033124 : if (!tds_get_n(s->tds, ptr, len))
191 : return -1;
192 1033124 : s->wire_size -= len;
193 1033124 : return len;
194 : }
195 :
196 : /**
197 : * Initialize a data input stream.
198 : * This stream read data from network.
199 : * \param stream input stream to initialize
200 : * \tds
201 : * \param wire_size byte to read
202 : */
203 : void
204 675377 : tds_datain_stream_init(TDSDATAINSTREAM * stream, TDSSOCKET * tds, size_t wire_size)
205 : {
206 675377 : stream->stream.read = tds_datain_stream_read;
207 675377 : stream->wire_size = wire_size;
208 675377 : stream->tds = tds;
209 675377 : }
210 :
211 : /**
212 : * Writes data to network for output stream
213 : */
214 : static int
215 93018 : tds_dataout_stream_write(TDSOUTSTREAM *stream, size_t len)
216 : {
217 93018 : TDSDATAOUTSTREAM *s = (TDSDATAOUTSTREAM *) stream;
218 93018 : TDSSOCKET *tds = s->tds;
219 :
220 93018 : assert(len <= stream->buf_len);
221 93018 : assert(stream->buffer == (char *) tds->out_buf + tds->out_pos);
222 93018 : assert(stream->buf_len == tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE);
223 :
224 93018 : tds->out_pos += len;
225 : /* this must be strictly test as equal means we send a full packet
226 : * and we could be just at the end of packet so server would
227 : * wait for another packet with flag != 0
228 : */
229 93018 : if (tds->out_pos > tds->out_buf_max)
230 460 : tds_write_packet(tds, 0x0);
231 93018 : stream->buffer = (char *) tds->out_buf + tds->out_pos;
232 93018 : stream->buf_len = tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE;
233 93018 : s->written += len;
234 93018 : return len;
235 : }
236 :
237 : /**
238 : * Initialize a data output stream.
239 : * This stream writes data to network.
240 : * \param stream output stream to initialize
241 : * \tds
242 : */
243 : void
244 96471 : tds_dataout_stream_init(TDSDATAOUTSTREAM * stream, TDSSOCKET * tds)
245 : {
246 : #if TDS_ADDITIONAL_SPACE < 4
247 : #error Not supported
248 : #endif
249 : /*
250 : * we use the extra space as we want possible space for converting
251 : * a character and cause we don't want to send an exactly entire
252 : * packet with 0 flag and then nothing
253 : */
254 96471 : size_t left = tds->out_buf_max - tds->out_pos + TDS_ADDITIONAL_SPACE;
255 :
256 96471 : assert(left > 0);
257 96471 : stream->stream.write = tds_dataout_stream_write;
258 96471 : stream->stream.buffer = (char *) tds->out_buf + tds->out_pos;
259 96471 : stream->stream.buf_len = left;
260 96471 : stream->written = 0;
261 96471 : stream->tds = tds;
262 96471 : }
263 :
264 : /**
265 : * Reads data from a static allocated buffer
266 : */
267 : static int
268 405362 : tds_staticin_stream_read(TDSINSTREAM *stream, void *ptr, size_t len)
269 : {
270 405362 : TDSSTATICINSTREAM *s = (TDSSTATICINSTREAM *) stream;
271 405362 : size_t cp = TDS_MIN(len, s->buf_left);
272 :
273 405362 : memcpy(ptr, s->buffer, cp);
274 405362 : s->buffer += cp;
275 405362 : s->buf_left -= cp;
276 405362 : return cp;
277 : }
278 :
279 : /**
280 : * Initialize an input stream for read from a static allocated buffer
281 : * \param stream stream to initialize
282 : * \param ptr buffer to read from
283 : * \param len buffer size in bytes
284 : */
285 : void
286 202505 : tds_staticin_stream_init(TDSSTATICINSTREAM * stream, const void *ptr, size_t len)
287 : {
288 202525 : stream->stream.read = tds_staticin_stream_read;
289 202525 : stream->buffer = (const char *) ptr;
290 202525 : stream->buf_left = len;
291 202505 : }
292 :
293 :
294 : /**
295 : * Writes data to a static allocated buffer
296 : */
297 : static int
298 858976 : tds_staticout_stream_write(TDSOUTSTREAM *stream, size_t len)
299 : {
300 858976 : assert(stream->buf_len >= len);
301 858976 : stream->buffer += len;
302 858976 : stream->buf_len -= len;
303 858976 : return len;
304 : }
305 :
306 : /**
307 : * Initialize an output stream for write into a static allocated buffer
308 : * \param stream stream to initialize
309 : * \param ptr buffer to write to
310 : * \param len buffer size in bytes
311 : */
312 : void
313 760099 : tds_staticout_stream_init(TDSSTATICOUTSTREAM * stream, void *ptr, size_t len)
314 : {
315 760099 : stream->stream.write = tds_staticout_stream_write;
316 760099 : stream->stream.buffer = (char *) ptr;
317 760099 : stream->stream.buf_len = len;
318 760099 : }
319 :
320 : /**
321 : * Writes data to a dynamic allocated buffer
322 : */
323 : static int
324 60522 : tds_dynamic_stream_write(TDSOUTSTREAM *stream, size_t len)
325 : {
326 60522 : TDSDYNAMICSTREAM *s = (TDSDYNAMICSTREAM *) stream;
327 : size_t wanted;
328 :
329 60522 : s->size += len;
330 : /* grow linearly till some limit then exponentially */
331 60522 : if (s->size + 256 > s->allocated) {
332 2796 : wanted = s->size + (s->size < 4096 ? 1024 : s->size / 8u);
333 2796 : if (TDS_UNLIKELY(!tds_realloc(s->buf, wanted)))
334 : return -1;
335 2796 : s->allocated = wanted;
336 : }
337 60522 : assert(s->allocated > s->size);
338 60522 : stream->buffer = (char *) *s->buf + s->size;
339 60522 : stream->buf_len = s->allocated - s->size;
340 60522 : return len;
341 : }
342 :
343 : /**
344 : * Initialize a dynamic output stream.
345 : * This stream write data into a dynamic allocated buffer.
346 : * \param stream stream to initialize
347 : * \param ptr pointer to pointer to buffer to fill. Buffer
348 : * will be extended as needed
349 : * \param allocated bytes initialially allocated for the buffer.
350 : * Useful to reuse buffers
351 : * \return TDS_SUCCESS on success, TDS_FAIL otherwise
352 : */
353 : TDSRET
354 8896 : tds_dynamic_stream_init(TDSDYNAMICSTREAM * stream, void **ptr, size_t allocated)
355 : {
356 8896 : const size_t initial_size = 1024;
357 :
358 8896 : stream->stream.write = tds_dynamic_stream_write;
359 8896 : stream->buf = ptr;
360 8896 : if (allocated < initial_size) {
361 8458 : free(*ptr);
362 8458 : *ptr = NULL;
363 8458 : allocated = initial_size;
364 : }
365 8896 : if (!*ptr) {
366 8884 : *ptr = malloc(allocated);
367 8884 : if (TDS_UNLIKELY(!*ptr))
368 : return TDS_FAIL;
369 : }
370 8896 : stream->allocated = allocated;
371 8896 : stream->size = 0;
372 8896 : stream->stream.buffer = (char *) *ptr;
373 8896 : stream->stream.buf_len = allocated;
374 8896 : return TDS_SUCCESS;
375 : }
376 :
377 :
378 : static int
379 234 : tds_fileout_stream_write(TDSOUTSTREAM *stream, size_t n)
380 : {
381 : /* The meaning of this function is that the caller has placed "n" bytes
382 : * at the memory location stream->buffer, and they want that flushed
383 : * to the actual output.
384 : *
385 : * In the context of our buffered stream, this implies also writing
386 : * any earlier buffered data.
387 : */
388 : size_t extent, n_written;
389 :
390 234 : TDSFILEOUTSTREAM *s = (TDSFILEOUTSTREAM *) stream;
391 :
392 234 : if (!s->fp)
393 0 : return n;
394 :
395 : /* Don't read off the end of buffer */
396 234 : if (n > stream->buf_len)
397 0 : n = stream->buf_len;
398 :
399 : /* How much data to output (the buffered data and the new data) */
400 234 : extent = (stream->buffer - s->block) + n;
401 234 : if (extent == 0)
402 : return 0;
403 :
404 234 : n_written = fwrite(s->block, 1, extent, s->fp);
405 :
406 234 : if (n_written == 0)
407 : return -1;
408 :
409 234 : if (n_written >= extent) {
410 : /* All stored data written; full block available */
411 234 : stream->buffer = s->block;
412 234 : stream->buf_len = sizeof(s->block);
413 : } else {
414 : /* If they requested to not write the whole buffer for some reason,
415 : * or the write partially failed, we'll have to keep the remaining
416 : * data buffered.
417 : */
418 0 : extent -= n_written;
419 0 : memmove(s->block, s->block + n_written, extent);
420 0 : stream->buffer = s->block + extent;
421 0 : stream->buf_len = sizeof(s->block) - extent;
422 : }
423 :
424 : /* Returning how many characters were written to the file, which might
425 : * be greater than n. (This seems compatible with how this function
426 : * is used at existing call sites). */
427 234 : return n_written;
428 : }
429 :
430 : void
431 164 : tds_fileout_stream_init(TDSFILEOUTSTREAM *s, FILE *fp, int buff_mode)
432 : {
433 164 : s->buff_mode = buff_mode;
434 164 : s->fp = fp;
435 164 : s->stream.write = tds_fileout_stream_write;
436 164 : s->stream.buffer = s->block;
437 164 : s->stream.buf_len = sizeof(s->block);
438 164 : }
439 :
440 : TDSRET
441 164 : tds_fileout_stream_flush(TDSFILEOUTSTREAM *s)
442 : {
443 164 : return s->stream.write(&s->stream, 0) >= 0 ? TDS_SUCCESS : TDS_FAIL;
444 : }
445 :
446 : static TDSRET
447 3494 : tds_fileout_stream_putbuf(TDSFILEOUTSTREAM *s, void const *src, size_t n)
448 : {
449 : /* Optimization - If data fully fits in output buffer
450 : * we can bypass the stream copy method */
451 3494 : if (n <= s->stream.buf_len) {
452 3474 : memcpy(s->stream.buffer, src, n);
453 3474 : s->stream.buffer += n;
454 3474 : s->stream.buf_len -= n;
455 3474 : if (s->stream.buf_len == 0)
456 : return tds_fileout_stream_flush(s);
457 : return TDS_SUCCESS;
458 : } else {
459 : TDSSTATICINSTREAM is;
460 :
461 : /* Copy all of the source data to the output */
462 20 : tds_staticin_stream_init(&is, src, n);
463 20 : return tds_copy_stream(&is.stream, &s->stream);
464 : }
465 : }
466 :
467 : TDSRET
468 3494 : tds_fileout_stream_put(TDSFILEOUTSTREAM *s, void const *src, size_t n)
469 : {
470 : size_t len;
471 :
472 3494 : if (!s->fp || !n)
473 : return TDS_SUCCESS;
474 :
475 3494 : if (s->buff_mode == _IONBF)
476 0 : return fwrite(src, 1, n, s->fp) == n ? TDS_SUCCESS : TDS_FAIL;
477 :
478 3494 : if (s->buff_mode == _IOFBF)
479 3494 : return tds_fileout_stream_putbuf(s, src, n);
480 :
481 : /* Otherwise, line buffered */
482 :
483 : /* Flush everything up to the last new-line in the source */
484 0 : for (len = n; len; --len)
485 0 : if (((char const *) src)[len - 1] == '\n')
486 : break;
487 :
488 0 : if (len) {
489 0 : TDS_PROPAGATE(tds_fileout_stream_putbuf(s, src, len));
490 : TDS_PROPAGATE(tds_fileout_stream_flush(s));
491 : }
492 :
493 : /* Buffer any remaining partial line */
494 0 : if (len == n)
495 : return TDS_SUCCESS;
496 :
497 0 : return tds_fileout_stream_putbuf(s, (char const *) src + len, n - len);
498 : }
|