You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The following test failure happens only when building on btrfs. Since everyone can easily reproduce that, and I'm clueless about lsof internals, I'll just leave this here:
I would like to know the detail of the way to reproduce.
Sure! But note that / or not doesn't matter: simply create any btrfs file system and unpack & build lsof on it. Something like:
dd if=/dev/zero of=/tmp/img bs=1M count=512
mkfs.btrfs /tmp/img
mount -o loop /tmp/img /mnt
cd /mnt
git clone https://github.com/lsof-org/lsof
cd lsof
./Configure linux
make
cd tests
make opt
# LTlock should fail with the message above
By the way, @masatake, the above is from memory, and was tried on an Ubuntu box (just to make sure the distro really doesn't matter after telling you it shouldn't 😉 ).
I don't have access to it now but if you still can't reproduce it, I can copy/paste my exact bash history tomorrow. It will be almost identical to what I wrote.
I found strange things about device minor number(s).
findmnt reports the minor number of mount point of btrfs is 67.
stat reports the minor number of a file under the mount point of btrfs is 68.
/proc/locks says the minor number of a locked file under the mount point of btrfs is 0x43 (== 67).
I guess lsof is confused with the mismatch of numbers.
Hey, I just found this issue in a context of LTlock failure in circleci job, and I have reproduced this in a Ubuntu 22.04 container.
A direct check confirms the cause: for a locked file on btrfs, /proc/locks reports device 00:3d while stat reports st_dev0x3e (one greater); lsof's FD column therefore lacks the lock character (3u instead of 3uW).
Now I am curious about if this is same failure as per #374 (comment) (to be checked later!)
This is only an hypothesis, see as follow:
In lib/dialects/linux/dnode.c, get_locks() parses /proc/locks and keys each lock on (pid, dev, inode), where dev comes from the MAJOR:MINOR field. check_lock() then compares that lp->dev against Lf->dev, which is stat(2)'s st_dev. On most filesystems these agree, but btrfs uses an anonymous superblock whose st_dev is one greater than the device number reported in /proc/locks (an inode-257 file reports 00:3d in /proc/locks but st_dev0x3e). The mismatch means check_lock() never assigns Lf->lock, leaving it as a space. The smallest plausible fix is to normalize the comparison in check_lock() (or the device stored in get_locks()) so that a btrfs /proc/locks device one less than Lf->dev is treated as the same file, ideally guarded by a btrfs mount/filesystem check to avoid false matches.
I have done a local change that normalizes the device comparison in check_lock() for btrfs. On btrfs, /proc/locks reports a device number one less than stat(2)'s st_dev, so Lf->dev == lp->dev never matched. The comparison now also accepts Lf->dev == lp->dev + 1 when the file resides on a btrfs mount.
Basically:
lib/dialects/linux/dlsof.h: added int is_btrfs to struct mounts.
lib/dialects/linux/dmnt.c: set mp->is_btrfs from the /proc/mounts filesystem type.
lib/dialects/linux/dnode.c: added btrfs_dev() guard and updated check_lock().
and the LTlock on btrfs is OK
I suppose this includes some risks as:
it assumes btrfs always reports the /proc/locks device as exactly one less than st_dev. A kernel/btrfs change could break this again.
It uses whole dev_t + 1 arithmetic; an unusual minor-number boundary could theoretically not map to the same major/minor pair.
btrfs_dev() relies on the cached mount table from readmnt(), so mount changes after the first read may be stale.
The normalization is intentionally limited to btrfs, so other filesystems with a similar off-by-one device discrepancy remain unfixed.
My tests covers LTlock on btrfs, ext4, and overlay, but not all btrfs configurations (subvolumes, multiple btrfs mounts, different kernels).
I have done some investigation to confirm that it is a genuine lock-reporting bug on Btrfs. The code matches locks using PID, device number and inode. However, Btrfs can report different device numbers through stat() and /proc/locks , causing lsof to omit the lock indicator even when the file is locked.
I did some changes, Linux-only fix in dproc.c , without touching the existing lock parser. When the original lookup misses, lsof uses the lock: records from /proc/PID/fdinfo/FD . These records are associated with the specific open descriptor, so no device-number comparison or arithmetic workaround is needed.
I have done some verification on Btrfs to confirm that the (original) code missed all four lock indicators ( W , R , w , r ), while my changes reported them correctly. Some snapshot tests also confirmed that an unlocked file with the same inode number in another subvolume remained unlocked. Ext4 and targeted compatibility tests passed.
I also ran the full Linux (docker) and native macOS suites. The lock regression passed, but the suites were not entirely green by now. Need to check further.
Activity
masatake commentedon Mar 21, 2021
Which distribution do you use?
nckx commentedon Mar 21, 2021
The bug isn't distribution-specific, but I use & first encountered it on Guix System.
masatake commentedon Mar 21, 2021
Thank you. I was confused with btrfs with zfs.
I will inspect this issue.
masatake commentedon Mar 21, 2021
Do you use btfs as your root file system?
I would like to know the detail of the way to reproduce.
nckx commentedon Mar 21, 2021
Yes.
Sure! But note that
/or not doesn't matter: simply create any btrfs file system and unpack & build lsof on it. Something like:nckx commentedon Mar 21, 2021
By the way, @masatake, the above is from memory, and was tried on an Ubuntu box (just to make sure the distro really doesn't matter after telling you it shouldn't 😉 ).
I don't have access to it now but if you still can't reproduce it, I can copy/paste my exact bash history tomorrow. It will be almost identical to what I wrote.
Thanks for responding so promptly!
nckx commentedon Mar 21, 2021
This report is about something else, but seems to exhibit the same failure: #61
masatake commentedon Jun 20, 2021
I found strange things about device minor number(s).
findmnt reports the minor number of mount point of btrfs is 67.
stat reports the minor number of a file under the mount point of btrfs is 68.
/proc/locks says the minor number of a locked file under the mount point of btrfs is 0x43 (== 67).
I guess lsof is confused with the mismatch of numbers.
masatake commentedon Jun 20, 2021
I guess this is something to do with the subvolume feature of btrfs.
masatake commentedon Jun 20, 2021
As the test case shows lsof cannot report the lock information (RW) for a locked file on btrfs correctly.
masatake commentedon Jun 21, 2021
https://www.spinics.net/lists/linux-btrfs/msg09044.html
mjt0k commentedon Nov 7, 2023
See also
https://unix.stackexchange.com/questions/345220/btrfs-how-to-get-real-device-id
http://www.sabi.co.uk/blog/21-two.html?210804#210804 (linked to from above)
util-linux/util-linux#2349 (comment) (similar issue with lsfd)
hugojosealmeida commentedon Oct 30, 2026
Hey, I just found this issue in a context of
LTlockfailure incirclecijob, and I have reproduced this in a Ubuntu 22.04 container.A direct check confirms the cause: for a locked file on btrfs,
/proc/locksreports device00:3dwhilestatreportsst_dev0x3e(one greater); lsof's FD column therefore lacks the lock character (3uinstead of3uW).Now I am curious about if this is same failure as per #374 (comment) (to be checked later!)
This is only an hypothesis, see as follow:
In
lib/dialects/linux/dnode.c,get_locks()parses/proc/locksand keys each lock on(pid, dev, inode), wheredevcomes from theMAJOR:MINORfield.check_lock()then compares thatlp->devagainstLf->dev, which isstat(2)'sst_dev. On most filesystems these agree, but btrfs uses an anonymous superblock whosest_devis one greater than the device number reported in/proc/locks(an inode-257 file reports00:3din/proc/locksbutst_dev0x3e). The mismatch meanscheck_lock()never assignsLf->lock, leaving it as a space. The smallest plausible fix is to normalize the comparison incheck_lock()(or the device stored inget_locks()) so that a btrfs/proc/locksdevice one less thanLf->devis treated as the same file, ideally guarded by a btrfs mount/filesystem check to avoid false matches.I have done a local change that normalizes the device comparison in
check_lock()for btrfs. On btrfs,/proc/locksreports a device number one less thanstat(2)'sst_dev, soLf->dev == lp->devnever matched. The comparison now also acceptsLf->dev == lp->dev + 1when the file resides on a btrfs mount.Basically:
lib/dialects/linux/dlsof.h: addedint is_btrfstostruct mounts.lib/dialects/linux/dmnt.c: setmp->is_btrfsfrom the/proc/mountsfilesystem type.lib/dialects/linux/dnode.c: addedbtrfs_dev()guard and updatedcheck_lock().and the
LTlockon btrfs is OKI suppose this includes some risks as:
/proc/locksdevice as exactly one less thanst_dev. A kernel/btrfs change could break this again.dev_t + 1arithmetic; an unusual minor-number boundary could theoretically not map to the same major/minor pair.btrfs_dev()relies on the cached mount table fromreadmnt(), so mount changes after the first read may be stale.LTlockon btrfs, ext4, and overlay, but not all btrfs configurations (subvolumes, multiple btrfs mounts, different kernels).This activity is AI Assisted.
hugojosealmeida commentedon Nov 3, 2026
I have done some investigation to confirm that it is a genuine lock-reporting bug on Btrfs. The code matches locks using PID, device number and inode. However, Btrfs can report different device numbers through
stat()and/proc/locks, causing lsof to omit the lock indicator even when the file is locked.I did some changes, Linux-only fix in
dproc.c, without touching the existing lock parser. When the original lookup misses, lsof uses thelock:records from/proc/PID/fdinfo/FD. These records are associated with the specific open descriptor, so no device-number comparison or arithmetic workaround is needed.I have done some verification on Btrfs to confirm that the (original) code missed all four lock indicators (
W,R,w,r), while my changes reported them correctly. Some snapshot tests also confirmed that an unlocked file with the same inode number in another subvolume remained unlocked. Ext4 and targeted compatibility tests passed.I also ran the full Linux (docker) and native macOS suites. The lock regression passed, but the suites were not entirely green by now. Need to check further.
hugojosealmeida commentedon Nov 3, 2026
@jiegec Could you have a look to the previous comment?