0

I've been breaking my head for the past few days over the following: I want to send a file of any size/type over SunRPC. On client side I read the file into a linked list, with each node containing a buffer of size 1024. If I print all the buffers before sending it over RPC, I get the correct output (i.e. the whole file is printed). However, when I send it over RPC and print all the buffers on the server's side, the last node is printed incorrectly, as if it is not transferred correctly.

I've tested my code with a large .txt file (100,5kB) and here it does seem to work. However with a .pdf file (7,9kB) it does not.

Here is my (relevant) code:

.x file:

struct node {
    string buf<>;
    long bytes;
    struct node* next;
};

struct paper {
    string author<>;
    string title<>;
    struct node* file;
    struct paper* next;
};

typedef long add_out;

program PAPERSERVER_PROG {
    version PAPERSERVER_VERS {
        add_out ADDPAPER(paper) = 1;
    } = 1;
} = 0x20001234;

paperclient.c: (when i print all the buffers at the end of this function, the output is correct)

#define BUF_SIZE 1024

void add_paper(char** argv, CLIENT* cl) {
    struct paper in;
    add_out *out;
    struct node* tmp;
    struct node* tmp2;
    struct node start;
    char buf[BUF_SIZE];
    size_t read;

    // Open the file
    FILE *fp = fopen(argv[5], "rb");
    if (fp == NULL) {
        perror("fopen");
        exit(EXIT_FAILURE);
    }

    in.author = strdup(argv[3]);
    in.title = strdup(argv[4]);
    in.file = &start;
    in.next = NULL;

    bzero(buf, BUF_SIZE);
    read = fread(buf, 1, BUF_SIZE, fp);
    if (ferror(fp)) {
        perror("fread error");
        exit(2);
    }

    tmp = &start;
    tmp->buf = calloc(BUF_SIZE, 1);
    if (tmp->buf == NULL) {
        perror("malloc tmp->buf");
        exit(2);
    }

    tmp->bytes = read;
    tmp->buf = memcpy(tmp->buf, buf, read);

    // Cycle until end of file
    while (!feof(fp)) {
        bzero(buf, BUF_SIZE);

        tmp->next = (struct node*) calloc(sizeof(struct node), 1);
        if (tmp->next == NULL) {
            perror("malloc tmp->next");
            exit(2);
        }

        tmp = tmp->next;

        read = fread(buf, 1, BUF_SIZE, fp);
        if (ferror(fp)) {
            perror("fread error");
            exit(2);
        }

        printf("read:%zu\n", read);

        tmp->buf = calloc(BUF_SIZE+1, 1);
        if (tmp->buf == NULL) {
            perror("malloc tmp->buf");
            exit(2);
        }

        tmp->bytes = read;
        tmp->buf = memcpy(tmp->buf, buf, read);      
    }
    tmp->next = NULL;

    out = addpaper_1(&in, cl);
    if (out == NULL) {
        printf("Error: %s\n", clnt_sperror(cl, argv[1]));
    } else {
        printf("%ld\n", *out);
    }

    clnt_destroy(cl);
}

paperserver.c (Here the output is wrong)

add_out* addpaper_1_svc(paper* in, struct svc_req* rqstp) { 
    static add_out out;
    struct node* file;
    long written;

    file = in->file;
    char* buf;

    while (file != NULL) {
        buf = calloc(BUF_SIZE + 1, 1);
        buf = memcpy(buf, file->buf, file->bytes);

        written = fwrite(buf, 1, file->bytes, stdout);

        if (ferror(stdout)) {
            perror("fwrite error");
            exit(2);
        } 

        file = file->next;
    }

    out = 0;

    return &out;
}

EDIT:

To specify more about my expected output. I expect the file I send over the server to be output exactly the same as if I would've called cat <filename>. However, the output seems to be in the wrong order. The last buffer seems to be overwritten with some of the contents of the beginning of the file. This is with one .pdf file is used. With another .pdf it DOES work, with .txt it DOES work, but with a .jpg it did not. When I get the md5sum of the output, it changes with every call. This makes me think it prints bytes not actually initialized by my program. Furthermore when I use valgrind I get the following errors (only with the files that don't work correctly):

==2375== Invalid read of size 8
==2375==    at 0x4C2F790: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x400D3E: addpaper_1_svc (paperserver.c:38)
==2375==    by 0x40128D: paperserver_prog_1 (paperserver_svc.c:76)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375==  Address 0x527af48 is 392 bytes inside a block of size 394 alloc'd
==2375==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x4F64D04: xdr_string (xdr.c:788)
==2375==    by 0x401487: xdr_node (paperserver_xdr.c:13)
==2375==    by 0x4F656B7: xdr_pointer (xdr_ref.c:84)
==2375==    by 0x40155C: xdr_paper (paperserver_xdr.c:31)
==2375==    by 0x401268: paperserver_prog_1 (paperserver_svc.c:72)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375== 
==2375== Invalid read of size 8
==2375==    at 0x4C2F79E: memcpy@@GLIBC_2.14 (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x400D3E: addpaper_1_svc (paperserver.c:38)
==2375==    by 0x40128D: paperserver_prog_1 (paperserver_svc.c:76)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)
==2375==  Address 0x527af50 is 6 bytes after a block of size 394 alloc'd
==2375==    at 0x4C2AB80: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==2375==    by 0x4F64D04: xdr_string (xdr.c:788)
==2375==    by 0x401487: xdr_node (paperserver_xdr.c:13)
==2375==    by 0x4F656B7: xdr_pointer (xdr_ref.c:84)
==2375==    by 0x40155C: xdr_paper (paperserver_xdr.c:31)
==2375==    by 0x401268: paperserver_prog_1 (paperserver_svc.c:72)
==2375==    by 0x4F626D0: svc_getreq_common (svc.c:534)
==2375==    by 0x4F6281D: svc_getreq_poll (svc.c:460)
==2375==    by 0x4F65BFE: svc_run (svc_run.c:96)
==2375==    by 0x401437: main (paperserver_svc.c:114)  
  • When you say that "the output is wrong", can you please be more specific? What did you expect the output to be? What is the actual output? – Some programmer dude Oct 19 '15 at 09:34
  • 1
    By the way, you might want to read ["Why is “while ( !feof (file) )” always wrong?"](http://stackoverflow.com/questions/5431941/why-is-while-feof-file-always-wrong) – Some programmer dude Oct 19 '15 at 09:36

1 Answers1

0

You are using string buf<> to contain the file data. A string will be terminated by the first zero byte, so if you have any zero bytes in the middle of the data (which a PDF file likely will, but a text file won't) then the full data won't get written.

(Note that there's no way for RPC to know how much data is in a node - you happen to put that data into bytes, but the RPC infrastructure doesn't know that...).

To solve the problem, you need to make buf into an opaque instead:

opaque buf<>;

...and make the corresponding changes to the C code, along the lines of

buf.buf_val = calloc(...);
buf.buf_len = num_bytes_read;
psmears
  • 26,070
  • 4
  • 40
  • 48