1

I try to send a file into the socket using the system call sendfile(), but get the error: ERROR sendfile: [EINVAL Invalid argument]. But the call read() works perfectly with the same arguments. I also tried to give to sendfile() two files description in arguments - got the same result.

Do you have any suggestions why this might be?

Some additional info: OS: Ubuntu 22.04.1 LTS on VirtualBox: 6.1.38 r153438 (Qt5.6.2); Linux kernel: 5.15.0-52-generic; glibc: 2.35.

There are examples of the two SIMPLEST programs that I used:

  1. Program copies data from one file to another using sendfile()
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/sendfile.h>
#include "gen_hdr.h"

int main() {
    /***        Get file size and open files       ***/
    int fd;
    size_t size;
    struct stat statBuf;
    
    if (stat("test_file.txt", &statBuf) == -1) {
        errExit("statBuf");
    }
    
    size = statBuf.st_size;

    fd = open("test_file.txt", O_RDONLY);
    if (-1 == fd) {
        errExit("open");
    }

    int fdNew, openFlags;
    mode_t filePerms;
    openFlags = O_CREAT | O_WRONLY | O_EXCL;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
                S_IROTH | S_IWOTH; /* rw-rw-rw- */
    
    fdNew = open("clone_test_file.txt", openFlags, filePerms);
    /***        END        ***/

    /***        SENDFILE        ***/
    off_t offset = 0;
    ssize_t sent = 0;

    for (; size > 0; ) {
    errno = 0;
    sent = sendfile(fdNew, fd, &offset, size);
    printf("%s\n", strerror(errno));
    printf("%ld\n", sent);
    if (sent <= 0) {                 // Error or end of file
        if (sent != 0) {
            errExit("sendfile");    // Was an error, report it
        }
        break;                      // End of file
    }

    size -= sent;           // Decrease the send length by the amount actually sent
    }
    /***        END        ***/

    if (close(fd) == -1) {
        errExit("close");
    }
    if (close(fdNew) == -1) {
        errExit("close");
    }

    return 0;
}
  1. Program creates a simple TCP server that just send file to a connected client. If I use the system call send()(section SENDFILE) instead of sendfile() - all works well.

Server:

#include <ctype.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/sendfile.h>
#include "gen_hdr.h"

#define PORT_NUM 55550
#define BACKLOG 5

int main() {
    /***        Get file size and open it       ***/
    int fd;
    size_t size_to_send;
    struct stat statBuf;

    if (stat("test_file.txt", &statBuf) == -1) {
        errExit("statBuf");
    }
    
    size_to_send = statBuf.st_size;

    fd = open("test_file.txt", O_RDONLY);
    if (-1 == fd) {
        errExit("open");
    }
    /***        END        ***/

    /***        Server Setup        ***/
    struct sockaddr_in svaddr, claddr;
    //struct sockaddr_storage claddr;
    socklen_t len;
    int sockfd, connfd;
    char claddrStr[INET_ADDRSTRLEN];

    memset(&svaddr, 0, sizeof(struct sockaddr_in));
    svaddr.sin_family = AF_INET;
    svaddr.sin_port = (in_port_t)htons(PORT_NUM);
    svaddr.sin_addr.s_addr = htonl(INADDR_ANY);
    
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd) {
        errExit("socket");
    }

    if (bind(sockfd, (struct sockaddr *)&svaddr, sizeof(struct sockaddr_in)) == -1) {
         errExit("bind");
    }

    if (listen(sockfd, BACKLOG) == -1) {
         errExit("listen");
    }
    printf("Server is waiting for connection...\n");
    
    len = sizeof(struct sockaddr_in);
    connfd = accept(sockfd, (struct sockaddr*)&claddr, &len);
    if (-1 == connfd) {
        errExit("accept");
    }

    if (inet_ntop(AF_INET, &claddr.sin_addr, claddrStr, INET_ADDRSTRLEN) == NULL) {
        printf("Coudn't convert client address to string\n");
    }
    else {
        printf("Connection from [%s, %u]\n", claddrStr, ntohs(claddr.sin_port));
    }
    /***        END        ***/

    /***        SENDFILE        ***/
    off_t offset = 0;
    ssize_t sent = 0;

    for (; size_to_send > 0; ) {
        errno = 0;
        sent = sendfile(connfd, fd, &offset, size_to_send);
        printf("%s\n", strerror(errno));
        printf("%ld\n", sent);
        if (sent <= 0) {                // Error or end of file
            if (sent != 0) {
                errExit("sendfile");    // Was an error, report it
            }
            break;                      // End of file
        }

        size_to_send -= sent;           // Decrease the send length by the amount actually sent
    }
    /***        END         ***/

    /***        SEND FILE        ***/
    // char* buf = (char*)malloc(size_to_send);
    // if (NULL == buf) {
    //     errExit("malloc");
    // }

    // if (read(fd, buf, size_to_send) == -1) {
    //     errExit("read");
    // }

    // if (send(connfd, buf, size_to_send, 0) == -1) {
    //     errExit("send");
    // }
    // free(buf);
    /***        END         ***/

    printf("File sent...\n");

    if (close(fd) == -1) {
        errExit("close");
    }
    if (close(connfd) == -1) {
        printf("ERROR on closing connection\n");
    }
    if (close(sockfd) == -1) {
        printf("ERROR on closing socket\n");
    }
    return 0;
}

