Skip to content

lsfd: fill NAME column of inotify files with the information about their monitoring targets#2308

Merged
karelzak merged 5 commits intoutil-linux:masterfrom
masatake:lsfd--inotify
Jun 14, 2023
Merged

lsfd: fill NAME column of inotify files with the information about their monitoring targets#2308
karelzak merged 5 commits intoutil-linux:masterfrom
masatake:lsfd--inotify

Conversation

@masatake
Copy link
Member

@masatake masatake commented Jun 12, 2023

    # ./lsfd  -p 1  -Q '(TYPE == "inotify") and (FD > 7)'
    COMMAND PID USER ASSOC MODE    TYPE       SOURCE MNTID INODE NAME
    systemd   1 root    11  r-- inotify anon_inodefs    15  1060 inodes=116@dm-0
    systemd   1 root    13  r-- inotify anon_inodefs    15  1060 inodes=299@tmpfs
    systemd   1 root    19  r-- inotify anon_inodefs    15  1060 inodes=41@tmpfs,2@tmpfs,1@tmpfs,96@dm-0
    systemd   1 root    21  r-- inotify anon_inodefs    15  1060 inodes=41@tmpfs,2@tmpfs,1@tmpfs,96@dm-0

In addition, INOTIFY.INODES and INOTIFY.INODES.RAW column are added.

INODES_RAW=$(${TS_CMD_LSFD} -n -o INOTIFY.INODES.RAW -Q "${EXPR}")
echo "INOTIFY.INODES.RAW": $?
if [[ "$INODES_RAW" == "$FSTAB","$ROOT" ]]; then
echo "INOTIFY.INODES.RAW" == "FSTAB","ROOT"

Check warning

Code scanning / shellcheck

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
@masatake masatake force-pushed the lsfd--inotify branch 2 times, most recently from df895f9 to cd601e4 Compare June 13, 2023 04:04
PID=
FD=3
ROOT_DEV=$(stat -c %d /)
ROOT="$(stat -c %i /)"@"$(((ROOT_DEV >> 8) & 255)):$((ROOT_DEV & 255))"

Check warning

Code scanning / shellcheck

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
ROOT_DEV=$(stat -c %d /)
ROOT="$(stat -c %i /)"@"$(((ROOT_DEV >> 8) & 255)):$((ROOT_DEV & 255))"
FSTAB_DEV=$(stat -c %d /etc/fstab)
FSTAB="$(stat -c %i /etc/fstab)"@"$(((FSTAB_DEV >> 8) & 255)):$((FSTAB_DEV & 255))"

Check warning

Code scanning / shellcheck

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?

Word is of the form "A"B"C" (B indicated). Did you mean "ABC" or "A\"B\"C"?
if (dev_major == 0) {
const char *filesystem = get_nodev_filesystem(dev_minor);
if (filesystem)
return snprintf(buf, bufsize, "%s", filesystem);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would probably be more effective to use xstrncpy(buf, filesystem, bufsiz) than snprinf("%s"). The decode_source() return code is nowhere used, so 0/-1 or void would be also good enough.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you. Updated.

masatake added 5 commits June 13, 2023 19:18
…eir monitoring targets

    # ./lsfd  -p 1  -Q '(TYPE == "inotify") and (FD > 7)'
    COMMAND PID USER ASSOC MODE    TYPE       SOURCE MNTID INODE NAME
    systemd   1 root    11  r-- inotify anon_inodefs    15  1060 inodes=116@dm-0
    systemd   1 root    13  r-- inotify anon_inodefs    15  1060 inodes=299@tmpfs
    systemd   1 root    19  r-- inotify anon_inodefs    15  1060 inodes=41@tmpfs,2@tmpfs,1@tmpfs,96@dm-0
    systemd   1 root    21  r-- inotify anon_inodefs    15  1060 inodes=41@tmpfs,2@tmpfs,1@tmpfs,96@dm-0

In addition, INOTIFY.INODES and INOTIFY.INODES.RAW column are added.

Signed-off-by: Masatake YAMATO <[email protected]>
@karelzak karelzak merged commit b9040ac into util-linux:master Jun 14, 2023
@t-8ch
Copy link
Member

t-8ch commented Jun 27, 2023

@masatake FYI this fails the testsuite for me reliably:

--- /home/t-8ch/src/util-linux/tests/expected/lsfd/mkfds-inotify	2023-06-22 19:16:23.736001931 +0200
+++ /home/t-8ch/src/util-linux/build-meson/tests/output/lsfd/mkfds-inotify	2023-06-27 14:30:35.634742182 +0200
@@ -1,4 +1,14 @@
 INOTIFY.INODES.RAW: 0
-INOTIFY.INODES.RAW == FSTAB\x0aROOT
+INOTIFY.INODES.RAW: 1261373@0:24\x0a256@0:24
+FSTAB: 1261373@0:26
+ROOT: 256@0:26
 INOTIFY.INODES.RAW (JSON): 0
-INOTIFY.INODES.RAW == EXPECTED_JSON
+INOTIFY.INODES.RAW: {
+   "lsfd": [
+      {
+         "inotify.inodes.raw": [
+             "1261373@0:24", "256@0:24"
+         ]
+      }
+   ]
+}
}}}-diff

