crash/filesys.c

4423 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 (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;
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];
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
}