/* 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 #include #include #include 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 (!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 5 #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", "/", 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-/linux * /usr/src/redhat/BUILD/kernel-/linux- * /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++; } pclose(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++; } } pclose(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 (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; iflags = 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__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 1 || !STREQ(buf, "/")) && !STRNEQ(tmpname, "/")) { sprintf(pathname, "%s%s%s", buf, "/", tmpname); } else { sprintf(pathname, "%s%s", buf, tmpname); } } } else { strncpy(pathname, buf, BUFSIZE); } parent = ULONG(dentry_buf + OFFSET(dentry_d_parent)); if (tmp_dentry == parent && full) { if (VALID_MEMBER(vfsmount_mnt_mountpoint)) { if (tmp_vfsmnt) { if (strncmp(pathname, "//", 2) == 0) shift_string_left(pathname, 1); readmem(tmp_vfsmnt, KVADDR, vfsmnt_buf, SIZE(vfsmount), "vfsmount buffer", FAULT_ON_ERROR); parent = ULONG(vfsmnt_buf + OFFSET(vfsmount_mnt_mountpoint)); mnt_parent = ULONG(vfsmnt_buf + OFFSET(vfsmount_mnt_parent)); if (tmp_vfsmnt == mnt_parent) break; else tmp_vfsmnt = mnt_parent; } } else if (VALID_STRUCT(mount)) { if (tmp_vfsmnt) { if (strncmp(pathname, "//", 2) == 0) shift_string_left(pathname, 1); readmem(tmp_vfsmnt - OFFSET(mount_mnt), KVADDR, mnt_buf, SIZE(mount), "mount buffer", FAULT_ON_ERROR); parent = ULONG(mnt_buf + OFFSET(mount_mnt_mountpoint)); mnt_parent = ULONG(mnt_buf + OFFSET(mount_mnt_parent)); if ((tmp_vfsmnt - OFFSET(mount_mnt)) == mnt_parent) break; else tmp_vfsmnt = mnt_parent + OFFSET(mount_mnt); } } else { parent = ULONG(dentry_buf + OFFSET(dentry_d_covers)); } } } while (tmp_dentry != parent && parent); if (mnt_buf) FREEBUF(mnt_buf); else if (vfsmnt_buf) FREEBUF(vfsmnt_buf); } /* * If the pathname component, which may be internal or external to the * dentry, has string length equal to what's expected, copy it into the * passed-in buffer, and return its length. If it doesn't match, return 0. */ static int get_pathname_component(ulong dentry, ulong d_name_name, int d_name_len, char *dentry_buf, char *pathbuf) { int len = d_name_len; /* presume success */ if (d_name_name == (dentry + OFFSET(dentry_d_iname))) { if (strlen(dentry_buf + OFFSET(dentry_d_iname)) == d_name_len) strcpy(pathbuf, dentry_buf + OFFSET(dentry_d_iname)); else len = 0; } else if ((read_string(d_name_name, pathbuf, BUFSIZE)) != d_name_len) len = 0; return len; } /* * Cache the passed-in file structure. */ char * fill_file_cache(ulong file) { int i; char *cache; ft->file_cache_fills++; for (i = 0; i < DENTRY_CACHE; i++) { if (ft->cached_file[i] == file) { ft->cached_file_hits[i]++; cache = ft->file_cache + (SIZE(file)*i); return(cache); } } cache = ft->file_cache + (SIZE(file)*ft->file_cache_index); readmem(file, KVADDR, cache, SIZE(file), "fill_file_cache", FAULT_ON_ERROR); ft->cached_file[ft->file_cache_index] = file; ft->file_cache_index = (ft->file_cache_index+1) % DENTRY_CACHE; return(cache); } /* * If active, clear the file references. */ void clear_file_cache(void) { int i; if (DUMPFILE()) return; for (i = 0; i < DENTRY_CACHE; i++) { ft->cached_file[i] = 0; ft->cached_file_hits[i] = 0; } ft->file_cache_fills = 0; ft->file_cache_index = 0; } /* * Cache the passed-in dentry structure. */ char * fill_dentry_cache(ulong dentry) { int i; char *cache; ft->dentry_cache_fills++; for (i = 0; i < DENTRY_CACHE; i++) { if (ft->cached_dentry[i] == dentry) { ft->cached_dentry_hits[i]++; cache = ft->dentry_cache + (SIZE(dentry)*i); return(cache); } } cache = ft->dentry_cache + (SIZE(dentry)*ft->dentry_cache_index); readmem(dentry, KVADDR, cache, SIZE(dentry), "fill_dentry_cache", FAULT_ON_ERROR); ft->cached_dentry[ft->dentry_cache_index] = dentry; ft->dentry_cache_index = (ft->dentry_cache_index+1) % DENTRY_CACHE; return(cache); } /* * If active, clear the dentry references. */ void clear_dentry_cache(void) { int i; if (DUMPFILE()) return; for (i = 0; i < DENTRY_CACHE; i++) { ft->cached_dentry[i] = 0; ft->cached_dentry_hits[i] = 0; } ft->dentry_cache_fills = 0; ft->dentry_cache_index = 0; } /* * Cache the passed-in inode structure. */ char * fill_inode_cache(ulong inode) { int i; char *cache; ft->inode_cache_fills++; for (i = 0; i < INODE_CACHE; i++) { if (ft->cached_inode[i] == inode) { ft->cached_inode_hits[i]++; cache = ft->inode_cache + (SIZE(inode)*i); return(cache); } } cache = ft->inode_cache + (SIZE(inode)*ft->inode_cache_index); readmem(inode, KVADDR, cache, SIZE(inode), "fill_inode_cache", FAULT_ON_ERROR); ft->cached_inode[ft->inode_cache_index] = inode; ft->inode_cache_index = (ft->inode_cache_index+1) % INODE_CACHE; return(cache); } /* * If active, clear the inode references. */ void clear_inode_cache(void) { int i; if (DUMPFILE()) return; for (i = 0; i < DENTRY_CACHE; i++) { ft->cached_inode[i] = 0; ft->cached_inode_hits[i] = 0; } ft->inode_cache_fills = 0; ft->inode_cache_index = 0; } /* * This command displays the tasks using specified files or sockets. * Tasks will be listed that reference the file as the current working * directory, root directory, an open file descriptor, or that mmap the * file. * The argument can be a full pathname without symbolic links, or inode * address. */ void cmd_fuser(void) { int c; char *spec_string, *tmp; struct foreach_data foreach_data, *fd; char task_buf[BUFSIZE]; char buf[BUFSIZE]; char uses[20]; char fuser_header[BUFSIZE]; int doing_fds, doing_mmap, len; int fuser_header_printed, lockd_header_printed; while ((c = getopt(argcnt, args, "")) != EOF) { switch(c) { default: argerrs++; break; } } if (argerrs) cmd_usage(pc->curcmd, SYNOPSIS); if (!args[optind]) { cmd_usage(pc->curcmd, SYNOPSIS); return; } sprintf(fuser_header, " PID %s COMM USAGE\n", mkstring(buf, VADDR_PRLEN, CENTER, "TASK")); doing_fds = doing_mmap = 0; while (args[optind]) { spec_string = args[optind]; if (STRNEQ(spec_string, "0x") && hexadecimal(spec_string, 0)) shift_string_left(spec_string, 2); len = strlen(spec_string); fuser_header_printed = 0; lockd_header_printed = 0; open_tmpfile(); BZERO(&foreach_data, sizeof(struct foreach_data)); fd = &foreach_data; fd->keyword_array[0] = FOREACH_FILES; fd->keyword_array[1] = FOREACH_VM; fd->keys = 2; fd->flags |= FOREACH_i_FLAG; foreach(fd); rewind(pc->tmpfile); BZERO(uses, 20); while (fgets(buf, BUFSIZE, pc->tmpfile)) { if (STRNEQ(buf, "PID:")) { if (!STREQ(uses, "")) { if (!fuser_header_printed) { fprintf(pc->saved_fp, "%s", fuser_header); fuser_header_printed = 1; } show_fuser(task_buf, uses); BZERO(uses, 20); } BZERO(task_buf, BUFSIZE); strcpy(task_buf, buf); doing_fds = doing_mmap = 0; continue; } if (STRNEQ(buf, "ROOT:")) { if ((tmp = strstr(buf, spec_string)) && (tmp[len] == ' ' || tmp[len] == '\n')) { if (strstr(tmp, "CWD:")) { strcat(uses, "root "); if ((tmp = strstr(tmp+len, spec_string)) && (tmp[len] == ' ' || tmp[len] == '\n')) { strcat(uses, "cwd "); } } else { strcat(uses, "cwd "); } } continue; } if (strstr(buf, "DENTRY")) { doing_fds = 1; continue; } if (strstr(buf, "TOTAL_VM")) { doing_fds = 0; continue; } if (strstr(buf, " VMA ")) { doing_mmap = 1; doing_fds = 0; continue; } if ((tmp = strstr(buf, spec_string)) && (tmp[len] == ' ' || tmp[len] == '\n')) { if (doing_fds) { strcat(uses, "fd "); doing_fds = 0; } if (doing_mmap) { strcat(uses, "mmap "); doing_mmap = 0; } } } if (!STREQ(uses, "")) { if (!fuser_header_printed) { fprintf(pc->saved_fp, "%s", fuser_header); fuser_header_printed = 1; } show_fuser(task_buf, uses); BZERO(uses, 20); } close_tmpfile(); optind++; if (!fuser_header_printed && !lockd_header_printed) { fprintf(fp, "No users of %s found\n", spec_string); } } } static void show_fuser(char *buf, char *uses) { char pid[10]; char task[20]; char command[20]; char *p; int i; BZERO(pid, 10); BZERO(task, 20); BZERO(command, 20); p = strstr(buf, "PID: ") + strlen("PID: "); i = 0; while (*p != ' ' && i < 10) { pid[i++] = *p++; } pid[i] = NULLCHAR; p = strstr(buf, "TASK: ") + strlen("TASK: "); while (*p == ' ') p++; i = 0; while (*p != ' ' && i < 20) { task[i++] = *p++; } task[i] = NULLCHAR; mkstring(task, VADDR_PRLEN, RJUST, task); p = strstr(buf, "COMMAND: ") + strlen("COMMAND: "); strncpy(command, p, 16); i = strlen(command) - 1; while (i < 16) { command[i++] = ' '; } command[16] = NULLCHAR; fprintf(pc->saved_fp, "%5s %s %s %s\n", pid, task, command, uses); } /* * Gather some host memory/swap statistics, passing back whatever the * caller requires. */ int monitor_memory(long *freemem_pages, long *freeswap_pages, long *mem_usage, long *swap_usage) { FILE *mp; char buf[BUFSIZE]; char *arglist[MAXARGS]; int argc ATTRIBUTE_UNUSED; int params; ulong freemem, memtotal, freeswap, swaptotal; if (!file_exists("/proc/meminfo", NULL)) return FALSE; if ((mp = fopen("/proc/meminfo", "r")) == NULL) return FALSE; params = 0; freemem = memtotal = freeswap = swaptotal = 0; while (fgets(buf, BUFSIZE, mp)) { if (strstr(buf, "SwapFree")) { params++; argc = parse_line(buf, arglist); if (decimal(arglist[1], 0)) freeswap = (atol(arglist[1]) * 1024)/PAGESIZE(); } if (strstr(buf, "MemFree")) { params++; argc = parse_line(buf, arglist); if (decimal(arglist[1], 0)) freemem = (atol(arglist[1]) * 1024)/PAGESIZE(); } if (strstr(buf, "MemTotal")) { params++; argc = parse_line(buf, arglist); if (decimal(arglist[1], 0)) memtotal = (atol(arglist[1]) * 1024)/PAGESIZE(); } if (strstr(buf, "SwapTotal")) { params++; argc = parse_line(buf, arglist); if (decimal(arglist[1], 0)) swaptotal = (atol(arglist[1]) * 1024)/PAGESIZE(); } } fclose(mp); if (params != 4) return FALSE; if (freemem_pages) *freemem_pages = freemem; if (freeswap_pages) *freeswap_pages = freeswap; if (mem_usage) *mem_usage = ((memtotal-freemem)*100) / memtotal; if (swap_usage) *swap_usage = ((swaptotal-freeswap)*100) / swaptotal; return TRUE; } /* * Determine whether two filenames reference the same file. */ int same_file(char *f1, char *f2) { struct stat stat1, stat2; if ((stat(f1, &stat1) != 0) || (stat(f2, &stat2) != 0)) return FALSE; if ((stat1.st_dev == stat2.st_dev) && (stat1.st_ino == stat2.st_ino)) return TRUE; return FALSE; } /* * Determine which live memory source to use. */ #define MODPROBE_CMD "/sbin/modprobe -l --type drivers/char 2>&1" static void get_live_memory_source(void) { FILE *pipe; char buf[BUFSIZE]; char modname1[BUFSIZE/2]; char modname2[BUFSIZE/2]; char *name; int use_module, crashbuiltin; struct stat stat1, stat2; struct utsname utsname; if (!(pc->flags & PROC_KCORE)) pc->flags |= DEVMEM; if (pc->live_memsrc) goto live_report; if (file_exists("/dev/mem", NULL)) 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 }