Maybe because it's on btrfs.

masatake added a commit to masatake/util-linux that referenced this pull request Feb 24, 2024
When filling SOURCE column, lsfd decodes the name of device where the
file object is. If the file object is sourced a file system, lsfd
fills the column with the name of the file system.

As util-linux#2349 and util-linux#2308, if the file system is btrfs, lsfd couldn't decode
the name correctly. This change and its preceeding changes fix this bug.

"devnum offset" causes the trouble.  On btrfs, the device number
reported by stat syscall and proc fs are different.
About the details of "devnum offset", see "Mechanisum behind the
devnum offset". About the way to fix, see "How to adjust the output of lsfd".

Mechanisum behind the devnum offset
-----------------------------------
Both stat command and the inotify field in fdinfo refer to an inode.

filename_lookup(https://elixir.bootlin.com/linux/v6.2.9/source/fs/namei.c#L2495)
is the function getting the inode for a given file
name. filename_lookup returns a struct path.  Via path->detnry->inode,
the caller of filename_lookup can get the inode.

stat command calls statx system call. statx calls filename_lookup
eventually.

inotify_add_watch system call takes a file name. The inotify_add_watch
calls the filename_lookup eventually for getting the inode for the
file name.  The inode number that inotify_add_watch gets via
filename_lookup is printed in the inotify field in fdinfo.

The device number, the subject of this issue, can be obtained via
path->detnry->inode->i_sb->s_dev. Both the stat command and the
inotify field in fdinfo use the filename_lookup for getting path. If
they use the same function, why don't the device numbers match? I
monitored the device numbers obtained via
path->detnry->inode->i_sb->s_dev by inserting a systemtap probe to
filename_lookup. I saw the numbers matched.

However, the number monitored via systemtap did not match the number
printed by the stat command.  statx system call doesn't use
path->detnry->inode->i_sb->s_dev , the value obtained via
filename_lookup, directly.  statx calls vfs_statx. vfs_statx calls
vfs_getattr after calling the filename_lookup for filling struct
kstat. vfs_getattr calls inode->i_op->getattr, a file system specific
method for filling struct kstat if it is available.  btrfs has an
implementation for the method,
btrfs_getattr(https://elixir.bootlin.com/linux/v6.2.9/source/fs/btrfs/inode.c#L9007):

    stat->dev = BTRFS_I(inode)->root->anon_dev;

The dev member is overwritten with btrfs specific value.

How to adjust the output of lsfd
--------------------------------
lsfd already reads mountinfo files.

1. Get the "rawnum" and mount point

   The device numbers in a mountinfo file are raw; btrfs is not
   considered. Let's call the number "rawnum" here. When reading the
   mountinfo file, lsfd can know the mount points of btrfs.

       grep btrfs /proc/self/mountinfo
       72 1 0:35 /root / rw,relatime shared:1 - btrfs

2. Get the cooked num

   By calling "stat" system call for the mount point getting in the
   step 1, lsdf can know the device number the btrfs customizes with
   its getattr method. Let's call the device number "cookednum".

3. Make a table mapping "rawnum" to "cookednum".

4. Look up the table when printing inodes.

Signed-off-by: Masatake YAMATO <[email protected]>
masatake added a commit to masatake/util-linux that referenced this pull request Feb 24, 2024
When filling SOURCE column, lsfd decodes the name of device where the
file object is. If the file object is sourced a file system, lsfd
fills the column with the name of the file system.

As util-linux#2349 and util-linux#2308, if the file system is btrfs, lsfd couldn't decode
the name correctly. This change and its preceeding changes fix this bug.

"devnum offset" causes the trouble.  On btrfs, the device number
reported by stat syscall and proc fs are different.
About the details of "devnum offset", see "Mechanisum behind the
devnum offset". About the way to fix, see "How to adjust the output of lsfd".

Without this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG   0:38     0 589767 /usr/bin/zsh

With this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG  btrfs     0 589767 /usr/bin/zsh

Mechanisum behind the devnum offset
-----------------------------------
Both stat command and the inotify field in fdinfo refer to an inode.

filename_lookup(https://elixir.bootlin.com/linux/v6.2.9/source/fs/namei.c#L2495)
is the function getting the inode for a given file
name. filename_lookup returns a struct path.  Via path->detnry->inode,
the caller of filename_lookup can get the inode.

stat command calls statx system call. statx calls filename_lookup
eventually.

inotify_add_watch system call takes a file name. The inotify_add_watch
calls the filename_lookup eventually for getting the inode for the
file name.  The inode number that inotify_add_watch gets via
filename_lookup is printed in the inotify field in fdinfo.

The device number, the subject of this issue, can be obtained via
path->detnry->inode->i_sb->s_dev. Both the stat command and the
inotify field in fdinfo use the filename_lookup for getting path. If
they use the same function, why don't the device numbers match? I
monitored the device numbers obtained via
path->detnry->inode->i_sb->s_dev by inserting a systemtap probe to
filename_lookup. I saw the numbers matched.

However, the number monitored via systemtap did not match the number
printed by the stat command.  statx system call doesn't use
path->detnry->inode->i_sb->s_dev , the value obtained via
filename_lookup, directly.  statx calls vfs_statx. vfs_statx calls
vfs_getattr after calling the filename_lookup for filling struct
kstat. vfs_getattr calls inode->i_op->getattr, a file system specific
method for filling struct kstat if it is available.  btrfs has an
implementation for the method,
btrfs_getattr(https://elixir.bootlin.com/linux/v6.2.9/source/fs/btrfs/inode.c#L9007):

    stat->dev = BTRFS_I(inode)->root->anon_dev;

The dev member is overwritten with btrfs specific value.

How to adjust the output of lsfd
--------------------------------
lsfd already reads mountinfo files.

1. Get the "rawnum" and mount point

   The device numbers in a mountinfo file are raw; btrfs is not
   considered. Let's call the number "rawnum" here. When reading the
   mountinfo file, lsfd can know the mount points of btrfs.

       grep btrfs /proc/self/mountinfo
       72 1 0:35 /root / rw,relatime shared:1 - btrfs

2. Get the cooked num

   By calling "stat" system call for the mount point getting in the
   step 1, lsdf can know the device number the btrfs customizes with
   its getattr method. Let's call the device number "cookednum".

3. Make a table mapping "rawnum" to "cookednum".

4. Look up the table when printing inodes.

Signed-off-by: Masatake YAMATO <[email protected]>
masatake added a commit to masatake/util-linux that referenced this pull request Feb 24, 2024
When filling SOURCE column, lsfd decodes the name of the device where the
file object is. If the file object is sourced from a file system, lsfd
fills the column with the file system's name.

As util-linux#2349 and util-linux#2308, if the file system is btrfs, lsfd couldn't decode
the name correctly. This change and its preceding changes fix this bug.

"devnum offset" causes the trouble.  On btrfs, the device number
reported by stat syscall and proc fs are different.
For the details of "devnum offset", see "Mechanism behind the
devnum offset". About the way to fix it, see "How to adjust the output of lsfd".

Without this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG   0:38     0 589767 /usr/bin/zsh

With this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG  btrfs     0 589767 /usr/bin/zsh

Mechanisum behind the devnum offset
-----------------------------------
Both stat command and the inotify field in fdinfo refer to an inode.

filename_lookup(https://elixir.bootlin.com/linux/v6.2.9/source/fs/namei.c#L2495)
is the function getting the inode for a given file
name. filename_lookup returns a struct path.  Via path->detnry->inode,
the caller of filename_lookup can get the inode.

stat command calls statx system call. statx calls filename_lookup
eventually.

inotify_add_watch system call takes a file name. The inotify_add_watch
calls the filename_lookup eventually for getting the inode for the
file name.  The inode number that inotify_add_watch gets via
filename_lookup is printed in the inotify field in fdinfo.

The device number, the subject of this issue, can be obtained via
path->detnry->inode->i_sb->s_dev. Both the stat command and the
inotify field in fdinfo use the filename_lookup for getting path. If
they use the same function, why don't the device numbers match? I
monitored the device numbers obtained via
path->detnry->inode->i_sb->s_dev by inserting a systemtap probe to
filename_lookup. I saw the numbers matched.

However, the number monitored via systemtap did not match the number
printed by the stat command.  statx system call doesn't use
path->detnry->inode->i_sb->s_dev , the value obtained via
filename_lookup, directly.  statx calls vfs_statx. vfs_statx calls
vfs_getattr after calling the filename_lookup for filling struct
kstat. vfs_getattr calls inode->i_op->getattr, a file system specific
method for filling struct kstat if it is available.  btrfs has an
implementation for the method,
btrfs_getattr(https://elixir.bootlin.com/linux/v6.2.9/source/fs/btrfs/inode.c#L9007):

    stat->dev = BTRFS_I(inode)->root->anon_dev;

The dev member is overwritten with btrfs specific value.

How to adjust the output of lsfd
--------------------------------
lsfd already reads mountinfo files.

1. Get the "rawnum" and mount point

   The device numbers in a mountinfo file are raw; btrfs is not
   considered. Let's call the number "rawnum" here. When reading the
   mountinfo file, lsfd can know the mount points of btrfs.

       grep btrfs /proc/self/mountinfo
       72 1 0:35 /root / rw,relatime shared:1 - btrfs

2. Get the cooked num

   By calling "stat" system call for the mount point getting in the
   step 1, lsdf can know the device number the btrfs customizes with
   its getattr method. Let's call the device number "cookednum".

3. Make a table mapping "rawnum" to "cookednum".

4. Look up the table when printing inodes.

Signed-off-by: Masatake YAMATO <[email protected]>
masatake added a commit to masatake/util-linux that referenced this pull request Feb 24, 2024
When filling SOURCE column, lsfd decodes the name of the device where the
file object is. If the file object is sourced from a file system, lsfd
fills the column with the file system's name.

As util-linux#2349 and util-linux#2308, if the file system is btrfs, lsfd couldn't decode
the name correctly. This change and its preceding changes fix this bug.

"devnum offset" causes the trouble.  On btrfs, the device number
reported by stat syscall and proc fs are different.
For the details of "devnum offset", see "Mechanism behind the
devnum offset". About the way to fix it, see "How to adjust the output of lsfd".

Without this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG   0:38     0 589767 /usr/bin/zsh

With this change:

    $ ./lsfd -Q '(ASSOC == "exe")' -p $$
    COMMAND   PID   USER ASSOC  XMODE TYPE SOURCE MNTID  INODE NAME
    zsh     19318 yamato   exe ------  REG  btrfs     0 589767 /usr/bin/zsh

Mechanisum behind the devnum offset
-----------------------------------
Both stat command and the inotify field in fdinfo refer to an inode.

filename_lookup(https://elixir.bootlin.com/linux/v6.2.9/source/fs/namei.c#L2495)
is the function getting the inode for a given file
name. filename_lookup returns a struct path.  Via path->detnry->inode,
the caller of filename_lookup can get the inode.

stat command calls statx system call. statx calls filename_lookup
eventually.

inotify_add_watch system call takes a file name. The inotify_add_watch
calls the filename_lookup eventually for getting the inode for the
file name.  The inode number that inotify_add_watch gets via
filename_lookup is printed in the inotify field in fdinfo.

The device number, the subject of this issue, can be obtained via
path->detnry->inode->i_sb->s_dev. Both the stat command and the
inotify field in fdinfo use the filename_lookup for getting path. If
they use the same function, why don't the device numbers match? I
monitored the device numbers obtained via
path->detnry->inode->i_sb->s_dev by inserting a systemtap probe to
filename_lookup. I saw the numbers matched.

However, the number monitored via systemtap did not match the number
printed by the stat command.  statx system call doesn't use
path->detnry->inode->i_sb->s_dev , the value obtained via
filename_lookup, directly.  statx calls vfs_statx. vfs_statx calls
vfs_getattr after calling the filename_lookup for filling struct
kstat. vfs_getattr calls inode->i_op->getattr, a file system specific
method for filling struct kstat if it is available.  btrfs has an
implementation for the method,
btrfs_getattr(https://elixir.bootlin.com/linux/v6.2.9/source/fs/btrfs/inode.c#L9007):

    stat->dev = BTRFS_I(inode)->root->anon_dev;

The dev member is overwritten with btrfs specific value.

How to adjust the output of lsfd
--------------------------------
lsfd already reads mountinfo files.

1. Get the "rawnum" and mount point

   The device numbers in a mountinfo file are raw; btrfs is not
   considered. Let's call the number "rawnum" here. When reading the
   mountinfo file, lsfd can know the mount points of btrfs.

       grep btrfs /proc/self/mountinfo
       72 1 0:35 /root / rw,relatime shared:1 - btrfs

2. Get the cooked num

   By calling "stat" system call for the mount point getting in the
   step 1, lsdf can know the device number the btrfs customizes with
   its getattr method. Let's call the device number "cookednum".

3. Make a table mapping "rawnum" to "cookednum".

4. Look up the table when printing inodes.

Signed-off-by: Masatake YAMATO <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants