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
14 static char fd_path[PATH_MAX];
15
tst_decode_fd(const int fd)16 const 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
safe_openat(const char * const file,const int lineno,const int dirfd,const char * const path,const int oflags,...)35 int 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
safe_file_readat(const char * const file,const int lineno,const int dirfd,const char * const path,char * const buf,const size_t nbyte)60 ssize_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
tst_file_vprintfat(const int dirfd,const char * const path,const char * const fmt,va_list va)86 int 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
tst_file_printfat(const int dirfd,const char * const path,const char * const fmt,...)107 int 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
safe_file_vprintfat(const char * const file,const int lineno,const int dirfd,const char * const path,const char * const fmt,va_list va)120 int 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
safe_file_printfat(const char * const file,const int lineno,const int dirfd,const char * const path,const char * const fmt,...)160 int 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
safe_unlinkat(const char * const file,const int lineno,const int dirfd,const char * const path,const int flags)174 int 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
safe_fchownat(const char * const file,const int lineno,const int dirfd,const char * const path,uid_t owner,gid_t group,int flags)202 int 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
safe_fstatat(const char * const file,const int lineno,const int dirfd,const char * const path,struct stat * statbuf,int flags)222 int 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