lsfd: (bugfix) do not reuse stat(2) buffer for files with identical names
To reduce the number of stat(2) calls, lsfd reused the buffer returned
from stat(2) when file descriptors opened files with the same name.
If file descriptors open different files that happen to have the same
name, lsfd may report incorrect results. In such cases, the stat(2)
buffer must not be reused.
The program a.out is run with a file name "D/a". It opens the file
twice during its execution with an interval. Between the two open()
calls, a different filesystem is mounted on "D".
<the source code of ./a.out>
#include <fcntl.h>
#include <err.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static void usage(const char *prog, int eval, FILE *fp)
{
fputs("Usage :\n", fp);
fprintf(fp, " %s FILE\n", prog);
exit(eval);
}
int main(int argc, char **argv)
{
const char *fname;
int fd0, fd1;
if (argc < 2)
errx(2, "too few arguements");
if (strcmp(argv[1], "-h") == 0 ||
strcmp(argv[1], "--h") == 0)
usage(argv[0], 0, stdout);
if (argc > 2)
errx(2, "too many arguements");
fname = argv[1];
printf("pid: %d\n", getpid());
fd0 = open(fname, O_RDONLY);
if (fd0 < 0)
err(1, "error in open \"%s\" in the first time", fname);
fputs("[press RETURN to go to the next step] ", stderr);
getchar();
fd1 = open(fname, O_RDONLY);
if (fd1 < 0)
err(1, "error in open \"%s\" in the second time", fname);
fputs("[press RETURN to exit] ", stderr);
getchar();
return 0;
}
<PREPARATION>
$ mkdir D
$ touch D/a
$ dd if=/dev/zero of=img.xfs count=1 bs=400MB
1+0 records in
1+0 records out
400000000 bytes (400 MB, 381 MiB) copied, 0.427125 s, 936 MB/s
$ mkfs.xfs img.xfs
meta-data=img.xfs isize=512 agcount=4, agsize=24414 blks
= sectsz=512 attr=2, projid32bit=1
= crc=1 finobt=1, sparse=1, rmapbt=1
= reflink=1 bigtime=1 inobtcount=1 nrext64=1
= exchange=0 metadir=0
data = bsize=4096 blocks=97656, imaxpct=25
= sunit=0 swidth=0 blks
naming =version 2 bsize=4096 ascii-ci=0, ftype=1, parent=0
log =internal log bsize=4096 blocks=16384, version=2
= sectsz=512 sunit=0 blks, lazy-count=1
realtime =none extsz=4096 blocks=0, rtextents=0
= rgcount=0 rgsize=0 extents
= zoned=0 start=0 reserved=0
$ sudo mount img.xfs D
$ sudo touch D/a
$ sudo umount D
Let's see the bug.
<TERMNAL-1>
$ ./a.out D/a
pid: 770257
[press RETURN to go to the next step]
<TERMNAL-2>
$ sudo mount img.xfs D
<TERMNAL-1>
(press RETURN)
[press RETURN to exit]
<TERMNAL-2>
$ ./lsfd-orignal -p 770257 -Q 'FD > 2'
COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME
a.out 770257 yamato 3 r----- REG dm-3 95
30947324 /home/yamato/D/a
a.out 770257 yamato 4 r----- REG dm-3 1631
30947324 /home/yamato/D/a
$ stat D/a
File: D/a
Size: 0 Blocks: 0 IO Block: 4096 regular empty file
Device: 7,10 Inode: 131 Links: 1
...
Although D/a has inode number 131, lsfd-original reports
30947324.
After removing the code that reuses the stat(2) buffer, lsfd-new reports:
<TERMNAL-2>
$ ./lsfd-new -p 770257 -Q 'FD > 2'
COMMAND PID USER ASSOC XMODE TYPE SOURCE MNTID INODE NAME
a.out 770257 yamato 3 r----- REG dm-3 95
30947324 /home/yamato/D/a
a.out 770257 yamato 4 r----- REG loop10 1631 131 /home/yamato/D/a
Signed-off-by: Masatake YAMATO <yamato@redhat.com>