Client:

#include <netinet/in.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "gen_hdr.h"

#define PORT_NUM 55550
#define BUF_SIZE 10

int main(int argc, char* argv[]) {
    /***        Get file size and allocate memory       ***/
    char* buf;
    size_t size_to_recv;
    struct stat statBuf;

    if (stat("test_file.txt", &statBuf) == -1) {
        errExit("statBuf");
    }
    
    size_to_recv = statBuf.st_size;

    buf = (char*)malloc(size_to_recv);
    if (NULL == buf) {
        errExit("malloc");
    }
    /***        END        ***/

    /***        Server Setup        ***/
    struct sockaddr_in svaddr;
    socklen_t len;
    int sockfd;
    ssize_t numBytes;
    char claddrStr[INET_ADDRSTRLEN];

    memset(&svaddr, 0, sizeof(struct sockaddr_in));
    svaddr.sin_family = AF_INET;
    svaddr.sin_port = (in_port_t)htons(PORT_NUM);
    svaddr.sin_addr.s_addr = inet_addr("127.0.0.1");

    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if (-1 == sockfd) {
        errExit("socket");
    }

    if (connect(sockfd, (struct sockaddr *)&svaddr, sizeof(struct sockaddr_in)) == -1) {
        errExit("connect");
    }
    /***        END        ***/

    printf("Getting file...\n");

    /***        RECEIVE FILE        ***/
    ssize_t numRead;                /* Bytes that we read from the last call of read() */
    size_t totRead;                 /* Number of bytes that we have read at the moment */
    char* recvBuf = buf;

    for (totRead = 0; totRead < size_to_recv; ) {
        numRead = read(sockfd, recvBuf, size_to_recv - totRead);

        if (numRead > 0) {
            totRead += numRead;
            recvBuf += numRead;
        } else if (numRead == 0) {          /* End of file */
            return totRead;                 /* It could be 0, if it is the first call of read() */
        } else {                            /* Error */
            if (errno == EINTR) {
                continue;                   /* Interrupted --> recall read() */
            }
            else {
                errExit("read");            /* Some other error */
            }
        }
    }
    /***        END         ***/

    printf("Ended\n");

    /***        WRITE IN NEW FILE        ***/
    int fd, openFlags;
    mode_t filePerms;
    openFlags = O_CREAT | O_WRONLY | O_EXCL;
    filePerms = S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
                S_IROTH | S_IWOTH; /* rw-rw-rw- */
    
    fd = open("clone_test_file.txt", openFlags, filePerms);

    if (write(fd, buf, size_to_recv) == -1) {
        errExit("write");
    }
    /***        END         ***/

    free(buf);
    if (close(fd) == -1) {
        errExit("close");
    }
    if (close(sockfd) == -1) {
        errExit("close");
    }

    return 0;
}

"gen_hdr.h" just includes "standard" libs and declaration of error handling functions:

#ifndef GEN_HDR_H
#define GEN_HDR_H

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdint.h>

typedef enum {FALSE, TRUE} boolean;

