I'm trying to read what type of file /dev/null
is. If I use stat()
it reports correctly that it's a character device.
If I use getdents()
, it also reports that it's a character device - unless I run it in a container, in which case it says it's a regular file!
Why does running it in a container give different results?
This was tested on recent versions of docker and podman giving the same results, using the ubuntu:22.04
image.
Below is reproduction code - the stat()
approach always works, but getdents
causes the assert to fail when run inside a container. Also worth noting that the code doesn't always get reproduced - on some systems / containers it seems to still work fine.
(Tested on linux 6.8.2-arch2-1 and 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);
}
stat()
andreaddir()
operate, and it also tells us something about the container environment is put together. Neat! Thanks for posting.6.8.0-76060800daily20240311-generic
andDocker version 26.0.0, build 2ae903e
/dev
is set up differs between theroot
-full androot
-less configurations.