This change tries to fix the failures in testing reported in:
* https://lkml.org/lkml/2022/1/31/1333, and
* https://lkml.org/lkml/2022/2/2/171
The original test cases depended on a wrong assumption; if M and M'
are the same, D and D' are the same, too. Here, M is a mnt_id in
/proc/$pid/mountinfo. D is the device number for M in
/proc/$pid/mountinfo. M' is the mnt_id value in
/proc/$pid/fdinfo/$fd. D' is the device number reported by stat
syscall for /proc/$pid/fd/$fd.
# ../../lsfd -oCOMMAND,PID,MNTID,DEV,NAME -p
2972623 -Q '(ASSOC =~ "^[0-9]+$") and (TYPE == "REG")'
COMMAND PID MNTID DEV NAME
a.out
2972623 1578 0:98 /home/jet/var/util-linux/o/merged/y
# stat -c %d /home/jet/var/util-linux/o/merged/y
98
# grep 1578 /proc/
2972623/mountinfo
1578 91 0:91 / /home/jet/var/util-linux/o/merged rw,relatime shared:724 - overlay overlay rw,seclabel,lowerdir=lower-base/lower,upperdir=upper-base/upper,workdir=upper-base/work
The original test compared the device numbers reported by findmnt and
lsfd. findmnt retrieves a device number from /proc/$pid/mountinfo.
lsfd retrieves a device number from stat syscall.
This assumption is not correct for a file on an overlay file system
whose block devices behind lowerdir and upperdir are different.
Using findmnt is not suitable for comparing device numbers.
This change uses stat command instead of findmnt to retrieve the
device number.
Reported-by: Anatoly Pugachev <matorola@gmail.com>
Reported-by: Chris Hofstaedtler <zeha@debian.org>
Signed-off-by: Masatake YAMATO <yamato@redhat.com>
MAJ_MIN[STR]: 0
RDEV[RUN]: 0
RDEV[STR]: 0
-MNTID[RUN]: 0
DEV[RUN]: 0
-FINDMNT[RUN]: 0
-DEV[STR]: 0
+STAT[RUN]: 0
+DEVNUM[STR]: 0
USER[STR]: 0
SIZE[RUN]: 0
SIZE[STR]: 0
-MNTID[RUN]: 0
DEV[RUN]: 0
-FINDMNT[RUN]: 0
-DEV[STR]: 0
+STAT[RUN]: 0
+DEVNUM[STR]: 0
3 rw- CHR /dev/zero mem:5 0 1:5 mem char 1:5
ASSOC,MODE,TYPE,NAME,SOURCE,POS,MAJ:MIN,CHRDRV,DEVTYPE,RDEV: 0
-MNTID[RUN]: 0
DEV[RUN]: 0
-FINDMNT[RUN]: 0
-DEV[STR]: 0
+STAT[RUN]: 0
+DEVNUM[STR]: 0
function lsfd_compare_dev {
local LSFD=$1
- local FINDMNT=$2
+ local FILE=$2
local EXPR=$3
ts_check_prog "grep"
+ ts_check_prog "expr"
+ ts_check_prog "stat"
- local MNTID=$("${LSFD}" --raw -n -o MNTID -Q "${EXPR}")
- echo 'MNTID[RUN]:' $?
local DEV=$("${LSFD}" --raw -n -o DEV -Q "${EXPR}")
echo 'DEV[RUN]:' $?
- # "stat -c" %d or "stat -c %D" can be used here instead of "findmnt".
- # "stat" just prints a device id.
- # Unlike "stat", "findmnt" can print the major part and minor part
- # for a given device separately.
- # We can save the code for extracting the major part and minor part
- # if we use findmnt.
- local FINDMNT_MNTID_DEV=$("${FINDMNT}" --raw -n -o ID,MAJ:MIN | grep "^${MNTID}\b")
- echo 'FINDMNT[RUN]:' $?
- if [ "${MNTID} ${DEV}" == "${FINDMNT_MNTID_DEV}" ]; then
- echo 'DEV[STR]:' 0
+ local MAJ=${DEV%:*}
+ local MIN=${DEV#*:}
+ local DEVNUM=$(( ( MAJ << 8 ) + MIN ))
+ local STAT_DEVNUM=$(stat -c "%d" "$FILE")
+ echo 'STAT[RUN]:' $?
+ if [ "${DEVNUM}" == "${STAT_DEVNUM}" ]; then
+ echo 'DEVNUM[STR]:' 0
else
- echo 'DEV[STR]:' 1
+ echo 'DEVNUM[STR]:' 1
# Print more information for debugging
- echo 'MNTID:' "${MNTID}"
echo 'DEV:' "${DEV}"
- echo 'MNTID DEV:' "${MNTID} ${DEV}"
- echo 'FINDMNT_MNTID_DEV:' "${FINDMNT_MNTID_DEV}"
+ echo 'MAJ:MIN' "${MAJ}:${MIN}"
+ echo 'DEVNUM:' "${DEVNUM}"
+ echo 'STAT_DEVNUM:' "${STAT_DEVNUM}"
fi
}
[ "${LSFD_RDEV}" == 7:"${LSFD_LOOP_BDEV_NUM}" ]
echo 'RDEV[STR]:' $?
- lsfd_compare_dev "${TS_CMD_LSFD}" "${TS_CMD_FINDMNT}" "${EXPR}"
+ lsfd_compare_dev "${TS_CMD_LSFD}" "${LSFD_LOOP_BDEV}" "${EXPR}"
kill -CONT ${PID}
wait ${MKFDS_PID}
. $TS_SELF/lsfd-functions.bash
ts_check_test_command "$TS_CMD_LSFD"
-ts_check_test_command "$TS_CMD_FINDMNT"
ts_check_test_command "$TS_HELPER_MKFDS"
EXPR=
{
- coproc MKFDS { "$TS_HELPER_MKFDS" --comm ABC ro-regular-file $FD offset=1; }
+ target=/etc/passwd
+ coproc MKFDS { "$TS_HELPER_MKFDS" --comm ABC ro-regular-file $FD offset=1 file=$target; }
if read -u ${MKFDS[0]} PID; then
EXPR='(PID == '"${PID}"') and (FD == '"$FD"')'
${TS_CMD_LSFD} -n -o COMMAND,ASSOC,MODE,TYPE,NAME,POS -Q "${EXPR}"
LSFD_INODE=$(${TS_CMD_LSFD} --raw -n -o INODE -Q "${EXPR}")
echo 'INODE[RUN]:' $?
- [ "${LSFD_INODE}" == "$(stat -c %i /etc/passwd)" ]
+ [ "${LSFD_INODE}" == "$(stat -c %i $target)" ]
echo 'INODE[STR]:' $?
LSFD_UID=$(${TS_CMD_LSFD} --raw -n -o UID -Q "${EXPR}")
LSFD_SIZE=$(${TS_CMD_LSFD} --raw -n -o SIZE -Q "${EXPR}")
echo 'SIZE[RUN]:' $?
- [ "${LSFD_SIZE}" == $(stat -c %s /etc/passwd) ]
+ [ "${LSFD_SIZE}" == $(stat -c %s $target) ]
echo 'SIZE[STR]:' $?
- lsfd_compare_dev "${TS_CMD_LSFD}" "${TS_CMD_FINDMNT}" "${EXPR}"
+ lsfd_compare_dev "${TS_CMD_LSFD}" $target "${EXPR}"
kill -CONT ${PID}
wait ${MKFDS_PID}
. $TS_SELF/lsfd-functions.bash
ts_check_test_command "$TS_CMD_LSFD"
-ts_check_test_command "$TS_CMD_FINDMNT"
ts_check_test_command "$TS_HELPER_MKFDS"
EXPR=
{
- coproc MKFDS { "$TS_HELPER_MKFDS" rw-character-device $FD chrdev=/dev/zero; }
+ target=/dev/zero
+ coproc MKFDS { "$TS_HELPER_MKFDS" rw-character-device $FD chrdev=$target; }
if read -u ${MKFDS[0]} PID; then
EXPR='(PID == '"${PID}"') and (FD == '"$FD"')'
${TS_CMD_LSFD} -n -o ASSOC,MODE,TYPE,NAME,SOURCE,POS,MAJ:MIN,CHRDRV,DEVTYPE,RDEV -Q "${EXPR}"
echo 'ASSOC,MODE,TYPE,NAME,SOURCE,POS,MAJ:MIN,CHRDRV,DEVTYPE,RDEV': $?
- lsfd_compare_dev "${TS_CMD_LSFD}" "${TS_CMD_FINDMNT}" "${EXPR}"
+ lsfd_compare_dev "${TS_CMD_LSFD}" $target "${EXPR}"
kill -CONT ${PID}
wait ${MKFDS_PID}