GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/nfs4/callback.cc
Date: 2026-11-01 02:41:19
Exec Total Coverage
Lines: 142 173 82.1%
Branches: 109 232 47.0%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "nfs4/callback.h"
6
7 #include <arpa/inet.h>
8 #include <errno.h>
9 #include <fcntl.h>
10 #include <netinet/in.h>
11 #include <netinet/tcp.h>
12 #include <poll.h>
13 #include <sys/socket.h>
14 #include <unistd.h>
15
16 #include <cstdlib>
17 #include <cstring>
18 #include <ctime>
19 #include <vector>
20
21 #include "nfs4/logging.h"
22 #include "nfs4/nfs4_const.h"
23 #include "nfs4/xdr.h"
24
25 namespace nfs4 {
26
27 namespace {
28
29 const uint32_t kCallbackVersion = 1;
30 const uint32_t kCbProcNull = 0;
31 const uint32_t kCbProcCompound = 1;
32 const uint32_t kOpCbRecall = 4;
33 const uint32_t kLastFragment = 0x80000000u;
34 const uint32_t kMaxReply = 64 * 1024;
35
36 /** Waits until fd is readable or writable, at most timeout_ms */
37 155 bool WaitFd(int fd, short events, unsigned timeout_ms) {
38 struct pollfd pfd;
39 155 pfd.fd = fd;
40 155 pfd.events = events;
41 155 pfd.revents = 0;
42 int retval;
43 do {
44
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 retval = poll(&pfd, 1, static_cast<int>(timeout_ms));
45
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
155 } while (retval < 0 && errno == EINTR);
46
2/4
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 return retval > 0 && !(pfd.revents & (POLLERR | POLLNVAL));
47 }
48
49 62 bool SendAll(int fd, const char *p, size_t len) {
50 62 int flags = 0;
51 #ifdef MSG_NOSIGNAL
52 62 flags = MSG_NOSIGNAL;
53 #endif
54
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 62 times.
124 while (len > 0) {
55 62 const ssize_t n = send(fd, p, len, flags);
56
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (n < 0) {
57 ✗ if (errno == EINTR)
58 ✗ continue;
59 ✗ return false;
60 }
61 62 p += n;
62 62 len -= n;
63 }
64 62 return true;
65 }
66
67 124 bool RecvAll(int fd, char *p, size_t len, unsigned timeout_ms) {
68
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 124 times.
248 while (len > 0) {
69
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 124 times.
124 if (!WaitFd(fd, POLLIN, timeout_ms))
70 ✗ return false;
71 124 const ssize_t n = recv(fd, p, len, 0);
72
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if (n == 0)
73 ✗ return false;
74
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if (n < 0) {
75 ✗ if (errno == EINTR || errno == EAGAIN)
76 ✗ continue;
77 ✗ return false;
78 }
79 124 p += n;
80 124 len -= n;
81 }
82 124 return true;
83 }
84
85 } // anonymous namespace
86
87
88 31 CallbackClient::CallbackClient(uint32_t program, uint32_t ident,
89 const std::string &netid,
90 31 const std::string &address)
91 31 : program_(program)
92 31 , ident_(ident)
93 31 , port_(0)
94 31 , fd_(-1)
95 31 , next_xid_(static_cast<uint32_t>(time(NULL)) << 8) {
96
2/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
31 if (netid != "tcp")
97 ✗ return;
98 // Universal address: a.b.c.d.p1.p2
99 31 const size_t last = address.rfind('.');
100
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 if (last == std::string::npos || last == 0)
101 ✗ return;
102 31 const size_t second = address.rfind('.', last - 1);
103
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (second == std::string::npos)
104 ✗ return;
105
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int hi = atoi(address.substr(second + 1, last - second - 1).c_str());
106
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int lo = atoi(address.substr(last + 1).c_str());
107
4/8
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
31 if (hi < 0 || hi > 255 || lo < 0 || lo > 255)
108 ✗ return;
109
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 host_ = address.substr(0, second);
110 struct in_addr probe;
111
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 if (inet_pton(AF_INET, host_.c_str(), &probe) != 1)
112 ✗ return;
113 31 port_ = static_cast<uint16_t>(hi * 256 + lo);
114 }
115
116
117 31 CallbackClient::~CallbackClient() { Disconnect(); }
118
119
120 31 void CallbackClient::Disconnect() {
121
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 if (fd_ >= 0)
122 31 close(fd_);
123 31 fd_ = -1;
124 31 }
125
126
127 62 bool CallbackClient::Connect(unsigned timeout_ms) {
128
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 if (fd_ >= 0)
129 31 return true;
130 31 fd_ = socket(AF_INET, SOCK_STREAM, 0);
131
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (fd_ < 0)
132 ✗ return false;
133 #ifdef SO_NOSIGPIPE
134 const int on = 1;
135 setsockopt(fd_, SOL_SOCKET, SO_NOSIGPIPE, &on, sizeof(on));
136 #endif
137 struct sockaddr_in addr;
138 31 memset(&addr, 0, sizeof(addr));
139 31 addr.sin_family = AF_INET;
140 31 addr.sin_port = htons(port_);
141 31 inet_pton(AF_INET, host_.c_str(), &addr.sin_addr);
142
143
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int flags = fcntl(fd_, F_GETFL);
144
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 fcntl(fd_, F_SETFL, flags | O_NONBLOCK);
145
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 int retval = connect(fd_, reinterpret_cast<struct sockaddr *>(&addr),
146 sizeof(addr));
147
5/10
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 31 times.
✗ Branch 10 not taken.
31 if (retval != 0 && errno == EINPROGRESS && WaitFd(fd_, POLLOUT, timeout_ms)) {
148 31 int error = 0;
149 31 socklen_t len = sizeof(error);
150 31 getsockopt(fd_, SOL_SOCKET, SO_ERROR, &error, &len);
151
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 retval = (error == 0) ? 0 : -1;
152 }
153
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (retval != 0) {
154 ✗ Log(kLogLevelDebug, "cannot connect to callback %s:%u", host_.c_str(),
155 ✗ port_);
156 ✗ Disconnect();
157 ✗ return false;
158 }
159 31 return true;
160 }
161
162
163 62 bool CallbackClient::Call(uint32_t procedure, const std::string &args,
164 std::string *result, unsigned timeout_ms) {
165
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::lock_guard<std::mutex> const guard(lock_);
166
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
62 if (!valid())
167 ✗ return false;
168
169 // A broken connection is reestablished once
170
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 for (unsigned attempt = 0; attempt < 2; ++attempt) {
171
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (!Connect(timeout_ms))
172 62 return false;
173 62 const uint32_t xid = next_xid_++;
174 62 XdrWriter w;
175
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 const size_t marker_offset = w.ReserveU32();
176
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(xid);
177
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(kRpcCall);
178
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(kRpcVersion);
179
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(program_);
180
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(kCallbackVersion);
181
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(procedure);
182 // AUTH_SYS as root: the Linux client rejects CB_COMPOUND with AUTH_NONE
183 62 XdrWriter cred;
184
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 cred.PutU32(static_cast<uint32_t>(time(NULL))); // stamp
185
2/4
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 62 times.
✗ Branch 6 not taken.
62 cred.PutString("cvmfs"); // machine name
186
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 cred.PutU32(0); // uid
187
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 cred.PutU32(0); // gid
188
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 cred.PutU32(0); // no auxiliary gids
189
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(kAuthSys);
190
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 w.PutOpaque(cred.buffer());
191
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(kAuthNone);
192
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 w.PutU32(0);
193
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 w.PutRaw(args.data(), args.size());
194 62 w.PatchU32(marker_offset,
195
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 kLastFragment | static_cast<uint32_t>(w.size() - 4));
196
2/4
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 62 times.
62 if (!SendAll(fd_, w.buffer().data(), w.size())) {
197 ✗ Disconnect();
198 ✗ continue;
199 }
200
201 // Read records until the reply to our xid arrives
202 while (true) {
203 uint32_t marker;
204
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (!RecvAll(fd_, reinterpret_cast<char *>(&marker), 4, timeout_ms)) {
205 ✗ Disconnect();
206 62 return false;
207 }
208 62 marker = ntohl(marker);
209 62 const uint32_t len = marker & ~kLastFragment;
210
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (len > kMaxReply) {
211 ✗ Disconnect();
212 ✗ return false;
213 }
214
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 std::string record(len, '\0');
215
5/10
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 62 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 62 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 62 times.
62 if (len > 0 && !RecvAll(fd_, &record[0], len, timeout_ms)) {
216 ✗ Disconnect();
217 ✗ return false;
218 }
219 62 XdrReader r(reinterpret_cast<const unsigned char *>(record.data()),
220 124 record.size());
221 uint32_t reply_xid, type, reply_stat, verf_flavor, accept;
222 62 std::string verf;
223
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&reply_xid);
224
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&type);
225
4/8
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 62 times.
62 if (!r.ok() || reply_xid != xid || type != kRpcReply)
226 ✗ continue;
227
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&reply_stat);
228
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&verf_flavor);
229
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetOpaque(&verf, 400);
230
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&accept);
231
4/8
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 62 times.
62 if (!r.ok() || reply_stat != kMsgAccepted || accept != kAcceptSuccess)
232 ✗ return false;
233
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 result->assign(reinterpret_cast<const char *>(r.cursor()),
234 r.remaining());
235 62 return true;
236
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
124 }
237
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
124 }
238 ✗ return false;
239 62 }
240
241
242 31 bool CallbackClient::Null(unsigned timeout_ms) {
243 31 std::string result;
244
2/4
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
62 return Call(kCbProcNull, "", &result, timeout_ms);
245 31 }
246
247
248 31 int CallbackClient::Recall(uint32_t stateid_seqid,
249 const std::string &stateid_other,
250 const std::string &fh, unsigned timeout_ms) {
251 31 XdrWriter args;
252
2/4
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
31 args.PutString(""); // tag
253
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutU32(0); // minor version
254
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutU32(ident_);
255
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutU32(1); // one op
256
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutU32(kOpCbRecall);
257
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutU32(stateid_seqid);
258
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 args.PutFixedOpaque(stateid_other.data(), kStateidOtherSize);
259
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutBool(false); // truncate
260
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 args.PutOpaque(fh);
261 31 std::string result;
262
2/4
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
31 if (!Call(kCbProcCompound, args.buffer(), &result, timeout_ms))
263 ✗ return -1;
264 31 XdrReader r(reinterpret_cast<const unsigned char *>(result.data()),
265 62 result.size());
266 uint32_t status;
267
2/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
31 if (!r.GetU32(&status))
268 ✗ return -1;
269 31 return static_cast<int>(status);
270 31 }
271
272 } // namespace nfs4
273