From: Masatake YAMATO Date: Mon, 4 Apr 2022 12:05:31 +0000 (+0900) Subject: lsfd: use /proc/$PID/map_files as the fallback information source X-Git-Tag: v2.39-rc1~726^2 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=e395a0388718b290662d87f32ebd5e2715aab3e6;p=thirdparty%2Futil-linux.git lsfd: use /proc/$PID/map_files as the fallback information source 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 --- diff --git a/misc-utils/lsfd.c b/misc-utils/lsfd.c index 49c3f4b1f2..e34f863fd2 100644 --- a/misc-utils/lsfd.c +++ b/misc-utils/lsfd.c @@ -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); }