getdents() 시스템 호출이 컨테이너 내에서 다른 결과를 반환하는 것 같습니다.

getdents() 시스템 호출이 컨테이너 내에서 다른 결과를 반환하는 것 같습니다.

파일 형식을 읽으려고 합니다 /dev/null. 사용하면 stat()문자장치라고 정확하게 보고됩니다.

을 사용하면 getdents()문자 장치라고 보고됩니다. 컨테이너에서 실행하지 않는 한 일반 파일이라고 표시됩니다!

컨테이너에서 실행하면 왜 다른 결과가 나오나요?

이 이미지를 사용하여 최신 버전의 docker 및 podman에서 테스트한 결과는 동일했습니다 ubuntu:22.04.

복사할 코드는 다음과 같습니다. 이 코드는 stat()항상 작동하지만 getdents컨테이너 내에서 실행될 때 어설션이 실패하게 됩니다. 또한 주목할 만한 점은 코드가 항상 복사되는 것은 아니라는 점입니다. 일부 시스템/컨테이너에서는 여전히 잘 작동하는 것 같습니다.

(리눅스 6.8.2-arch2-1 및 podman 5.0.0에서 테스트됨)

#include <assert.h>
#include <dirent.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/syscall.h>

#define BUF_SIZE 1024

struct linux_dirent {
    long           d_ino;
    off_t          d_off;
    unsigned short d_reclen;
    char           d_name[];
};

int main() {
    // stat approach

    struct stat st;
    stat("/dev/null", &st);

    printf("stat type: %d\n", st.st_mode & S_IFMT);

    assert((st.st_mode & S_IFMT) == S_IFCHR);

    // getdirents approach

    int fd, nread;
    char buf[BUF_SIZE];
    struct linux_dirent *d;
    int bpos;
    char d_type;

    fd = open("/dev", O_RDONLY | O_DIRECTORY);

    for (;;) {
        nread = syscall(SYS_getdents, fd, buf, BUF_SIZE);

        for (bpos = 0; bpos < nread;) {
            d = (struct linux_dirent *)(buf + bpos);
            if (strcmp(d->d_name, "null") == 0) {
                d_type = *(buf + bpos + d->d_reclen - 1);
                printf("getdents type: %d\n", d_type);
                assert(d_type == DT_CHR);
                exit(EXIT_SUCCESS);
            }
            bpos += d->d_reclen;
        }
    }
    close(fd);

    exit(EXIT_SUCCESS);
}

답변1

getdirents이것이 당신에게 말하는 진실이라는 것이 밝혀졌습니다 !

루트 없는 podman 컨테이너로 이동하여 실행하면 이것이 실제로 바인드 마운트임을 mount알 수 있습니다 ( 컨테이너 내부에서 예제 코드에 액세스할 수 있도록 여기에 있습니다)./dev/null-v ...

$ podman run -it --rm  -v $PWD:/src:z fedora:39
[root@00af7efc8781 /]# mount |grep /dev/null
devtmpfs on /dev/null type devtmpfs (rw,nosuid,noexec,seclabel,size=4096k,nr_inodes=8186582,mode=755,inode64)

이 번들 설치를 제거하면 무엇을 볼 수 있습니까? 알아 보자:

  • 먼저 컨테이너 pid가 필요합니다.

    $ podman container inspect -l | jq .[0].State.Pid
    50502
    
  • 이 방법으로 nsenter관련 mount 및 pid 네임스페이스를 입력할 수 있습니다.

    $ sudo nsenter -t 50502 -m -p
    
  • /dev/null마지막으로 바인드 마운트를 제거할 수 있습니다 .

    [root@fizzgig /]# umount /dev/null
    

이제 다음을 볼 수 있습니다.

[root@fizzgig /]# ls -l /dev/null
-rwx------. 1 21937 21937 0 Apr  2 20:03 /dev/null

놀랍게도 이것은 파일입니다!


호출은 getdirents디렉토리 항목에서 읽는 중입니다 /dev. 이는 바인드 마운트에 대해 알지 못함을 의미하므로 d_type기본 항목이 표시됩니다.

관련 정보