1 /* 2 * Copyright © 2018 Alexey Dobriyan <adobriyan@gmail.com> 3 * 4 * Permission to use, copy, modify, and distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 // Test 17 // 1) read and lseek on every file in /proc 18 // 2) readlink of every symlink in /proc 19 // 3) recursively (1) + (2) for every directory in /proc 20 // 4) write to /proc/*/clear_refs and /proc/*/task/*/clear_refs 21 // 5) write to /proc/sysrq-trigger 22 #undef NDEBUG 23 #include <assert.h> 24 #include <errno.h> 25 #include <sys/types.h> 26 #include <dirent.h> 27 #include <stdbool.h> 28 #include <stdlib.h> 29 #include <stdio.h> 30 #include <string.h> 31 #include <sys/stat.h> 32 #include <sys/vfs.h> 33 #include <fcntl.h> 34 #include <unistd.h> 35 36 #include "proc.h" 37 38 static void f_reg(DIR *d, const char *filename) 39 { 40 char buf[4096]; 41 int fd; 42 ssize_t rv; 43 44 /* read from /proc/kmsg can block */ 45 fd = openat(dirfd(d), filename, O_RDONLY|O_NONBLOCK); 46 if (fd == -1) 47 return; 48 /* struct proc_ops::proc_lseek is mandatory if file is seekable. */ 49 (void)lseek(fd, 0, SEEK_SET); 50 rv = read(fd, buf, sizeof(buf)); 51 assert((0 <= rv && rv <= sizeof(buf)) || rv == -1); 52 close(fd); 53 } 54 55 static void f_reg_write(DIR *d, const char *filename, const char *buf, size_t len) 56 { 57 int fd; 58 ssize_t rv; 59 60 fd = openat(dirfd(d), filename, O_WRONLY); 61 if (fd == -1) 62 return; 63 rv = write(fd, buf, len); 64 assert((0 <= rv && rv <= len) || rv == -1); 65 close(fd); 66 } 67 68 static void f_lnk(DIR *d, const char *filename) 69 { 70 char buf[4096]; 71 ssize_t rv; 72 73 rv = readlinkat(dirfd(d), filename, buf, sizeof(buf)); 74 assert((0 <= rv && rv <= sizeof(buf)) || rv == -1); 75 } 76 77 static void f(DIR *d, unsigned int level) 78 { 79 struct dirent *de; 80 81 de = xreaddir(d); 82 assert(de->d_type == DT_DIR); 83 assert(streq(de->d_name, ".")); 84 85 de = xreaddir(d); 86 assert(de->d_type == DT_DIR); 87 assert(streq(de->d_name, "..")); 88 89 while ((de = xreaddir(d))) { 90 assert(!streq(de->d_name, ".")); 91 assert(!streq(de->d_name, "..")); 92 93 switch (de->d_type) { 94 DIR *dd; 95 int fd; 96 97 case DT_REG: 98 if (level == 0 && streq(de->d_name, "sysrq-trigger")) { 99 f_reg_write(d, de->d_name, "h", 1); 100 } else if (level == 1 && streq(de->d_name, "clear_refs")) { 101 f_reg_write(d, de->d_name, "1", 1); 102 } else if (level == 3 && streq(de->d_name, "clear_refs")) { 103 f_reg_write(d, de->d_name, "1", 1); 104 } else { 105 f_reg(d, de->d_name); 106 } 107 break; 108 case DT_DIR: 109 fd = openat(dirfd(d), de->d_name, O_DIRECTORY|O_RDONLY); 110 if (fd == -1) 111 continue; 112 dd = fdopendir(fd); 113 if (!dd) 114 continue; 115 f(dd, level + 1); 116 closedir(dd); 117 break; 118 case DT_LNK: 119 f_lnk(d, de->d_name); 120 break; 121 default: 122 assert(0); 123 } 124 } 125 } 126 127 int main(void) 128 { 129 DIR *d; 130 struct statfs sfs; 131 132 d = opendir("/proc"); 133 if (!d) 134 return 4; 135 136 /* Ensure /proc is proc. */ 137 if (fstatfs(dirfd(d), &sfs) == -1) { 138 return 1; 139 } 140 if (sfs.f_type != 0x9fa0) { 141 fprintf(stderr, "error: unexpected f_type %lx\n", (long)sfs.f_type); 142 return 2; 143 } 144 145 f(d, 0); 146 147 return 0; 148 } 149