xref: /third_party/ltp/lib/tst_safe_file_at.c (revision f08c3bdf)
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