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