How can I copy a file on Unix using C? How can I copy a file on Unix using C? unix unix

How can I copy a file on Unix using C?


There is no need to either call non-portable APIs like sendfile, or shell out to external utilities. The same method that worked back in the 70s still works now:

#include <fcntl.h>#include <unistd.h>#include <errno.h>int cp(const char *to, const char *from){    int fd_to, fd_from;    char buf[4096];    ssize_t nread;    int saved_errno;    fd_from = open(from, O_RDONLY);    if (fd_from < 0)        return -1;    fd_to = open(to, O_WRONLY | O_CREAT | O_EXCL, 0666);    if (fd_to < 0)        goto out_error;    while (nread = read(fd_from, buf, sizeof buf), nread > 0)    {        char *out_ptr = buf;        ssize_t nwritten;        do {            nwritten = write(fd_to, out_ptr, nread);            if (nwritten >= 0)            {                nread -= nwritten;                out_ptr += nwritten;            }            else if (errno != EINTR)            {                goto out_error;            }        } while (nread > 0);    }    if (nread == 0)    {        if (close(fd_to) < 0)        {            fd_to = -1;            goto out_error;        }        close(fd_from);        /* Success! */        return 0;    }  out_error:    saved_errno = errno;    close(fd_from);    if (fd_to >= 0)        close(fd_to);    errno = saved_errno;    return -1;}


There is no baked-in equivalent CopyFile function in the APIs. But sendfile can be used to copy a file in kernel mode which is a faster and better solution (for numerous reasons) than opening a file, looping over it to read into a buffer, and writing the output to another file.

Update:

As of Linux kernel version 2.6.33, the limitation requiring the output of sendfile to be a socket was lifted and the original code would work on both Linux and — however, as of OS X 10.9 Mavericks, sendfile on OS X now requires the output to be a socket and the code won't work!

The following code snippet should work on the most OS X (as of 10.5), (Free)BSD, and Linux (as of 2.6.33). The implementation is "zero-copy" for all platforms, meaning all of it is done in kernelspace and there is no copying of buffers or data in and out of userspace. Pretty much the best performance you can get.

#include <fcntl.h>#include <unistd.h>#if defined(__APPLE__) || defined(__FreeBSD__)#include <copyfile.h>#else#include <sys/sendfile.h>#endifint OSCopyFile(const char* source, const char* destination){        int input, output;        if ((input = open(source, O_RDONLY)) == -1)    {        return -1;    }        if ((output = creat(destination, 0660)) == -1)    {        close(input);        return -1;    }    //Here we use kernel-space copying for performance reasons#if defined(__APPLE__) || defined(__FreeBSD__)    //fcopyfile works on FreeBSD and OS X 10.5+     int result = fcopyfile(input, output, 0, COPYFILE_ALL);#else    //sendfile will work with non-socket output (i.e. regular file) on Linux 2.6.33+    off_t bytesCopied = 0;    struct stat fileinfo = {0};    fstat(input, &fileinfo);    int result = sendfile(output, input, &bytesCopied, fileinfo.st_size);#endif    close(input);    close(output);    return result;}

EDIT: Replaced the opening of the destination with the call to creat() as we want the flag O_TRUNC to be specified. See comment below.


It's straight forward to use fork/execl to run cp to do the work for you. This has advantages over system in that it is not prone to a Bobby Tables attack and you don't need to sanitize the arguments to the same degree. Further, since system() requires you to cobble together the command argument, you are not likely to have a buffer overflow issue due to sloppy sprintf() checking.

The advantage to calling cp directly instead of writing it is not having to worry about elements of the target path existing in the destination. Doing that in roll-you-own code is error-prone and tedious.

I wrote this example in ANSI C and only stubbed out the barest error handling, other than that it's straight forward code.

void copy(char *source, char *dest){    int childExitStatus;    pid_t pid;    int status;    if (!source || !dest) {        /* handle as you wish */    }    pid = fork();    if (pid == 0) { /* child */        execl("/bin/cp", "/bin/cp", source, dest, (char *)0);    }    else if (pid < 0) {        /* error - couldn't start process - you decide how to handle */    }    else {        /* parent - wait for child - this has all error handling, you         * could just call wait() as long as you are only expecting to         * have one child process at a time.         */        pid_t ws = waitpid( pid, &childExitStatus, WNOHANG);        if (ws == -1)        { /* error - handle as you wish */        }        if( WIFEXITED(childExitStatus)) /* exit code in childExitStatus */        {            status = WEXITSTATUS(childExitStatus); /* zero is normal exit */            /* handle non-zero as you wish */        }        else if (WIFSIGNALED(childExitStatus)) /* killed */        {        }        else if (WIFSTOPPED(childExitStatus)) /* stopped */        {        }    }}