int OSCopyFile(const char* source, const char* destination, bool overwrite) { /* This function was taken from http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c */ int input, output; if ((input = open(source, O_RDONLY)) == -1) { return -1; } if ((output = open(destination, O_WRONLY | O_CREAT | (overwrite ? O_TRUNC : O_EXCL), 0666)) == -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) == -1 ? -1 : 0; #endif close(input); close(output); return result; }
// `rename`, but works across devices static int move(const char *oldpath, const char *newpath) { if (rename(oldpath, newpath) < 0) { if (errno == EXDEV) { int src = open(oldpath, O_RDONLY); if (src < 0) return -1; int dst = open(newpath, O_WRONLY|O_TRUNC|O_CREAT, 0644); if (dst < 0) { close(src); return -1; } int r; #if defined(__APPLE__) || defined(__FreeBSD__) r = fcopyfile(src, dst, NULL, COPYFILE_DATA); #else struct stat st; if (fstat(src, &st) < 0) { close(dst); close(src); return -1; } r = sendfile(dst, src, NULL, st.st_size); #endif close(dst); close(src); if (r == 0) unlink(oldpath); return r; } return -1; } return 0; }
int main(void) { int i = 0; char *files[] = { "/etc/passwd", "/tmp/mypwd", "/tmp/mypwd", "/etc/passwd", "/tmp/", "/tmp/passwd", "/etc/passwd", "/tmp", "/tmp/passwd", NULL }; FILE *src, *dst; printf("=>Start testing fcopyfile()\n"); while (files[i]) { printf("fcopyfile(%s, %s)\t", files[i], files[i + 1]); src = fopen(files[i], "r"); dst = fopen(files[i + 1], "w"); if (fcopyfile(src, dst)) { if (!fisdir(files[i + 1])) err(1, "Failed fcopyfile(%s, %s)", files[i], files[i + 1]); } if (src) fclose(src); if (dst) fclose(dst); if (fexist(files[i + 2])) printf("OK => %s", files[i + 2]); printf("\n"); erase(files[i + 2]); i += 3; } printf("\n=>Start testing copyfile()\n"); i = 0; while (files[i]) { printf("copyfile(%s, %s)\t", files[i], files[i + 1]); if (!copyfile(files[i], files[i + 1], 0, 0)) err(1, "Failed copyfile(%s, %s)", files[i], files[i + 1]); if (fexist(files[i + 2])) printf("OK => %s", files[i + 2]); printf("\n"); erase(files[i + 2]); i += 3; } return 0; }
extern "C" int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) { int inFd = ToFileDescriptor(sourceFd); int outFd = ToFileDescriptor(destinationFd); #if HAVE_FCOPYFILE // If fcopyfile is available (OS X), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. // Copy data and metadata. return fcopyfile(inFd, outFd, nullptr, COPYFILE_ALL) == 0 ? 0 : -1; #else // Get the stats on the source file. int ret; struct stat_ sourceStat; bool copied = false; #if HAVE_SENDFILE // If sendfile is available (Linux), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret != 0) { return -1; } // We use `auto' here to adapt the type of `size' depending on the running platform. // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. auto size = UnsignedCast(sourceStat.st_size); // Note that per man page for large files, you have to iterate until the // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). while (size > 0) { ssize_t sent = sendfile(outFd, inFd, nullptr, (size >= SSIZE_MAX ? SSIZE_MAX : static_cast<size_t>(size))); if (sent < 0) { if (errno != EINVAL && errno != ENOSYS) { return -1; } else { break; } } else { assert(UnsignedCast(sent) <= size); size -= UnsignedCast(sent); } } if (size == 0) { copied = true; } // sendfile couldn't be used; fall back to a manual copy below. This could happen // if we're on an old kernel, for example, where sendfile could only be used // with sockets and not regular files. #endif // HAVE_SENDFILE // Manually read all data from the source and write it to the destination. if (!copied && CopyFile_ReadWrite(inFd, outFd) != 0) { return -1; } // Now that the data from the file has been copied, copy over metadata // from the source file. First copy the file times. while (CheckInterrupted(ret = fstat_(inFd, &sourceStat))); if (ret == 0) { struct timeval origTimes[2]; origTimes[0].tv_sec = sourceStat.st_atime; origTimes[0].tv_usec = 0; origTimes[1].tv_sec = sourceStat.st_mtime; origTimes[1].tv_usec = 0; while (CheckInterrupted(ret = futimes(outFd, origTimes))); } if (ret != 0) { return -1; } // Then copy permissions. while (CheckInterrupted(ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO)))); if (ret != 0) { return -1; } return 0; #endif // HAVE_FCOPYFILE }
int32_t SystemNative_CopyFile(intptr_t sourceFd, intptr_t destinationFd) { int inFd = ToFileDescriptor(sourceFd); int outFd = ToFileDescriptor(destinationFd); #if HAVE_FCOPYFILE // If fcopyfile is available (OS X), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. // Copy data and metadata. return fcopyfile(inFd, outFd, NULL, COPYFILE_ALL) == 0 ? 0 : -1; #else // Get the stats on the source file. int ret; struct stat_ sourceStat; bool copied = false; #if HAVE_SENDFILE_4 // If sendfile is available (Linux), try to use it, as the whole copy // can be performed in the kernel, without lots of unnecessary copying. while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR); if (ret != 0) { return -1; } // On 32-bit, if you use 64-bit offsets, the last argument of `sendfile' will be a // `size_t' a 32-bit integer while the `st_size' field of the stat structure will be off64_t. // So `size' will have to be `uint64_t'. In all other cases, it will be `size_t'. uint64_t size = (uint64_t)sourceStat.st_size; // Note that per man page for large files, you have to iterate until the // whole file is copied (Linux has a limit of 0x7ffff000 bytes copied). while (size > 0) { ssize_t sent = sendfile(outFd, inFd, NULL, (size >= SSIZE_MAX ? SSIZE_MAX : (size_t)size)); if (sent < 0) { if (errno != EINVAL && errno != ENOSYS) { return -1; } else { break; } } else { assert((size_t)sent <= size); size -= (size_t)sent; } } if (size == 0) { copied = true; } // sendfile couldn't be used; fall back to a manual copy below. This could happen // if we're on an old kernel, for example, where sendfile could only be used // with sockets and not regular files. #endif // HAVE_SENDFILE_4 // Manually read all data from the source and write it to the destination. if (!copied && CopyFile_ReadWrite(inFd, outFd) != 0) { return -1; } // Now that the data from the file has been copied, copy over metadata // from the source file. First copy the file times. // If futimes nor futimes are available on this platform, file times will // not be copied over. while ((ret = fstat_(inFd, &sourceStat)) < 0 && errno == EINTR); if (ret == 0) { #if HAVE_FUTIMES struct timeval origTimes[2]; origTimes[0].tv_sec = sourceStat.st_atime; origTimes[0].tv_usec = ST_ATIME_NSEC(&sourceStat) / 1000; origTimes[1].tv_sec = sourceStat.st_mtime; origTimes[1].tv_usec = ST_MTIME_NSEC(&sourceStat) / 1000; while ((ret = futimes(outFd, origTimes)) < 0 && errno == EINTR); #elif HAVE_FUTIMENS // futimes is not a POSIX function, and not available on Android, // but futimens is struct timespec origTimes[2]; origTimes[0].tv_sec = (time_t)sourceStat.st_atime; origTimes[0].tv_nsec = ST_ATIME_NSEC(&sourceStat); origTimes[1].tv_sec = (time_t)sourceStat.st_mtime; origTimes[1].tv_nsec = ST_MTIME_NSEC(&sourceStat); while ((ret = futimens(outFd, origTimes)) < 0 && errno == EINTR); #endif } if (ret != 0) { return -1; } // Then copy permissions. while ((ret = fchmod(outFd, sourceStat.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO))) < 0 && errno == EINTR); if (ret != 0) { return -1; } return 0; #endif // HAVE_FCOPYFILE }
/* * compress the given file: create a corresponding .gz file and remove the * original. */ static off_t file_compress(char *file, char *outfile, size_t outsize) { int in; int out; off_t size, insize; #ifndef SMALL struct stat isb, osb; const suffixes_t *suff; #endif in = open(file, O_RDONLY); if (in == -1) { maybe_warn("can't open %s", file); return (-1); } #ifndef SMALL bzero(&isb, sizeof(isb)); if (fstat(in, &isb) != 0) { maybe_warn("couldn't stat: %s", file); close(in); return (-1); } #endif if (cflag == 0) { #ifndef SMALL if (isb.st_nlink > 1 && fflag == 0) { maybe_warnx("%s has %d other link%s -- skipping", file, isb.st_nlink - 1, (isb.st_nlink - 1) == 1 ? "" : "s"); close(in); return (-1); } if (fflag == 0 && (suff = check_suffix(file, 0)) && suff->zipped[0] != 0) { maybe_warnx("%s already has %s suffix -- unchanged", file, suff->zipped); close(in); return (-1); } #endif /* Add (usually) .gz to filename */ if ((size_t)snprintf(outfile, outsize, "%s%s", file, suffixes[0].zipped) >= outsize) memcpy(outfile + outsize - suffixes[0].ziplen - 1, suffixes[0].zipped, suffixes[0].ziplen + 1); #ifndef SMALL if (check_outfile(outfile) == 0) { close(in); return (-1); } #endif } if (cflag == 0) { out = open(outfile, O_WRONLY | O_CREAT | O_EXCL, 0600); if (out == -1) { maybe_warn("could not create output: %s", outfile); fclose(stdin); return (-1); } #ifndef SMALL remove_file = outfile; #endif } else out = fileno(stdout); insize = gz_compress(in, out, &size, basename(file), (uint32_t)isb.st_mtime); #ifndef __APPLE__ (void)close(in); #endif /* !__APPLE__ */ /* * If there was an error, insize will be -1. * If we compressed to stdout, just return the size. * Otherwise stat the file and check it is the correct size. * We only blow away the file if we can stat the output and it * has the expected size. */ if (cflag != 0) return (insize == -1 ? -1 : size); #ifndef SMALL if (fstat(out, &osb) != 0) { maybe_warn("couldn't stat: %s", outfile); goto bad_outfile; } if (osb.st_size != size) { maybe_warnx("output file: %s wrong size (%ju != %ju), deleting", outfile, (uintmax_t)osb.st_size, (uintmax_t)size); goto bad_outfile; } #ifdef __APPLE__ fcopyfile(in, out, 0, COPYFILE_ACL | COPYFILE_XATTR); clear_type_and_creator(out); #endif /* __APPLE__ */ copymodes(out, &isb, outfile); remove_file = NULL; #endif #ifdef __APPLE__ (void)close(in); #endif /* __APPLE__ */ if (close(out) == -1) maybe_warn("couldn't close output"); /* output is good, ok to delete input */ unlink_input(file, &isb); return (size); #ifndef SMALL bad_outfile: if (close(out) == -1) maybe_warn("couldn't close output"); maybe_warnx("leaving original %s", file); unlink(outfile); return (size); #endif }