왜 이런 일이 발생합니까: "cp image.bin /dev/mapper/loop0p1"?

왜 이런 일이 발생합니까: "cp image.bin /dev/mapper/loop0p1"?

이것이 작동하는 이유는 무엇입니까?

cp image.bin /dev/mapper/loop0p1

image.bin파티션 이미지입니다.

시도해 봤는데 효과가 있지만 왜 그럴까요? a를 사용하면 안 되나요 dd?

답변1

GNU coreutils는 cp그렇게 작성되었기 때문에 작동합니다. 블록 장치에 쓰는 것은 복잡하지 않으며 기본적으로 일반 파일에 쓰는 것과 동일한 작업입니다.

그러나 이 접근 방식을 사용해서는 안 됩니다 cp. GNU coreutils가 작동할 것이라고 확신하는 경우 이를 수행할 수 있습니다. 그러나 다른 특징도 있습니다 cp. 예를 들어 busybox cp장치에 대한 쓰기를 전혀 지원하지 않고 장치 노드의 링크를 해제(제거)하고 그 자리에 새 파일을 생성합니다.

dd이 점에서는 더 안전합니다. 이는 장치에 쓰기 위해 설계되었으며 장치 작업 시 자주 필요한 도구(bs, 탐색, 건너뛰기, 계산...)를 제공합니다. 모든 스타일에서 예상한 대로 작동해야 합니다 dd.

답변2

귀하의 기대는 프로그램/시스템 설계와 다릅니다. 이것에 대해 무엇을 말할 수 있습니까? :-)

"모든 것은 파일이다"…

cp 및/또는 dd를 실행하여 strace -e trace=open일반 파일과 블록 장치에 대한 시스템 호출이 동일한지 확인할 수 있습니다. 시스템 호출이 이들을 구별할 수 없다면 왜 cp가 신경써야 할까요?

답변3

내가 아는 한, 하는 cp일은

  • 쓰기 모드로 대상 파일 열기
  • 소스 파일에서 대상 파일로 데이터 쓰기(블록 크기는 확실하지 않지만 세부 사항일 뿐입니다)

일반 파일의 경우 이로 인해 다음이 발생할 수 있습니다.

  • 각 쓰기 호출에 따라 커지는 새 파일을 생성합니다.
  • 덮어쓸 기존 파일 덮어쓰기:
    첫 번째 쓰기 호출은 파일의 내용을 지우고 새 데이터를 파일에 넣습니다. 그 시점부터 파일은 새 파일처럼 커집니다.

이제 /dev/mapper/*는 블록 장치(또는 구체적으로 블록 장치에 대한 심볼릭 링크)입니다. 여기에는 정적 파일 크기가 있습니다. 따라서 이러한 파일을 열면 각 write()호출은 전송한 대상 파일의 n바이트를 덮어쓰게 됩니다(어디에도 호출이 없다고 가정 fseek()).

그럼, 우리 자신의 가난한 사람의 CP를 작성해 봅시다:

#include <stdlib.h>
#include <stdio.h>

void usage() {fprintf(stderr, "Usage: cp <srcFile> <tgtFile>\n"); exit(1);}
void error(const char *msg) {fprintf(stderr, "Error: %s\n", msg); exit(2);}

void printPosAndSize(FILE *f) {
    off_t curPos = ftello(f);
    fseeko(f, 0, SEEK_END);
    off_t size = ftell(f);
    fseeko(f, curPos, SEEK_SET);
    printf("Pos: %llu,\tSize: %llu\n", curPos, size);
}

int main(int argc, const char *argv[]) {
    if (argc != 3) usage();

    const char *srcPath = argv[1];
    const char *tgtPath = argv[2];

    FILE *inFile = fopen(srcPath, "rb");
    FILE *outFile = fopen(tgtPath, "wb");

    printf("inFile: %s, outfile: %s\n", srcPath, tgtPath);

    if (!inFile) error("Couldn't open source file!");
    if (!outFile) error("Couldn't open target file!");

    while (!feof(inFile)) {
        char buff[2048];
        size_t count = fread(buff, 1, sizeof(buff), inFile);
        fwrite(buff, 1, count, outFile);
        printPosAndSize(outFile);
    }

    fclose(inFile);
    fclose(outFile);

    return 0;
}

이 cp는 file1에서 file2로 2048바이트 블록을 씁니다. 일반 파일을 복사하면 출력은 다음과 같습니다.

# copying to new file:
$ sudo ./cp /var/log/syslog /tmp/foo.txt 
inFile: /var/log/syslog, outfile: /tmp/foo.txt
Pos: 2048,      Size: 2048
Pos: 4096,      Size: 4096
Pos: 4949,      Size: 4949

# overwriting existing file:
$ sudo ./cp /var/log/syslog /tmp/foo.txt 
inFile: /var/log/syslog, outfile: /tmp/foo.txt
Pos: 2048,      Size: 2048
Pos: 4096,      Size: 4096
Pos: 4949,      Size: 4949

쓰기 모드로 열기 전에 대상 파일이 존재하는지 여부가 중요하지 않다는 것을 보여주기 위해 두 번 실행했습니다. 일단 작성하면 내용이 완전히 덮어쓰여지며 파일 크기에 이를 반영합니다.

따라서 다른 것을 시도해 보겠습니다. $ sudo ./cp /var/log/syslog /dev/null inFile: /var/log/syslog, outfile: /dev/null Pos: 0, Size: 0 Pos: 2048 , Size: 0 위치: 974, 크기: 0

/dev/null캐릭터 장치입니다. 크기는 항상 0입니다. 출력은 (예: 직렬 포트)에 기록된 후 잊어집니다.

하지만 귀하의 질문에 답해 보겠습니다. 블록 장치에 쓰는 경우(경고하다! 이렇게 하면 드라이브의 메타 정보가 파괴되므로 장치의 모든 데이터를 읽을 수 없게 됩니다!이 데모에서는 오래된 USB 드라이브를 사용했습니다.)

$ sudo ./cp /var/log/syslog /dev/sdb
inFile: /var/log/syslog, outfile: /dev/sdb
Pos: 2048,      Size: 2003828736
Pos: 4096,      Size: 2003828736
Pos: 5306,      Size: 2003828736

블록 파일은 단순히 위치 0에서 열리고 바이트 단위로 덮어쓰기됩니다(다른 모든 데이터는 그대로 유지됩니다).

관련 정보