]> git.ipfire.org Git - thirdparty/util-linux.git/commitdiff
lsfd: use /proc/$PID/map_files as the fallback information source
authorMasatake YAMATO <yamato@redhat.com>
Mon, 4 Apr 2022 12:05:31 +0000 (21:05 +0900)
committerMasatake YAMATO <yamato@redhat.com>
Sat, 9 Apr 2022 11:33:46 +0000 (20:33 +0900)
The original code scanning /proc/$PID/maps assumed two things; an entry
of the file contains a path started from '/' if a file is mapped, and
stat(2) works well with the path in the entry.

A mmap'ed socket breaks the former assumption.
tcpdump/lipcap does mmap an AF_PACKET socket to its address space!

A file is deleted after mapping breaks the latter assumption.

With this change, lsfd utilizes dentries under /proc/$PID/map_files
for gathering information of the mapped file in the case that the
assumptions are broken.

Private anonymous mappings are skipped to avoid wasting CPU time.

Example output:

  # ./lsfd -p 1541379 -Q '(ASSOC == "shm")'
  COMMAND     PID    USER ASSOC MODE TYPE SOURCE MNTID     INODE NAME
  tcpdump 1541379 tcpdump   shm  rw- SOCK sockfs     0 114535087 PACKET:[114535087]

  # ./lsfd -Q '(ASSOC == "shm") and DELETED' | head -2
  COMMAND             PID            USER ASSOC MODE TYPE SOURCE MNTID    INODE NAME
  systemd-resolve     870 systemd-resolve   shm  r--  REG   dm-0     0  1180413 /var/lib/sss/mc/passwd (deleted)

NOTE: CAP_SYS_ADMIN capability is required to access map_files.
See proc(5) for more details.

Signed-off-by: Masatake YAMATO <yamato@redhat.com>
misc-utils/lsfd.c

index 49c3f4b1f2de41a2d058b40cd15c59d4999ddaed..e34f863fd2ae40c8e2bc5b76cdcc80427d667adb 100644 (file)
@@ -590,7 +590,7 @@ static void collect_fd_files(struct path_cxt *pc, struct proc *proc)
        }
 }
 
-static void parse_maps_line(char *buf, struct proc *proc)
+static void parse_maps_line(struct path_cxt *pc, char *buf, struct proc *proc)
 {
        uint64_t start, end, offset, ino;
        unsigned long major, minor;
@@ -600,12 +600,6 @@ static void parse_maps_line(char *buf, struct proc *proc)
        char *path, modestr[5];
        dev_t devno;
 
-       /* ignore non-path entries */
-       path = strchr(buf, '/');
-       if (!path)
-               return;
-       rtrim_whitespace((unsigned char *) path);
-
        /* read rest of the map */
        if (sscanf(buf, "%"SCNx64               /* start */
                        "-%"SCNx64              /* end */
@@ -618,6 +612,10 @@ static void parse_maps_line(char *buf, struct proc *proc)
                        &major, &minor, &ino) != 7)
                return;
 
+       /* Skip private anonymous mappings. */
+       if (major == 0 && minor == 0 && ino == 0)
+               return;
+
        devno = makedev(major, minor);
 
        if (modestr[3] == 's')
@@ -631,14 +629,34 @@ static void parse_maps_line(char *buf, struct proc *proc)
        if (prev && prev->stat.st_dev == devno && prev->stat.st_ino == ino) {
                f = copy_file(prev);
                f->association = -assoc;
-       } else {
+       } else if ((path = strchr(buf, '/'))) {
+               rtrim_whitespace((unsigned char *) path);
                if (stat(path, &sb) < 0)
-                       return;
+                       /* If a file is mapped but deleted from the file system,
+                        * "stat by the file name" may not work. In that case,
+                        */
+                       goto try_map_files;
                f = new_file(proc, stat2class(&sb));
                if (!f)
                        return;
 
                file_set_path(f, &sb, path, -assoc);
+       } else {
+               /* As used in tcpdump, AF_PACKET socket can be mmap'ed. */
+               char map_file[sizeof("map_files/000000000000-ffffffffffff")];
+               char sym[PATH_MAX] = { '\0' };
+
+       try_map_files:
+               snprintf(map_file, sizeof(map_file), "map_files/%"SCNx64"-%"SCNx64, start, end);
+               if (ul_path_stat(pc, &sb, 0, map_file) < 0)
+                       return;
+               if (ul_path_readlink(pc, sym, sizeof(sym), map_file) < 0)
+                       return;
+               f = new_file(proc, stat2class(&sb));
+               if (!f)
+                       return;
+
+               file_set_path(f, &sb, sym, -assoc);
        }
 
        if (modestr[0] == 'r')
@@ -665,7 +683,7 @@ static void collect_mem_files(struct path_cxt *pc, struct proc *proc)
                return;
 
        while (fgets(buf, sizeof(buf), fp))
-               parse_maps_line(buf, proc);
+               parse_maps_line(pc, buf, proc);
 
        fclose(fp);
 }