#include "error_functions.h"

#define max(a,b) (((a) > (b)) ? (a) : (b))
#define min(a,b) (((a) < (b)) ? (a) : (b))

#endif /* GEN_HDR_H */

Error_functions.c:

#include "gen_hdr.h"
#include "ename.c.inc"

static void outputError(int err, boolean flushStdout, const char* msg) {
    #define BUF_SIZE 500
    char buf[BUF_SIZE];
    snprintf(buf, BUF_SIZE, "ERROR %s: [%s %s]\n", msg, ((err > 0 && err < MAX_ENAME) ? ename[err] : "?UNKNOWN?"), strerror(err));
    if (flushStdout)
    {
        fflush(stdout);
    }
    fputs(buf, stderr);
    fflush(stderr);
}

void errExit(char* msg) {
    outputError(errno, TRUE, msg);
    exit(EXIT_FAILURE);
}

void errExitEN(int errNum, char* msg) {
    outputError(errNum, TRUE, msg);
    exit(EXIT_FAILURE);
}
kancler
  • 81
  • 1
  • 8
  • 2
    There are several possible explanations. To determine which, we probably need a [mre] that demonstrates the failure behavior. The code presented is both too much and too little: it includes a bunch of stuff that seems unlikely to be necessary to reproduce the issue, but not everything that *is* necessary, such as the code for opening the socket and calling `connectionHandler()` (or doing the equivalent). – John Bollinger Nov 10 '22 at 16:19
  • 3
    `printf` can modify `errno`, so you're not getting the actual error code from `sendfile`. Either get rid of the `printf` call, or save `errno` around it. – Barmar Nov 10 '22 at 16:35
  • @JohnBollinger slightly corrected the description. socket and file open without error and read() works with them. @Barmar did so: `...for (; size_to_send > 0; ) {errno = 0; sent = sendfile(socket, fd, &offset, size_to_send); printf("%s\n", strerror(errno)); printf("%ld\n", sent);...`, but still gets `Invalid argument -1 ERROR sendfile: [EINVAL Invalid argument]` – kancler Nov 10 '22 at 16:55
  • 1
    Evidently, the concept of a "minimal reproducible example" is unfamiliar to you. That's ok, it's why in my previous comment I provided a handy hyperlink to a definition of that term as we use it here, with guidance on how to prepare one. – John Bollinger Nov 10 '22 at 17:00
  • @JohnBollinger Thank you, I guess now it's much better :) – kancler Nov 11 '22 at 11:41
  • 1
    We don't know what "gen_hdr.h" is. Please fix your code such that it can be compiled by an innocent bystander. – n. m. could be an AI Nov 11 '22 at 11:50
  • 1
    Anyway, I guessed what gen_hdr.h might contain, so I went ahead and built your client and server. They run with no error on my system and do what you say they are supposed to do. – n. m. could be an AI Nov 11 '22 at 12:00
  • @n.m. Thank you, I'll try to run without any additional files. Added a description of these files... – kancler Nov 11 '22 at 12:04
  • 1
    It is much preferred to make each code fragment stand alone. Don't #include custom headers if possible, paste their guts (trimmed to a minimum) to the main .c file. – n. m. could be an AI Nov 11 '22 at 12:15
  • @n.m. deleted "gen_hdr.h", and add #define errExit(a) printf(a); exit(1)Server is waiting for connection... Connection from [127.0.0.1, 51836] Invalid argument -1 sendfile – kancler Nov 11 '22 at 12:16
  • 1
    The man page says "EINVAL Descriptor is not valid or locked, or an mmap(2)-like operation is not available for in_fd, or count is negative". (How a `size_t` can be negative is beyond me, but that's what the man page says) You may want to (a) print the arguments to `sendfile` (b) test whether you can `mmap` your file. – n. m. could be an AI Nov 11 '22 at 12:22
  • @n.m. I found that `sendfile()` doesn't work in the shared folder with windows. But `mmap()` works well. Thank you, sir, for your help! – kancler Nov 11 '22 at 12:50

1 Answers1

0

If a file is located in the shared with windows folder, sendfile() doesn't work, although 'mmap()' works.

kancler
  • 81
  • 1
  • 8