C를 사용하여 Unix에서 파일을 복사하려면 어떻게 해야 합니까?
저는 Win32의 Copy File과 동등한 Unix를 찾고 있습니다.저만의 버전을 써서 시간을 낭비하고 싶지는 않습니다.
.를 들어 비포터블 API를 호출할 필요는 .sendfile
또는 외부 유틸리티에 셸아웃합니다. 있다: 70년 대에 통하고 있다.
#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;
}
API에 내장된 동등한 CopyFile 함수는 없습니다.그러나 sendfile을 사용하여 파일을 커널 모드로 복사할 수 있습니다.이것은 파일을 열고 버퍼에 읽기 위해 루프하여 출력을 다른 파일에 쓰는 것보다 빠르고 뛰어난 솔루션입니다(여러 가지 이유로).
업데이트:
에서는 Linux의 사항 2.6.33 입니다.sendfile
의 코드가 상에서 X 10 Mavericks에서는 Linux X 10.9 Mavericks 。sendfile
OS X 에서는, 출력이 소켓이 되어, 코드가 동작하지 않게 됩니다.
다음 코드 스니펫은 대부분의 OS X(10.5 현재)에서 동작합니다.(무료)BSD 및 Linux (2.6.33 기준)구현은 모든 플랫폼에 대해 "제로 복사"됩니다. 즉, 모든 것이 커널 공간에서 수행되며 버퍼나 데이터가 사용자 공간에서 복사되거나 사용자 공간에서 복사되지 않습니다.최고의 퍼포먼스입니다.
#include <fcntl.h>
#include <unistd.h>
#if defined(__APPLE__) || defined(__FreeBSD__)
#include <copyfile.h>
#else
#include <sys/sendfile.h>
#endif
int 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;
}
편집: 행선지의 개시를, 다음의 콜로 대체해 주세요.creat()
가 대로, 깃발 "" " " " " " "O_TRUNC
특정할 필요가 있습니다.아래의 코멘트를 참조해 주세요.
파일을 바이트 단위로 복사하는 것은 가능하지만, 현대의 UNIX에서는 느리고 낭비적입니다.최신 UNIX에는 파일 시스템에 "copy-on-write" 지원이 내장되어 있습니다.시스템 콜은 디스크상의 기존 바이트를 가리키는 새로운 디렉토리 엔트리를 만듭니다.복사 중 하나가 수정될 때까지 디스크의 파일 콘텐츠 바이트는 변경되지 않습니다.이 시점에서 변경된 블록만 디스크에 기록됩니다.따라서 파일 크기에 관계없이 추가 파일 블록을 사용하지 않는 거의 즉각적인 파일 복사가 가능합니다.예를 들어 xfs에서의 동작에 대한 자세한 내용은 다음과 같습니다.
Linux 에서는 기본적으로 coreutils cp now와 같이 를 사용합니다ioctl
.
#ifdef FICLONE
return ioctl (dest_fd, FICLONE, src_fd);
#else
errno = ENOTSUP;
return -1;
#endif
MacOS에서는 APFS 볼륨에서 즉시 복사하기 위해 clonefile(2)을 사용합니다.이것이 애플의 것이다.cp -c
용도는 명확하지 않지만,가 있는 copyfile(3)도 이 기능을 사용하고 있을 가능성이 있습니다.그걸 테스트해보고 싶으면 댓글을 남겨주세요.
OS가 너무 오래되었거나 기본 파일 시스템이 지원하지 않거나 서로 다른 파일 시스템 간에 파일을 복사하는 경우 등 이러한 쓰기 복사 작업이 지원되지 않는 경우 sendfile을 시도하거나 바이트 단위로 복사하는 마지막 수단으로 되돌릴 필요가 있습니다., 모든 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★FICLONE
★★★★★★★★★★★★★★★★★」clonefile(2)
저저시시시시
적어도 Linux 및 FreeBSD에서 지원되는 copy_file_range는 아직 언급되지 않았습니다.이 방법의 장점은 리링크와 같은 CoW 기술을 사용하는 능력을 명확하게 문서화할 수 있다는 것입니다.견적:
copy_file_range()
는 파일 시스템에 리링크(즉, 동일한 쓰기 시 복사 디스크 블록에 대한 포인터를 공유하는 2개 이상의 inode) 또는 서버 측 복사(NFS의 경우)와 같은 "복사 가속" 기술을 구현할 수 있는 기회를 제공합니다.
오래된 WWIW인지 확실하지 않습니다.sendfile
네, 그렇습니다.내가 찾은 몇 가지 언급은 그렇지 않다고 주장한다.런런 in in in in in in in incopy_file_range
에 있다sendfile
.
다음에, 콜의 사용 예를 나타냅니다(매뉴얼에서 말 그대로 복사).이 코드를 사용하여 복사한 후 확인했습니다.bash
BTRFS 파일 시스템 내의 바이너리 복사본은 원본에 재링크됩니다(파일을 호출하여 메시지를 표시함).
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <unistd.h>
int
main(int argc, char **argv)
{
int fd_in, fd_out;
struct stat stat;
off64_t len, ret;
if (argc != 3) {
fprintf(stderr, "Usage: %s <source> <destination>\n", argv[0]);
exit(EXIT_FAILURE);
}
fd_in = open(argv[1], O_RDONLY);
if (fd_in == -1) {
perror("open (argv[1])");
exit(EXIT_FAILURE);
}
if (fstat(fd_in, &stat) == -1) {
perror("fstat");
exit(EXIT_FAILURE);
}
len = stat.st_size;
fd_out = open(argv[2], O_CREAT | O_WRONLY | O_TRUNC, 0644);
if (fd_out == -1) {
perror("open (argv[2])");
exit(EXIT_FAILURE);
}
do {
ret = copy_file_range(fd_in, NULL, fd_out, NULL, len, 0);
if (ret == -1) {
perror("copy_file_range");
exit(EXIT_FAILURE);
}
len -= ret;
} while (len > 0 && ret > 0);
close(fd_in);
close(fd_out);
exit(EXIT_SUCCESS);
}
fork/execl을 사용하여 cp를 실행하여 작업을 수행하는 것은 간단합니다.이는 Bobby Tables 공격이 발생하기 쉽고 인수를 동일한 수준으로 삭제할 필요가 없다는 점에서 시스템에 비해 이점이 있습니다.또한 system()은 명령어 인수를 정리해야 하므로 sprintf() 체크가 엉성하기 때문에 버퍼 오버플로 문제가 발생할 가능성은 높지 않습니다.
cp를 직접 호출하는 것이 아니라 수신처에 존재하는 타깃 패스의 요소에 대해 걱정할 필요가 없다는 장점이 있습니다.Roll-you-Own 코드로 이 작업을 수행하는 것은 오류가 발생하기 쉽고 지루합니다.
이 예는 ANSI C로 작성되어 스트레이트 코드 이외에는 가장 기본적인 에러 처리만을 생략하고 있습니다.
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 */
{
}
}
}
루프가 없는 일반 POSIX 콜을 사용하는 복사 기능의 다른 변종.caf 입니다.고 : 사사를 사용합니다.mmap
는 32비트 시스템에서는 쉽게 장애가 발생할 수 있으며 64비트 시스템에서는 위험성이 낮아집니다.
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <sys/mman.h>
int cp(const char *to, const char *from)
{
int fd_from = open(from, O_RDONLY);
if(fd_from < 0)
return -1;
struct stat Stat;
if(fstat(fd_from, &Stat)<0)
goto out_error;
void *mem = mmap(NULL, Stat.st_size, PROT_READ, MAP_SHARED, fd_from, 0);
if(mem == MAP_FAILED)
goto out_error;
int fd_to = creat(to, 0666);
if(fd_to < 0)
goto out_error;
ssize_t nwritten = write(fd_to, mem, Stat.st_size);
if(nwritten < Stat.st_size)
goto out_error;
if(close(fd_to) < 0) {
fd_to = -1;
goto out_error;
}
close(fd_from);
/* Success! */
return 0;
}
out_error:;
int saved_errno = errno;
close(fd_from);
if(fd_to >= 0)
close(fd_to);
errno = saved_errno;
return -1;
}
EDIT: 파일 작성 버그를 수정했습니다.http://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c/2180157#2180157 의 회답 코멘트를 참조해 주세요.
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#define print_err(format, args...) printf("[%s:%d][error]" format "\n", __func__, __LINE__, ##args)
#define DATA_BUF_SIZE (64 * 1024) //limit to read maximum 64 KB data per time
int32_t get_file_size(const char *fname){
struct stat sbuf;
if (NULL == fname || strlen(fname) < 1){
return 0;
}
if (stat(fname, &sbuf) < 0){
print_err("%s, %s", fname, strerror(errno));
return 0;
}
return sbuf.st_size; /* off_t shall be signed interge types, used for file size */
}
bool copyFile(CHAR *pszPathIn, CHAR *pszPathOut)
{
INT32 fdIn, fdOut;
UINT32 ulFileSize_in = 0;
UINT32 ulFileSize_out = 0;
CHAR *szDataBuf;
if (!pszPathIn || !pszPathOut)
{
print_err(" Invalid param!");
return false;
}
if ((1 > strlen(pszPathIn)) || (1 > strlen(pszPathOut)))
{
print_err(" Invalid param!");
return false;
}
if (0 != access(pszPathIn, F_OK))
{
print_err(" %s, %s!", pszPathIn, strerror(errno));
return false;
}
if (0 > (fdIn = open(pszPathIn, O_RDONLY)))
{
print_err("open(%s, ) failed, %s", pszPathIn, strerror(errno));
return false;
}
if (0 > (fdOut = open(pszPathOut, O_CREAT | O_WRONLY | O_TRUNC, 0777)))
{
print_err("open(%s, ) failed, %s", pszPathOut, strerror(errno));
close(fdIn);
return false;
}
szDataBuf = malloc(DATA_BUF_SIZE);
if (NULL == szDataBuf)
{
print_err("malloc() failed!");
return false;
}
while (1)
{
INT32 slSizeRead = read(fdIn, szDataBuf, sizeof(szDataBuf));
INT32 slSizeWrite;
if (slSizeRead <= 0)
{
break;
}
slSizeWrite = write(fdOut, szDataBuf, slSizeRead);
if (slSizeWrite < 0)
{
print_err("write(, , slSizeRead) failed, %s", slSizeRead, strerror(errno));
break;
}
if (slSizeWrite != slSizeRead) /* verify wheter write all byte data successfully */
{
print_err(" write(, , %d) failed!", slSizeRead);
break;
}
}
close(fdIn);
fsync(fdOut); /* causes all modified data and attributes to be moved to a permanent storage device */
close(fdOut);
ulFileSize_in = get_file_size(pszPathIn);
ulFileSize_out = get_file_size(pszPathOut);
if (ulFileSize_in == ulFileSize_out) /* verify again wheter write all byte data successfully */
{
free(szDataBuf);
return true;
}
free(szDataBuf);
return false;
}
매우 심플:
#define BUF_SIZE 65536
int cp(const char *from, const char*to){
FILE *src, *dst;
size_t in, out;
char *buf = (char*) malloc(BUF_SIZE* sizeof(char));
src = fopen(from, "rb");
if (NULL == src) exit(2);
dst = fopen(to, "wb");
if (dst < 0) exit(3);
while (1) {
in = fread(buf, sizeof(char), BUF_SIZE, src);
if (0 == in) break;
out = fwrite(buf, sizeof(char), in, dst);
if (0 == out) break;
}
fclose(src);
fclose(dst);
}
Windows 및 Linux에서 작동합니다.
하는 , 방법은 따로 있습니다.system
call, abda, abda, abda와 .
#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 */
}
의 샘플은 '오류체크!'를 사용하고 있습니다.open
,close
★★★★★★★★★★★★★★★★★」sendfile
.
편집: 카페가 지적한 바와 같이, 레이스 조건은 다음 중 하나의 레이스 사이에 발생할 수 있습니다.open
★★★★★★★★★★★★★★★★★」stat
★★★★★★★★★★★★★★★★★★...이 메커니즘은 Linux를 사용하는 경우lockf
수 있도록 , 「」를 사용해 .#ifdef
다른 플랫폼/컴파일러를 구별하기 위한 매크로...이걸 발견해줘서 고마워...여기에는 "범용 잠금 루틴"을 생성한 사이트에 대한 링크가 있습니다.
는 '어울리다'를 할 수 .system()
cp
다시 할 수 cp(1)
령을내내 내내내다다 되는 , 이 링크는 ' 낫다'로 할 수.link()
또는 또는symlink()
.
sprintf( cmd, "/bin/cp -p \'%s\' \'%s\'", old, new);
system( cmd);
일부 오류 검사 추가...
그 이외의 경우는, 양쪽 모두를 열어, 읽기/쓰기시에 루프를 실시합니다만, 원하는 것은 아닐 가능성이 있습니다.
...
유효한 보안 문제를 해결하기 위한 업데이트:
system()을 사용하는 대신 fork/wait를 실행하여 자녀에게 execv() 또는 execl()을 호출합니다.
execl( "/bin/cp", "-p", old, new);
좋은 질문입니다.또 다른 좋은 질문과 관련하여:
cp의 "가장 간단한" 구현에는 두 가지 접근방식이 있습니다.하나의 접근법은 일종의 파일 복사 시스템 호출 함수를 사용합니다. 즉 Unix cp 명령의 C 함수 버전에 가장 가까운 것입니다.다른 접근법은 버퍼 및 읽기/쓰기 시스템 호출 함수를 직접 사용하거나 FILE 래퍼(wrapper)를 사용합니다.
커널 소유 메모리에서만 실행되는 파일 복사 시스템 호출은 커널 소유 메모리와 사용자 소유 메모리에서 모두 실행되는 시스템 호출보다 빠를 수 있습니다. 특히 네트워크 파일 시스템 설정(머신 간 복사)에서 더 빠를 수 있습니다.단, 테스트(예를 들어 Unix 명령 시간)가 필요하며 코드가 컴파일되고 실행되는 하드웨어에 따라 달라집니다.
또, 표준 Unix 라이브러리를 탑재하고 있지 않은 OS 를 사용하고 싶은 사람도 있을 가능성이 있습니다.버퍼 읽기/쓰기 버전은 <stdlib>에만 의존하기 때문에 사용하는 것이 좋습니다.h > 및 <stdio>를 참조해 주세요.(및 친구)
<리스트 없음.h>
여기사용하는 예가 있습니다 함수를 기능 여기를 사용하는 예가 있습니다.copy_file_range
그unix 표준부터 도서관 유닉스 표준 라이브러리에서.<unistd.h>
,(존재하지 않는 가능한)대상 파일에 소스 파일 복사합니다.소스 파일을(존재하지 않는)대상 파일에 복사합니다.원고는 커널 공간에서 열린다.복사는커널 공간에서 이루어집니다.
/* copy.c
*
* Defines function copy:
*
* Copy source file to destination file on the same filesystem (possibly NFS).
* If the destination file does not exist, it is created. If the destination
* file does exist, the old data is truncated to zero and replaced by the
* source data. The copy takes place in the kernel space.
*
* Compile with:
*
* gcc copy.c -o copy -Wall -g
*/
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>
/* On versions of glibc < 2.27, need to use syscall.
*
* To determine glibc version used by gcc, compute an integer representing the
* version. The strides are chosen to allow enough space for two-digit
* minor version and patch level.
*
*/
#define GCC_VERSION (__GNUC__*10000 + __GNUC_MINOR__*100 + __gnuc_patchlevel__)
#if GCC_VERSION < 22700
static loff_t copy_file_range(int in, loff_t* off_in, int out,
loff_t* off_out, size_t s, unsigned int flags)
{
return syscall(__NR_copy_file_range, in, off_in, out, off_out, s,
flags);
}
#endif
/* The copy function.
*/
int copy(const char* src, const char* dst){
int in, out;
struct stat stat;
loff_t s, n;
if(0>(in = open(src, O_RDONLY))){
perror("open(src, ...)");
exit(EXIT_FAILURE);
}
if(fstat(in, &stat)){
perror("fstat(in, ...)");
exit(EXIT_FAILURE);
}
s = stat.st_size;
if(0>(out = open(dst, O_CREAT|O_WRONLY|O_TRUNC, 0644))){
perror("open(dst, ...)");
exit(EXIT_FAILURE);
}
do{
if(1>(n = copy_file_range(in, NULL, out, NULL, s, 0))){
perror("copy_file_range(...)");
exit(EXIT_FAILURE);
}
s-=n;
}while(0<s && 0<n);
close(in);
close(out);
return EXIT_SUCCESS;
}
/* Test it out.
*
* BASH:
*
* gcc copy.c -o copy -Wall -g
* echo 'Hello, world!' > src.txt
* ./copy src.txt dst.txt
* [ -z "$(diff src.txt dst.txt)" ]
*
*/
int main(int argc, char* argv[argc]){
if(argc!=3){
printf("Usage: %s <SOURCE> <DESTINATION>", argv[0]);
exit(EXIT_FAILURE);
}
copy(argv[1], argv[2]);
return EXIT_SUCCESS;
}
Ubuntu 20.x Linux 디스트리뷰션의 copy_file_range용 man 페이지의 예를 기반으로 합니다.man page에서 다음을 확인하십시오.
> man copy_file_range
그리고 그럼 때려요를 쳤다j
또는 또는Enter
기 전까지는 그렇게 예를 들어 코너에 간다.예 섹션으로 이동할 때까지 계속 진행합니다.또는 입력으로를 입력하여 아니면 검색 검색/example
.
<stdio.h>/<stdlib>h>만
여기만 다음예시는 다음 예만 사용합니다를 사용하는 예가 있습니다.stdlib/stdio
. 그 단점 그것 user-space에 중간 버퍼를 사용하기 때문이다.단점은 사용자 공간에서 중간 버퍼를 사용한다는 것입니다.
/* copy.c
*
* Compile with:
*
* gcc copy.c -o copy -Wall -g
*
* Defines function copy:
*
* Copy a source file to a destination file. If the destination file already
* exists, this clobbers it. If the destination file does not exist, it is
* created.
*
* Uses a buffer in user-space, so may not perform as well as
* copy_file_range, which copies in kernel-space.
*
*/
#include <stdlib.h>
#include <stdio.h>
#define BUF_SIZE 65536 //2^16
int copy(const char* in_path, const char* out_path){
size_t n;
FILE* in=NULL, * out=NULL;
char* buf = calloc(BUF_SIZE, 1);
if((in = fopen(in_path, "rb")) && (out = fopen(out_path, "wb")))
while((n = fread(buf, 1, BUF_SIZE, in)) && fwrite(buf, 1, n, out));
free(buf);
if(in) fclose(in);
if(out) fclose(out);
return EXIT_SUCCESS;
}
/* Test it out.
*
* BASH:
*
* gcc copy.c -o copy -Wall -g
* echo 'Hello, world!' > src.txt
* ./copy src.txt dst.txt
* [ -z "$(diff src.txt dst.txt)" ]
*
*/
int main(int argc, char* argv[argc]){
if(argc!=3){
printf("Usage: %s <SOURCE> <DESTINATION>\n", argv[0]);
exit(EXIT_FAILURE);
}
return copy(argv[1], argv[2]);
}
언급URL : https://stackoverflow.com/questions/2180079/how-can-i-copy-a-file-on-unix-using-c
'programing' 카테고리의 다른 글
scanf("%*s") 및 scanf("%*d") 형식의 식별자란 무엇입니까? (0) | 2022.08.09 |
---|---|
vuex getter에서 메서드 사용 (0) | 2022.08.09 |
Vue 컴포넌트 테스트 - 모의 상태 및 방법 (0) | 2022.08.09 |
Nvidia GPU에서 Java 사용(CUDA) (0) | 2022.08.09 |
여러 .so 공유 라이브러리 병합 (0) | 2022.08.09 |