LCOV - code coverage report
Current view: top level - src/tds - stream.c (source / functions) Hit Total Coverage
Test: FreeTDS coverage Lines: 165 178 92.7 %
Date: 2026-03-02 06:50:21 Functions: 17 17 100.0 %

          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             : }

Generated by: LCOV version 1.13