LCOV - code coverage report
Current view: top level - common/stream - mapi_stream.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 117 136 86.0 %
Date: 2024-10-04 20:04:04 Functions: 11 11 100.0 %

          Line data    Source code
       1             : /*
       2             :  * SPDX-License-Identifier: MPL-2.0
       3             :  *
       4             :  * This Source Code Form is subject to the terms of the Mozilla Public
       5             :  * License, v. 2.0.  If a copy of the MPL was not distributed with this
       6             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
       7             :  *
       8             :  * Copyright 2024 MonetDB Foundation;
       9             :  * Copyright August 2008 - 2023 MonetDB B.V.;
      10             :  * Copyright 1997 - July 2008 CWI.
      11             :  */
      12             : 
      13             : #include "monetdb_config.h"
      14             : #include "stream.h"
      15             : #include "stream_internal.h"
      16             : #include "mapi_prompt.h"
      17             : 
      18             : static ssize_t
      19     1800612 : byte_counting_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
      20             : {
      21     1800612 :         uint64_t *counter = (uint64_t*) s->stream_data.p;
      22     1800612 :         ssize_t nwritten = s->inner->write(s->inner, buf, elmsize, cnt);
      23     1800612 :         if (nwritten >= 0) {
      24     1800612 :                 *counter += elmsize * nwritten;
      25             :         }
      26     1800612 :         return nwritten;
      27             : }
      28             : 
      29             : 
      30             : stream *
      31         600 : byte_counting_stream(stream *wrapped, uint64_t *counter)
      32             : {
      33         600 :         stream *s = create_wrapper_stream(NULL, wrapped);
      34         600 :         if (!s)
      35             :                 return NULL;
      36         600 :         s->stream_data.p = counter;
      37         600 :         s->write = &byte_counting_write;
      38         600 :         s->destroy = &destroy_stream;
      39         600 :         return s;
      40             : }
      41             : 
      42             : 
      43             : 
      44             : static void
      45         102 : discard(stream *s)
      46             : {
      47         177 :         static char bitbucket[8192];
      48         177 :         while (1) {
      49         177 :                 ssize_t nread = mnstr_read(s, bitbucket, 1, sizeof(bitbucket));
      50         177 :                 if (nread <= 0)
      51         102 :                         return;
      52             :                 assert(1);
      53             :         }
      54             : }
      55             : 
      56             : struct mapi_filetransfer {
      57             :         stream *from_client; // set to NULL after sending MAPI_PROMPT3
      58             :         stream *to_client; // set to NULL when client sends empty
      59             : };
      60             : 
      61             : static ssize_t
      62     5059903 : upload_read(stream *restrict s, void *restrict buf, size_t elmsize, size_t cnt)
      63             : {
      64     5059903 :         struct mapi_filetransfer *state = s->stream_data.p;
      65             : 
      66     5059903 :         if (state->from_client == NULL) {
      67           0 :                 assert(s->eof);
      68             :                 return 0;
      69             :         }
      70             : 
      71     5059903 :         ssize_t nread = mnstr_read(state->from_client, buf, elmsize, cnt);
      72     5059903 :         if (nread != 0 || state->from_client->eof)
      73             :                 return nread;
      74             : 
      75             :         // before returning the 0 we send the prompt and make another attempt.
      76        1623 :         if (
      77        1623 :                         mnstr_write(state->to_client, PROMPT2, strlen(PROMPT2), 1) != 1
      78        1623 :                 ||      mnstr_flush(state->to_client, MNSTR_FLUSH_ALL) < 0
      79             :         ) {
      80           0 :                 mnstr_set_error(s, mnstr_errnr(state->to_client), "%s", mnstr_peek_error(state->to_client));
      81           0 :                 return -1;
      82             :         }
      83             : 
      84             :         // if it succeeds, return that to the client.
      85             :         // if it's still a block boundary, return that to the client.
      86             :         // if there's an error, return that to the client.
      87        1623 :         nread = mnstr_read(state->from_client, buf, elmsize, cnt);
      88        1623 :         if (nread > 0)
      89             :                 return nread;
      90         129 :         if (nread == 0) {
      91         129 :                 s->eof = true;
      92         129 :                 state->from_client = NULL;
      93         129 :                 return nread;
      94             :         } else {
      95           0 :                 mnstr_set_error(s, mnstr_errnr(state->from_client), "%s", mnstr_peek_error(state->from_client));
      96           0 :                 return -1;
      97             :         }
      98             : }
      99             : 
     100             : static void
     101         154 : upload_close(stream *s)
     102             : {
     103         154 :         struct mapi_filetransfer *state = s->stream_data.p;
     104             : 
     105         154 :         stream *from = state->from_client;
     106         154 :         if (from)
     107          25 :                 discard(from);
     108             : 
     109         154 :         stream *to = state->to_client;
     110         154 :         mnstr_write(to, PROMPT3, strlen(PROMPT3), 1);
     111         154 :         mnstr_flush(to, MNSTR_FLUSH_DATA);
     112         154 : }
     113             : 
     114             : static ssize_t
     115    15000113 : download_write(stream *restrict s, const void *restrict buf, size_t elmsize, size_t cnt)
     116             : {
     117    15000113 :         struct mapi_filetransfer *state = s->stream_data.p;
     118    15000113 :         stream *to = state->to_client;
     119    15000113 :         return to->write(to, buf, elmsize, cnt);
     120             : }
     121             : 
     122             : static void
     123          75 : download_close(stream *s)
     124             : {
     125          75 :         struct mapi_filetransfer *state = s->stream_data.p;
     126             : 
     127          75 :         stream *to = state->to_client;
     128          75 :         stream *from = state->from_client;
     129          75 :         if (to)
     130          75 :                 mnstr_flush(to, MNSTR_FLUSH_DATA);
     131          75 :         if (from)
     132          75 :                 discard(from);
     133          75 : }
     134             : 
     135             : static void
     136         229 : destroy(stream *s)
     137             : {
     138         229 :         struct mapi_filetransfer *state = s->stream_data.p;
     139         229 :         free(state);
     140         229 :         destroy_stream(s);
     141         229 : }
     142             : 
     143             : 
     144             : static stream*
     145         231 : setup_transfer(const char *req, const char *filename, bstream *bs, stream *ws)
     146             : {
     147         231 :         const char *msg = NULL;
     148         231 :         stream *s = NULL;
     149         231 :         struct mapi_filetransfer *state = NULL;
     150         231 :         ssize_t nwritten;
     151         231 :         ssize_t nread;
     152         231 :         bool ok;
     153         231 :         int oob = 0;
     154             : 
     155         298 :         while (!bs->eof) {
     156          67 :                 if (bstream_next(bs) < 0) {
     157           0 :                         msg = mnstr_peek_error(ws);
     158           0 :                         goto end;
     159             :                 }
     160             :         }
     161         231 :         stream *rs = bs->s;
     162         231 :         assert(isa_block_stream(ws));
     163         231 :         assert(isa_block_stream(rs));
     164             : 
     165         231 :         nwritten = mnstr_printf(ws, PROMPT3 "%s %s\n", req, filename);
     166         231 :         if (nwritten <= 0) {
     167           0 :                 msg = mnstr_peek_error(ws);
     168           0 :                 goto end;
     169             :         }
     170         231 :         if (mnstr_flush(ws, MNSTR_FLUSH_ALL) < 0) {
     171           0 :                 msg = mnstr_peek_error(ws);
     172           0 :                 goto end;
     173             :         }
     174             : 
     175         231 :         char buf[256];
     176         231 :         nread = mnstr_readline(rs, buf, sizeof(buf));
     177         231 :         ok = ((nread == 0 || (nread == 1 && buf[0] == '\n')) && !(oob = mnstr_getoob(rs)));
     178         231 :         if (!ok) {
     179           2 :                 switch (oob) {
     180             :                 case 1:                                 /* client side interrupt */
     181             :                         msg = "Query aborted";
     182             :                         break;
     183           0 :                 case 2:
     184           0 :                         msg = "Read error on client";
     185           0 :                         break;
     186           2 :                 default:
     187           2 :                         msg = nread > 0 ? buf : "Unknown error";
     188             :                         break;
     189             :                 }
     190           2 :                 discard(rs);
     191           2 :                 goto end;
     192             :         }
     193             : 
     194             :         // Client accepted the request
     195         229 :         state = malloc(sizeof(*state));
     196         229 :         if (!state) {
     197           0 :                 msg = "malloc failed";
     198           0 :                 goto end;
     199             :         }
     200         229 :         s = create_stream("ONCLIENT");
     201         229 :         if (!s) {
     202           0 :                 free(state);                    /* no chance to free through destroy function */
     203           0 :                 msg = mnstr_peek_error(NULL);
     204           0 :                 goto end;
     205             :         }
     206         229 :         state->from_client = rs;
     207         229 :         state->to_client = ws;
     208         229 :         s->stream_data.p = state;
     209           2 : end:
     210         231 :         if (msg) {
     211           2 :                 mnstr_destroy(s);
     212           2 :                 mnstr_set_open_error(filename, 0, "ON CLIENT: %s", msg);
     213           2 :                 return NULL;
     214             :         } else {
     215         229 :                 return s;
     216             :         }
     217             : }
     218             : 
     219             : stream*
     220         156 : mapi_request_upload(const char *filename, bool binary, bstream *bs, stream *ws)
     221             : {
     222         156 :         const char *req = binary ? "rb" : "r 0";
     223         156 :         stream *s = setup_transfer(req, filename, bs, ws);
     224         156 :         if (s == NULL)
     225             :                 return NULL;
     226             : 
     227         154 :         s->binary = binary;
     228         154 :         s->read = upload_read;
     229         154 :         s->close = upload_close;
     230         154 :         s->destroy = destroy;
     231             : 
     232         154 :         return s;
     233             : }
     234             : 
     235             : stream*
     236          75 : mapi_request_download(const char *filename, bool binary, bstream *bs, stream *ws)
     237             : {
     238          75 :         const char *req = binary ? "wb" : "w";
     239          75 :         stream *s = setup_transfer(req, filename, bs, ws);
     240          75 :         if (s == NULL)
     241             :                 return NULL;
     242             : 
     243          75 :         s->binary = binary;
     244          75 :         s->readonly = false;
     245          75 :         s->write = download_write;
     246          75 :         s->close = download_close;
     247          75 :         s->destroy = destroy;
     248             : 
     249          75 :         return s;
     250             : }

Generated by: LCOV version 1.14