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 va_list ap;
39 int fd;
40 mode_t mode;
41
42 va_start(ap, oflags);
43 mode = va_arg(ap, int);
44 va_end(ap);
45
46 fd = openat(dirfd, path, oflags, mode);
47 if (fd > -1)
48 return fd;
49
50 tst_brk_(file, lineno, TBROK | TERRNO,
51 "openat(%d<%s>, '%s', %o, %o)",
52 dirfd, tst_decode_fd(dirfd), path, oflags, mode);
53
54 return fd;
55 }
56
safe_file_readat(const char * const file,const int lineno,const int dirfd,const char * const path,char * const buf,const size_t nbyte)57 ssize_t safe_file_readat(const char *const file, const int lineno,
58 const int dirfd, const char *const path,
59 char *const buf, const size_t nbyte)
60 {
61 int fd = safe_openat(file, lineno, dirfd, path, O_RDONLY);
62 ssize_t rval;
63
64 if (fd < 0)
65 return -1;
66
67 rval = safe_read(file, lineno, NULL, 0, fd, buf, nbyte - 1);
68 if (rval < 0)
69 return -1;
70
71 close(fd);
72 buf[rval] = '\0';
73
74 if (rval >= (ssize_t)nbyte - 1) {
75 tst_brk_(file, lineno, TBROK,
76 "Buffer length %zu too small to read %d<%s>/%s",
77 nbyte, dirfd, tst_decode_fd(dirfd), path);
78 }
79
80 return rval;
81 }
82
tst_file_vprintfat(const int dirfd,const char * const path,const char * const fmt,va_list va)83 int tst_file_vprintfat(const int dirfd, const char *const path,
84 const char *const fmt, va_list va)
85 {
86 const int fd = openat(dirfd, path, O_WRONLY);
87 int ret, errno_cpy;
88
89 if (fd < 0)
90 return -1;
91
92 ret = vdprintf(fd, fmt, va);
93 errno_cpy = errno;
94 close(fd);
95
96 if (ret < 0) {
97 errno = errno_cpy;
98 return -2;
99 }
100
101 return ret;
102 }
103
tst_file_printfat(const int dirfd,const char * const path,const char * const fmt,...)104 int tst_file_printfat(const int dirfd, const char *const path,
105 const char *const fmt, ...)
106 {
107 va_list va;
108 int rval;
109
110 va_start(va, fmt);
111 rval = tst_file_vprintfat(dirfd, path, fmt, va);
112 va_end(va);
113
114 return rval;
115 }
116
safe_file_vprintfat(const char * const file,const int lineno,const int dirfd,const char * const path,const char * const fmt,va_list va)117 int safe_file_vprintfat(const char *const file, const int lineno,
118 const int dirfd, const char *const path,
119 const char *const fmt, va_list va)
120 {
121 char buf[16];
122 va_list vac;
123 int rval, errno_cpy;
124
125 va_copy(vac, va);
126
127 rval = tst_file_vprintfat(dirfd, path, fmt, va);
128
129 if (rval == -2) {
130 errno_cpy = errno;
131 rval = vsnprintf(buf, sizeof(buf), fmt, vac);
132 va_end(vac);
133
134 if (rval >= (ssize_t)sizeof(buf))
135 strcpy(buf + sizeof(buf) - 5, "...");
136 else if (rval < 0)
137 buf[0] = '\0';
138
139 errno = errno_cpy;
140 tst_brk_(file, lineno, TBROK | TERRNO,
141 "vdprintf(%d<%s>, '%s', '%s'<%s>)",
142 dirfd, tst_decode_fd(dirfd), path, fmt, buf);
143 return -1;
144 }
145
146 va_end(vac);
147
148 if (rval == -1) {
149 tst_brk_(file, lineno, TBROK | TERRNO,
150 "openat(%d<%s>, '%s', O_WRONLY)",
151 dirfd, tst_decode_fd(dirfd), path);
152 }
153
154 return rval;
155 }
156
safe_file_printfat(const char * const file,const int lineno,const int dirfd,const char * const path,const char * const fmt,...)157 int safe_file_printfat(const char *const file, const int lineno,
158 const int dirfd, const char *const path,
159 const char *const fmt, ...)
160 {
161 va_list va;
162 int rval;
163
164 va_start(va, fmt);
165 rval = safe_file_vprintfat(file, lineno, dirfd, path, fmt, va);
166 va_end(va);
167
168 return rval;
169 }
170
safe_unlinkat(const char * const file,const int lineno,const int dirfd,const char * const path,const int flags)171 int safe_unlinkat(const char *const file, const int lineno,
172 const int dirfd, const char *const path, const int flags)
173 {
174 const int rval = unlinkat(dirfd, path, flags);
175 const char *flags_sym;
176
177 if (!rval)
178 return rval;
179
180 switch(flags) {
181 case AT_REMOVEDIR:
182 flags_sym = "AT_REMOVEDIR";
183 break;
184 case 0:
185 flags_sym = "0";
186 break;
187 default:
188 flags_sym = "?";
189 break;
190 }
191
192 tst_brk_(file, lineno, TBROK | TERRNO,
193 "unlinkat(%d<%s>, '%s', %s)",
194 dirfd, tst_decode_fd(dirfd), path, flags_sym);
195
196 return rval;
197 }
198