crash/filesys.c
Lianbo Jiang 538b9ed456 Fix "fuser" command to properly deal with an invalid argument
The man page of the "fuser" command suggests that the argument can be a
full pathname or inode address. However, the "fuser" command accepts an
invalid argument and prints a bogus result as below:

  crash> fuser x
   PID         TASK        COMM             USAGE
  100507  ffff9914431f4c80  "packagekitd"    fd
  100508  ffff991574e59980  "gmain"          fd
  100509  ffff9914431f3300  "gdbus"          fd
  102020  ffff991574400000  "sshd"           fd
  102043  ffff991441d19980  "sshd"           fd

The current fuser command has no checking mechanism to determine if an
argument is valid or not. Let's add it to handle such cases.

With the patch:
  crash> fuser x
  fuser: invalid argument: x

In addition, also add a note that fuser does not expect an argument other
than an inode address and full pathname, and if others are specified, the
output can be an unexpected result.

Reported-by: Buland Kumar Singh <bsingh@redhat.com>
Signed-off-by: Lianbo Jiang <lijiang@redhat.com>
Signed-off-by: Kazuhito Hagio <k-hagio-ab@nec.com>
2023-04-10 13:32:24 +09:00

4433 lines
114 KiB
C

/* filesys.c - core analysis suite
*
* Copyright (C) 1999, 2000, 2001, 2002 Mission Critical Linux, Inc.
* Copyright (C) 2002-2019 David Anderson
* Copyright (C) 2002-2019 Red Hat, Inc. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*/
#include "defs.h"
#include <sys/sysmacros.h>
#include <linux/major.h>
#include <regex.h>
#include <sys/utsname.h>
static void show_mounts(ulong, int, struct task_context *);
static int find_booted_kernel(void);
static int find_booted_system_map(void);
static int verify_utsname(char *);
static char **build_searchdirs(int, int *);
static int build_kernel_directory(char *);
static int redhat_kernel_directory_v1(char *);
static int redhat_kernel_directory_v2(char *);
static int redhat_debug_directory(char *);
static ulong *create_dentry_array(ulong, int *);
static ulong *create_dentry_array_percpu(ulong, int *);
static void show_fuser(char *, char *);
static int mount_point(char *);
static int open_file_reference(struct reference *);
static void memory_source_init(void);
static int get_pathname_component(ulong, ulong, int, char *, char *);
char *inode_type(char *, char *);
static void match_proc_version(void);
static void get_live_memory_source(void);
static int memory_driver_module_loaded(int *);
static int insmod_memory_driver_module(void);
static int get_memory_driver_dev(dev_t *);
static int memory_driver_init(void);
static int create_memory_device(dev_t);
static int match_file_string(char *, char *, char *);
static ulong get_root_vfsmount(char *);
static void check_live_arch_mismatch(void);
static long get_inode_nrpages(ulong);
static void dump_inode_page_cache_info(ulong);
#define DENTRY_CACHE (20)
#define INODE_CACHE (20)
#define FILE_CACHE (20)
static struct filesys_table {
char *dentry_cache;
ulong cached_dentry[DENTRY_CACHE];
ulong cached_dentry_hits[DENTRY_CACHE];
int dentry_cache_index;
ulong dentry_cache_fills;
char *inode_cache;
ulong cached_inode[INODE_CACHE];
ulong cached_inode_hits[INODE_CACHE];
int inode_cache_index;
ulong inode_cache_fills;
char *file_cache;
ulong cached_file[FILE_CACHE];
ulong cached_file_hits[FILE_CACHE];
int file_cache_index;
ulong file_cache_fills;
} filesys_table = { 0 };
static struct filesys_table *ft = &filesys_table;
/*
* Open the namelist, dumpfile and output devices.
*/
void
fd_init(void)
{
pc->nfd = pc->kfd = pc->mfd = pc->dfd = -1;
if ((pc->nullfp = fopen("/dev/null", "w+")) == NULL)
error(INFO, "cannot open /dev/null (for extraneous output)");
if (REMOTE())
remote_fd_init();
else {
if (pc->namelist && pc->namelist_debug && pc->system_map) {
error(INFO,
"too many namelist options:\n %s\n %s\n %s\n",
pc->namelist, pc->namelist_debug,
pc->system_map);
program_usage(SHORT_FORM);
}
if (pc->namelist) {
if (XEN_HYPER_MODE() && !pc->dumpfile)
error(FATAL,
"Xen hypervisor mode requires a dumpfile\n");
if (!pc->dumpfile && !get_proc_version())
error(INFO, "/proc/version: %s\n",
strerror(errno));
} else {
if (pc->dumpfile) {
error(INFO, "namelist argument required\n");
program_usage(SHORT_FORM);
}
if (!pc->dumpfile)
check_live_arch_mismatch();
if (!find_booted_kernel())
program_usage(SHORT_FORM);
}
if (!pc->dumpfile) {
pc->flags |= LIVE_SYSTEM;
get_live_memory_source();
}
if ((pc->nfd = open(pc->namelist, O_RDONLY)) < 0)
error(FATAL, "%s: %s\n", pc->namelist, strerror(errno));
else {
close(pc->nfd);
pc->nfd = -1;
}
if (LOCAL_ACTIVE() && !(pc->namelist_debug || pc->system_map)) {
memory_source_init();
match_proc_version();
}
}
memory_source_init();
if (ACTIVE())
proc_kcore_init(fp, UNUSED);
if (CRASHDEBUG(1)) {
fprintf(fp, "readmem: %s() ", readmem_function_name());
if (ACTIVE()) {
fprintf(fp, "-> %s ", pc->live_memsrc);
if (pc->flags & MEMMOD)
fprintf(fp, "(module)");
else if (pc->flags & CRASHBUILTIN)
fprintf(fp, "(built-in)");
}
fprintf(fp, "\n");
}
}
/*
* Do whatever's necessary to handle the memory source.
*/
static void
memory_source_init(void)
{
if (REMOTE() && !(pc->flags2 & MEMSRC_LOCAL))
return;
if (pc->flags & KERNEL_DEBUG_QUERY)
return;
if (LOCAL_ACTIVE()) {
if (pc->mfd != -1) /* already been here */
return;
if (!STREQ(pc->live_memsrc, "/dev/mem") &&
STREQ(pc->live_memsrc, pc->memory_device)) {
if (memory_driver_init())
return;
error(INFO, "cannot initialize crash memory driver\n");
error(INFO, "using /dev/mem\n\n");
pc->flags &= ~MEMMOD;
pc->flags |= DEVMEM;
pc->readmem = read_dev_mem;
pc->writemem = write_dev_mem;
pc->live_memsrc = "/dev/mem";
}
if (STREQ(pc->live_memsrc, "/dev/mem")) {
if ((pc->mfd = open("/dev/mem", O_RDWR)) < 0) {
if ((pc->mfd = open("/dev/mem", O_RDONLY)) < 0)
error(FATAL, "/dev/mem: %s\n",
strerror(errno));
} else
pc->flags |= MFD_RDWR;
} else if (STREQ(pc->live_memsrc, "/proc/kcore")) {
if ((pc->mfd = open("/proc/kcore", O_RDONLY)) < 0)
error(FATAL, "/proc/kcore: %s\n",
strerror(errno));
if (!proc_kcore_init(fp, pc->mfd))
error(FATAL,
"/proc/kcore: initialization failed\n");
} else {
if (!pc->live_memsrc)
error(FATAL, "cannot find a live memory device\n");
else
error(FATAL, "unknown memory device: %s\n",
pc->live_memsrc);
}
return;
}
if (pc->dumpfile) {
if (!file_exists(pc->dumpfile, NULL))
error(FATAL, "%s: %s\n", pc->dumpfile,
strerror(ENOENT));
if (!(pc->flags & DUMPFILE_TYPES))
error(FATAL, "%s: dump format not supported!\n",
pc->dumpfile);
if (pc->flags & NETDUMP) {
if (!netdump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & KDUMP) {
if (!kdump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & XENDUMP) {
if (!xendump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & KVMDUMP) {
if (!kvmdump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & DISKDUMP) {
if (!diskdump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & LKCD) {
if ((pc->dfd = open(pc->dumpfile, O_RDONLY)) < 0)
error(FATAL, "%s: %s\n", pc->dumpfile,
strerror(errno));
if (!lkcd_dump_init(fp, pc->dfd, pc->dumpfile))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & S390D) {
if (!s390_dump_init(pc->dumpfile))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else if (pc->flags & VMWARE_VMSS) {
if (pc->flags2 & VMWARE_VMSS_GUESTDUMP) {
if (!vmware_guestdump_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
} else {
if (!vmware_vmss_init(pc->dumpfile, fp))
error(FATAL, "%s: initialization failed\n",
pc->dumpfile);
}
}
}
}
/*
* If only a namelist argument is entered for a live system, and the
* version string doesn't match /proc/version, try to avert a failure
* by assigning it to a matching System.map.
*/
static void
match_proc_version(void)
{
char buffer[BUFSIZE], *p1, *p2;
if (pc->flags & KERNEL_DEBUG_QUERY)
return;
if (!strlen(kt->proc_version))
return;
if (match_file_string(pc->namelist, kt->proc_version, buffer)) {
if (CRASHDEBUG(1)) {
fprintf(fp, "/proc/version:\n%s\n", kt->proc_version);
fprintf(fp, "%s:\n%s", pc->namelist, buffer);
}
return;
}
error(WARNING, "%s%sand /proc/version do not match!\n\n",
pc->namelist,
strlen(pc->namelist) > 39 ? "\n " : " ");
/*
* find_booted_system_map() requires VTOP(), which used to be a
* hardwired masking of the kernel address. But some architectures
* may not know what their physical base address is at this point,
* and others may have different machdep->kvbase values, so for all
* but the 0-based kernel virtual address architectures, bail out
* here with a relevant error message.
*/
if (!machine_type("S390") && !machine_type("S390X")) {
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = strstr(p1, " ");
*p2 = NULLCHAR;
error(WARNING, "/proc/version indicates kernel version: %s\n", p1);
error(FATAL, "please use the vmlinux file for that kernel version, or try using\n"
" the System.map for that kernel version as an additional argument.\n", p1);
clean_exit(1);
}
if (find_booted_system_map())
pc->flags |= SYSMAP;
}
#define CREATE 1
#define DESTROY 0
#define DEFAULT_SEARCHDIRS 6
#define EXTRA_SEARCHDIRS 5
static char **
build_searchdirs(int create, int *preferred)
{
int i;
int cnt, start;
DIR *dirp;
struct dirent *dp;
char dirbuf[BUFSIZE];
static char **searchdirs = { 0 };
static char *default_searchdirs[DEFAULT_SEARCHDIRS+1] = {
"/usr/src/linux/",
"/boot/",
"/boot/efi/redhat",
"/boot/efi/EFI/redhat",
"/usr/lib/debug/boot/",
"/",
NULL
};
if (!create) {
if (searchdirs) {
for (i = DEFAULT_SEARCHDIRS; searchdirs[i]; i++)
free(searchdirs[i]);
free(searchdirs);
}
return NULL;
}
if (preferred)
*preferred = 0;
/*
* Allow, at a minimum, the defaults plus an extra four directories:
*
* /lib/modules
* /usr/src/redhat/BUILD/kernel-<version>/linux
* /usr/src/redhat/BUILD/kernel-<version>/linux-<version>
* /usr/lib/debug/lib/modules
*
*/
cnt = DEFAULT_SEARCHDIRS + EXTRA_SEARCHDIRS;
if ((dirp = opendir("/usr/src"))) {
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp))
cnt++;
if ((searchdirs = calloc(cnt, sizeof(char *))) == NULL) {
error(INFO, "/usr/src/ directory list malloc: %s\n",
strerror(errno));
closedir(dirp);
return default_searchdirs;
}
for (i = 0; i < DEFAULT_SEARCHDIRS; i++)
searchdirs[i] = default_searchdirs[i];
cnt = DEFAULT_SEARCHDIRS;
rewinddir(dirp);
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if (STREQ(dp->d_name, "linux") ||
STREQ(dp->d_name, "redhat") ||
STREQ(dp->d_name, ".") ||
STREQ(dp->d_name, ".."))
continue;
sprintf(dirbuf, "/usr/src/%s", dp->d_name);
if (mount_point(dirbuf))
continue;
if (!is_directory(dirbuf))
continue;
if ((searchdirs[cnt] = (char *)
malloc(strlen(dirbuf)+2)) == NULL) {
error(INFO,
"/usr/src/ directory entry malloc: %s\n",
strerror(errno));
break;
}
sprintf(searchdirs[cnt], "%s/", dirbuf);
cnt++;
}
closedir(dirp);
searchdirs[cnt] = NULL;
} else {
if ((searchdirs = calloc(cnt, sizeof(char *))) == NULL) {
error(INFO, "search directory list malloc: %s\n",
strerror(errno));
return default_searchdirs;
}
for (i = 0; i < DEFAULT_SEARCHDIRS; i++)
searchdirs[i] = default_searchdirs[i];
cnt = DEFAULT_SEARCHDIRS;
}
if (build_kernel_directory(dirbuf)) {
if ((searchdirs[cnt] = (char *)
malloc(strlen(dirbuf)+2)) == NULL) {
error(INFO,
"/lib/modules/ directory entry malloc: %s\n",
strerror(errno));
} else {
sprintf(searchdirs[cnt], "%s/", dirbuf);
cnt++;
}
}
if (redhat_kernel_directory_v1(dirbuf)) {
if ((searchdirs[cnt] = (char *)
malloc(strlen(dirbuf)+2)) == NULL) {
error(INFO,
"/usr/src/redhat directory entry malloc: %s\n",
strerror(errno));
} else {
sprintf(searchdirs[cnt], "%s/", dirbuf);
cnt++;
}
}
if (redhat_kernel_directory_v2(dirbuf)) {
if ((searchdirs[cnt] = (char *)
malloc(strlen(dirbuf)+2)) == NULL) {
error(INFO,
"/usr/src/redhat directory entry malloc: %s\n",
strerror(errno));
} else {
sprintf(searchdirs[cnt], "%s/", dirbuf);
cnt++;
}
}
if (redhat_debug_directory(dirbuf)) {
if ((searchdirs[cnt] = (char *)
malloc(strlen(dirbuf)+2)) == NULL) {
error(INFO, "%s directory entry malloc: %s\n",
dirbuf, strerror(errno));
} else {
sprintf(searchdirs[cnt], "%s/", dirbuf);
if (preferred)
*preferred = cnt;
cnt++;
}
}
searchdirs[cnt] = NULL;
if (CRASHDEBUG(1)) {
i = start = preferred ? *preferred : 0;
do {
fprintf(fp, "searchdirs[%d]: %s\n",
i, searchdirs[i]);
if (++i == cnt) {
if (start != 0)
i = 0;
else
break;
}
} while (i != start);
}
return searchdirs;
}
static int
build_kernel_directory(char *buf)
{
char *p1, *p2;
if (!strstr(kt->proc_version, "Linux version "))
return FALSE;
BZERO(buf, BUFSIZE);
sprintf(buf, "/lib/modules/");
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = &buf[strlen(buf)];
while (*p1 != ' ')
*p2++ = *p1++;
strcat(buf, "/build");
return TRUE;
}
static int
redhat_kernel_directory_v1(char *buf)
{
char *p1, *p2;
if (!strstr(kt->proc_version, "Linux version "))
return FALSE;
BZERO(buf, BUFSIZE);
sprintf(buf, "/usr/src/redhat/BUILD/kernel-");
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = &buf[strlen(buf)];
while (((*p1 >= '0') && (*p1 <= '9')) || (*p1 == '.'))
*p2++ = *p1++;
strcat(buf, "/linux");
return TRUE;
}
static int
redhat_kernel_directory_v2(char *buf)
{
char *p1, *p2;
if (!strstr(kt->proc_version, "Linux version "))
return FALSE;
BZERO(buf, BUFSIZE);
sprintf(buf, "/usr/src/redhat/BUILD/kernel-");
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = &buf[strlen(buf)];
while (((*p1 >= '0') && (*p1 <= '9')) || (*p1 == '.'))
*p2++ = *p1++;
strcat(buf, "/linux-");
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = &buf[strlen(buf)];
while (((*p1 >= '0') && (*p1 <= '9')) || (*p1 == '.'))
*p2++ = *p1++;
return TRUE;
}
static int
redhat_debug_directory(char *buf)
{
char *p1, *p2;
if (!strstr(kt->proc_version, "Linux version "))
return FALSE;
BZERO(buf, BUFSIZE);
sprintf(buf, "%s/", pc->redhat_debug_loc);
p1 = &kt->proc_version[strlen("Linux version ")];
p2 = &buf[strlen(buf)];
while (*p1 != ' ')
*p2++ = *p1++;
return TRUE;
}
/*
* If a namelist was not entered, presume we're using the currently-running
* kernel. Read its version string from /proc/version, and then look in
* the search directories for a kernel with the same version string embedded
* in it.
*/
static int
find_booted_kernel(void)
{
char kernel[BUFSIZE];
char buffer[BUFSIZE];
char **searchdirs;
int i, preferred, wrapped;
DIR *dirp;
struct dirent *dp;
int found;
pc->flags |= FINDKERNEL;
fflush(fp);
if (!file_exists("/proc/version", NULL)) {
error(INFO,
"/proc/version: %s: cannot determine booted kernel\n",
strerror(ENOENT));
return FALSE;
}
if (!get_proc_version()) {
error(INFO, "/proc/version: %s\n", strerror(errno));
return FALSE;
}
if (CRASHDEBUG(1))
fprintf(fp, "\nfind_booted_kernel: search for [%s]\n",
kt->proc_version);
searchdirs = build_searchdirs(CREATE, &preferred);
for (i = preferred, wrapped = found = FALSE; !found; i++) {
if (!searchdirs[i]) {
if (preferred && !wrapped) {
wrapped = TRUE;
i = 0;
} else
break;
} else if (wrapped && (preferred == i))
break;
dirp = opendir(searchdirs[i]);
if (!dirp)
continue;
for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
if (dp->d_name[0] == '.')
continue;
sprintf(kernel, "%s%s", searchdirs[i], dp->d_name);
if (mount_point(kernel) ||
!file_readable(kernel) ||
!is_kernel(kernel))
continue;
if (CRASHDEBUG(1))
fprintf(fp, "find_booted_kernel: check: %s\n",
kernel);
found = match_file_string(kernel, kt->proc_version, buffer);
if (found)
break;
}
closedir(dirp);
}
mount_point(DESTROY);
build_searchdirs(DESTROY, NULL);
if (found) {
if ((pc->namelist = (char *)malloc
(strlen(kernel)+1)) == NULL)
error(FATAL, "booted kernel name malloc: %s\n",
strerror(errno));
else {
strcpy(pc->namelist, kernel);
if (CRASHDEBUG(1))
fprintf(fp, "find_booted_kernel: found: %s\n",
pc->namelist);
return TRUE;
}
}
error(INFO,
"cannot find booted kernel -- please enter namelist argument\n\n");
return FALSE;
}
/*
* Determine whether a file is a mount point, without the benefit of stat().
* This horrendous kludge is necessary to avoid uninterruptible stat() or
* fstat() calls on nfs mount-points where the remote directory is no longer
* available.
*/
static int
mount_point(char *name)
{
int i;
static int mount_points_gathered = -1;
static char **mount_points;
char *arglist[MAXARGS];
char buf[BUFSIZE];
char mntfile[BUFSIZE];
int argc, found;
FILE *mp;
/*
* The first time through, stash a list of mount points.
*/
if (mount_points_gathered < 0) {
found = mount_points_gathered = 0;
if (file_exists("/proc/mounts", NULL))
sprintf(mntfile, "/proc/mounts");
else if (file_exists("/etc/mtab", NULL))
sprintf(mntfile, "/etc/mtab");
else
return FALSE;
if ((mp = fopen(mntfile, "r")) == NULL)
return FALSE;
while (fgets(buf, BUFSIZE, mp)) {
argc = parse_line(buf, arglist);
if (argc < 2)
continue;
found++;
}
fclose(mp);
if (!(mount_points = (char **)malloc(sizeof(char *) * found)))
return FALSE;
if ((mp = fopen(mntfile, "r")) == NULL)
return FALSE;
i = 0;
while (fgets(buf, BUFSIZE, mp) &&
(mount_points_gathered < found)) {
argc = parse_line(buf, arglist);
if (argc < 2)
continue;
if ((mount_points[i] = (char *)
malloc(strlen(arglist[1])*2))) {
strcpy(mount_points[i], arglist[1]);
mount_points_gathered++, i++;
}
}
fclose(mp);
if (CRASHDEBUG(2))
for (i = 0; i < mount_points_gathered; i++)
fprintf(fp, "mount_points[%d]: %s (%lx)\n",
i, mount_points[i],
(ulong)mount_points[i]);
}
/*
* A null name string means we're done with this routine forever,
* so the malloc'd memory can be freed.
*/
if (!name) {
for (i = 0; i < mount_points_gathered; i++)
free(mount_points[i]);
free(mount_points);
return FALSE;
}
for (i = 0; i < mount_points_gathered; i++) {
if (STREQ(name, mount_points[i]))
return TRUE;
}
return FALSE;
}
/*
* If /proc/version exists, get it for verification purposes later.
*/
int
get_proc_version(void)
{
FILE *version;
if (strlen(kt->proc_version)) /* been here, done that... */
return TRUE;
if (!file_exists("/proc/version", NULL))
return FALSE;
if ((version = fopen("/proc/version", "r")) == NULL)
return FALSE;
if (fread(&kt->proc_version, sizeof(char),
BUFSIZE-1, version) <= 0) {
fclose(version);
return FALSE;
}
fclose(version);
strip_linefeeds(kt->proc_version);
return TRUE;
}
/*
* Given a non-matching kernel namelist, try to find a System.map file
* that has a system_utsname whose contents match /proc/version.
*/
static int
find_booted_system_map(void)
{
char system_map[BUFSIZE];
char **searchdirs;
int i;
DIR *dirp;
struct dirent *dp;
int found;
fflush(fp);
if (!file_exists("/proc/version", NULL)) {
error(INFO,
"/proc/version: %s: cannot determine booted System.map\n",
strerror(ENOENT));
return FALSE;
}
if (!get_proc_version()) {
error(INFO, "/proc/version: %s\n", strerror(errno));
return FALSE;
}
found = FALSE;
/*
* To avoid a search, try the obvious first.
*/
sprintf(system_map, "/boot/System.map");
if (file_readable(system_map) && verify_utsname(system_map)) {
found = TRUE;
} else {
searchdirs = build_searchdirs(CREATE, NULL);
for (i = 0; !found && searchdirs[i]; i++) {
dirp = opendir(searchdirs[i]);
if (!dirp)
continue;
for (dp = readdir(dirp); dp != NULL;
dp = readdir(dirp)) {
if (!strstr(dp->d_name, "System.map"))
continue;
sprintf(system_map, "%s%s", searchdirs[i],
dp->d_name);
if (mount_point(system_map) ||
!file_readable(system_map) ||
!is_system_map(system_map))
continue;
if (verify_utsname(system_map)) {
found = TRUE;
break;
}
}
closedir(dirp);
}
mount_point(DESTROY);
build_searchdirs(DESTROY, NULL);
}
if (found) {
if ((pc->system_map = (char *)malloc
(strlen(system_map)+1)) == NULL)
error(FATAL, "booted system map name malloc: %s\n",
strerror(errno));
strcpy(pc->system_map, system_map);
if (CRASHDEBUG(1))
fprintf(fp, "find_booted_system_map: found: %s\n",
pc->system_map);
return TRUE;
}
error(INFO,
"cannot find booted system map -- please enter namelist or system map\n\n");
return FALSE;
}
/*
* Read the system_utsname from /dev/mem, based upon the address found
* in the passed-in System.map file, and compare it to /proc/version.
*/
static int
verify_utsname(char *system_map)
{
char buffer[BUFSIZE];
ulong value;
struct new_utsname new_utsname;
if (CRASHDEBUG(1))
fprintf(fp, "verify_utsname: check: %s\n", system_map);
if (!match_file_string(system_map, "D system_utsname", buffer))
return FALSE;
if (extract_hex(buffer, &value, NULLCHAR, TRUE) &&
(READMEM(pc->mfd, &new_utsname,
sizeof(struct new_utsname), value,
VTOP(value)) > 0) &&
ascii_string(new_utsname.release) &&
ascii_string(new_utsname.version) &&
STRNEQ(new_utsname.release, "2.") &&
(strlen(new_utsname.release) > 4) &&
(strlen(new_utsname.version) > 27)) {
if (CRASHDEBUG(1)) {
fprintf(fp, "release: [%s]\n", new_utsname.release);
fprintf(fp, "version: [%s]\n", new_utsname.version);
}
if (strstr(kt->proc_version, new_utsname.release) &&
strstr(kt->proc_version, new_utsname.version)) {
return TRUE;
}
}
return FALSE;
}
/*
* Determine whether a file exists, using the caller's stat structure if
* one was passed in.
*/
int
file_exists(char *file, struct stat *sp)
{
struct stat sbuf;
if (stat(file, sp ? sp : &sbuf) == 0)
return TRUE;
return FALSE;
}
/*
* Determine whether a file exists, and if so, if it's readable.
*/
int
file_readable(char *file)
{
char tmp;
int fd;
if (!file_exists(file, NULL))
return FALSE;
if ((fd = open(file, O_RDONLY)) < 0)
return FALSE;
if (read(fd, &tmp, sizeof(tmp)) != sizeof(tmp)) {
close(fd);
return FALSE;
}
close(fd);
return TRUE;
}
/*
* Quick file checksummer.
*/
int
file_checksum(char *file, long *retsum)
{
int i;
int fd;
ssize_t cnt;
char buf[MIN_PAGE_SIZE];
long csum;
if ((fd = open(file, O_RDONLY)) < 0)
return FALSE;
csum = 0;
BZERO(buf, MIN_PAGE_SIZE);
while ((cnt = read(fd, buf, MIN_PAGE_SIZE)) > 0) {
for (i = 0; i < cnt; i++)
csum += buf[i];
BZERO(buf, MIN_PAGE_SIZE);
}
close(fd);
*retsum = csum;
return TRUE;
}
int
is_directory(char *file)
{
struct stat sbuf;
if (!file || !strlen(file))
return(FALSE);
if (stat(file, &sbuf) == -1)
return(FALSE); /* This file doesn't exist. */
return((sbuf.st_mode & S_IFMT) == S_IFDIR ? TRUE : FALSE);
}
/*
* Search a directory tree for filename, and if found, return a temporarily
* allocated buffer containing the full pathname. The "done" business is
* protection against fgets() prematurely returning NULL before the find
* command completes. (I thought this was impossible until I saw it happen...)
* When time permits, rewrite this doing the search by hand.
*/
char *
search_directory_tree(char *directory, char *file, int follow_links)
{
char command[BUFSIZE];
char buf[BUFSIZE];
char *retbuf, *start, *end, *module;
FILE *pipe;
regex_t regex;
int regex_used, done;
if (!file_exists("/usr/bin/find", NULL) ||
!file_exists("/bin/echo", NULL) ||
!is_directory(directory) ||
(*file == '('))
return NULL;
sprintf(command,
"/usr/bin/find %s %s -name %s -print; /bin/echo search done",
follow_links ? "-L" : "", directory, file);
if ((pipe = popen(command, "r")) == NULL) {
error(INFO, "%s: %s\n", command, strerror(errno));
return NULL;
}
done = FALSE;
retbuf = NULL;
regex_used = ((start = strstr(file, "[")) &&
(end = strstr(file, "]")) && (start < end) &&
(regcomp(&regex, file, 0) == 0));
while (fgets(buf, BUFSIZE-1, pipe) || !done) {
if (STREQ(buf, "search done\n")) {
done = TRUE;
break;
}
if (!retbuf && !regex_used &&
STREQ((char *)basename(strip_linefeeds(buf)), file)) {
retbuf = GETBUF(strlen(buf)+1);
strcpy(retbuf, buf);
}
if (!retbuf && regex_used) {
module = basename(strip_linefeeds(buf));
if (regexec(&regex, module, 0, NULL, 0) == 0) {
retbuf = GETBUF(strlen(buf)+1);
strcpy(retbuf, buf);
}
}
}
if (regex_used)
regfree(&regex);
pclose(pipe);
return retbuf;
}
/*
* Determine whether a file exists, and if so, if it's a tty.
*/
int
is_a_tty(char *filename)
{
int fd;
if ((fd = open(filename, O_RDONLY)) < 0)
return FALSE;
if (isatty(fd)) {
close(fd);
return TRUE;
}
close(fd);
return FALSE;
}
/*
* Open a tmpfile for command output. fp is stashed in pc->saved_fp, and
* temporarily set to the new FILE pointer. This allows a command to still
* print to the original output while the tmpfile is still open.
*/
#define OPEN_ONLY_ONCE
#ifdef OPEN_ONLY_ONCE
void
open_tmpfile(void)
{
int ret ATTRIBUTE_UNUSED;
if (pc->tmpfile)
error(FATAL, "recursive temporary file usage\n");
if (!pc->tmp_fp) {
if ((pc->tmp_fp = tmpfile()) == NULL)
error(FATAL, "cannot open temporary file\n");
}
fflush(pc->tmpfile);
ret = ftruncate(fileno(pc->tmp_fp), 0);
rewind(pc->tmp_fp);
pc->tmpfile = pc->tmp_fp;
pc->saved_fp = fp;
fp = pc->tmpfile;
}
#else
void
open_tmpfile(void)
{
if (pc->tmpfile)
error(FATAL, "recursive temporary file usage\n");
if ((pc->tmpfile = tmpfile()) == NULL) {
error(FATAL, "cannot open temporary file\n");
} else {
pc->saved_fp = fp;
fp = pc->tmpfile;
}
}
#endif
/*
* Destroy the reference to the tmpfile, and restore fp to the state
* it had when open_tmpfile() was called.
*/
#ifdef OPEN_ONLY_ONCE
void
close_tmpfile(void)
{
int ret ATTRIBUTE_UNUSED;
if (pc->tmpfile) {
fflush(pc->tmpfile);
ret = ftruncate(fileno(pc->tmpfile), 0);
rewind(pc->tmpfile);
pc->tmpfile = NULL;
fp = pc->saved_fp;
} else
error(FATAL, "trying to close an unopened temporary file\n");
}
#else
void
close_tmpfile(void)
{
if (pc->tmpfile) {
fp = pc->saved_fp;
fclose(pc->tmpfile);
pc->tmpfile = NULL;
} else
error(FATAL, "trying to close an unopened temporary file\n");
}
#endif
/*
* open_tmpfile2(), set_tmpfile2() and close_tmpfile2() do not use a
* permanent tmpfile, and do NOT modify the global fp pointer or pc->saved_fp.
* That being the case, all wrapped functions must be aware of it, or the
* global fp pointer has to explicitly manipulated by the calling function.
* The secondary tmpfile should only be used by common functions that might
* be called by a higher-level function using the primary permanent tmpfile,
* or alternatively a caller may pass in a FILE pointer to set_tmpfile2().
*/
void
open_tmpfile2(void)
{
if (pc->tmpfile2)
error(FATAL, "recursive secondary temporary file usage\n");
if ((pc->tmpfile2 = tmpfile()) == NULL)
error(FATAL, "cannot open secondary temporary file\n");
rewind(pc->tmpfile2);
}
void
close_tmpfile2(void)
{
if (pc->tmpfile2) {
fflush(pc->tmpfile2);
fclose(pc->tmpfile2);
pc->tmpfile2 = NULL;
}
}
void
set_tmpfile2(FILE *fptr)
{
if (pc->tmpfile2)
error(FATAL, "secondary temporary file already in use\n");
pc->tmpfile2 = fptr;
}
#define MOUNT_PRINT_INODES 0x1
#define MOUNT_PRINT_FILES 0x2
/*
* Display basic information about the currently mounted filesystems.
* The -f option lists the open files for the filesystem(s).
* The -i option dumps the dirty inodes of the filesystem(s).
* If an inode address, mount, vfsmount, superblock, device name or
* directory name is also entered, just show the data for the
* filesystem indicated by the argument.
*/
static char mount_hdr[BUFSIZE] = { 0 };
void
cmd_mount(void)
{
int i;
int c, found;
struct task_context *tc, *namespace_context;
ulong value1, value2;
char *spec_string;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char *arglist[MAXARGS*2];
ulong vfsmount = 0;
int flags = 0;
int save_next;
ulong pid;
/* find a context */
pid = 1;
while ((namespace_context = pid_to_context(pid)) == NULL)
pid++;
while ((c = getopt(argcnt, args, "ifn:")) != EOF) {
switch(c)
{
case 'i':
if (INVALID_MEMBER(super_block_s_dirty)) {
error(INFO,
"the super_block.s_dirty linked list does "
"not exist in this kernel\n");
option_not_supported(c);
}
flags |= MOUNT_PRINT_INODES;
break;
case 'f':
flags |= MOUNT_PRINT_FILES;
break;
case 'n':
switch (str_to_context(optarg, &value1, &tc)) {
case STR_PID:
case STR_TASK:
namespace_context = tc;
break;
case STR_INVALID:
error(FATAL, "invalid task or pid value: %s\n",
optarg);
break;
}
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (args[optind] == 0) {
show_mounts(0, flags, namespace_context);
return;
}
/*
* Dump everything into a tmpfile, and then walk
* through it for each search argument entered.
*/
open_tmpfile();
show_mounts(0, MOUNT_PRINT_FILES |
(VALID_MEMBER(super_block_s_dirty) ? MOUNT_PRINT_INODES : 0),
namespace_context);
pc->curcmd_flags &= ~HEADER_PRINTED;
do {
spec_string = args[optind];
if (STRNEQ(spec_string, "0x") &&
hexadecimal(spec_string, 0))
shift_string_left(spec_string, 2);
found = FALSE;
rewind(pc->tmpfile);
save_next = 0;
while (fgets(buf1, BUFSIZE, pc->tmpfile)) {
if (STRNEQ(buf1, mount_hdr)) {
save_next = TRUE;
continue;
}
if (save_next) {
strcpy(buf2, buf1);
save_next = FALSE;
}
if (!(c = parse_line(buf1, arglist)))
continue;
for (i = 0; i < c; i++) {
if (PATHEQ(arglist[i], spec_string))
found = TRUE;
/*
* Check for a vfsmount address
* embedded in a struct mount.
*/
if ((i == 0) && (c == 5) &&
VALID_MEMBER(mount_mnt) &&
hexadecimal(spec_string, 0) &&
hexadecimal(arglist[i], 0)) {
value1 = htol(spec_string,
FAULT_ON_ERROR, NULL);
value2 = htol(arglist[i],
FAULT_ON_ERROR, NULL) +
OFFSET(mount_mnt);
if (value1 == value2)
found = TRUE;
}
}
if (found) {
fp = pc->saved_fp;
if (flags) {
sscanf(buf2,"%lx", &vfsmount);
show_mounts(vfsmount, flags,
namespace_context);
} else {
if (!(pc->curcmd_flags & HEADER_PRINTED)) {
fprintf(fp, "%s", mount_hdr);
pc->curcmd_flags |= HEADER_PRINTED;
}
fprintf(fp, "%s", buf2);
}
found = FALSE;
fp = pc->tmpfile;
}
}
} while (args[++optind]);
close_tmpfile();
}
/*
* Do the work for cmd_mount();
*/
static void
show_mounts(ulong one_vfsmount, int flags, struct task_context *namespace_context)
{
ulong one_vfsmount_list;
long sb_s_files;
long s_dirty;
ulong devp, dirp, sbp, dirty, type, name;
struct list_data list_data, *ld;
char buf1[BUFSIZE*2];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE/2];
ulong *dentry_list, *dp, *mntlist;
ulong *vfsmnt;
char *vfsmount_buf, *super_block_buf, *mount_buf;
ulong dentry, inode, inode_sb, mnt_parent;
char *dentry_buf, *inode_buf;
int cnt, i, m, files_header_printed;
int mount_cnt;
int devlen;
char mount_files_header[BUFSIZE];
long per_cpu_s_files;
sprintf(mount_files_header, "%s%s%s%sTYPE%sPATH\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "INODE"),
space(MINSPACE),
space(MINSPACE));
dirp = dentry = mnt_parent = sb_s_files = s_dirty = 0;
if (VALID_MEMBER(super_block_s_dirty))
s_dirty = OFFSET(super_block_s_dirty);
per_cpu_s_files = MEMBER_EXISTS("file", "f_sb_list_cpu");
dentry_list = NULL;
mntlist = 0;
ld = &list_data;
if (one_vfsmount) {
one_vfsmount_list = one_vfsmount;
mount_cnt = 1;
mntlist = &one_vfsmount_list;
} else
mntlist = get_mount_list(&mount_cnt, namespace_context);
devlen = strlen("DEVNAME")+2;
if (!strlen(mount_hdr)) {
snprintf(mount_hdr, sizeof(mount_hdr), "%s %s %s %s DIRNAME\n",
mkstring(buf1, VADDR_PRLEN, CENTER,
VALID_STRUCT(mount) ? "MOUNT" : "VFSMOUNT"),
mkstring(buf2, VADDR_PRLEN, CENTER, "SUPERBLK"),
mkstring(buf3, strlen("rootfs"), LJUST, "TYPE"),
mkstring(buf4, devlen, LJUST, "DEVNAME"));
}
if (flags == 0)
fprintf(fp, "%s", mount_hdr);
sb_s_files = VALID_MEMBER(super_block_s_files) ?
OFFSET(super_block_s_files) : INVALID_OFFSET;
if ((flags & MOUNT_PRINT_FILES) && (sb_s_files == INVALID_OFFSET)) {
/*
* super_block.s_files deprecated
*/
if (!kernel_symbol_exists("inuse_filps")) {
error(INFO, "the super_block.s_files linked list does "
"not exist in this kernel\n");
option_not_supported('f');
}
/*
* No open files list in super_block (2.2).
* Use inuse_filps list instead.
*/
dentry_list = create_dentry_array(symbol_value("inuse_filps"),
&cnt);
}
if (VALID_STRUCT(mount)) {
mount_buf = GETBUF(SIZE(mount));
vfsmount_buf = mount_buf + OFFSET(mount_mnt);
} else {
mount_buf = NULL;
vfsmount_buf = GETBUF(SIZE(vfsmount));
}
super_block_buf = GETBUF(SIZE(super_block));
for (m = 0, vfsmnt = mntlist; m < mount_cnt; m++, vfsmnt++) {
if (VALID_STRUCT(mount)) {
readmem(*vfsmnt, KVADDR, mount_buf, SIZE(mount),
"mount buffer", FAULT_ON_ERROR);
devp = ULONG(mount_buf + OFFSET(mount_mnt_devname));
} else {
readmem(*vfsmnt, KVADDR, vfsmount_buf, SIZE(vfsmount),
"vfsmount buffer", FAULT_ON_ERROR);
devp = ULONG(vfsmount_buf + OFFSET(vfsmount_mnt_devname));
}
if (VALID_MEMBER(vfsmount_mnt_dirname)) {
dirp = ULONG(vfsmount_buf +
OFFSET(vfsmount_mnt_dirname));
} else {
if (VALID_STRUCT(mount)) {
mnt_parent = ULONG(mount_buf +
OFFSET(mount_mnt_parent));
dentry = ULONG(mount_buf +
OFFSET(mount_mnt_mountpoint));
} else {
mnt_parent = ULONG(vfsmount_buf +
OFFSET(vfsmount_mnt_parent));
dentry = ULONG(vfsmount_buf +
OFFSET(vfsmount_mnt_mountpoint));
}
}
sbp = ULONG(vfsmount_buf + OFFSET(vfsmount_mnt_sb));
if (!IS_KVADDR(sbp)) {
error(WARNING, "cannot get super_block from vfsmnt: 0x%lx\n", *vfsmnt);
continue;
}
if (flags)
fprintf(fp, "%s", mount_hdr);
fprintf(fp, "%s %s ",
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(*vfsmnt)),
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX,
MKSTR(sbp)));
readmem(sbp, KVADDR, super_block_buf, SIZE(super_block),
"super_block buffer", FAULT_ON_ERROR);
type = ULONG(super_block_buf + OFFSET(super_block_s_type));
readmem(type + OFFSET(file_system_type_name),
KVADDR, &name, sizeof(void *),
"file_system_type name", FAULT_ON_ERROR);
if (read_string(name, buf4, (BUFSIZE/2)-1))
sprintf(buf3, "%-6s ", buf4);
else
sprintf(buf3, "unknown ");
if (read_string(devp, buf1, BUFSIZE-1))
sprintf(buf4, "%s ",
mkstring(buf2, devlen, LJUST, buf1));
else
sprintf(buf4, "%s ",
mkstring(buf2, devlen, LJUST, "(unknown)"));
sprintf(buf1, "%s%s", buf3, buf4);
while ((strlen(buf1) > 17) && (buf1[strlen(buf1)-2] == ' '))
strip_ending_char(buf1, ' ');
fprintf(fp, "%s", buf1);
if (VALID_MEMBER(vfsmount_mnt_dirname)) {
if (read_string(dirp, buf1, BUFSIZE-1))
fprintf(fp, "%-10s\n", buf1);
else
fprintf(fp, "%-10s\n", "(unknown)");
} else {
get_pathname(dentry, buf1, BUFSIZE, 1, VALID_STRUCT(mount) ?
mnt_parent + OFFSET(mount_mnt) : mnt_parent);
fprintf(fp, "%-10s\n", buf1);
}
if (flags & MOUNT_PRINT_FILES) {
if (sb_s_files != INVALID_OFFSET) {
dentry_list = per_cpu_s_files ?
create_dentry_array_percpu(sbp+
sb_s_files, &cnt) :
create_dentry_array(sbp+sb_s_files,
&cnt);
}
files_header_printed = 0;
for (i=0, dp = dentry_list; i<cnt; i++, dp++) {
dentry_buf = fill_dentry_cache(*dp);
inode = ULONG(dentry_buf +
OFFSET(dentry_d_inode));
if (!inode)
continue;
inode_buf = fill_inode_cache(inode);
inode_sb = ULONG(inode_buf +
OFFSET(inode_i_sb));
if (inode_sb != sbp)
continue;
if (files_header_printed == 0) {
fprintf(fp, "%s\n",
mkstring(buf2, VADDR_PRLEN,
CENTER, "OPEN FILES"));
fprintf(fp, "%s", mount_files_header);
files_header_printed = 1;
}
file_dump(0, *dp, inode, 0, DUMP_DENTRY_ONLY);
}
if (files_header_printed == 0) {
fprintf(fp, "%s\nNo open files found\n",
mkstring(buf2, VADDR_PRLEN,
CENTER, "OPEN FILES"));
}
}
if (flags & MOUNT_PRINT_INODES) {
dirty = ULONG(super_block_buf + s_dirty);
if (dirty != (sbp+s_dirty)) {
BZERO(ld, sizeof(struct list_data));
ld->flags = VERBOSE;
ld->start = dirty;
ld->end = (sbp+s_dirty);
ld->header = "DIRTY INODES\n";
hq_open();
do_list(ld);
hq_close();
} else {
fprintf(fp,
"DIRTY INODES\nNo dirty inodes found\n");
}
}
if (flags && !one_vfsmount)
fprintf(fp, "\n");
}
if (!one_vfsmount)
FREEBUF(mntlist);
if (VALID_STRUCT(mount))
FREEBUF(mount_buf);
else
FREEBUF(vfsmount_buf);
FREEBUF(super_block_buf);
}
/*
* Allocate and fill a list of the currently-mounted vfsmount pointers.
*/
ulong *
get_mount_list(int *cntptr, struct task_context *namespace_context)
{
struct list_data list_data, *ld;
ulong namespace, root, nsproxy, mnt_ns;
struct task_context *tc;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->flags |= LIST_ALLOCATE;
if (symbol_exists("vfsmntlist")) {
get_symbol_data("vfsmntlist", sizeof(void *), &ld->start);
ld->end = symbol_value("vfsmntlist");
} else if (VALID_MEMBER(task_struct_nsproxy)) {
tc = namespace_context;
readmem(tc->task + OFFSET(task_struct_nsproxy), KVADDR,
&nsproxy, sizeof(void *), "task nsproxy",
FAULT_ON_ERROR);
if (!readmem(nsproxy + OFFSET(nsproxy_mnt_ns), KVADDR,
&mnt_ns, sizeof(void *), "nsproxy mnt_ns",
RETURN_ON_ERROR|QUIET))
error(FATAL, "cannot determine mount list location!\n");
if (!readmem(mnt_ns + OFFSET(mnt_namespace_root), KVADDR,
&root, sizeof(void *), "mnt_namespace root",
RETURN_ON_ERROR|QUIET))
error(FATAL, "cannot determine mount list location!\n");
ld->start = root + OFFSET_OPTION(vfsmount_mnt_list, mount_mnt_list);
ld->end = mnt_ns + OFFSET(mnt_namespace_list);
} else if (VALID_MEMBER(namespace_root)) {
tc = namespace_context;
readmem(tc->task + OFFSET(task_struct_namespace), KVADDR,
&namespace, sizeof(void *), "task namespace",
FAULT_ON_ERROR);
if (!readmem(namespace + OFFSET(namespace_root), KVADDR,
&root, sizeof(void *), "namespace root",
RETURN_ON_ERROR|QUIET))
error(FATAL, "cannot determine mount list location!\n");
if (CRASHDEBUG(1))
console("namespace: %lx => root: %lx\n",
namespace, root);
ld->start = root + OFFSET_OPTION(vfsmount_mnt_list, mount_mnt_list);
ld->end = namespace + OFFSET(namespace_list);
} else
error(FATAL, "cannot determine mount list location!\n");
if (VALID_MEMBER(vfsmount_mnt_list))
ld->list_head_offset = OFFSET(vfsmount_mnt_list);
else if (VALID_STRUCT(mount))
ld->list_head_offset = OFFSET(mount_mnt_list);
else
ld->member_offset = OFFSET(vfsmount_mnt_next);
*cntptr = do_list(ld);
return(ld->list_ptr);
}
/*
* Given a dentry, display its address, inode, super_block, pathname.
*/
static void
display_dentry_info(ulong dentry)
{
int m, found;
char *dentry_buf, *inode_buf, *vfsmount_buf, *mount_buf;
ulong inode, superblock, sb, vfs;
ulong *mntlist, *vfsmnt;
char pathname[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
int mount_cnt;
fprintf(fp, "%s%s%s%s%s%sTYPE%sPATH\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "INODE"),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "SUPERBLK"),
space(MINSPACE),
space(MINSPACE));
dentry_buf = fill_dentry_cache(dentry);
inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
pathname[0] = NULLCHAR;
if (inode) {
inode_buf = fill_inode_cache(inode);
superblock = ULONG(inode_buf + OFFSET(inode_i_sb));
} else {
inode_buf = NULL;
superblock = ULONG(dentry_buf + OFFSET(dentry_d_sb));
}
if (!superblock)
goto nopath;
if (VALID_MEMBER(file_f_vfsmnt)) {
mntlist = get_mount_list(&mount_cnt, pid_to_context(1));
if (VALID_STRUCT(mount)) {
mount_buf = GETBUF(SIZE(mount));
vfsmount_buf = mount_buf + OFFSET(mount_mnt);
} else {
mount_buf = NULL;
vfsmount_buf = GETBUF(SIZE(vfsmount));
}
for (m = found = 0, vfsmnt = mntlist;
m < mount_cnt; m++, vfsmnt++) {
if (VALID_STRUCT(mount))
readmem(*vfsmnt, KVADDR, mount_buf, SIZE(mount),
"mount buffer", FAULT_ON_ERROR);
else
readmem(*vfsmnt, KVADDR, vfsmount_buf, SIZE(vfsmount),
"vfsmount buffer", FAULT_ON_ERROR);
sb = ULONG(vfsmount_buf + OFFSET(vfsmount_mnt_sb));
if (superblock && (sb == superblock)) {
get_pathname(dentry, pathname, BUFSIZE, 1,
VALID_STRUCT(mount) ?
*vfsmnt+OFFSET(mount_mnt) : *vfsmnt);
found = TRUE;
}
}
if (!found && symbol_exists("pipe_mnt")) {
get_symbol_data("pipe_mnt", sizeof(long), &vfs);
if (VALID_STRUCT(mount))
readmem(vfs - OFFSET(mount_mnt), KVADDR, mount_buf, SIZE(mount),
"mount buffer", FAULT_ON_ERROR);
else
readmem(vfs, KVADDR, vfsmount_buf, SIZE(vfsmount),
"vfsmount buffer", FAULT_ON_ERROR);
sb = ULONG(vfsmount_buf + OFFSET(vfsmount_mnt_sb));
if (superblock && (sb == superblock)) {
get_pathname(dentry, pathname, BUFSIZE, 1, vfs);
found = TRUE;
}
}
if (!found && symbol_exists("sock_mnt")) {
get_symbol_data("sock_mnt", sizeof(long), &vfs);
if (VALID_STRUCT(mount))
readmem(vfs - OFFSET(mount_mnt), KVADDR, mount_buf, SIZE(mount),
"mount buffer", FAULT_ON_ERROR);
else
readmem(vfs, KVADDR, vfsmount_buf, SIZE(vfsmount),
"vfsmount buffer", FAULT_ON_ERROR);
sb = ULONG(vfsmount_buf + OFFSET(vfsmount_mnt_sb));
if (superblock && (sb == superblock)) {
get_pathname(dentry, pathname, BUFSIZE, 1, vfs);
found = TRUE;
}
}
} else {
mntlist = 0;
get_pathname(dentry, pathname, BUFSIZE, 1, 0);
}
if (mntlist) {
FREEBUF(mntlist);
if (VALID_STRUCT(mount))
FREEBUF(mount_buf);
else
FREEBUF(vfsmount_buf);
}
nopath:
fprintf(fp, "%s%s%s%s%s%s%s%s%s\n",
mkstring(buf1, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(dentry)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN, RJUST|LONG_HEX, MKSTR(inode)),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN, CENTER|LONG_HEX, MKSTR(superblock)),
space(MINSPACE),
inode ? inode_type(inode_buf, pathname) : "N/A",
space(MINSPACE), pathname);
}
/*
* Return a 4-character type string of an inode, modifying a previously
* gathered pathname if necessary.
*/
char *
inode_type(char *inode_buf, char *pathname)
{
char *type;
uint32_t umode32;
uint16_t umode16;
uint mode;
ulong inode_i_op;
ulong inode_i_fop;
long i_fop_off;
mode = umode16 = umode32 = 0;
switch (SIZE(umode_t))
{
case SIZEOF_32BIT:
umode32 = UINT(inode_buf + OFFSET(inode_i_mode));
mode = umode32;
break;
case SIZEOF_16BIT:
umode16 = USHORT(inode_buf + OFFSET(inode_i_mode));
mode = (uint)umode16;
break;
}
type = "UNKN";
if (S_ISREG(mode))
type = "REG ";
if (S_ISLNK(mode))
type = "LNK ";
if (S_ISDIR(mode))
type = "DIR ";
if (S_ISCHR(mode))
type = "CHR ";
if (S_ISBLK(mode))
type = "BLK ";
if (S_ISFIFO(mode)) {
type = "FIFO";
if (symbol_exists("pipe_inode_operations")) {
inode_i_op = ULONG(inode_buf + OFFSET(inode_i_op));
if (inode_i_op ==
symbol_value("pipe_inode_operations")) {
type = "PIPE";
pathname[0] = NULLCHAR;
}
} else {
if (symbol_exists("rdwr_pipe_fops") &&
(i_fop_off = OFFSET(inode_i_fop)) > 0) {
inode_i_fop = ULONG(inode_buf + i_fop_off);
if (inode_i_fop ==
symbol_value("rdwr_pipe_fops")) {
type = "PIPE";
pathname[0] = NULLCHAR;
}
}
}
}
if (S_ISSOCK(mode)) {
type = "SOCK";
if (STREQ(pathname, "/"))
pathname[0] = NULLCHAR;
}
return type;
}
/*
* Walk an open file list and return an array of open dentries.
*/
static ulong *
create_dentry_array(ulong list_addr, int *count)
{
struct list_data list_data, *ld;
ulong *file, *files_list, *dentry_list;
ulong dentry, inode;
char *file_buf, *dentry_buf;
int cnt, f_count, i;
int dentry_cnt = 0;
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
readmem(list_addr, KVADDR, &ld->start, sizeof(void *), "file list head",
FAULT_ON_ERROR);
if (list_addr == ld->start) { /* empty list? */
*count = 0;
return NULL;
}
ld->end = list_addr;
hq_open();
cnt = do_list(ld);
if (cnt == 0) {
hq_close();
*count = 0;
return NULL;
}
files_list = (ulong *)GETBUF(cnt * sizeof(ulong));
cnt = retrieve_list(files_list, cnt);
hq_close();
hq_open();
for (i=0, file = files_list; i<cnt; i++, file++) {
file_buf = fill_file_cache(*file);
f_count = INT(file_buf + OFFSET(file_f_count));
if (!f_count)
continue;
dentry = ULONG(file_buf + OFFSET(file_f_dentry));
if (!dentry)
continue;
dentry_buf = fill_dentry_cache(dentry);
inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
if (!inode)
continue;
if (hq_enter(dentry))
dentry_cnt++;
}
if (dentry_cnt) {
dentry_list = (ulong *)GETBUF(dentry_cnt * sizeof(ulong));
*count = retrieve_list(dentry_list, dentry_cnt);
} else {
*count = 0;
dentry_list = NULL;
}
hq_close();
FREEBUF(files_list);
return dentry_list;
}
/*
* Walk each per-cpu open file list and return an array of open dentries.
*/
static ulong *
create_dentry_array_percpu(ulong percpu_list_addr, int *count)
{
int i, j, c, total;
int cpu;
ulong percpu_list_offset, list_addr;
ulong *dentry_list;
struct percpu_list {
ulong *dentry_list;
int count;
} *percpu_list;
if ((cpu = get_highest_cpu_online()) < 0)
error(FATAL, "cannot determine highest cpu online\n");
percpu_list = (struct percpu_list *)
GETBUF(sizeof(struct percpu_list) * (cpu+1));
readmem(percpu_list_addr, KVADDR, &percpu_list_offset, sizeof(void *),
"percpu file list head offset", FAULT_ON_ERROR);
for (c = total = 0; c < (cpu+1); c++) {
list_addr = percpu_list_offset + kt->__per_cpu_offset[c];
percpu_list[c].dentry_list = create_dentry_array(list_addr,
&percpu_list[c].count);
total += percpu_list[c].count;
}
if (total) {
dentry_list = (ulong *)GETBUF(total * sizeof(ulong));
for (c = i = 0; c < (cpu+1); c++) {
if (percpu_list[c].count == 0)
continue;
for (j = 0; j < percpu_list[c].count; j++)
dentry_list[i++] =
percpu_list[c].dentry_list[j];
FREEBUF(percpu_list[c].dentry_list);
}
} else
dentry_list = NULL;
FREEBUF(percpu_list);
*count = total;
return dentry_list;
}
/*
* Stash vfs structure offsets
*/
void
vfs_init(void)
{
MEMBER_OFFSET_INIT(nlm_file_f_file, "nlm_file", "f_file");
MEMBER_OFFSET_INIT(task_struct_files, "task_struct", "files");
MEMBER_OFFSET_INIT(task_struct_fs, "task_struct", "fs");
MEMBER_OFFSET_INIT(fs_struct_root, "fs_struct", "root");
MEMBER_OFFSET_INIT(fs_struct_pwd, "fs_struct", "pwd");
MEMBER_OFFSET_INIT(fs_struct_rootmnt, "fs_struct", "rootmnt");
MEMBER_OFFSET_INIT(fs_struct_pwdmnt, "fs_struct", "pwdmnt");
MEMBER_OFFSET_INIT(files_struct_open_fds_init,
"files_struct", "open_fds_init");
MEMBER_OFFSET_INIT(files_struct_fdt, "files_struct", "fdt");
if (VALID_MEMBER(files_struct_fdt)) {
MEMBER_OFFSET_INIT(fdtable_max_fds, "fdtable", "max_fds");
MEMBER_OFFSET_INIT(fdtable_max_fdset, "fdtable", "max_fdset");
MEMBER_OFFSET_INIT(fdtable_open_fds, "fdtable", "open_fds");
MEMBER_OFFSET_INIT(fdtable_fd, "fdtable", "fd");
} else {
MEMBER_OFFSET_INIT(files_struct_max_fds, "files_struct", "max_fds");
MEMBER_OFFSET_INIT(files_struct_max_fdset, "files_struct", "max_fdset");
MEMBER_OFFSET_INIT(files_struct_open_fds, "files_struct", "open_fds");
MEMBER_OFFSET_INIT(files_struct_fd, "files_struct", "fd");
}
MEMBER_OFFSET_INIT(file_f_dentry, "file", "f_dentry");
MEMBER_OFFSET_INIT(file_f_vfsmnt, "file", "f_vfsmnt");
MEMBER_OFFSET_INIT(file_f_count, "file", "f_count");
MEMBER_OFFSET_INIT(path_mnt, "path", "mnt");
MEMBER_OFFSET_INIT(path_dentry, "path", "dentry");
if (INVALID_MEMBER(file_f_dentry)) {
MEMBER_OFFSET_INIT(file_f_path, "file", "f_path");
ASSIGN_OFFSET(file_f_dentry) = OFFSET(file_f_path) + OFFSET(path_dentry);
ASSIGN_OFFSET(file_f_vfsmnt) = OFFSET(file_f_path) + OFFSET(path_mnt);
}
MEMBER_OFFSET_INIT(dentry_d_inode, "dentry", "d_inode");
MEMBER_OFFSET_INIT(dentry_d_parent, "dentry", "d_parent");
MEMBER_OFFSET_INIT(dentry_d_covers, "dentry", "d_covers");
MEMBER_OFFSET_INIT(dentry_d_name, "dentry", "d_name");
MEMBER_OFFSET_INIT(dentry_d_iname, "dentry", "d_iname");
MEMBER_OFFSET_INIT(dentry_d_sb, "dentry", "d_sb");
MEMBER_OFFSET_INIT(inode_i_mode, "inode", "i_mode");
MEMBER_OFFSET_INIT(inode_i_op, "inode", "i_op");
MEMBER_OFFSET_INIT(inode_i_sb, "inode", "i_sb");
MEMBER_OFFSET_INIT(inode_u, "inode", "u");
MEMBER_OFFSET_INIT(qstr_name, "qstr", "name");
MEMBER_OFFSET_INIT(qstr_len, "qstr", "len");
if (INVALID_MEMBER(qstr_len))
ANON_MEMBER_OFFSET_INIT(qstr_len, "qstr", "len");
MEMBER_OFFSET_INIT(vfsmount_mnt_next, "vfsmount", "mnt_next");
MEMBER_OFFSET_INIT(vfsmount_mnt_devname, "vfsmount", "mnt_devname");
if (INVALID_MEMBER(vfsmount_mnt_devname))
MEMBER_OFFSET_INIT(mount_mnt_devname, "mount", "mnt_devname");
MEMBER_OFFSET_INIT(vfsmount_mnt_dirname, "vfsmount", "mnt_dirname");
MEMBER_OFFSET_INIT(vfsmount_mnt_sb, "vfsmount", "mnt_sb");
MEMBER_OFFSET_INIT(vfsmount_mnt_list, "vfsmount", "mnt_list");
if (INVALID_MEMBER(vfsmount_mnt_devname))
MEMBER_OFFSET_INIT(mount_mnt_list, "mount", "mnt_list");
MEMBER_OFFSET_INIT(vfsmount_mnt_parent, "vfsmount", "mnt_parent");
if (INVALID_MEMBER(vfsmount_mnt_devname))
MEMBER_OFFSET_INIT(mount_mnt_parent, "mount", "mnt_parent");
MEMBER_OFFSET_INIT(vfsmount_mnt_mountpoint,
"vfsmount", "mnt_mountpoint");
if (INVALID_MEMBER(vfsmount_mnt_devname))
MEMBER_OFFSET_INIT(mount_mnt_mountpoint,
"mount", "mnt_mountpoint");
MEMBER_OFFSET_INIT(mount_mnt, "mount", "mnt");
MEMBER_OFFSET_INIT(namespace_root, "namespace", "root");
MEMBER_OFFSET_INIT(task_struct_nsproxy, "task_struct", "nsproxy");
if (VALID_MEMBER(namespace_root)) {
MEMBER_OFFSET_INIT(namespace_list, "namespace", "list");
MEMBER_OFFSET_INIT(task_struct_namespace,
"task_struct", "namespace");
} else if (VALID_MEMBER(task_struct_nsproxy)) {
MEMBER_OFFSET_INIT(nsproxy_mnt_ns, "nsproxy", "mnt_ns");
MEMBER_OFFSET_INIT(mnt_namespace_root, "mnt_namespace", "root");
MEMBER_OFFSET_INIT(mnt_namespace_list, "mnt_namespace", "list");
} else if (THIS_KERNEL_VERSION >= LINUX(2,4,20)) {
if (CRASHDEBUG(2))
fprintf(fp, "hardwiring namespace stuff\n");
ASSIGN_OFFSET(task_struct_namespace) = OFFSET(task_struct_files) +
sizeof(void *);
ASSIGN_OFFSET(namespace_root) = sizeof(void *);
ASSIGN_OFFSET(namespace_list) = sizeof(void *) * 2;
}
MEMBER_OFFSET_INIT(super_block_s_dirty, "super_block", "s_dirty");
MEMBER_OFFSET_INIT(super_block_s_type, "super_block", "s_type");
MEMBER_OFFSET_INIT(file_system_type_name, "file_system_type", "name");
MEMBER_OFFSET_INIT(super_block_s_files, "super_block", "s_files");
MEMBER_OFFSET_INIT(inode_i_flock, "inode", "i_flock");
MEMBER_OFFSET_INIT(file_lock_fl_owner, "file_lock", "fl_owner");
MEMBER_OFFSET_INIT(nlm_host_h_exportent, "nlm_host", "h_exportent");
MEMBER_OFFSET_INIT(svc_client_cl_ident, "svc_client", "cl_ident");
MEMBER_OFFSET_INIT(inode_i_fop, "inode","i_fop");
STRUCT_SIZE_INIT(umode_t, "umode_t");
STRUCT_SIZE_INIT(dentry, "dentry");
STRUCT_SIZE_INIT(files_struct, "files_struct");
if (VALID_MEMBER(files_struct_fdt))
STRUCT_SIZE_INIT(fdtable, "fdtable");
STRUCT_SIZE_INIT(file, "file");
STRUCT_SIZE_INIT(inode, "inode");
STRUCT_SIZE_INIT(mount, "mount");
STRUCT_SIZE_INIT(vfsmount, "vfsmount");
STRUCT_SIZE_INIT(fs_struct, "fs_struct");
STRUCT_SIZE_INIT(super_block, "super_block");
if (!(ft->file_cache = (char *)malloc(SIZE(file)*FILE_CACHE)))
error(FATAL, "cannot malloc file cache\n");
if (!(ft->dentry_cache = (char *)malloc(SIZE(dentry)*DENTRY_CACHE)))
error(FATAL, "cannot malloc dentry cache\n");
if (!(ft->inode_cache = (char *)malloc(SIZE(inode)*INODE_CACHE)))
error(FATAL, "cannot malloc inode cache\n");
MEMBER_OFFSET_INIT(rb_root_rb_node,
"rb_root","rb_node");
MEMBER_OFFSET_INIT(rb_node_rb_left,
"rb_node","rb_left");
MEMBER_OFFSET_INIT(rb_node_rb_right,
"rb_node","rb_right");
}
void
dump_filesys_table(int verbose)
{
int i;
ulong fhits, dhits, ihits;
if (!verbose)
goto show_hit_rates;
for (i = 0; i < FILE_CACHE; i++)
fprintf(fp, " cached_file[%2d]: %lx (%ld)\n",
i, ft->cached_file[i],
ft->cached_file_hits[i]);
fprintf(fp, " file_cache: %lx\n", (ulong)ft->file_cache);
fprintf(fp, " file_cache_index: %d\n", ft->file_cache_index);
fprintf(fp, " file_cache_fills: %ld\n", ft->file_cache_fills);
for (i = 0; i < DENTRY_CACHE; i++)
fprintf(fp, " cached_dentry[%2d]: %lx (%ld)\n",
i, ft->cached_dentry[i],
ft->cached_dentry_hits[i]);
fprintf(fp, " dentry_cache: %lx\n", (ulong)ft->dentry_cache);
fprintf(fp, "dentry_cache_index: %d\n", ft->dentry_cache_index);
fprintf(fp, "dentry_cache_fills: %ld\n", ft->dentry_cache_fills);
for (i = 0; i < INODE_CACHE; i++)
fprintf(fp, " cached_inode[%2d]: %lx (%ld)\n",
i, ft->cached_inode[i],
ft->cached_inode_hits[i]);
fprintf(fp, " inode_cache: %lx\n", (ulong)ft->inode_cache);
fprintf(fp, " inode_cache_index: %d\n", ft->inode_cache_index);
fprintf(fp, " inode_cache_fills: %ld\n", ft->inode_cache_fills);
show_hit_rates:
if (ft->file_cache_fills) {
for (i = fhits = 0; i < FILE_CACHE; i++)
fhits += ft->cached_file_hits[i];
fprintf(fp, " file hit rate: %2ld%% (%ld of %ld)\n",
(fhits * 100)/ft->file_cache_fills,
fhits, ft->file_cache_fills);
}
if (ft->dentry_cache_fills) {
for (i = dhits = 0; i < DENTRY_CACHE; i++)
dhits += ft->cached_dentry_hits[i];
fprintf(fp, " dentry hit rate: %2ld%% (%ld of %ld)\n",
(dhits * 100)/ft->dentry_cache_fills,
dhits, ft->dentry_cache_fills);
}
if (ft->inode_cache_fills) {
for (i = ihits = 0; i < INODE_CACHE; i++)
ihits += ft->cached_inode_hits[i];
fprintf(fp, " inode hit rate: %2ld%% (%ld of %ld)\n",
(ihits * 100)/ft->inode_cache_fills,
ihits, ft->inode_cache_fills);
}
}
/*
* Get the page count for the specific mapping
*/
static long
get_inode_nrpages(ulong i_mapping)
{
char *address_space_buf;
ulong nrpages;
address_space_buf = GETBUF(SIZE(address_space));
readmem(i_mapping, KVADDR, address_space_buf,
SIZE(address_space), "address_space buffer",
FAULT_ON_ERROR);
nrpages = ULONG(address_space_buf + OFFSET(address_space_nrpages));
FREEBUF(address_space_buf);
return nrpages;
}
static void
dump_inode_page_cache_info(ulong inode)
{
char *inode_buf;
ulong i_mapping, nrpages, root_rnode, xarray, count;
struct list_pair lp;
char header[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
inode_buf = GETBUF(SIZE(inode));
readmem(inode, KVADDR, inode_buf, SIZE(inode), "inode buffer",
FAULT_ON_ERROR);
i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
nrpages = get_inode_nrpages(i_mapping);
sprintf(header, "%s NRPAGES\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"));
fprintf(fp, "%s", header);
fprintf(fp, "%s %s\n\n",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
mkstring(buf2, strlen("NRPAGES"),
RJUST|LONG_DEC,
MKSTR(nrpages)));
FREEBUF(inode_buf);
if (!nrpages)
return;
xarray = root_rnode = count = 0;
if (MEMBER_EXISTS("address_space", "i_pages") &&
(STREQ(MEMBER_TYPE_NAME("address_space", "i_pages"), "xarray") ||
(STREQ(MEMBER_TYPE_NAME("address_space", "i_pages"), "radix_tree_root") &&
MEMBER_EXISTS("radix_tree_root", "xa_head"))))
xarray = i_mapping + OFFSET(address_space_page_tree);
else
root_rnode = i_mapping + OFFSET(address_space_page_tree);
lp.index = 0;
lp.value = (void *)&dump_inode_page;
if (root_rnode)
count = do_radix_tree(root_rnode, RADIX_TREE_DUMP_CB, &lp);
else if (xarray)
count = do_xarray(xarray, XARRAY_DUMP_CB, &lp);
if (count != nrpages)
error(INFO, "%s page count: %ld nrpages: %ld\n",
root_rnode ? "radix tree" : "xarray",
count, nrpages);
return;
}
/*
* This command displays information about the open files of a context.
* For each open file descriptor the file descriptor number, a pointer
* to the file struct, pointer to the dentry struct, pointer to the inode
* struct, indication of file type and pathname are printed.
* The argument can be a task address or a PID number; if no args, the
* current context is used.
* If the flag -l is passed, any files held open in the kernel by the
* lockd server on behalf of an NFS client are displayed.
*/
void
cmd_files(void)
{
int c;
ulong value;
struct task_context *tc;
int subsequent;
struct reference reference, *ref;
char *refarg;
int open_flags = 0;
ref = NULL;
refarg = NULL;
while ((c = getopt(argcnt, args, "d:R:p:c")) != EOF) {
switch(c)
{
case 'R':
if (ref) {
error(INFO, "only one -R option allowed\n");
argerrs++;
} else {
ref = &reference;
BZERO(ref, sizeof(struct reference));
ref->str = refarg = optarg;
}
break;
case 'd':
value = htol(optarg, FAULT_ON_ERROR, NULL);
display_dentry_info(value);
return;
case 'p':
if (VALID_MEMBER(address_space_page_tree) &&
VALID_MEMBER(inode_i_mapping)) {
value = htol(optarg, FAULT_ON_ERROR, NULL);
dump_inode_page_cache_info(value);
} else
option_not_supported('p');
return;
case 'c':
if (VALID_MEMBER(address_space_nrpages) &&
VALID_MEMBER(inode_i_mapping))
open_flags |= PRINT_NRPAGES;
else
option_not_supported('c');
break;
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (!args[optind]) {
if (!ref)
print_task_header(fp, CURRENT_CONTEXT(), 0);
open_files_dump(CURRENT_TASK(), open_flags, ref);
return;
}
subsequent = 0;
while (args[optind]) {
if (ref && subsequent) {
BZERO(ref, sizeof(struct reference));
ref->str = refarg;
}
switch (str_to_context(args[optind], &value, &tc))
{
case STR_PID:
for (tc = pid_to_context(value); tc; tc = tc->tc_next) {
if (!ref)
print_task_header(fp, tc, subsequent);
open_files_dump(tc->task, open_flags, ref);
fprintf(fp, "\n");
}
break;
case STR_TASK:
if (!ref)
print_task_header(fp, tc, subsequent);
open_files_dump(tc->task, open_flags, ref);
break;
case STR_INVALID:
error(INFO, "invalid task or pid value: %s\n",
args[optind]);
break;
}
subsequent++;
optind++;
}
}
#define FILES_REF_HEXNUM (0x1)
#define FILES_REF_DECNUM (0x2)
#define FILES_REF_FOUND (0x4)
#define PRINT_FILE_REFERENCE() \
if (!root_pwd_printed) { \
print_task_header(fp, tc, 0); \
fprintf(fp, "%s", root_pwd); \
root_pwd_printed = TRUE; \
} \
if (!header_printed) { \
fprintf(fp, "%s", files_header);\
header_printed = TRUE; \
} \
fprintf(fp, "%s", buf4); \
ref->cmdflags |= FILES_REF_FOUND;
#define FILENAME_COMPONENT(P,C) \
((STREQ((P), "/") && STREQ((C), "/")) || \
(!STREQ((C), "/") && strstr((P),(C))))
/*
* open_files_dump() does the work for cmd_files().
*/
void
open_files_dump(ulong task, int flags, struct reference *ref)
{
struct task_context *tc;
ulong files_struct_addr;
ulong fdtable_addr = 0;
char *files_struct_buf, *fdtable_buf = NULL;
ulong fs_struct_addr;
char *dentry_buf, *fs_struct_buf;
char *ret ATTRIBUTE_UNUSED;
ulong root_dentry, pwd_dentry;
ulong root_inode, pwd_inode;
ulong vfsmnt;
int max_fdset = 0;
int max_fds = 0;
ulong open_fds_addr;
int open_fds_size;
ulong *open_fds;
ulong fd;
ulong file;
ulong value;
int i, j, use_path;
int header_printed = 0;
char root_pathname[BUFSIZE];
char pwd_pathname[BUFSIZE];
char files_header[BUFSIZE];
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
char buf4[BUFSIZE];
char root_pwd[BUFSIZE*4];
int root_pwd_printed = 0;
int file_dump_flags = 0;
BZERO(root_pathname, BUFSIZE);
BZERO(pwd_pathname, BUFSIZE);
files_struct_buf = GETBUF(SIZE(files_struct));
if (VALID_STRUCT(fdtable))
fdtable_buf = GETBUF(SIZE(fdtable));
fill_task_struct(task);
if (flags & PRINT_NRPAGES) {
sprintf(files_header, " FD%s%s%s%s%sNRPAGES%sTYPE%sPATH\n",
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "INODE"),
space(MINSPACE),
mkstring(buf2, MAX(VADDR_PRLEN, strlen("I_MAPPING")),
BITS32() ? (CENTER|RJUST) : (CENTER|LJUST), "I_MAPPING"),
space(MINSPACE),
space(MINSPACE),
space(MINSPACE));
} else {
sprintf(files_header, " FD%s%s%s%s%s%s%sTYPE%sPATH\n",
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "FILE"),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "DENTRY"),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN, CENTER|LJUST, "INODE"),
space(MINSPACE),
space(MINSPACE));
}
tc = task_to_context(task);
if (ref)
ref->cmdflags = 0;
fs_struct_addr = ULONG(tt->task_struct + OFFSET(task_struct_fs));
if (fs_struct_addr) {
fs_struct_buf = GETBUF(SIZE(fs_struct));
readmem(fs_struct_addr, KVADDR, fs_struct_buf, SIZE(fs_struct),
"fs_struct buffer", FAULT_ON_ERROR);
use_path = (MEMBER_TYPE("fs_struct", "root") == TYPE_CODE_STRUCT);
if (use_path)
root_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_root) +
OFFSET(path_dentry));
else
root_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_root));
if (root_dentry) {
if (VALID_MEMBER(fs_struct_rootmnt)) {
vfsmnt = ULONG(fs_struct_buf +
OFFSET(fs_struct_rootmnt));
get_pathname(root_dentry, root_pathname,
BUFSIZE, 1, vfsmnt);
} else if (use_path) {
vfsmnt = ULONG(fs_struct_buf +
OFFSET(fs_struct_root) +
OFFSET(path_mnt));
get_pathname(root_dentry, root_pathname,
BUFSIZE, 1, vfsmnt);
} else {
get_pathname(root_dentry, root_pathname,
BUFSIZE, 1, 0);
}
}
if (use_path)
pwd_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_pwd) +
OFFSET(path_dentry));
else
pwd_dentry = ULONG(fs_struct_buf + OFFSET(fs_struct_pwd));
if (pwd_dentry) {
if (VALID_MEMBER(fs_struct_pwdmnt)) {
vfsmnt = ULONG(fs_struct_buf +
OFFSET(fs_struct_pwdmnt));
get_pathname(pwd_dentry, pwd_pathname,
BUFSIZE, 1, vfsmnt);
} else if (use_path) {
vfsmnt = ULONG(fs_struct_buf +
OFFSET(fs_struct_pwd) +
OFFSET(path_mnt));
get_pathname(pwd_dentry, pwd_pathname,
BUFSIZE, 1, vfsmnt);
} else {
get_pathname(pwd_dentry, pwd_pathname,
BUFSIZE, 1, 0);
}
}
if ((flags & PRINT_INODES) && root_dentry && pwd_dentry) {
dentry_buf = fill_dentry_cache(root_dentry);
root_inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
dentry_buf = fill_dentry_cache(pwd_dentry);
pwd_inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
fprintf(fp, "ROOT: %lx %s CWD: %lx %s\n",
root_inode, root_pathname, pwd_inode,
pwd_pathname);
} else if (ref) {
snprintf(root_pwd, sizeof(root_pwd),
"ROOT: %s CWD: %s \n",
root_pathname, pwd_pathname);
if (FILENAME_COMPONENT(root_pathname, ref->str) ||
FILENAME_COMPONENT(pwd_pathname, ref->str)) {
print_task_header(fp, tc, 0);
fprintf(fp, "%s", root_pwd);
root_pwd_printed = TRUE;
ref->cmdflags |= FILES_REF_FOUND;
}
} else
fprintf(fp, "ROOT: %s CWD: %s\n",
root_pathname, pwd_pathname);
FREEBUF(fs_struct_buf);
}
files_struct_addr = ULONG(tt->task_struct + OFFSET(task_struct_files));
if (files_struct_addr) {
readmem(files_struct_addr, KVADDR, files_struct_buf,
SIZE(files_struct), "files_struct buffer",
FAULT_ON_ERROR);
if (VALID_MEMBER(files_struct_max_fdset)) {
max_fdset = INT(files_struct_buf +
OFFSET(files_struct_max_fdset));
max_fds = INT(files_struct_buf +
OFFSET(files_struct_max_fds));
}
}
if (VALID_MEMBER(files_struct_fdt)) {
fdtable_addr = ULONG(files_struct_buf + OFFSET(files_struct_fdt));
if (fdtable_addr) {
readmem(fdtable_addr, KVADDR, fdtable_buf,
SIZE(fdtable), "fdtable buffer", FAULT_ON_ERROR);
if (VALID_MEMBER(fdtable_max_fdset))
max_fdset = INT(fdtable_buf +
OFFSET(fdtable_max_fdset));
else
max_fdset = -1;
max_fds = INT(fdtable_buf +
OFFSET(fdtable_max_fds));
}
}
if ((VALID_MEMBER(files_struct_fdt) && !fdtable_addr) ||
!files_struct_addr || max_fdset == 0 || max_fds == 0) {
if (ref) {
if (ref->cmdflags & FILES_REF_FOUND)
fprintf(fp, "\n");
} else
fprintf(fp, "No open files\n");
if (fdtable_buf)
FREEBUF(fdtable_buf);
FREEBUF(files_struct_buf);
return;
}
if (ref && IS_A_NUMBER(ref->str)) {
if (hexadecimal_only(ref->str, 0)) {
ref->hexval = htol(ref->str, FAULT_ON_ERROR, NULL);
ref->cmdflags |= FILES_REF_HEXNUM;
} else {
value = dtol(ref->str, FAULT_ON_ERROR, NULL);
if (value <= MAX(max_fdset, max_fds)) {
ref->decval = value;
ref->cmdflags |= FILES_REF_DECNUM;
} else {
ref->hexval = htol(ref->str,
FAULT_ON_ERROR, NULL);
ref->cmdflags |= FILES_REF_HEXNUM;
}
}
}
if (VALID_MEMBER(fdtable_open_fds))
open_fds_addr = ULONG(fdtable_buf +
OFFSET(fdtable_open_fds));
else
open_fds_addr = ULONG(files_struct_buf +
OFFSET(files_struct_open_fds));
open_fds_size = MAX(max_fdset, max_fds) / BITS_PER_BYTE;
open_fds = (ulong *)GETBUF(open_fds_size);
if (!open_fds) {
if (fdtable_buf)
FREEBUF(fdtable_buf);
FREEBUF(files_struct_buf);
return;
}
if (open_fds_addr) {
if (VALID_MEMBER(files_struct_open_fds_init) &&
(open_fds_addr == (files_struct_addr +
OFFSET(files_struct_open_fds_init))))
BCOPY(files_struct_buf +
OFFSET(files_struct_open_fds_init),
open_fds, open_fds_size);
else
readmem(open_fds_addr, KVADDR, open_fds,
open_fds_size, "fdtable open_fds",
FAULT_ON_ERROR);
}
if (VALID_MEMBER(fdtable_fd))
fd = ULONG(fdtable_buf + OFFSET(fdtable_fd));
else
fd = ULONG(files_struct_buf + OFFSET(files_struct_fd));
if (!open_fds_addr || !fd) {
if (ref && (ref->cmdflags & FILES_REF_FOUND))
fprintf(fp, "\n");
if (fdtable_buf)
FREEBUF(fdtable_buf);
FREEBUF(files_struct_buf);
FREEBUF(open_fds);
return;
}
file_dump_flags = DUMP_FULL_NAME | DUMP_EMPTY_FILE;
if (flags & PRINT_NRPAGES)
file_dump_flags |= DUMP_FILE_NRPAGES;
j = 0;
for (;;) {
unsigned long set;
i = j * BITS_PER_LONG;
if (((max_fdset >= 0) && (i >= max_fdset)) ||
(i >= max_fds))
break;
set = open_fds[j++];
while (set) {
if (set & 1) {
readmem(fd + i*sizeof(struct file *), KVADDR,
&file, sizeof(struct file *),
"fd file", FAULT_ON_ERROR);
if (ref && file) {
open_tmpfile();
if (file_dump(file, 0, 0, i, file_dump_flags)) {
BZERO(buf4, BUFSIZE);
rewind(pc->tmpfile);
ret = fgets(buf4, BUFSIZE,
pc->tmpfile);
close_tmpfile();
ref->refp = buf4;
if (open_file_reference(ref)) {
PRINT_FILE_REFERENCE();
}
} else
close_tmpfile();
}
else if (file) {
if (!header_printed) {
fprintf(fp, "%s", files_header);
header_printed = 1;
}
file_dump(file, 0, 0, i, file_dump_flags);
}
}
i++;
set >>= 1;
}
}
if (!header_printed && !ref)
fprintf(fp, "No open files\n");
if (ref && (ref->cmdflags & FILES_REF_FOUND))
fprintf(fp, "\n");
if (fdtable_buf)
FREEBUF(fdtable_buf);
FREEBUF(files_struct_buf);
FREEBUF(open_fds);
}
/*
* Check an open file string for references.
*/
static int
open_file_reference(struct reference *ref)
{
char buf[BUFSIZE];
char *arglist[MAXARGS];
int i, fd, argcnt;
ulong vaddr;
strcpy(buf, ref->refp);
if ((argcnt = parse_line(buf, arglist)) < 5)
return FALSE;
if (ref->cmdflags & (FILES_REF_HEXNUM|FILES_REF_DECNUM)) {
fd = dtol(arglist[0], FAULT_ON_ERROR, NULL);
if (((ref->cmdflags & FILES_REF_HEXNUM) &&
(fd == ref->hexval)) ||
((ref->cmdflags & FILES_REF_DECNUM) &&
(fd == ref->decval))) {
return TRUE;
}
for (i = 1; i < 4; i++) {
if (STREQ(arglist[i], "?"))
continue;
vaddr = htol(arglist[i], FAULT_ON_ERROR, NULL);
if (vaddr == ref->hexval)
return TRUE;
}
}
if (STREQ(ref->str, arglist[4])) {
return TRUE;
}
if ((argcnt == 6) && FILENAME_COMPONENT(arglist[5], ref->str)) {
return TRUE;
}
return FALSE;
}
#ifdef DEPRECATED
/*
* nlm_files_dump() prints files held open by lockd server on behalf
* of NFS clients
*/
#define FILE_NRHASH 32
char nlm_files_header[BUFSIZE] = { 0 };
char *nlm_header = \
"Files open by lockd for client discretionary file locks:\n";
void
nlm_files_dump(void)
{
int header_printed = 0;
int i, j, cnt;
ulong nlmsvc_ops, nlm_files;
struct syment *nsp;
ulong nlm_files_array[FILE_NRHASH];
struct list_data list_data, *ld;
ulong *file, *files_list;
ulong dentry, inode, flock, host, client;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
if (!strlen(nlm_files_header)) {
sprintf(nlm_files_header,
"CLIENT %s %s%sTYPE%sPATH\n",
mkstring(buf1, VADDR_PRLEN, CENTER|LJUST, "NLM_FILE"),
mkstring(buf2, VADDR_PRLEN, CENTER|LJUST, "INODE"),
space(MINSPACE),
space(MINSPACE));
}
if (!symbol_exists("nlm_files") || !symbol_exists("nlmsvc_ops")
|| !symbol_exists("nfsd_nlm_ops")) {
goto out;
}
get_symbol_data("nlmsvc_ops", sizeof(void *), &nlmsvc_ops);
if (nlmsvc_ops != symbol_value("nfsd_nlm_ops")) {
goto out;
}
if ((nsp = next_symbol("nlm_files", NULL)) == NULL) {
error(WARNING, "cannot find next symbol after nlm_files\n");
goto out;
}
nlm_files = symbol_value("nlm_files");
if (((nsp->value - nlm_files) / sizeof(void *)) != FILE_NRHASH ) {
error(WARNING, "FILE_NRHASH has changed from %d\n",
FILE_NRHASH);
if (((nsp->value - nlm_files) / sizeof(void *)) <
FILE_NRHASH )
goto out;
}
readmem(nlm_files, KVADDR, nlm_files_array,
sizeof(ulong) * FILE_NRHASH, "nlm_files array",
FAULT_ON_ERROR);
for (i = 0; i < FILE_NRHASH; i++) {
if (nlm_files_array[i] == 0) {
continue;
}
ld = &list_data;
BZERO(ld, sizeof(struct list_data));
ld->start = nlm_files_array[i];
hq_open();
cnt = do_list(ld);
files_list = (ulong *)GETBUF(cnt * sizeof(ulong));
cnt = retrieve_list(files_list, cnt);
hq_close();
for (j=0, file = files_list; j<cnt; j++, file++) {
readmem(*file + OFFSET(nlm_file_f_file) +
OFFSET(file_f_dentry), KVADDR, &dentry,
sizeof(void *), "nlm_file dentry",
FAULT_ON_ERROR);
if (!dentry)
continue;
readmem(dentry + OFFSET(dentry_d_inode), KVADDR,
&inode, sizeof(void *), "dentry d_inode",
FAULT_ON_ERROR);
if (!inode)
continue;
readmem(inode + OFFSET(inode_i_flock), KVADDR,
&flock, sizeof(void *), "inode i_flock",
FAULT_ON_ERROR);
if (!flock)
continue;
readmem(flock + OFFSET(file_lock_fl_owner), KVADDR,
&host, sizeof(void *),
"file_lock fl_owner", FAULT_ON_ERROR);
if (!host)
continue;
readmem(host + OFFSET(nlm_host_h_exportent), KVADDR,
&client, sizeof(void *),
"nlm_host h_exportent", FAULT_ON_ERROR);
if (!client)
continue;
if (!read_string(client + OFFSET(svc_client_cl_ident),
buf1, BUFSIZE-1))
continue;
if (!header_printed) {
fprintf(fp, nlm_header);
fprintf(fp, nlm_files_header);
header_printed = 1;
}
fprintf(fp, "%-20s %8lx ", buf1, *file);
file_dump(*file, dentry, inode, 0,
DUMP_INODE_ONLY | DUMP_FULL_NAME);
}
}
out:
if (!header_printed)
fprintf(fp, "No lockd server files open for NFS clients\n");
}
#endif
/*
* file_dump() prints info for an open file descriptor
*/
int
file_dump(ulong file, ulong dentry, ulong inode, int fd, int flags)
{
ulong vfsmnt;
char *dentry_buf, *file_buf, *inode_buf, *type;
char pathname[BUFSIZE];
char *printpath;
char buf1[BUFSIZE];
char buf2[BUFSIZE];
char buf3[BUFSIZE];
ulong i_mapping = 0;
ulong nrpages = 0;
file_buf = NULL;
if (!dentry && file) {
file_buf = fill_file_cache(file);
dentry = ULONG(file_buf + OFFSET(file_f_dentry));
}
if (!dentry) {
if (flags & DUMP_EMPTY_FILE) {
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(file)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN,
CENTER|LONG_HEX|ZERO_FILL,
MKSTR(dentry)),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN,
CENTER,
"?"),
space(MINSPACE),
"? ",
space(MINSPACE),
"?");
return TRUE;
}
return FALSE;
}
if (!inode) {
dentry_buf = fill_dentry_cache(dentry);
inode = ULONG(dentry_buf + OFFSET(dentry_d_inode));
}
if (!inode) {
if (flags & DUMP_EMPTY_FILE) {
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(file)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(dentry)),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN,
CENTER|LONG_HEX|ZERO_FILL,
MKSTR(inode)),
space(MINSPACE),
"? ",
space(MINSPACE),
"?");
return TRUE;
}
return FALSE;
}
inode_buf = fill_inode_cache(inode);
if (flags & DUMP_FULL_NAME) {
if (VALID_MEMBER(file_f_vfsmnt)) {
vfsmnt = get_root_vfsmount(file_buf);
get_pathname(dentry, pathname, BUFSIZE, 1, vfsmnt);
if (STRNEQ(pathname, "/pts/") &&
STREQ(vfsmount_devname(vfsmnt, buf1, BUFSIZE),
"devpts"))
string_insert("/dev", pathname);
} else {
get_pathname(dentry, pathname, BUFSIZE, 1, 0);
}
} else
get_pathname(dentry, pathname, BUFSIZE, 0, 0);
type = inode_type(inode_buf, pathname);
if (flags & DUMP_FULL_NAME)
printpath = pathname;
else
printpath = pathname+1;
if (flags & DUMP_INODE_ONLY) {
fprintf(fp, "%s%s%s%s%s\n",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
space(MINSPACE),
type,
space(MINSPACE),
printpath);
} else {
if (flags & DUMP_DENTRY_ONLY) {
fprintf(fp, "%s%s%s%s%s%s%s\n",
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(dentry)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
space(MINSPACE),
type,
space(MINSPACE),
pathname+1);
} else if (flags & DUMP_FILE_NRPAGES) {
i_mapping = ULONG(inode_buf + OFFSET(inode_i_mapping));
nrpages = get_inode_nrpages(i_mapping);
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
space(MINSPACE),
mkstring(buf2, MAX(VADDR_PRLEN, strlen("I_MAPPING")),
CENTER|RJUST|LONG_HEX,
MKSTR(i_mapping)),
space(MINSPACE),
mkstring(buf3, strlen("NRPAGES"),
RJUST|LONG_DEC,
MKSTR(nrpages)),
space(MINSPACE),
type,
space(MINSPACE),
pathname);
} else {
fprintf(fp, "%3d%s%s%s%s%s%s%s%s%s%s\n",
fd,
space(MINSPACE),
mkstring(buf1, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(file)),
space(MINSPACE),
mkstring(buf2, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(dentry)),
space(MINSPACE),
mkstring(buf3, VADDR_PRLEN,
CENTER|RJUST|LONG_HEX,
MKSTR(inode)),
space(MINSPACE),
type,
space(MINSPACE),
pathname);
}
}
return TRUE;
}
/*
* Get the dentry associated with a file.
*/
ulong
file_to_dentry(ulong file)
{
char *file_buf;
ulong dentry;
file_buf = fill_file_cache(file);
dentry = ULONG(file_buf + OFFSET(file_f_dentry));
return dentry;
}
/*
* Get the vfsmnt associated with a file.
*/
ulong
file_to_vfsmnt(ulong file)
{
char *file_buf;
ulong vfsmnt;
file_buf = fill_file_cache(file);
vfsmnt = ULONG(file_buf + OFFSET(file_f_vfsmnt));
return vfsmnt;
}
/*
* get_pathname() fills in a pathname string for an ending dentry
* See __d_path() in the kernel for help fixing problems.
*/
void
get_pathname(ulong dentry, char *pathname, int length, int full, ulong vfsmnt)
{
char buf[BUFSIZE];
char tmpname[BUFSIZE];
ulong tmp_dentry, parent;
int d_name_len = 0;
ulong d_name_name;
ulong tmp_vfsmnt, mnt_parent;
char *dentry_buf, *vfsmnt_buf, *mnt_buf;
BZERO(buf, BUFSIZE);
BZERO(tmpname, BUFSIZE);
BZERO(pathname, length);
if (VALID_STRUCT(mount)) {
if (VALID_MEMBER(mount_mnt_mountpoint)) {
mnt_buf = GETBUF(SIZE(mount));
vfsmnt_buf = mnt_buf + OFFSET(mount_mnt);
} else {
mnt_buf = NULL;
vfsmnt_buf = NULL;
}
} else {
mnt_buf = NULL;
vfsmnt_buf = VALID_MEMBER(vfsmount_mnt_mountpoint) ?
GETBUF(SIZE(vfsmount)) : NULL;
}
parent = dentry;
tmp_vfsmnt = vfsmnt;
do {
tmp_dentry = parent;
dentry_buf = fill_dentry_cache(tmp_dentry);
d_name_len = INT(dentry_buf +
OFFSET(dentry_d_name) + OFFSET(qstr_len));
if (!d_name_len)
break;
d_name_name = ULONG(dentry_buf + OFFSET(dentry_d_name)
+ OFFSET(qstr_name));
if (!d_name_name)
break;
if (!get_pathname_component(tmp_dentry, d_name_name, d_name_len,
dentry_buf, buf))
break;
if (tmp_dentry != dentry) {
strncpy(tmpname, pathname, BUFSIZE-1);
if (strlen(tmpname) + d_name_len < BUFSIZE) {
if ((d_name_len > 1 || !STREQ(buf, "/")) &&
!STRNEQ(tmpname, "/")) {
sprintf(pathname, "%s%s%s", buf,
"/", tmpname);
} else {
sprintf(pathname,
"%s%s", buf, tmpname);
}
}
} else {
strncpy(pathname, buf, BUFSIZE);
}
parent = ULONG(dentry_buf + OFFSET(dentry_d_parent));
if (tmp_dentry == parent && full) {
if (VALID_MEMBER(vfsmount_mnt_mountpoint)) {
if (tmp_vfsmnt) {
if (strncmp(pathname, "//", 2) == 0)
shift_string_left(pathname, 1);
readmem(tmp_vfsmnt, KVADDR, vfsmnt_buf,
SIZE(vfsmount),
"vfsmount buffer",
FAULT_ON_ERROR);
parent = ULONG(vfsmnt_buf +
OFFSET(vfsmount_mnt_mountpoint));
mnt_parent = ULONG(vfsmnt_buf +
OFFSET(vfsmount_mnt_parent));
if (tmp_vfsmnt == mnt_parent)
break;
else
tmp_vfsmnt = mnt_parent;
}
} else if (VALID_STRUCT(mount)) {
if (tmp_vfsmnt) {
if (strncmp(pathname, "//", 2) == 0)
shift_string_left(pathname, 1);
readmem(tmp_vfsmnt - OFFSET(mount_mnt),
KVADDR, mnt_buf,
SIZE(mount),
"mount buffer",
FAULT_ON_ERROR);
parent = ULONG(mnt_buf +
OFFSET(mount_mnt_mountpoint));
mnt_parent = ULONG(mnt_buf +
OFFSET(mount_mnt_parent));
if ((tmp_vfsmnt - OFFSET(mount_mnt)) == mnt_parent)
break;
else
tmp_vfsmnt = mnt_parent + OFFSET(mount_mnt);
}
}
else {
parent = ULONG(dentry_buf +
OFFSET(dentry_d_covers));
}
}
} while (tmp_dentry != parent && parent);
if (mnt_buf)
FREEBUF(mnt_buf);
else if (vfsmnt_buf)
FREEBUF(vfsmnt_buf);
}
/*
* If the pathname component, which may be internal or external to the
* dentry, has string length equal to what's expected, copy it into the
* passed-in buffer, and return its length. If it doesn't match, return 0.
*/
static int
get_pathname_component(ulong dentry,
ulong d_name_name,
int d_name_len,
char *dentry_buf,
char *pathbuf)
{
int len = d_name_len; /* presume success */
if (d_name_name == (dentry + OFFSET(dentry_d_iname))) {
if (strlen(dentry_buf + OFFSET(dentry_d_iname)) == d_name_len)
strcpy(pathbuf, dentry_buf + OFFSET(dentry_d_iname));
else
len = 0;
} else if ((read_string(d_name_name, pathbuf, BUFSIZE)) != d_name_len)
len = 0;
return len;
}
/*
* Cache the passed-in file structure.
*/
char *
fill_file_cache(ulong file)
{
int i;
char *cache;
ft->file_cache_fills++;
for (i = 0; i < DENTRY_CACHE; i++) {
if (ft->cached_file[i] == file) {
ft->cached_file_hits[i]++;
cache = ft->file_cache + (SIZE(file)*i);
return(cache);
}
}
cache = ft->file_cache + (SIZE(file)*ft->file_cache_index);
readmem(file, KVADDR, cache, SIZE(file),
"fill_file_cache", FAULT_ON_ERROR);
ft->cached_file[ft->file_cache_index] = file;
ft->file_cache_index = (ft->file_cache_index+1) % DENTRY_CACHE;
return(cache);
}
/*
* If active, clear the file references.
*/
void
clear_file_cache(void)
{
int i;
if (DUMPFILE())
return;
for (i = 0; i < DENTRY_CACHE; i++) {
ft->cached_file[i] = 0;
ft->cached_file_hits[i] = 0;
}
ft->file_cache_fills = 0;
ft->file_cache_index = 0;
}
/*
* Cache the passed-in dentry structure.
*/
char *
fill_dentry_cache(ulong dentry)
{
int i;
char *cache;
ft->dentry_cache_fills++;
for (i = 0; i < DENTRY_CACHE; i++) {
if (ft->cached_dentry[i] == dentry) {
ft->cached_dentry_hits[i]++;
cache = ft->dentry_cache + (SIZE(dentry)*i);
return(cache);
}
}
cache = ft->dentry_cache + (SIZE(dentry)*ft->dentry_cache_index);
readmem(dentry, KVADDR, cache, SIZE(dentry),
"fill_dentry_cache", FAULT_ON_ERROR);
ft->cached_dentry[ft->dentry_cache_index] = dentry;
ft->dentry_cache_index = (ft->dentry_cache_index+1) % DENTRY_CACHE;
return(cache);
}
/*
* If active, clear the dentry references.
*/
void
clear_dentry_cache(void)
{
int i;
if (DUMPFILE())
return;
for (i = 0; i < DENTRY_CACHE; i++) {
ft->cached_dentry[i] = 0;
ft->cached_dentry_hits[i] = 0;
}
ft->dentry_cache_fills = 0;
ft->dentry_cache_index = 0;
}
/*
* Cache the passed-in inode structure.
*/
char *
fill_inode_cache(ulong inode)
{
int i;
char *cache;
ft->inode_cache_fills++;
for (i = 0; i < INODE_CACHE; i++) {
if (ft->cached_inode[i] == inode) {
ft->cached_inode_hits[i]++;
cache = ft->inode_cache + (SIZE(inode)*i);
return(cache);
}
}
cache = ft->inode_cache + (SIZE(inode)*ft->inode_cache_index);
readmem(inode, KVADDR, cache, SIZE(inode),
"fill_inode_cache", FAULT_ON_ERROR);
ft->cached_inode[ft->inode_cache_index] = inode;
ft->inode_cache_index = (ft->inode_cache_index+1) % INODE_CACHE;
return(cache);
}
/*
* If active, clear the inode references.
*/
void
clear_inode_cache(void)
{
int i;
if (DUMPFILE())
return;
for (i = 0; i < DENTRY_CACHE; i++) {
ft->cached_inode[i] = 0;
ft->cached_inode_hits[i] = 0;
}
ft->inode_cache_fills = 0;
ft->inode_cache_index = 0;
}
/*
* This command displays the tasks using specified files or sockets.
* Tasks will be listed that reference the file as the current working
* directory, root directory, an open file descriptor, or that mmap the
* file.
* The argument can be a full pathname without symbolic links, or inode
* address.
*/
void
cmd_fuser(void)
{
int c;
char *spec_string, *tmp;
struct foreach_data foreach_data, *fd;
char task_buf[BUFSIZE];
char buf[BUFSIZE];
char uses[20];
char fuser_header[BUFSIZE];
int doing_fds, doing_mmap, len;
int fuser_header_printed, lockd_header_printed;
ulong spec_addr;
while ((c = getopt(argcnt, args, "")) != EOF) {
switch(c)
{
default:
argerrs++;
break;
}
}
if (argerrs)
cmd_usage(pc->curcmd, SYNOPSIS);
if (!args[optind]) {
cmd_usage(pc->curcmd, SYNOPSIS);
return;
}
sprintf(fuser_header, " PID %s COMM USAGE\n",
mkstring(buf, VADDR_PRLEN, CENTER, "TASK"));
doing_fds = doing_mmap = 0;
while (args[optind]) {
spec_string = args[optind];
spec_addr = htol(spec_string, RETURN_ON_ERROR|QUIET, NULL);
if ((spec_addr == BADADDR || !IS_KVADDR(spec_addr)) &&
spec_string[0] != '/')
error(FATAL, "invalid argument: %s\n", args[optind]);
if (STRNEQ(spec_string, "0x") && hexadecimal(spec_string, 0))
shift_string_left(spec_string, 2);
len = strlen(spec_string);
fuser_header_printed = 0;
lockd_header_printed = 0;
open_tmpfile();
BZERO(&foreach_data, sizeof(struct foreach_data));
fd = &foreach_data;
fd->keyword_array[0] = FOREACH_FILES;
fd->keyword_array[1] = FOREACH_VM;
fd->keys = 2;
fd->flags |= FOREACH_i_FLAG;
foreach(fd);
rewind(pc->tmpfile);
BZERO(uses, 20);
while (fgets(buf, BUFSIZE, pc->tmpfile)) {
if (STRNEQ(buf, "PID:")) {
if (!STREQ(uses, "")) {
if (!fuser_header_printed) {
fprintf(pc->saved_fp,
"%s", fuser_header);
fuser_header_printed = 1;
}
show_fuser(task_buf, uses);
BZERO(uses, 20);
}
BZERO(task_buf, BUFSIZE);
strcpy(task_buf, buf);
doing_fds = doing_mmap = 0;
continue;
}
if (STRNEQ(buf, "ROOT:")) {
if ((tmp = strstr(buf, spec_string)) &&
(tmp[len] == ' ' || tmp[len] == '\n')) {
if (strstr(tmp, "CWD:")) {
strcat(uses, "root ");
if ((tmp = strstr(tmp+len,
spec_string)) &&
(tmp[len] == ' ' ||
tmp[len] == '\n')) {
strcat(uses, "cwd ");
}
} else {
strcat(uses, "cwd ");
}
}
continue;
}
if (strstr(buf, "DENTRY")) {
doing_fds = 1;
continue;
}
if (strstr(buf, "TOTAL_VM")) {
doing_fds = 0;
continue;
}
if (strstr(buf, " VMA ")) {
doing_mmap = 1;
doing_fds = 0;
continue;
}
if ((tmp = strstr(buf, spec_string)) &&
(tmp[len] == ' ' || tmp[len] == '\n')) {
if (doing_fds) {
strcat(uses, "fd ");
doing_fds = 0;
}
if (doing_mmap) {
strcat(uses, "mmap ");
doing_mmap = 0;
}
}
}
if (!STREQ(uses, "")) {
if (!fuser_header_printed) {
fprintf(pc->saved_fp, "%s", fuser_header);
fuser_header_printed = 1;
}
show_fuser(task_buf, uses);
BZERO(uses, 20);
}
close_tmpfile();
optind++;
if (!fuser_header_printed && !lockd_header_printed) {
fprintf(fp, "No users of %s found\n", spec_string);
}
}
}
static void
show_fuser(char *buf, char *uses)
{
char pid[10];
char task[20];
char command[20];
char *p;
int i;
BZERO(pid, 10);
BZERO(task, 20);
BZERO(command, 20);
p = strstr(buf, "PID: ") + strlen("PID: ");
i = 0;
while (*p != ' ' && i < 10) {
pid[i++] = *p++;
}
pid[i] = NULLCHAR;
p = strstr(buf, "TASK: ") + strlen("TASK: ");
while (*p == ' ')
p++;
i = 0;
while (*p != ' ' && i < 20) {
task[i++] = *p++;
}
task[i] = NULLCHAR;
mkstring(task, VADDR_PRLEN, RJUST, task);
p = strstr(buf, "COMMAND: ") + strlen("COMMAND: ");
strncpy(command, p, 16);
i = strlen(command) - 1;
while (i < 16) {
command[i++] = ' ';
}
command[16] = NULLCHAR;
fprintf(pc->saved_fp, "%5s %s %s %s\n",
pid, task, command, uses);
}
/*
* Gather some host memory/swap statistics, passing back whatever the
* caller requires.
*/
int
monitor_memory(long *freemem_pages,
long *freeswap_pages,
long *mem_usage,
long *swap_usage)
{
FILE *mp;
char buf[BUFSIZE];
char *arglist[MAXARGS];
int argc ATTRIBUTE_UNUSED;
int params;
ulong freemem, memtotal, freeswap, swaptotal;
if (!file_exists("/proc/meminfo", NULL))
return FALSE;
if ((mp = fopen("/proc/meminfo", "r")) == NULL)
return FALSE;
params = 0;
freemem = memtotal = freeswap = swaptotal = 0;
while (fgets(buf, BUFSIZE, mp)) {
if (strstr(buf, "SwapFree")) {
params++;
argc = parse_line(buf, arglist);
if (decimal(arglist[1], 0))
freeswap = (atol(arglist[1]) * 1024)/PAGESIZE();
}
if (strstr(buf, "MemFree")) {
params++;
argc = parse_line(buf, arglist);
if (decimal(arglist[1], 0))
freemem = (atol(arglist[1]) * 1024)/PAGESIZE();
}
if (strstr(buf, "MemTotal")) {
params++;
argc = parse_line(buf, arglist);
if (decimal(arglist[1], 0))
memtotal = (atol(arglist[1]) * 1024)/PAGESIZE();
}
if (strstr(buf, "SwapTotal")) {
params++;
argc = parse_line(buf, arglist);
if (decimal(arglist[1], 0))
swaptotal = (atol(arglist[1]) * 1024)/PAGESIZE();
}
}
fclose(mp);
if (params != 4)
return FALSE;
if (freemem_pages)
*freemem_pages = freemem;
if (freeswap_pages)
*freeswap_pages = freeswap;
if (mem_usage)
*mem_usage = ((memtotal-freemem)*100) / memtotal;
if (swap_usage)
*swap_usage = ((swaptotal-freeswap)*100) / swaptotal;
return TRUE;
}
/*
* Determine whether two filenames reference the same file.
*/
int
same_file(char *f1, char *f2)
{
struct stat stat1, stat2;
if ((stat(f1, &stat1) != 0) || (stat(f2, &stat2) != 0))
return FALSE;
if ((stat1.st_dev == stat2.st_dev) &&
(stat1.st_ino == stat2.st_ino))
return TRUE;
return FALSE;
}
/*
* Determine which live memory source to use.
*/
#define MODPROBE_CMD "/sbin/modprobe -l --type drivers/char 2>&1"
static void
get_live_memory_source(void)
{
FILE *pipe;
char buf[BUFSIZE];
char modname1[BUFSIZE/2];
char modname2[BUFSIZE/2];
char *name;
int use_module, crashbuiltin;
struct stat stat1, stat2;
struct utsname utsname;
if (!(pc->flags & PROC_KCORE))
pc->flags |= DEVMEM;
if (pc->live_memsrc)
goto live_report;
if (file_readable("/dev/mem"))
pc->live_memsrc = "/dev/mem";
else if (file_exists("/proc/kcore", NULL)) {
pc->flags &= ~DEVMEM;
pc->flags |= PROC_KCORE;
pc->live_memsrc = "/proc/kcore";
}
use_module = crashbuiltin = FALSE;
if (file_exists("/dev/mem", &stat1) &&
file_exists(pc->memory_device, &stat2) &&
S_ISCHR(stat1.st_mode) && S_ISCHR(stat2.st_mode) &&
(stat1.st_rdev == stat2.st_rdev)) {
if (!STREQ(pc->memory_device, "/dev/mem"))
error(INFO, "%s: same device as /dev/mem\n%s",
pc->memory_device,
pc->memory_module ? "" : "\n");
if (pc->memory_module)
error(INFO, "ignoring --memory_module %s request\n\n",
pc->memory_module);
} else if (pc->memory_module && memory_driver_module_loaded(NULL)) {
error(INFO, "using pre-loaded \"%s\" module\n\n",
pc->memory_module);
pc->flags |= MODPRELOAD;
use_module = TRUE;
} else {
pc->memory_module = MEMORY_DRIVER_MODULE;
if ((pipe = popen(MODPROBE_CMD, "r")) == NULL) {
error(INFO, "%s: %s\n", MODPROBE_CMD, strerror(errno));
return;
}
sprintf(modname1, "%s.o", pc->memory_module);
sprintf(modname2, "%s.ko", pc->memory_module);
while (fgets(buf, BUFSIZE, pipe)) {
if (strstr(buf, "invalid option") &&
(uname(&utsname) == 0)) {
sprintf(buf,
"/lib/modules/%s/kernel/drivers/char/%s",
utsname.release, modname2);
if (file_exists(buf, &stat1))
use_module = TRUE;
else {
strcat(buf, ".xz");
if (file_exists(buf, &stat1))
use_module = TRUE;
}
break;
}
name = basename(strip_linefeeds(buf));
if (STREQ(name, modname1) || STREQ(name, modname2)) {
use_module = TRUE;
break;
}
}
pclose(pipe);
if (!use_module && file_exists("/dev/crash", &stat1) &&
S_ISCHR(stat1.st_mode))
crashbuiltin = TRUE;
}
if (use_module) {
pc->flags &= ~(DEVMEM|PROC_KCORE);
pc->flags |= MEMMOD;
pc->readmem = read_memory_device;
pc->writemem = write_memory_device;
pc->live_memsrc = pc->memory_device;
}
if (crashbuiltin) {
pc->flags &= ~(DEVMEM|PROC_KCORE);
pc->flags |= CRASHBUILTIN;
pc->readmem = read_memory_device;
pc->writemem = write_memory_device;
pc->live_memsrc = pc->memory_device;
pc->memory_module = NULL;
}
live_report:
if (CRASHDEBUG(1))
fprintf(fp, "get_live_memory_source: %s\n", pc->live_memsrc);
}
/*
* Read /proc/modules to determine whether the crash driver module
* has been loaded.
*/
static int
memory_driver_module_loaded(int *count)
{
FILE *modules;
int argcnt, module_loaded;
char *arglist[MAXARGS];
char buf[BUFSIZE];
if ((modules = fopen("/proc/modules", "r")) == NULL) {
error(INFO, "/proc/modules: %s\n", strerror(errno));
return FALSE;
}
module_loaded = FALSE;
while (fgets(buf, BUFSIZE, modules)) {
console("%s", buf);
argcnt = parse_line(buf, arglist);
if (argcnt < 3)
continue;
if (STREQ(arglist[0], pc->memory_module)) {
module_loaded = TRUE;
if (CRASHDEBUG(1))
fprintf(stderr,
"\"%s\" module loaded: [%s][%s][%s]\n",
arglist[0], arglist[0],
arglist[1], arglist[2]);
if (count)
*count = atoi(arglist[2]);
break;
}
}
fclose(modules);
return module_loaded;
}
/*
* Insmod the memory driver module.
*/
static int
insmod_memory_driver_module(void)
{
FILE *pipe;
char buf[BUFSIZE];
char command[BUFSIZE];
sprintf(command, "/sbin/modprobe %s", pc->memory_module);
if (CRASHDEBUG(1))
fprintf(fp, "%s\n", command);
if ((pipe = popen(command, "r")) == NULL) {
error(INFO, "%s: %s", command, strerror(errno));
return FALSE;
}
while (fgets(buf, BUFSIZE, pipe))
fprintf(fp, "%s\n", buf);
pclose(pipe);
if (!memory_driver_module_loaded(NULL)) {
error(INFO, "cannot insmod \"%s\" module\n", pc->memory_module);
return FALSE;
}
return TRUE;
}
/*
* Return the dev_t for the memory device driver. The major number will
* be that of the kernel's misc driver; the minor is dynamically created
* when the module at inmod time, and found in /proc/misc.
*/
static int
get_memory_driver_dev(dev_t *devp)
{
char buf[BUFSIZE];
char *arglist[MAXARGS];
int argcnt;
FILE *misc;
int minor;
dev_t dev;
dev = 0;
if ((misc = fopen("/proc/misc", "r")) == NULL) {
error(INFO, "/proc/misc: %s", strerror(errno));
} else {
while (fgets(buf, BUFSIZE, misc)) {
argcnt = parse_line(buf, arglist);
if ((argcnt == 2) &&
STREQ(arglist[1], pc->memory_module)) {
minor = atoi(arglist[0]);
dev = makedev(MISC_MAJOR, minor);
if (CRASHDEBUG(1))
fprintf(fp,
"/proc/misc: %s %s => %d/%d\n",
arglist[0], arglist[1],
major(dev), minor(dev));
break;
}
}
fclose(misc);
}
if (!dev) {
error(INFO, "cannot determine minor number of %s driver\n",
pc->memory_module);
return FALSE;
}
*devp = dev;
return TRUE;
}
/*
* Deal with the creation or verification of the memory device file:
*
* 1. If the device exists, and has the correct major/minor device numbers,
* nothing needs to be done.
* 2. If the filename exists, but it's not a device file, has the wrong
* major/minor device numbers, or the wrong permissions, advise the
* user to delete it.
* 3. Otherwise, create it.
*/
static int
create_memory_device(dev_t dev)
{
struct stat stat;
if (file_exists(pc->live_memsrc, &stat)) {
/*
* It already exists -- just use it.
*/
if ((stat.st_mode == MEMORY_DRIVER_DEVICE_MODE) &&
(stat.st_rdev == dev))
return TRUE;
/*
* Either it's not a device special file, or it's got
* the wrong major/minor numbers, or the wrong permissions.
* Unlink the file -- it shouldn't be there.
*/
if (!S_ISCHR(stat.st_mode))
error(FATAL,
"%s: not a character device -- please delete it!\n",
pc->live_memsrc);
else if (dev != stat.st_rdev)
error(FATAL,
"%s: invalid device: %d/%d -- please delete it!\n",
pc->live_memsrc, major(stat.st_rdev),
minor(stat.st_rdev));
else
unlink(pc->live_memsrc);
}
/*
* Either it doesn't exist or it was just unlinked.
* In either case, try to create it.
*/
if (mknod(pc->live_memsrc, MEMORY_DRIVER_DEVICE_MODE, dev)) {
error(INFO, "%s: mknod: %s\n", pc->live_memsrc,
strerror(errno));
return FALSE;
}
return TRUE;
}
/*
* If we're here, the memory driver module is being requested:
*
* 1. If /dev/crash is built into the kernel, just open it.
* 2. If the module is not already loaded, insmod it.
* 3. Determine the misc driver minor device number that it was assigned.
* 4. Create (or verify) the device file.
* 5. Then just open it.
*/
static int
memory_driver_init(void)
{
dev_t dev;
if (pc->flags & CRASHBUILTIN)
goto open_device;
if (!memory_driver_module_loaded(NULL)) {
if (!insmod_memory_driver_module())
return FALSE;
} else
pc->flags |= MODPRELOAD;
if (!get_memory_driver_dev(&dev))
return FALSE;
if (!create_memory_device(dev))
return FALSE;
open_device:
if ((pc->mfd = open(pc->memory_device, O_RDONLY)) < 0) {
error(INFO, "%s: open: %s\n", pc->memory_device,
strerror(errno));
return FALSE;
}
return TRUE;
}
/*
* Remove the memory driver module and associated file.
*/
int
cleanup_memory_driver(void)
{
int errors, count;
char command[BUFSIZE];
count = errors = 0;
if (pc->flags & KERNEL_DEBUG_QUERY)
return TRUE;
close(pc->mfd);
if (file_exists(pc->memory_device, NULL) &&
unlink(pc->memory_device)) {
error(INFO, "%s: %s\n", pc->memory_device, strerror(errno));
errors++;
}
if (!(pc->flags & MODPRELOAD) &&
memory_driver_module_loaded(&count) && !count) {
sprintf(command, "/sbin/rmmod %s", pc->memory_module);
if (CRASHDEBUG(1))
fprintf(fp, "%s\n", command);
errors += system(command);
}
if (errors)
error(NOTE, "cleanup_memory_driver failed\n");
return errors ? FALSE : TRUE;
}
struct do_radix_tree_info {
ulong maxcount;
ulong count;
void *data;
};
static void do_radix_tree_count(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_radix_tree_info *info = private;
info->count++;
}
static void do_radix_tree_search(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_radix_tree_info *info = private;
struct list_pair *rtp = info->data;
if (rtp->index == index) {
rtp->value = (void *)slot;
info->count = 1;
}
}
static void do_radix_tree_dump(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_radix_tree_info *info = private;
fprintf(fp, "[%ld] %lx\n", index, slot);
info->count++;
}
static void do_radix_tree_gather(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_radix_tree_info *info = private;
struct list_pair *rtp = info->data;
if (info->maxcount) {
rtp[info->count].index = index;
rtp[info->count].value = (void *)slot;
info->count++;
info->maxcount--;
}
}
static void do_radix_tree_dump_cb(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_radix_tree_info *info = private;
struct list_pair *rtp = info->data;
int (*cb)(ulong) = rtp->value;
/* Caller defined operation */
if (!cb(slot)) {
if ((slot & RADIX_TREE_ENTRY_MASK) == RADIX_TREE_EXCEPTIONAL_ENTRY) {
if (CRASHDEBUG(1))
error(INFO, "RADIX_TREE_EXCEPTIONAL_ENTRY: %lx\n", slot);
return;
}
error(FATAL, "do_radix_tree: callback "
"operation failed: entry: %ld item: %lx\n",
info->count, slot);
}
info->count++;
}
/*
* do_radix_tree argument usage:
*
* root: Address of a radix_tree_root structure
*
* flag: RADIX_TREE_COUNT - Return the number of entries in the tree.
* RADIX_TREE_SEARCH - Search for an entry at rtp->index; if found,
* store the entry in rtp->value and return a count of 1; otherwise
* return a count of 0.
* RADIX_TREE_DUMP - Dump all existing index/value pairs.
* RADIX_TREE_GATHER - Store all existing index/value pairs in the
* passed-in array of list_pair structs starting at rtp,
* returning the count of entries stored; the caller can/should
* limit the number of returned entries by putting the array size
* (max count) in the rtp->index field of the first structure
* in the passed-in array.
* RADIX_TREE_DUMP_CB - Similar with RADIX_TREE_DUMP, but for each
* radix tree entry, a user defined callback at rtp->value will
* be invoked.
*
* rtp: Unused by RADIX_TREE_COUNT and RADIX_TREE_DUMP.
* A pointer to a list_pair structure for RADIX_TREE_SEARCH.
* A pointer to an array of list_pair structures for
* RADIX_TREE_GATHER; the dimension (max count) of the array may
* be stored in the index field of the first structure to avoid
* any chance of an overrun.
* For RADIX_TREE_DUMP_CB, the rtp->value must be initialized as a
* callback function. The callback prototype must be: int (*)(ulong);
*/
ulong
do_radix_tree(ulong root, int flag, struct list_pair *rtp)
{
struct do_radix_tree_info info = {
.count = 0,
.data = rtp,
};
struct radix_tree_ops ops = {
.radix = 16,
.private = &info,
};
switch (flag)
{
case RADIX_TREE_COUNT:
ops.entry = do_radix_tree_count;
break;
case RADIX_TREE_SEARCH:
/*
* FIXME: do_radix_tree_traverse() traverses whole
* radix tree, not binary search. So this search is
* not efficient.
*/
ops.entry = do_radix_tree_search;
break;
case RADIX_TREE_DUMP:
ops.entry = do_radix_tree_dump;
break;
case RADIX_TREE_GATHER:
if (!(info.maxcount = rtp->index))
info.maxcount = (ulong)(-1); /* caller beware */
ops.entry = do_radix_tree_gather;
break;
case RADIX_TREE_DUMP_CB:
if (rtp->value == NULL) {
error(FATAL, "do_radix_tree: need set callback function");
return -EINVAL;
}
ops.entry = do_radix_tree_dump_cb;
break;
default:
error(FATAL, "do_radix_tree: invalid flag: %lx\n", flag);
}
do_radix_tree_traverse(root, 1, &ops);
return info.count;
}
struct do_xarray_info {
ulong maxcount;
ulong count;
void *data;
};
static void do_xarray_count(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
info->count++;
}
static void do_xarray_search(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
struct list_pair *xp = info->data;
if (xp->index == index) {
xp->value = (void *)slot;
info->count = 1;
}
}
static void do_xarray_dump(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
fprintf(fp, "[%ld] %lx\n", index, slot);
info->count++;
}
static void do_xarray_gather(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
struct list_pair *xp = info->data;
if (info->maxcount) {
xp[info->count].index = index;
xp[info->count].value = (void *)slot;
info->count++;
info->maxcount--;
}
}
static void do_xarray_dump_cb(ulong node, ulong slot, const char *path,
ulong index, void *private)
{
struct do_xarray_info *info = private;
struct list_pair *xp = info->data;
int (*cb)(ulong) = xp->value;
/* Caller defined operation */
if (!cb(slot)) {
if (slot & XARRAY_TAG_MASK) {
if (CRASHDEBUG(1))
error(INFO, "entry has XARRAY_TAG_MASK bits set: %lx\n", slot);
return;
}
error(FATAL, "do_xarray: callback "
"operation failed: entry: %ld item: %lx\n",
info->count, slot);
}
info->count++;
}
/*
* do_xarray argument usage:
*
* root: Address of a xarray structure
*
* flag: XARRAY_COUNT - Return the number of entries in the tree.
* XARRAY_SEARCH - Search for an entry at xp->index; if found,
* store the entry in xp->value and return a count of 1; otherwise
* return a count of 0.
* XARRY_DUMP - Dump all existing index/value pairs.
* XARRAY_GATHER - Store all existing index/value pairs in the
* passed-in array of list_pair structs starting at xp,
* returning the count of entries stored; the caller can/should
* limit the number of returned entries by putting the array size
* (max count) in the xp->index field of the first structure
* in the passed-in array.
* XARRAY_DUMP_CB - Similar with XARRAY_DUMP, but for each
* xarray entry, a user defined callback at xp->value will
* be invoked.
*
* xp: Unused by XARRAY_COUNT and XARRAY_DUMP.
* A pointer to a list_pair structure for XARRAY_SEARCH.
* A pointer to an array of list_pair structures for
* XARRAY_GATHER; the dimension (max count) of the array may
* be stored in the index field of the first structure to avoid
* any chance of an overrun.
* For XARRAY_DUMP_CB, the rtp->value must be initialized as a
* callback function. The callback prototype must be: int (*)(ulong);
*/
ulong
do_xarray(ulong root, int flag, struct list_pair *xp)
{
struct do_xarray_info info = {
.count = 0,
.data = xp,
};
struct xarray_ops ops = {
.radix = 16,
.private = &info,
};
switch (flag)
{
case XARRAY_COUNT:
ops.entry = do_xarray_count;
break;
case XARRAY_SEARCH:
ops.entry = do_xarray_search;
break;
case XARRAY_DUMP:
ops.entry = do_xarray_dump;
break;
case XARRAY_GATHER:
if (!(info.maxcount = xp->index))
info.maxcount = (ulong)(-1); /* caller beware */
ops.entry = do_xarray_gather;
break;
case XARRAY_DUMP_CB:
if (xp->value == NULL) {
error(FATAL, "do_xarray: no callback function specified");
return -EINVAL;
}
ops.entry = do_xarray_dump_cb;
break;
default:
error(FATAL, "do_xarray: invalid flag: %lx\n", flag);
}
do_xarray_traverse(root, 1, &ops);
return info.count;
}
int
is_readable(char *filename)
{
int fd;
if ((fd = open(filename, O_RDONLY)) < 0) {
error(INFO, "%s: %s\n", filename, strerror(errno));
return FALSE;
} else
close(fd);
return TRUE;
}
static int
match_file_string(char *filename, char *string, char *buffer)
{
int found;
char command[BUFSIZE];
FILE *pipe;
sprintf(command, "/usr/bin/strings %s", filename);
if ((pipe = popen(command, "r")) == NULL) {
error(INFO, "%s: %s\n", filename, strerror(errno));
return FALSE;
}
found = FALSE;
while (fgets(buffer, BUFSIZE-1, pipe)) {
if (strstr(buffer, string)) {
found = TRUE;
break;
}
}
pclose(pipe);
return found;
}
char *
vfsmount_devname(ulong vfsmnt, char *buf, int maxlen)
{
ulong devp;
BZERO(buf, maxlen);
if (VALID_STRUCT(mount)) {
if (!readmem(vfsmnt - OFFSET(mount_mnt) + OFFSET(mount_mnt_devname),
KVADDR, &devp, sizeof(void *), "mount mnt_devname",
QUIET|RETURN_ON_ERROR))
return buf;
} else {
if (!readmem(vfsmnt + OFFSET(vfsmount_mnt_devname),
KVADDR, &devp, sizeof(void *), "vfsmount mnt_devname",
QUIET|RETURN_ON_ERROR))
return buf;
}
if (read_string(devp, buf, BUFSIZE-1))
return buf;
return buf;
}
static ulong
get_root_vfsmount(char *file_buf)
{
char buf1[BUFSIZE];
char buf2[BUFSIZE];
ulong vfsmnt;
ulong mnt_parent;
vfsmnt = ULONG(file_buf + OFFSET(file_f_vfsmnt));
if (!strlen(vfsmount_devname(vfsmnt, buf1, BUFSIZE)))
return vfsmnt;
if (STREQ(buf1, "udev") || STREQ(buf1, "devtmpfs")) {
if (VALID_STRUCT(mount)) {
if (!readmem(vfsmnt - OFFSET(mount_mnt) + OFFSET(mount_mnt_parent), KVADDR,
&mnt_parent, sizeof(void *), "mount mnt_parent",
QUIET|RETURN_ON_ERROR))
return vfsmnt;
} else {
if (!readmem(vfsmnt + OFFSET(vfsmount_mnt_parent), KVADDR,
&mnt_parent, sizeof(void *), "vfsmount mnt_parent",
QUIET|RETURN_ON_ERROR))
return vfsmnt;
}
if (!strlen(vfsmount_devname(mnt_parent, buf2, BUFSIZE)))
return vfsmnt;
if (STREQ(buf1, "udev") && STREQ(buf2, "udev"))
return mnt_parent;
if (STREQ(buf1, "devtmpfs") && STREQ(buf2, "devtmpfs"))
return mnt_parent;
}
return vfsmnt;
}
void
check_live_arch_mismatch(void)
{
struct utsname utsname;
if (machine_type("X86") && (uname(&utsname) == 0) &&
STRNEQ(utsname.machine, "x86_64"))
error(FATAL, "compiled for the X86 architecture\n");
#if defined(__i386__) || defined(__x86_64__)
if (machine_type("ARM"))
error(FATAL, "compiled for the ARM architecture\n");
#endif
#ifdef __x86_64__
if (machine_type("ARM64"))
error(FATAL, "compiled for the ARM64 architecture\n");
#endif
#ifdef __x86_64__
if (machine_type("PPC64"))
error(FATAL, "compiled for the PPC64 architecture\n");
#endif
#ifdef __powerpc64__
if (machine_type("PPC"))
error(FATAL, "compiled for the PPC architecture\n");
#endif
}