cfile-descriptorfcntl

read() not working as expected. Returns value 1 on successful read of large block


I am developing a process that, among other things, will copy files. I've run across a problem that the read function does not work as I expected.

My research and my prior experience tell me that the (fcntl) read() function is supposed to return number of bytes read, but it's returning 1 instead. I've reached my limit of things to try (playing mostly with varying the buffer size from 16 to 16MB). No matter what I try, I always end up with either a segmentation fault (buffer size 16MB) or read() returns a value of 1.

To demonstrate this, I've reduced the program to a bare minimum that still compiles and execute, but doesn't do what's needed. To get good performance, I need a nice large buffer (preferably 15MB but 1MB would be OK in a grind). In this application, with a buffer size of 1M, the output filesize ends up being 1/1048567 times the original size (so a 16MB input file is copied to 16 bytes). Yes, I could go to a buffer size of 1, but as stated, this should be more efficient than that.

This can be tested on your system by setting "in" to some existing file on your system, and "out" to a filename that would also be valid. NOTE: "out" will be overwritten if it exists!!!

Environment is Cygwin 3.2.0(0.340/5/3)

Any clues what I need to do to make this work as documented?

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

#define BOOL char
#define TRUE (1 < 2)
#define FALSE (1 > 2)
#define MAX_BUFFER_SIZE 1048576 // 1M.  16MB (16777216) will caus segmentation fault

int    copyfile(const char *in, const char *out) ;
BOOL debug = TRUE ;


int main(int argc, char *argv[]) {
    int rc = 0 ;
    char *in = "/e/DCIM/100MEDIA/DJI_0292.MP4" ;
    char *out = "/m/Videos/2021/2021-08-Other/temp/08140708_dji_0292.mp4" ;

    rc = copyfile(in, out) ;
    return rc ;
    }



int copyfile(const char *in, const char *out) {
    int rtnVal = TRUE ;
    int fd_in, fd_out ;
    char buff[MAX_BUFFER_SIZE], *out_ptr ;
    ssize_t nRead, nRemaining, nWritten, totWritten = 0 ;
    int save_errno ;
    struct stat st ;

    unlink(out) ; // In case it exists ...
    if ((fd_in = open(in, O_RDONLY)) < 0) {
        fprintf(stderr, "Could not open input file %s\n\t%s\n", in, strerror(errno)) ;
        rtnVal = FALSE ;
        }
    else if ((fd_out = open(out, O_WRONLY | O_CREAT | O_EXCL, 0666)) < 0) {
        fprintf(stderr, "Could not open output file %s\n\t%s\n", out, strerror(errno)) ;
        rtnVal = FALSE ;
        }
    if (rtnVal) {
        if (stat(in, &st) < 0) {
            fprintf(stderr, "Could not stat input file: %s\n\t%s\n", in, strerror(errno)) ;
            exit(1) ;
            }
        if (debug)
            fprintf(stdout, "\n\n\nSize of %s is %ld\n", in, st.st_size) ;
        while (((nRead = read(fd_in, buff, MAX_BUFFER_SIZE) > 0) && (rtnVal == TRUE))) {
            // if (debug)
            //     fprintf(stdout, "%ld bytes read this time.\n", nRead) ;
            out_ptr = buff ;
            nRemaining = nRead ;
            while (nRemaining > 0) {
                nWritten = write(fd_out, out_ptr, nRemaining) ;
                if (nWritten >= 0) {
                    out_ptr += nWritten ;
                    totWritten += nWritten ;
                    nRemaining -= nWritten ;
                    if (debug)
                        fprintf(stdout, "\tDEBUG: %ld bytes read. So far, %ld bytes have been written. %ld remain\n"
                                      , nRead, totWritten, nRemaining) ;
                    }
                else if (errno != EINTR) { // No error - try again (see https://stackoverflow.com/questions/41474299/checking-if-errno-eintr-what-does-it-mean)
                    rtnVal = FALSE ;
                    fprintf(stderr, "Could not copy from: %s\n"
                                    "                 to: %s\n"
                                    "     %s\n"
                                  , in, out, strerror(errno)
                                   ) ;
                    break ;
                    }
                } // While remaining
            }     // While read()
        }         // If rtnVal
    save_errno = errno ;
    if (fd_in)  close(fd_in) ;
    if (fd_out) close(fd_out) ;

    if (stat(out, &st) < 0) {
        fprintf(stderr, "Could not stat output file: %s\n\t%s\n", in, strerror(errno)) ;
        exit(1) ;
        }
    if (debug)
        fprintf(stdout, "Size of %s is %ld\n", out, st.st_size) ;
    errno = save_errno ;
    return(rtnVal) ;
    }

sample run:

Size of /e/DCIM/100MEDIA/DJI_0292.MP4 is 406514367
    DEBUG: 1 bytes read. So far, 1 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 2 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 3 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 4 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 5 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 6 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 7 bytes have been written. 0 remain
...
...
    DEBUG: 1 bytes read. So far, 385 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 386 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 387 bytes have been written. 0 remain
    DEBUG: 1 bytes read. So far, 388 bytes have been written. 0 remain
Size of /m/Videos/2021/2021-08-Other/temp/08140708_dji_0292.mp4 is 388

Solution

  • You're setting nread to the result of the > comparison operation, not the result of read(), because > has higher precedence than =. You need parentheses around the assignment.

    while ((nRead = read(fd_in, buff, MAX_BUFFER_SIZE)) > 0 && rtnVal) {