1// SPDX-License-Identifier: GPL-2.0-or-later 2/* 3 * Copyright (c) 2021 SUSE LLC <rpalethorpe@suse.com> 4 */ 5 6#define _GNU_SOURCE 7#include <stdio.h> 8#include "lapi/fcntl.h" 9#include "tst_safe_file_at.h" 10 11#define TST_NO_DEFAULT_MAIN 12#include "tst_test.h" 13 14static char fd_path[PATH_MAX]; 15 16const char *tst_decode_fd(const int fd) 17{ 18 ssize_t ret; 19 char proc_path[32]; 20 21 if (fd < 0) 22 return "!"; 23 24 sprintf(proc_path, "/proc/self/fd/%d", fd); 25 ret = readlink(proc_path, fd_path, sizeof(fd_path)); 26 27 if (ret < 0) 28 return "?"; 29 30 fd_path[ret] = '\0'; 31 32 return fd_path; 33} 34 35int safe_openat(const char *const file, const int lineno, 36 const int dirfd, const char *const path, const int oflags, ...) 37{ 38 int fd; 39 mode_t mode = 0; 40 41 if (TST_OPEN_NEEDS_MODE(oflags)) { 42 va_list ap; 43 44 va_start(ap, oflags); 45 mode = va_arg(ap, int); 46 va_end(ap); 47 } 48 49 fd = openat(dirfd, path, oflags, mode); 50 if (fd > -1) 51 return fd; 52 53 tst_brk_(file, lineno, TBROK | TERRNO, 54 "openat(%d<%s>, '%s', %o, %o)", 55 dirfd, tst_decode_fd(dirfd), path, oflags, mode); 56 57 return fd; 58} 59 60ssize_t safe_file_readat(const char *const file, const int lineno, 61 const int dirfd, const char *const path, 62 char *const buf, const size_t nbyte) 63{ 64 int fd = safe_openat(file, lineno, dirfd, path, O_RDONLY); 65 ssize_t rval; 66 67 if (fd < 0) 68 return -1; 69 70 rval = safe_read(file, lineno, NULL, 0, fd, buf, nbyte - 1); 71 if (rval < 0) 72 return -1; 73 74 close(fd); 75 buf[rval] = '\0'; 76 77 if (rval >= (ssize_t)nbyte - 1) { 78 tst_brk_(file, lineno, TBROK, 79 "Buffer length %zu too small to read %d<%s>/%s", 80 nbyte, dirfd, tst_decode_fd(dirfd), path); 81 } 82 83 return rval; 84} 85 86int tst_file_vprintfat(const int dirfd, const char *const path, 87 const char *const fmt, va_list va) 88{ 89 const int fd = openat(dirfd, path, O_WRONLY); 90 int ret, errno_cpy; 91 92 if (fd < 0) 93 return -1; 94 95 ret = vdprintf(fd, fmt, va); 96 errno_cpy = errno; 97 close(fd); 98 99 if (ret < 0) { 100 errno = errno_cpy; 101 return -2; 102 } 103 104 return ret; 105} 106 107int tst_file_printfat(const int dirfd, const char *const path, 108 const char *const fmt, ...) 109{ 110 va_list va; 111 int rval; 112 113 va_start(va, fmt); 114 rval = tst_file_vprintfat(dirfd, path, fmt, va); 115 va_end(va); 116 117 return rval; 118} 119 120int safe_file_vprintfat(const char *const file, const int lineno, 121 const int dirfd, const char *const path, 122 const char *const fmt, va_list va) 123{ 124 char buf[16]; 125 va_list vac; 126 int rval, errno_cpy; 127 128 va_copy(vac, va); 129 130 rval = tst_file_vprintfat(dirfd, path, fmt, va); 131 132 if (rval == -2) { 133 errno_cpy = errno; 134 rval = vsnprintf(buf, sizeof(buf), fmt, vac); 135 va_end(vac); 136 137 if (rval >= (ssize_t)sizeof(buf)) 138 strcpy(buf + sizeof(buf) - 5, "..."); 139 else if (rval < 0) 140 buf[0] = '\0'; 141 142 errno = errno_cpy; 143 tst_brk_(file, lineno, TBROK | TERRNO, 144 "vdprintf(%d<%s>, '%s', '%s'<%s>)", 145 dirfd, tst_decode_fd(dirfd), path, fmt, buf); 146 return -1; 147 } 148 149 va_end(vac); 150 151 if (rval == -1) { 152 tst_brk_(file, lineno, TBROK | TERRNO, 153 "openat(%d<%s>, '%s', O_WRONLY)", 154 dirfd, tst_decode_fd(dirfd), path); 155 } 156 157 return rval; 158} 159 160int safe_file_printfat(const char *const file, const int lineno, 161 const int dirfd, const char *const path, 162 const char *const fmt, ...) 163{ 164 va_list va; 165 int rval; 166 167 va_start(va, fmt); 168 rval = safe_file_vprintfat(file, lineno, dirfd, path, fmt, va); 169 va_end(va); 170 171 return rval; 172} 173 174int safe_unlinkat(const char *const file, const int lineno, 175 const int dirfd, const char *const path, const int flags) 176{ 177 const int rval = unlinkat(dirfd, path, flags); 178 const char *flags_sym; 179 180 if (!rval) 181 return rval; 182 183 switch(flags) { 184 case AT_REMOVEDIR: 185 flags_sym = "AT_REMOVEDIR"; 186 break; 187 case 0: 188 flags_sym = "0"; 189 break; 190 default: 191 flags_sym = "?"; 192 break; 193 } 194 195 tst_brk_(file, lineno, TBROK | TERRNO, 196 "unlinkat(%d<%s>, '%s', %s)", 197 dirfd, tst_decode_fd(dirfd), path, flags_sym); 198 199 return rval; 200} 201 202int safe_fchownat(const char *const file, const int lineno, 203 const int dirfd, const char *const path, uid_t owner, gid_t group, int flags) 204{ 205 int rval; 206 207 rval = fchownat(dirfd, path, owner, group, flags); 208 209 if (rval == -1) { 210 tst_brk_(file, lineno, TBROK | TERRNO, 211 "fchownat(%d<%s>, '%s', %d, %d, %d) failed", dirfd, 212 tst_decode_fd(dirfd), path, owner, group, flags); 213 } else if (rval) { 214 tst_brk_(file, lineno, TBROK | TERRNO, 215 "Invalid fchownat(%d<%s>, '%s', %d, %d, %d) return value %d", 216 dirfd, tst_decode_fd(dirfd), path, owner, group, flags, rval); 217 } 218 219 return rval; 220} 221 222int safe_fstatat(const char *const file, const int lineno, 223 const int dirfd, const char *const path, struct stat *statbuf, int flags) 224{ 225 int rval; 226 227 rval = fstatat(dirfd, path, statbuf, flags); 228 229 if (rval == -1) { 230 tst_brk_(file, lineno, TBROK | TERRNO, 231 "fstatat(%d<%s>, '%s', %p, %d) failed", dirfd, 232 tst_decode_fd(dirfd), path, statbuf, flags); 233 } else if (rval) { 234 tst_brk_(file, lineno, TBROK | TERRNO, 235 "Invalid fstatat(%d<%s>, '%s', %p, %d) return value %d", 236 dirfd, tst_decode_fd(dirfd), path, statbuf, flags, rval); 237 } 238 239 return rval; 240} 241