I\'m looking for the Unix equivalent of Win32\'s CopyFile, I don\'t want to reinvent the wheel by writing my own version.
There is a way to do this, without resorting to the system
call, you need to incorporate a wrapper something like this:
#include <sys/sendfile.h>
#include <fcntl.h>
#include <unistd.h>
/*
** http://www.unixguide.net/unix/programming/2.5.shtml
** About locking mechanism...
*/
int copy_file(const char *source, const char *dest){
int fdSource = open(source, O_RDWR);
/* Caf's comment about race condition... */
if (fdSource > 0){
if (lockf(fdSource, F_LOCK, 0) == -1) return 0; /* FAILURE */
}else return 0; /* FAILURE */
/* Now the fdSource is locked */
int fdDest = open(dest, O_CREAT);
off_t lCount;
struct stat sourceStat;
if (fdSource > 0 && fdDest > 0){
if (!stat(source, &sourceStat)){
int len = sendfile(fdDest, fdSource, &lCount, sourceStat.st_size);
if (len > 0 && len == sourceStat.st_size){
close(fdDest);
close(fdSource);
/* Sanity Check for Lock, if this is locked -1 is returned! */
if (lockf(fdSource, F_TEST, 0) == 0){
if (lockf(fdSource, F_ULOCK, 0) == -1){
/* WHOOPS! WTF! FAILURE TO UNLOCK! */
}else{
return 1; /* Success */
}
}else{
/* WHOOPS! WTF! TEST LOCK IS -1 WTF! */
return 0; /* FAILURE */
}
}
}
}
return 0; /* Failure */
}
The above sample (error checking is omitted!) employs open
, close
and sendfile
.
Edit: As caf has pointed out a race condition can occur between the open
and stat
so I thought I'd make this a bit more robust...Keep in mind that the locking mechanism varies from platform to platform...under Linux, this locking mechanism with lockf
would suffice. If you want to make this portable, use the #ifdef
macros to distinguish between different platforms/compilers...Thanks caf for spotting this...There is a link to a site that yielded "universal locking routines" here.
sprintf( cmd, "/bin/cp -p \'%s\' \'%s\'", old, new);
system( cmd);
Add some error checks...
Otherwise, open both and loop on read/write, but probably not what you want.
...
UPDATE to address valid security concerns:
Rather than using "system()", do a fork/wait, and call execv() or execl() in the child.
execl( "/bin/cp", "-p", old, new);