| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (c) 2021 SUSE LLC <rpalethorpe@suse.com> |
| */ |
| |
| #define _GNU_SOURCE |
| #include <stdio.h> |
| #include "lapi/fcntl.h" |
| #include "tst_safe_file_at.h" |
| |
| #define TST_NO_DEFAULT_MAIN |
| #include "tst_test.h" |
| |
| static char fd_path[PATH_MAX]; |
| |
| const char *tst_decode_fd(const int fd) |
| { |
| ssize_t ret; |
| char proc_path[32]; |
| |
| if (fd < 0) |
| return "!"; |
| |
| sprintf(proc_path, "/proc/self/fd/%d", fd); |
| ret = readlink(proc_path, fd_path, sizeof(fd_path)); |
| |
| if (ret < 0) |
| return "?"; |
| |
| fd_path[ret] = '\0'; |
| |
| return fd_path; |
| } |
| |
| int safe_openat(const char *const file, const int lineno, |
| const int dirfd, const char *const path, const int oflags, ...) |
| { |
| va_list ap; |
| int fd; |
| mode_t mode; |
| |
| va_start(ap, oflags); |
| mode = va_arg(ap, int); |
| va_end(ap); |
| |
| fd = openat(dirfd, path, oflags, mode); |
| if (fd > -1) |
| return fd; |
| |
| tst_brk_(file, lineno, TBROK | TERRNO, |
| "openat(%d<%s>, '%s', %o, %o)", |
| dirfd, tst_decode_fd(dirfd), path, oflags, mode); |
| |
| return fd; |
| } |
| |
| ssize_t safe_file_readat(const char *const file, const int lineno, |
| const int dirfd, const char *const path, |
| char *const buf, const size_t nbyte) |
| { |
| int fd = safe_openat(file, lineno, dirfd, path, O_RDONLY); |
| ssize_t rval; |
| |
| if (fd < 0) |
| return -1; |
| |
| rval = safe_read(file, lineno, NULL, 0, fd, buf, nbyte - 1); |
| if (rval < 0) |
| return -1; |
| |
| close(fd); |
| buf[rval] = '\0'; |
| |
| if (rval >= (ssize_t)nbyte - 1) { |
| tst_brk_(file, lineno, TBROK, |
| "Buffer length %zu too small to read %d<%s>/%s", |
| nbyte, dirfd, tst_decode_fd(dirfd), path); |
| } |
| |
| return rval; |
| } |
| |
| int tst_file_vprintfat(const int dirfd, const char *const path, |
| const char *const fmt, va_list va) |
| { |
| const int fd = openat(dirfd, path, O_WRONLY); |
| int ret, errno_cpy; |
| |
| if (fd < 0) |
| return -1; |
| |
| ret = vdprintf(fd, fmt, va); |
| errno_cpy = errno; |
| close(fd); |
| |
| if (ret < 0) { |
| errno = errno_cpy; |
| return -2; |
| } |
| |
| return ret; |
| } |
| |
| int tst_file_printfat(const int dirfd, const char *const path, |
| const char *const fmt, ...) |
| { |
| va_list va; |
| int rval; |
| |
| va_start(va, fmt); |
| rval = tst_file_vprintfat(dirfd, path, fmt, va); |
| va_end(va); |
| |
| return rval; |
| } |
| |
| int safe_file_vprintfat(const char *const file, const int lineno, |
| const int dirfd, const char *const path, |
| const char *const fmt, va_list va) |
| { |
| char buf[16]; |
| va_list vac; |
| int rval, errno_cpy; |
| |
| va_copy(vac, va); |
| |
| rval = tst_file_vprintfat(dirfd, path, fmt, va); |
| |
| if (rval == -2) { |
| errno_cpy = errno; |
| rval = vsnprintf(buf, sizeof(buf), fmt, vac); |
| va_end(vac); |
| |
| if (rval >= (ssize_t)sizeof(buf)) |
| strcpy(buf + sizeof(buf) - 5, "..."); |
| else if (rval < 0) |
| buf[0] = '\0'; |
| |
| errno = errno_cpy; |
| tst_brk_(file, lineno, TBROK | TERRNO, |
| "vdprintf(%d<%s>, '%s', '%s'<%s>)", |
| dirfd, tst_decode_fd(dirfd), path, fmt, buf); |
| return -1; |
| } |
| |
| va_end(vac); |
| |
| if (rval == -1) { |
| tst_brk_(file, lineno, TBROK | TERRNO, |
| "openat(%d<%s>, '%s', O_WRONLY)", |
| dirfd, tst_decode_fd(dirfd), path); |
| } |
| |
| return rval; |
| } |
| |
| int safe_file_printfat(const char *const file, const int lineno, |
| const int dirfd, const char *const path, |
| const char *const fmt, ...) |
| { |
| va_list va; |
| int rval; |
| |
| va_start(va, fmt); |
| rval = safe_file_vprintfat(file, lineno, dirfd, path, fmt, va); |
| va_end(va); |
| |
| return rval; |
| } |
| |
| int safe_unlinkat(const char *const file, const int lineno, |
| const int dirfd, const char *const path, const int flags) |
| { |
| const int rval = unlinkat(dirfd, path, flags); |
| const char *flags_sym; |
| |
| if (!rval) |
| return rval; |
| |
| switch(flags) { |
| case AT_REMOVEDIR: |
| flags_sym = "AT_REMOVEDIR"; |
| break; |
| case 0: |
| flags_sym = "0"; |
| break; |
| default: |
| flags_sym = "?"; |
| break; |
| } |
| |
| tst_brk_(file, lineno, TBROK | TERRNO, |
| "unlinkat(%d<%s>, '%s', %s)", |
| dirfd, tst_decode_fd(dirfd), path, flags_sym); |
| |
| return rval; |
| } |