GCC Code Coverage Report


Directory: cvmfs/
File: cvmfs/nfs4/nfs4_server.cc
Date: 2026-11-01 02:41:19
Exec Total Coverage
Lines: 980 1390 70.5%
Branches: 799 1651 48.4%

Line Branch Exec Source
1 /**
2 * This file is part of the CernVM File System.
3 */
4
5 #include "nfs4/nfs4_server.h"
6
7 #include <errno.h>
8 #include <inttypes.h>
9 #include <sys/stat.h>
10
11 #include <chrono>
12 #include <cstdio>
13 #include <cstring>
14 #include <ctime>
15
16 #include "nfs4/logging.h"
17
18 #ifndef ENODATA
19 #define ENODATA ENOATTR
20 #endif
21
22 namespace nfs4 {
23
24 namespace {
25
26 const uint32_t kMaxName = 255;
27 const uint64_t kAttrDirFileIdBit = 1ull << 63;
28 const uint64_t kNamedAttrFileIdBit = 1ull << 62;
29
30 // Attributes this server can return, as bitmap words 0 and 1
31 const unsigned kSupportedAttrs[] = {FATTR4_SUPPORTED_ATTRS,
32 FATTR4_TYPE,
33 FATTR4_FH_EXPIRE_TYPE,
34 FATTR4_CHANGE,
35 FATTR4_SIZE,
36 FATTR4_LINK_SUPPORT,
37 FATTR4_SYMLINK_SUPPORT,
38 FATTR4_NAMED_ATTR,
39 FATTR4_FSID,
40 FATTR4_UNIQUE_HANDLES,
41 FATTR4_LEASE_TIME,
42 FATTR4_RDATTR_ERROR,
43 FATTR4_ACLSUPPORT,
44 FATTR4_CANSETTIME,
45 FATTR4_CASE_INSENSITIVE,
46 FATTR4_CASE_PRESERVING,
47 FATTR4_CHOWN_RESTRICTED,
48 FATTR4_FILEHANDLE,
49 FATTR4_FILEID,
50 FATTR4_FILES_AVAIL,
51 FATTR4_FILES_FREE,
52 FATTR4_FILES_TOTAL,
53 FATTR4_HOMOGENEOUS,
54 FATTR4_MAXFILESIZE,
55 FATTR4_MAXLINK,
56 FATTR4_MAXNAME,
57 FATTR4_MAXREAD,
58 FATTR4_MAXWRITE,
59 FATTR4_MODE,
60 FATTR4_NO_TRUNC,
61 FATTR4_NUMLINKS,
62 FATTR4_OWNER,
63 FATTR4_OWNER_GROUP,
64 FATTR4_RAWDEV,
65 FATTR4_SPACE_AVAIL,
66 FATTR4_SPACE_FREE,
67 FATTR4_SPACE_TOTAL,
68 FATTR4_SPACE_USED,
69 FATTR4_TIME_ACCESS,
70 FATTR4_TIME_DELTA,
71 FATTR4_TIME_METADATA,
72 FATTR4_TIME_MODIFY,
73 FATTR4_MOUNTED_ON_FILEID};
74
75 const unsigned kNumBitmapWords = 2;
76
77 2170 void SupportedBitmap(uint32_t *words) {
78 2170 words[0] = words[1] = 0;
79
2/2
✓ Branch 0 taken 93310 times.
✓ Branch 1 taken 2170 times.
95480 for (unsigned i = 0; i < sizeof(kSupportedAttrs) / sizeof(unsigned); ++i)
80 93310 words[kSupportedAttrs[i] / 32] |= 1u << (kSupportedAttrs[i] % 32);
81 2170 }
82
83 147963 bool IsSet(const uint32_t *bitmap, unsigned len, unsigned attr) {
84
3/4
✓ Branch 0 taken 147963 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 2108 times.
✓ Branch 3 taken 145855 times.
147963 return (attr / 32 < len) && (bitmap[attr / 32] & (1u << (attr % 32)));
85 }
86
87 62 void PutTime(const Timespec &t, XdrWriter *w) {
88 62 w->PutI64(t.sec);
89 62 w->PutU32(t.nsec);
90 62 }
91
92 2108 void PutBitmap(const uint32_t *words, unsigned len, XdrWriter *w) {
93 // Trailing zero words are dropped, as recommended by RFC 7530
94
4/4
✓ Branch 0 taken 4154 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 2139 times.
✓ Branch 3 taken 2015 times.
4247 while (len > 0 && words[len - 1] == 0)
95 2139 len--;
96 2108 w->PutU32(len);
97
2/2
✓ Branch 0 taken 2077 times.
✓ Branch 1 taken 2108 times.
4185 for (unsigned i = 0; i < len; ++i)
98 2077 w->PutU32(words[i]);
99 2108 }
100
101 1860 uint32_t FileType(const FileHandle &fh, mode_t mode) {
102
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1860 times.
1860 if (fh.kind == FileHandle::kAttrDir)
103 ✗ return NF4ATTRDIR;
104
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1829 times.
1860 if (fh.kind == FileHandle::kNamedAttr)
105 31 return NF4NAMEDATTR;
106
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISDIR(mode))
107 ✗ return NF4DIR;
108
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISLNK(mode))
109 ✗ return NF4LNK;
110
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISCHR(mode))
111 ✗ return NF4CHR;
112
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISBLK(mode))
113 ✗ return NF4BLK;
114
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISFIFO(mode))
115 ✗ return NF4FIFO;
116
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 1829 times.
1829 if (S_ISSOCK(mode))
117 ✗ return NF4SOCK;
118 1829 return NF4REG;
119 }
120
121 6262 bool IsValidUtf8(const std::string &s) {
122 6262 unsigned i = 0;
123
2/2
✓ Branch 1 taken 36580 times.
✓ Branch 2 taken 6262 times.
42842 while (i < s.size()) {
124 36580 const unsigned char c = s[i];
125 unsigned n;
126
1/2
✓ Branch 0 taken 36580 times.
✗ Branch 1 not taken.
36580 if (c < 0x80)
127 36580 n = 0;
128 ✗ else if ((c & 0xE0) == 0xC0)
129 ✗ n = 1;
130 ✗ else if ((c & 0xF0) == 0xE0)
131 ✗ n = 2;
132 ✗ else if ((c & 0xF8) == 0xF0)
133 ✗ n = 3;
134 else
135 ✗ return false;
136
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 36580 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 36580 times.
36580 if (i + n >= s.size() && n > 0)
137 ✗ return false;
138
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 36580 times.
36580 for (unsigned j = 1; j <= n; ++j) {
139 ✗ if ((static_cast<unsigned char>(s[i + j]) & 0xC0) != 0x80)
140 ✗ return false;
141 }
142 36580 i += n + 1;
143 }
144 6262 return true;
145 }
146
147 4402 uint32_t CheckName(const std::string &name) {
148
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4402 times.
4402 if (name.empty())
149 ✗ return NFS4ERR_INVAL;
150
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4402 times.
4402 if (name.size() > kMaxName)
151 ✗ return NFS4ERR_NAMETOOLONG;
152
6/8
✓ Branch 1 taken 4402 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 4371 times.
✓ Branch 5 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 4371 times.
✓ Branch 9 taken 31 times.
✓ Branch 10 taken 4371 times.
4402 if (name == "." || name == ".." || name.find('/') != std::string::npos)
153 31 return NFS4ERR_BADNAME;
154
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 4371 times.
4371 if (!IsValidUtf8(name))
155 ✗ return NFS4ERR_INVAL;
156 4371 return NFS4_OK;
157 }
158
159 ✗ std::string NumericId(uint32_t id) {
160 char buf[16];
161 ✗ snprintf(buf, sizeof(buf), "%u", id);
162 ✗ return buf;
163 }
164
165 /**
166 * Permission check against the mode bits. Write access is never granted.
167 */
168 403 uint32_t AccessFromMode(const Attr &attr, const Credentials &creds) {
169 unsigned perms;
170
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 403 times.
403 if (creds.uid == 0) {
171 ✗ perms = 04 | ((attr.mode & 0111) ? 01 : 0);
172
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 62 times.
403 } else if (creds.uid == attr.uid) {
173 341 perms = (attr.mode >> 6) & 07;
174 } else {
175 62 bool in_group = (creds.gid == attr.gid);
176
4/6
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
62 for (unsigned i = 0; !in_group && i < creds.gids.size(); ++i)
177 ✗ in_group = (creds.gids[i] == attr.gid);
178
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 perms = in_group ? (attr.mode >> 3) & 07 : attr.mode & 07;
179 }
180
181 403 uint32_t granted = 0;
182
2/2
✓ Branch 0 taken 341 times.
✓ Branch 1 taken 62 times.
403 if (perms & 04)
183 341 granted |= ACCESS4_READ;
184
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 403 times.
403 if (perms & 01)
185 ✗ granted |= S_ISDIR(attr.mode) ? ACCESS4_LOOKUP : ACCESS4_EXECUTE;
186 403 return granted;
187 }
188
189 // Errors after which an open-owner's seqid is not advanced (RFC 7530 9.1.7)
190 465 bool SeqidAdvances(uint32_t status) {
191
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 465 times.
465 switch (status) {
192 ✗ case NFS4ERR_STALE_CLIENTID:
193 case NFS4ERR_STALE_STATEID:
194 case NFS4ERR_BAD_STATEID:
195 case NFS4ERR_BAD_SEQID:
196 case NFS4ERR_BADXDR:
197 case NFS4ERR_RESOURCE:
198 case NFS4ERR_NOFILEHANDLE:
199 case NFS4ERR_MOVED:
200 ✗ return false;
201 465 default:
202 465 return true;
203 }
204 }
205
206 } // anonymous namespace
207
208
209 372 uint32_t ErrnoToStatus(int err) {
210
2/18
✓ Branch 0 taken 310 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✗ Branch 12 not taken.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
372 switch (err) {
211 310 case 0:
212 310 return NFS4_OK;
213 ✗ case EPERM:
214 ✗ return NFS4ERR_PERM;
215 62 case ENOENT:
216 case ENODATA:
217 62 return NFS4ERR_NOENT;
218 ✗ case EACCES:
219 ✗ return NFS4ERR_ACCESS;
220 ✗ case ENOTDIR:
221 ✗ return NFS4ERR_NOTDIR;
222 ✗ case EISDIR:
223 ✗ return NFS4ERR_ISDIR;
224 ✗ case EINVAL:
225 ✗ return NFS4ERR_INVAL;
226 ✗ case ENAMETOOLONG:
227 ✗ return NFS4ERR_NAMETOOLONG;
228 ✗ case EROFS:
229 ✗ return NFS4ERR_ROFS;
230 ✗ case ESTALE:
231 ✗ return NFS4ERR_STALE;
232 ✗ case EAGAIN:
233 case EBUSY:
234 ✗ return NFS4ERR_DELAY;
235 ✗ case ENOSPC:
236 ✗ return NFS4ERR_NOSPC;
237 ✗ case EFBIG:
238 ✗ return NFS4ERR_FBIG;
239 ✗ case ERANGE:
240 ✗ return NFS4ERR_TOOSMALL;
241 ✗ case ELOOP:
242 ✗ return NFS4ERR_SYMLINK;
243 ✗ case EMFILE:
244 case ENFILE:
245 ✗ return NFS4ERR_DELAY;
246 ✗ case EIO:
247 ✗ return NFS4ERR_IO;
248 ✗ default:
249 ✗ Log(kLogLevelWarn, "mapping unexpected errno %d to NFS4ERR_IO", err);
250 ✗ return NFS4ERR_IO;
251 }
252 }
253
254
255 558 Nfs4Server::Nfs4Server(Backend *backend, const Options &options)
256 558 : backend_(backend)
257 558 , options_(options)
258 1116 , fsid_(backend->FsId())
259 558 , boot_(static_cast<uint32_t>(time(NULL)))
260 558 , next_clientid_(1)
261 558 , next_stateid_(1)
262 558 , num_frozen_attrs_(0)
263 558 , last_generation_(0)
264 558 , monitor_stopping_(false)
265
2/4
✓ Branch 2 taken 558 times.
✗ Branch 3 not taken.
✓ Branch 17 taken 558 times.
✗ Branch 18 not taken.
1674 , executors_stopping_(false) { }
266
267
268 2170 Nfs4Server::~Nfs4Server() {
269 {
270 1116 std::lock_guard<std::mutex> const lock(state_lock_);
271 1116 monitor_stopping_ = true;
272 }
273 1116 monitor_cond_.notify_all();
274
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 527 times.
1116 if (monitor_.joinable())
275 62 monitor_.join();
276 {
277 1116 std::lock_guard<std::mutex> const lock(executor_lock_);
278 1116 executors_stopping_ = true;
279 }
280 1116 executor_cond_.notify_all();
281
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 558 times.
1240 for (unsigned i = 0; i < executors_.size(); ++i)
282 124 executors_[i].join();
283 2170 }
284
285
286 124 void Nfs4Server::ExecutorLoop() {
287 while (true) {
288 124 TaskPtr task;
289 {
290
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 std::unique_lock<std::mutex> lock(executor_lock_);
291 124 executor_cond_.wait(
292
4/4
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 62 times.
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 62 times.
186 lock, [this] { return executors_stopping_ || !tasks_.empty(); });
293
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 62 times.
124 if (tasks_.empty())
294 124 return;
295 62 task = tasks_.front();
296 62 tasks_.pop_front();
297
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 62 times.
124 }
298 62 XdrReader args(reinterpret_cast<const unsigned char *>(task->args.data()),
299 124 task->args.size());
300
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 ProcessCompound(task->call, &args, &task->res);
301 {
302
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::lock_guard<std::mutex> const lock(executor_lock_);
303 62 task->done = true;
304 62 }
305 62 task->done_cond.notify_one();
306
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 62 times.
186 }
307 }
308
309
310 62 void Nfs4Server::RunWithDeadline(const RpcCall &call, XdrReader *args,
311 XdrWriter *res) {
312
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 62 times.
✗ Branch 6 not taken.
62 TaskPtr task(new Task());
313
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 task->call = call;
314
1/2
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 task->args.assign(reinterpret_cast<const char *>(args->cursor()),
315 args->remaining());
316 62 task->call.args = NULL;
317 62 task->call.args_size = 0;
318
319
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::unique_lock<std::mutex> lock(executor_lock_);
320
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 31 times.
62 if (executors_.empty()) {
321
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 31 times.
93 for (unsigned i = 0; i < options_.executor_threads; ++i)
322
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 62 times.
✗ Branch 5 not taken.
62 executors_.push_back(std::thread(&Nfs4Server::ExecutorLoop, this));
323 }
324
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 tasks_.push_back(task);
325 62 executor_cond_.notify_one();
326
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
124 const bool done = task->done_cond.wait_for(
327 62 lock, std::chrono::milliseconds(options_.delay_after_ms),
328 124 [&task] { return task->done; });
329
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 if (done) {
330
1/2
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
31 res->PutRaw(task->res.buffer().data(), task->res.size());
331 31 return;
332 }
333
334 // Still queued or running: the task completes on its own, and the client's
335 // retry benefits from the warmed cache (or the open-owner replay cache)
336
1/2
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
31 for (std::deque<TaskPtr>::iterator it = tasks_.begin(); it != tasks_.end();
337 ✗ ++it) {
338 ✗ if (*it == task) {
339 ✗ tasks_.erase(it);
340 ✗ break;
341 }
342 }
343
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 lock.unlock();
344
345 31 XdrReader r(reinterpret_cast<const unsigned char *>(task->args.data()),
346 62 task->args.size());
347 31 std::string tag;
348 31 uint32_t minor_version = 0, num_ops = 0, first_op = 0;
349
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 r.GetOpaque(&tag, kNfs4OpaqueLimit);
350
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 r.GetU32(&minor_version);
351
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 r.GetU32(&num_ops);
352
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 r.GetU32(&first_op);
353
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 Log(kLogLevelDebug, "xid %u delayed", call.xid);
354
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutU32(NFS4ERR_DELAY);
355
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutOpaque(tag);
356
3/6
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 31 times.
31 if (num_ops == 0 || !r.ok()) {
357 ✗ res->PutU32(0);
358 ✗ return;
359 }
360
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutU32(1);
361
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutU32(first_op);
362
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutU32(NFS4ERR_DELAY);
363
5/6
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31 times.
✓ Branch 5 taken 31 times.
✓ Branch 7 taken 31 times.
✓ Branch 8 taken 31 times.
93 }
364
365
366 3534 AcceptStat Nfs4Server::Dispatch(const RpcCall &call, XdrReader *args,
367 XdrWriter *res) {
368
1/3
✗ Branch 0 not taken.
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 switch (call.procedure) {
369 ✗ case kNfsProcNull:
370 ✗ return kAcceptSuccess;
371 3534 case kNfsProcCompound:
372
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 3472 times.
3534 if (options_.delay_after_ms > 0)
373 62 RunWithDeadline(call, args, res);
374 else
375 3472 ProcessCompound(call, args, res);
376 3534 return kAcceptSuccess;
377 ✗ default:
378 ✗ return kAcceptProcUnavail;
379 }
380 }
381
382
383 3534 uint32_t Nfs4Server::ProcessCompound(const RpcCall &call, XdrReader *args,
384 XdrWriter *res) {
385 3534 std::string tag;
386 3534 uint32_t minor_version = 0, num_ops = 0;
387
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 args->GetOpaque(&tag, kNfs4OpaqueLimit);
388
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 args->GetU32(&minor_version);
389
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 args->GetU32(&num_ops);
390
391
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 const size_t status_offset = res->ReserveU32();
392
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 res->PutOpaque(tag);
393
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 const size_t count_offset = res->ReserveU32();
394
395 3534 uint32_t status = NFS4_OK;
396 3534 uint32_t num_results = 0;
397
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 3534 times.
3534 if (!args->ok()) {
398 ✗ status = NFS4ERR_BADXDR;
399
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 3503 times.
3534 } else if (minor_version != 0) {
400 31 status = NFS4ERR_MINOR_VERS_MISMATCH;
401
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3503 times.
3503 } else if (num_ops > options_.max_compound_ops) {
402 ✗ status = NFS4ERR_RESOURCE;
403 } else {
404 3503 Compound c;
405 3503 c.call = &call;
406
2/2
✓ Branch 0 taken 8742 times.
✓ Branch 1 taken 2697 times.
11439 for (uint32_t i = 0; i < num_ops; ++i) {
407 uint32_t op;
408
2/4
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 8742 times.
8742 if (!args->GetU32(&op)) {
409 ✗ status = NFS4ERR_BADXDR;
410 806 break;
411 }
412 // The op's result is written in place; on failure it is cut off again
413
1/2
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
8742 const size_t op_offset = res->ReserveU32();
414
1/2
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
8742 const size_t status_offset_op = res->ReserveU32();
415 8742 const size_t body_offset = res->size();
416
1/2
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
8742 status = ProcessOp(op, args, &c, res);
417
4/6
✓ Branch 0 taken 7936 times.
✓ Branch 1 taken 806 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 7936 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 8742 times.
8742 if (status == NFS4_OK && !args->ok())
418 ✗ status = NFS4ERR_BADXDR;
419
1/2
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
8742 Log(kLogLevelDebug, "xid %u op %u -> %u", call.xid, op, status);
420 8742 const bool illegal = (status == NFS4ERR_OP_ILLEGAL);
421
3/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 8711 times.
✓ Branch 3 taken 8742 times.
✗ Branch 4 not taken.
8742 res->PatchU32(op_offset, illegal ? static_cast<uint32_t>(OP_ILLEGAL) : op);
422
1/2
✓ Branch 1 taken 8742 times.
✗ Branch 2 not taken.
8742 res->PatchU32(status_offset_op, status);
423
2/2
✓ Branch 0 taken 806 times.
✓ Branch 1 taken 7936 times.
8742 if (status != NFS4_OK) {
424
1/2
✓ Branch 1 taken 806 times.
✗ Branch 2 not taken.
806 res->Truncate(body_offset);
425
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 775 times.
806 if (op == OP_SETATTR)
426
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 res->PutU32(0); // attrsset is returned on failure, too
427 }
428 8742 num_results++;
429
2/2
✓ Branch 0 taken 806 times.
✓ Branch 1 taken 7936 times.
8742 if (status != NFS4_OK)
430 806 break;
431 }
432 }
433
434
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 3503 times.
3534 if (num_results == 0) {
435
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 Log(kLogLevelDebug, "xid %u: compound of %u ops rejected -> %u", call.xid,
436 num_ops, status);
437 }
438
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 res->PatchU32(status_offset, status);
439
1/2
✓ Branch 1 taken 3534 times.
✗ Branch 2 not taken.
3534 res->PatchU32(count_offset, num_results);
440 3534 return status;
441 3534 }
442
443
444 8742 uint32_t Nfs4Server::ProcessOp(uint32_t op, XdrReader *args, Compound *c,
445 XdrWriter *body) {
446
21/28
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 93 times.
✓ Branch 2 taken 186 times.
✓ Branch 3 taken 1302 times.
✓ Branch 4 taken 2077 times.
✓ Branch 5 taken 155 times.
✓ Branch 6 taken 31 times.
✓ Branch 7 taken 31 times.
✓ Branch 8 taken 496 times.
✓ Branch 9 taken 93 times.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 1674 times.
✓ Branch 13 taken 1395 times.
✓ Branch 14 taken 217 times.
✓ Branch 15 taken 372 times.
✓ Branch 16 taken 31 times.
✓ Branch 17 taken 62 times.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✓ Branch 21 taken 155 times.
✓ Branch 22 taken 155 times.
✗ Branch 23 not taken.
✓ Branch 24 taken 62 times.
✗ Branch 25 not taken.
✓ Branch 26 taken 62 times.
✓ Branch 27 taken 31 times.
8742 switch (op) {
447 62 case OP_ACCESS:
448 62 return OpAccess(args, c, body);
449 93 case OP_CLOSE:
450 93 return OpClose(args, c, body);
451 186 case OP_GETATTR:
452 186 return OpGetattr(args, c, body);
453 1302 case OP_GETFH:
454 1302 return OpGetfh(c, body);
455 2077 case OP_LOOKUP:
456 2077 return OpLookup(args, c);
457 155 case OP_LOOKUPP:
458 155 return OpLookupp(c);
459 31 case OP_NVERIFY:
460 31 return OpVerify(args, c, true);
461 31 case OP_VERIFY:
462 31 return OpVerify(args, c, false);
463 496 case OP_OPEN:
464 496 return OpOpen(args, c, body);
465 93 case OP_OPENATTR:
466 93 return OpOpenattr(args, c);
467 ✗ case OP_OPEN_CONFIRM:
468 ✗ return OpOpenConfirm(args, c, body);
469 ✗ case OP_OPEN_DOWNGRADE:
470 ✗ return OpOpenDowngrade(args, c, body);
471 1674 case OP_PUTFH:
472 1674 return OpPutfh(args, c);
473 1395 case OP_PUTPUBFH:
474 case OP_PUTROOTFH:
475 1395 c->fh = FileHandle(FileHandle::kObject, fsid_, backend_->RootInode());
476 1395 c->has_fh = true;
477 1395 return NFS4_OK;
478 217 case OP_READ:
479 217 return OpRead(args, c, body);
480 372 case OP_READDIR:
481 372 return OpReaddir(args, c, body);
482 31 case OP_READLINK:
483 31 return OpReadlink(c, body);
484 62 case OP_RENEW:
485 62 return OpRenew(args);
486 ✗ case OP_RESTOREFH:
487 ✗ if (!c->has_saved)
488 ✗ return NFS4ERR_RESTOREFH;
489 ✗ c->fh = c->saved;
490 ✗ c->has_fh = true;
491 ✗ return NFS4_OK;
492 ✗ case OP_SAVEFH:
493 ✗ if (!c->has_fh)
494 ✗ return NFS4ERR_NOFILEHANDLE;
495 ✗ c->saved = c->fh;
496 ✗ c->has_saved = true;
497 ✗ return NFS4_OK;
498 ✗ case OP_SECINFO:
499 ✗ return OpSecinfo(args, c, body);
500 155 case OP_SETCLIENTID:
501 155 return OpSetclientid(args, body);
502 155 case OP_SETCLIENTID_CONFIRM:
503 155 return OpSetclientidConfirm(args);
504 ✗ case OP_RELEASE_LOCKOWNER:
505 ✗ return NFS4_OK;
506 62 case OP_DELEGRETURN:
507 62 return OpDelegreturn(args);
508 ✗ case OP_DELEGPURGE:
509 case OP_LOCK:
510 case OP_LOCKT:
511 case OP_LOCKU:
512 ✗ return NFS4ERR_NOTSUPP;
513 62 case OP_COMMIT:
514 case OP_CREATE:
515 case OP_LINK:
516 case OP_REMOVE:
517 case OP_RENAME:
518 case OP_SETATTR:
519 case OP_WRITE:
520
1/2
✓ Branch 0 taken 62 times.
✗ Branch 1 not taken.
62 return c->has_fh ? NFS4ERR_ROFS : NFS4ERR_NOFILEHANDLE;
521 31 default:
522 31 return NFS4ERR_OP_ILLEGAL;
523 }
524 }
525
526
527 //------------------------------------------------------------------------------
528 // Object resolution
529
530
531 155 uint32_t Nfs4Server::ResolveXattrName(uint64_t ino, uint32_t hash,
532 std::string *name) {
533 155 std::vector<std::string> names;
534
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 const int retval = backend_->ListXattr(ino, &names);
535
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 if (retval != 0)
536 ✗ return ErrnoToStatus(retval);
537
1/2
✓ Branch 1 taken 279 times.
✗ Branch 2 not taken.
279 for (unsigned i = 0; i < names.size(); ++i) {
538
3/4
✓ Branch 2 taken 279 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
✓ Branch 5 taken 124 times.
279 if (HashName(names[i]) == hash) {
539
1/2
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 *name = names[i];
540 155 return NFS4_OK;
541 }
542 }
543 ✗ return NFS4ERR_STALE;
544 155 }
545
546
547 3720 uint32_t Nfs4Server::Resolve(const FileHandle &fh, const Credentials &creds,
548 Object *obj) {
549 3720 obj->fh = fh;
550 3720 int retval = backend_->GetAttr(fh.ino, &obj->attr);
551
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 3689 times.
3720 if (retval == ENOENT)
552 31 return NFS4ERR_STALE;
553
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3689 times.
3689 if (retval != 0)
554 ✗ return ErrnoToStatus(retval);
555
556
6/6
✓ Branch 0 taken 3441 times.
✓ Branch 1 taken 248 times.
✓ Branch 3 taken 155 times.
✓ Branch 4 taken 3286 times.
✓ Branch 5 taken 155 times.
✓ Branch 6 taken 3534 times.
3689 if (fh.kind == FileHandle::kObject && num_frozen_attrs_ > 0) {
557
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 std::lock_guard<std::mutex> const lock(state_lock_);
558
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 std::map<uint64_t, Attr>::const_iterator const it = frozen_attrs_.find(fh.ino);
559
2/2
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 124 times.
155 if (it != frozen_attrs_.end())
560 31 obj->attr = it->second;
561 155 }
562
2/2
✓ Branch 0 taken 93 times.
✓ Branch 1 taken 3596 times.
3689 if (fh.kind == FileHandle::kAttrDir) {
563 93 obj->attr.mode = S_IFDIR | 0555;
564 93 obj->attr.nlink = 2;
565 93 obj->attr.size = 0;
566
2/2
✓ Branch 0 taken 155 times.
✓ Branch 1 taken 3441 times.
3596 } else if (fh.kind == FileHandle::kNamedAttr) {
567
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 const uint32_t status = ResolveXattrName(fh.ino, fh.aux, &obj->xattr_name);
568
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 if (status != NFS4_OK)
569 ✗ return status;
570 155 std::string value;
571
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 retval = backend_->GetXattr(fh.ino, obj->xattr_name, creds, &value);
572 // Unreadable values (e.g. privileged attributes) still get a handle;
573 // the error surfaces on READ
574
1/2
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
155 obj->attr.size = (retval == 0) ? value.size() : 0;
575 155 obj->attr.mode = S_IFREG | 0444;
576 155 obj->attr.nlink = 1;
577 155 }
578 3689 return NFS4_OK;
579 }
580
581
582 4402 uint32_t Nfs4Server::LookupChild(const Object &dir, const std::string &name,
583 const Credentials &creds, Object *child) {
584 4402 uint32_t const status = CheckName(name);
585
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 4371 times.
4402 if (status != NFS4_OK)
586 31 return status;
587
588
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 4247 times.
4371 if (dir.fh.kind == FileHandle::kAttrDir) {
589 124 std::vector<std::string> names;
590
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 const int retval = backend_->ListXattr(dir.fh.ino, &names);
591
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if (retval != 0)
592 ✗ return ErrnoToStatus(retval);
593
2/2
✓ Branch 1 taken 217 times.
✓ Branch 2 taken 31 times.
248 for (unsigned i = 0; i < names.size(); ++i) {
594
2/2
✓ Branch 2 taken 93 times.
✓ Branch 3 taken 124 times.
217 if (names[i] == name) {
595
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 5 taken 93 times.
✗ Branch 6 not taken.
186 return Resolve(FileHandle(FileHandle::kNamedAttr, fsid_, dir.fh.ino,
596 HashName(name)),
597 93 creds, child);
598 }
599 }
600 31 return NFS4ERR_NOENT;
601 124 }
602
603
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4247 times.
4247 if (dir.fh.kind != FileHandle::kObject)
604 ✗ return NFS4ERR_NOTDIR;
605
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 4247 times.
4247 if (S_ISLNK(dir.attr.mode))
606 ✗ return NFS4ERR_SYMLINK;
607
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 4216 times.
4247 if (!S_ISDIR(dir.attr.mode))
608 31 return NFS4ERR_NOTDIR;
609
610 4216 const int retval = backend_->Lookup(dir.fh.ino, name, &child->attr);
611
2/2
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 4154 times.
4216 if (retval != 0)
612 62 return ErrnoToStatus(retval);
613 4154 child->fh = FileHandle(FileHandle::kObject, fsid_, child->attr.ino);
614 4154 child->xattr_name.clear();
615 4154 return NFS4_OK;
616 }
617
618
619 341 uint32_t Nfs4Server::ListChildren(const Object &dir,
620 std::vector<DirEntry> *entries) {
621
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 310 times.
341 if (dir.fh.kind == FileHandle::kAttrDir) {
622 31 std::vector<std::string> names;
623
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int retval = backend_->ListXattr(dir.fh.ino, &names);
624
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (retval != 0)
625 ✗ return ErrnoToStatus(retval);
626 31 entries->clear();
627
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 31 times.
93 for (unsigned i = 0; i < names.size(); ++i)
628
2/4
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 62 times.
✗ Branch 6 not taken.
62 entries->push_back(DirEntry(names[i], dir.fh.ino));
629 31 return NFS4_OK;
630 31 }
631
2/4
✓ Branch 0 taken 310 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 310 times.
310 if (dir.fh.kind != FileHandle::kObject || !S_ISDIR(dir.attr.mode))
632 ✗ return NFS4ERR_NOTDIR;
633 310 return ErrnoToStatus(backend_->ReadDir(dir.fh.ino, entries));
634 }
635
636
637 31 uint64_t Nfs4Server::FileId(const Object &obj) const {
638
1/3
✗ Branch 0 not taken.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 switch (obj.fh.kind) {
639 ✗ case FileHandle::kAttrDir:
640 ✗ return obj.fh.ino | kAttrDirFileIdBit;
641 ✗ case FileHandle::kNamedAttr:
642 ✗ return (obj.fh.ino ^ (static_cast<uint64_t>(obj.fh.aux) << 32))
643 ✗ | kNamedAttrFileIdBit;
644 31 default:
645 31 return obj.fh.ino;
646 }
647 }
648
649
650 //------------------------------------------------------------------------------
651 // Attributes
652
653
654 682 bool Nfs4Server::DecodeBitmap(XdrReader *r, std::vector<uint32_t> *bitmap) {
655 uint32_t len;
656
4/8
✓ Branch 1 taken 682 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 682 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 682 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 682 times.
682 if (!r->GetU32(&len) || len > 8)
657 ✗ return false;
658
1/2
✓ Branch 1 taken 682 times.
✗ Branch 2 not taken.
682 bitmap->resize(len);
659
2/2
✓ Branch 0 taken 868 times.
✓ Branch 1 taken 682 times.
1550 for (uint32_t i = 0; i < len; ++i)
660
1/2
✓ Branch 2 taken 868 times.
✗ Branch 3 not taken.
868 r->GetU32(&(*bitmap)[i]);
661 682 return r->ok();
662 }
663
664
665 2108 bool Nfs4Server::EncodeFattr(const Object &obj, const uint32_t *request,
666 unsigned request_len, const Credentials &creds,
667 XdrWriter *out) {
668 uint32_t supported[kNumBitmapWords];
669 2108 SupportedBitmap(supported);
670 2108 uint32_t returned[kNumBitmapWords] = {0, 0};
671
4/4
✓ Branch 0 taken 4216 times.
✓ Branch 1 taken 155 times.
✓ Branch 2 taken 2263 times.
✓ Branch 3 taken 1953 times.
4371 for (unsigned i = 0; i < kNumBitmapWords && i < request_len; ++i)
672 2263 returned[i] = request[i] & supported[i];
673 // Errors are reported per op, not in the attributes of a single object
674 2108 returned[0] &= ~(1u << FATTR4_RDATTR_ERROR);
675
676 2108 FsStat fs;
677 2108 const bool need_fs = IsSet(returned, 2, FATTR4_FILES_AVAIL)
678
1/2
✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
2108 || IsSet(returned, 2, FATTR4_FILES_FREE)
679
1/2
✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
2108 || IsSet(returned, 2, FATTR4_FILES_TOTAL)
680
1/2
✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
2108 || IsSet(returned, 2, FATTR4_SPACE_AVAIL)
681
1/2
✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
2108 || IsSet(returned, 2, FATTR4_SPACE_FREE)
682
2/4
✓ Branch 0 taken 2108 times.
✗ Branch 1 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2108 times.
4216 || IsSet(returned, 2, FATTR4_SPACE_TOTAL);
683
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2108 times.
2108 if (need_fs)
684 ✗ backend_->StatFs(&fs);
685
686 2108 const Attr &a = obj.attr;
687 2108 XdrWriter v;
688
2/2
✓ Branch 0 taken 134912 times.
✓ Branch 1 taken 2108 times.
137020 for (unsigned attr = 0; attr < 32 * kNumBitmapWords; ++attr) {
689
2/2
✓ Branch 1 taken 132804 times.
✓ Branch 2 taken 2108 times.
134912 if (!IsSet(returned, kNumBitmapWords, attr))
690 132804 continue;
691
4/34
✗ Branch 0 not taken.
✓ Branch 1 taken 1860 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 155 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✗ Branch 9 not taken.
✗ Branch 10 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 31 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 15 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
✗ Branch 18 not taken.
✗ Branch 19 not taken.
✗ Branch 20 not taken.
✗ Branch 21 not taken.
✗ Branch 22 not taken.
✗ Branch 23 not taken.
✗ Branch 24 not taken.
✗ Branch 25 not taken.
✗ Branch 26 not taken.
✗ Branch 27 not taken.
✗ Branch 28 not taken.
✗ Branch 29 not taken.
✗ Branch 30 not taken.
✗ Branch 31 not taken.
✓ Branch 32 taken 62 times.
✗ Branch 33 not taken.
2108 switch (attr) {
692 ✗ case FATTR4_SUPPORTED_ATTRS:
693 ✗ PutBitmap(supported, kNumBitmapWords, &v);
694 ✗ break;
695 1860 case FATTR4_TYPE:
696
1/2
✓ Branch 2 taken 1860 times.
✗ Branch 3 not taken.
1860 v.PutU32(FileType(obj.fh, a.mode));
697 1860 break;
698 ✗ case FATTR4_FH_EXPIRE_TYPE:
699 ✗ v.PutU32(FH4_PERSISTENT);
700 ✗ break;
701 ✗ case FATTR4_CHANGE:
702 ✗ if (obj.fh.kind == FileHandle::kObject) {
703 ✗ v.PutU64((static_cast<uint64_t>(a.ctime.sec) << 30) ^ a.ctime.nsec);
704 } else {
705 ✗ v.PutU64(backend_->Generation());
706 }
707 ✗ break;
708 155 case FATTR4_SIZE:
709
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 v.PutU64(a.size);
710 155 break;
711 ✗ case FATTR4_LINK_SUPPORT:
712 case FATTR4_SYMLINK_SUPPORT:
713 case FATTR4_UNIQUE_HANDLES:
714 case FATTR4_CASE_PRESERVING:
715 case FATTR4_CHOWN_RESTRICTED:
716 case FATTR4_HOMOGENEOUS:
717 case FATTR4_NO_TRUNC:
718 ✗ v.PutBool(true);
719 ✗ break;
720 ✗ case FATTR4_NAMED_ATTR:
721 ✗ v.PutBool(obj.fh.kind == FileHandle::kObject);
722 ✗ break;
723 ✗ case FATTR4_FSID:
724 ✗ v.PutU64(fsid_);
725 ✗ v.PutU64(0);
726 ✗ break;
727 ✗ case FATTR4_LEASE_TIME:
728 ✗ v.PutU32(options_.lease_time);
729 ✗ break;
730 ✗ case FATTR4_ACLSUPPORT:
731 ✗ v.PutU32(0);
732 ✗ break;
733 ✗ case FATTR4_CANSETTIME:
734 case FATTR4_CASE_INSENSITIVE:
735 ✗ v.PutBool(false);
736 ✗ break;
737 ✗ case FATTR4_FILEHANDLE:
738 ✗ v.PutOpaque(obj.fh.Encode());
739 ✗ break;
740 31 case FATTR4_FILEID:
741 case FATTR4_MOUNTED_ON_FILEID:
742
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 v.PutU64(FileId(obj));
743 31 break;
744 ✗ case FATTR4_FILES_AVAIL:
745 ✗ v.PutU64(fs.files_avail);
746 ✗ break;
747 ✗ case FATTR4_FILES_FREE:
748 ✗ v.PutU64(fs.files_free);
749 ✗ break;
750 ✗ case FATTR4_FILES_TOTAL:
751 ✗ v.PutU64(fs.files_total);
752 ✗ break;
753 ✗ case FATTR4_MAXFILESIZE:
754 ✗ v.PutU64(0x7fffffffffffffffull);
755 ✗ break;
756 ✗ case FATTR4_MAXLINK:
757 ✗ v.PutU32(32000);
758 ✗ break;
759 ✗ case FATTR4_MAXNAME:
760 ✗ v.PutU32(kMaxName);
761 ✗ break;
762 ✗ case FATTR4_MAXREAD:
763 case FATTR4_MAXWRITE:
764 ✗ v.PutU64(options_.max_read);
765 ✗ break;
766 ✗ case FATTR4_MODE:
767 ✗ v.PutU32(a.mode & 07777);
768 ✗ break;
769 ✗ case FATTR4_NUMLINKS:
770 ✗ v.PutU32(a.nlink);
771 ✗ break;
772 ✗ case FATTR4_OWNER:
773 ✗ v.PutString(NumericId(a.uid));
774 ✗ break;
775 ✗ case FATTR4_OWNER_GROUP:
776 ✗ v.PutString(NumericId(a.gid));
777 ✗ break;
778 ✗ case FATTR4_RAWDEV:
779 ✗ v.PutU32(static_cast<uint32_t>(a.rdev >> 32));
780 ✗ v.PutU32(static_cast<uint32_t>(a.rdev));
781 ✗ break;
782 ✗ case FATTR4_SPACE_AVAIL:
783 ✗ v.PutU64(fs.bytes_avail);
784 ✗ break;
785 ✗ case FATTR4_SPACE_FREE:
786 ✗ v.PutU64(fs.bytes_free);
787 ✗ break;
788 ✗ case FATTR4_SPACE_TOTAL:
789 ✗ v.PutU64(fs.bytes_total);
790 ✗ break;
791 ✗ case FATTR4_SPACE_USED:
792 ✗ v.PutU64(a.size);
793 ✗ break;
794 ✗ case FATTR4_TIME_ACCESS:
795 ✗ PutTime(a.atime, &v);
796 ✗ break;
797 ✗ case FATTR4_TIME_DELTA: {
798 ✗ Timespec delta;
799 ✗ delta.nsec = 1;
800 ✗ PutTime(delta, &v);
801 ✗ break;
802 }
803 ✗ case FATTR4_TIME_METADATA:
804 ✗ PutTime(a.ctime, &v);
805 ✗ break;
806 62 case FATTR4_TIME_MODIFY:
807
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 PutTime(a.mtime, &v);
808 62 break;
809 ✗ default:
810 ✗ return false;
811 }
812 }
813
814
1/2
✓ Branch 1 taken 2108 times.
✗ Branch 2 not taken.
2108 PutBitmap(returned, kNumBitmapWords, out);
815
1/2
✓ Branch 2 taken 2108 times.
✗ Branch 3 not taken.
2108 out->PutOpaque(v.buffer());
816 (void)creds;
817 2108 return true;
818 2108 }
819
820
821 //------------------------------------------------------------------------------
822 // Operations
823
824
825 62 uint32_t Nfs4Server::OpAccess(XdrReader *args, Compound *c, XdrWriter *body) {
826 uint32_t requested;
827
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (!args->GetU32(&requested))
828 ✗ return NFS4ERR_BADXDR;
829
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (!c->has_fh)
830 ✗ return NFS4ERR_NOFILEHANDLE;
831 62 Object obj;
832
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
833
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (status != NFS4_OK)
834 ✗ return status;
835
836 62 const uint32_t supported = requested
837 62 & (ACCESS4_READ | ACCESS4_LOOKUP | ACCESS4_MODIFY
838 | ACCESS4_EXTEND | ACCESS4_DELETE
839 | ACCESS4_EXECUTE);
840
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 body->PutU32(supported);
841
1/2
✓ Branch 2 taken 62 times.
✗ Branch 3 not taken.
62 body->PutU32(AccessFromMode(obj.attr, c->call->creds) & supported);
842 62 return NFS4_OK;
843 62 }
844
845
846 186 uint32_t Nfs4Server::OpGetattr(XdrReader *args, Compound *c, XdrWriter *body) {
847 186 std::vector<uint32_t> request;
848
2/4
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 186 times.
186 if (!DecodeBitmap(args, &request))
849 ✗ return NFS4ERR_BADXDR;
850
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186 times.
186 if (!c->has_fh)
851 ✗ return NFS4ERR_NOFILEHANDLE;
852 186 Object obj;
853
1/2
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
186 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
854
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 155 times.
186 if (status != NFS4_OK)
855 31 return status;
856
3/6
✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
✗ Branch 7 not taken.
✓ Branch 8 taken 155 times.
155 if (!EncodeFattr(obj, request.empty() ? NULL : &request[0],
857 155 static_cast<unsigned>(request.size()), c->call->creds,
858 body)) {
859 ✗ return NFS4ERR_SERVERFAULT;
860 }
861 155 return NFS4_OK;
862 186 }
863
864
865 1302 uint32_t Nfs4Server::OpGetfh(Compound *c, XdrWriter *body) {
866
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1271 times.
1302 if (!c->has_fh)
867 31 return NFS4ERR_NOFILEHANDLE;
868
1/2
✓ Branch 2 taken 1271 times.
✗ Branch 3 not taken.
1271 body->PutOpaque(c->fh.Encode());
869 1271 return NFS4_OK;
870 }
871
872
873 2077 uint32_t Nfs4Server::OpLookup(XdrReader *args, Compound *c) {
874 2077 std::string name;
875
2/4
✓ Branch 1 taken 2077 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 2077 times.
2077 if (!args->GetString(&name, kNfs4OpaqueLimit))
876 ✗ return NFS4ERR_BADXDR;
877
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2077 times.
2077 if (!c->has_fh)
878 ✗ return NFS4ERR_NOFILEHANDLE;
879 2077 Object dir, child;
880
1/2
✓ Branch 1 taken 2077 times.
✗ Branch 2 not taken.
2077 uint32_t status = Resolve(c->fh, c->call->creds, &dir);
881
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 2077 times.
2077 if (status != NFS4_OK)
882 ✗ return status;
883
1/2
✓ Branch 1 taken 2077 times.
✗ Branch 2 not taken.
2077 status = LookupChild(dir, name, c->call->creds, &child);
884
2/2
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 1953 times.
2077 if (status != NFS4_OK)
885 124 return status;
886 1953 c->fh = child.fh;
887 1953 return NFS4_OK;
888 2077 }
889
890
891 155 uint32_t Nfs4Server::OpLookupp(Compound *c) {
892
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 if (!c->has_fh)
893 ✗ return NFS4ERR_NOFILEHANDLE;
894
3/3
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 93 times.
155 switch (c->fh.kind) {
895 31 case FileHandle::kAttrDir:
896 31 c->fh.kind = FileHandle::kObject;
897 31 return NFS4_OK;
898 31 case FileHandle::kNamedAttr:
899 31 c->fh.kind = FileHandle::kAttrDir;
900 31 c->fh.aux = 0;
901 31 return NFS4_OK;
902 93 default:
903 93 break;
904 }
905
906 186 Object obj;
907
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
908
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (status != NFS4_OK)
909 ✗ return status;
910
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 62 times.
93 if (!S_ISDIR(obj.attr.mode))
911
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 return S_ISLNK(obj.attr.mode) ? NFS4ERR_SYMLINK : NFS4ERR_NOTDIR;
912
3/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 31 times.
62 if (c->fh.ino == backend_->RootInode())
913 31 return NFS4ERR_NOENT;
914 31 Attr parent;
915
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int retval = backend_->LookupParent(c->fh.ino, &parent);
916
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (retval != 0)
917 ✗ return ErrnoToStatus(retval);
918 31 c->fh = FileHandle(FileHandle::kObject, fsid_, parent.ino);
919 31 return NFS4_OK;
920 }
921
922
923 62 uint32_t Nfs4Server::OpVerify(XdrReader *args, Compound *c, bool negate) {
924 62 std::vector<uint32_t> request;
925 62 std::string values;
926
5/10
✓ Branch 1 taken 62 times.
✗ Branch 2 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 (!DecodeBitmap(args, &request) || !args->GetOpaque(&values))
927 ✗ return NFS4ERR_BADXDR;
928
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (!c->has_fh)
929 ✗ return NFS4ERR_NOFILEHANDLE;
930
931 uint32_t supported[kNumBitmapWords];
932 62 SupportedBitmap(supported);
933
2/2
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 62 times.
124 for (unsigned i = 0; i < request.size(); ++i) {
934
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (request[i] & ~(i < kNumBitmapWords ? supported[i] : 0))
935 ✗ return NFS4ERR_ATTRNOTSUPP;
936 }
937
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 62 times.
62 if (IsSet(request.empty() ? NULL : &request[0],
938 62 static_cast<unsigned>(request.size()), FATTR4_RDATTR_ERROR)) {
939 ✗ return NFS4ERR_INVAL;
940 }
941
942 62 Object obj;
943
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
944
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (status != NFS4_OK)
945 ✗ return status;
946 62 XdrWriter mine;
947
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 62 times.
✓ Branch 5 taken 62 times.
✗ Branch 6 not taken.
62 EncodeFattr(obj, request.empty() ? NULL : &request[0],
948 62 static_cast<unsigned>(request.size()), c->call->creds, &mine);
949 // Skip our bitmap and the opaque length to compare the raw values
950 62 XdrReader r(reinterpret_cast<const unsigned char *>(mine.buffer().data()),
951 124 mine.size());
952 62 std::vector<uint32_t> ignored;
953 62 std::string my_values;
954
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 DecodeBitmap(&r, &ignored);
955
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetOpaque(&my_values);
956
957 62 const bool same = (my_values == values);
958
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 if (negate)
959
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 return same ? NFS4ERR_SAME : NFS4_OK;
960
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 return same ? NFS4_OK : NFS4ERR_NOT_SAME;
961 62 }
962
963
964 93 uint32_t Nfs4Server::OpOpenattr(XdrReader *args, Compound *c) {
965 bool create_dir;
966
2/4
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 93 times.
93 if (!args->GetBool(&create_dir))
967 ✗ return NFS4ERR_BADXDR;
968
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (!c->has_fh)
969 ✗ return NFS4ERR_NOFILEHANDLE;
970
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (c->fh.kind != FileHandle::kObject)
971 ✗ return NFS4ERR_INVAL;
972 93 Object obj;
973
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
974
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (status != NFS4_OK)
975 ✗ return status;
976 93 c->fh.kind = FileHandle::kAttrDir;
977 93 c->fh.aux = 0;
978 93 return NFS4_OK;
979 93 }
980
981
982 1674 uint32_t Nfs4Server::OpPutfh(XdrReader *args, Compound *c) {
983 1674 std::string raw;
984
2/4
✓ Branch 1 taken 1674 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 1674 times.
1674 if (!args->GetOpaque(&raw, kNfs4FhSize))
985 ✗ return NFS4ERR_BADXDR;
986 1674 FileHandle fh;
987
3/4
✓ Branch 1 taken 1674 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 1643 times.
1674 if (!FileHandle::Decode(raw, &fh))
988 31 return NFS4ERR_BADHANDLE;
989
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 1612 times.
1643 if (fh.fsid != fsid_)
990 31 return NFS4ERR_STALE;
991 1612 c->fh = fh;
992 1612 c->has_fh = true;
993 1612 return NFS4_OK;
994 1674 }
995
996
997 217 uint32_t Nfs4Server::OpRead(XdrReader *args, Compound *c, XdrWriter *body) {
998 217 Stateid stateid;
999 uint64_t offset;
1000 uint32_t count;
1001
3/6
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 217 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 217 times.
✗ Branch 7 not taken.
434 if (!GetStateid(args, &stateid) || !args->GetU64(&offset)
1002
4/8
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 217 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 217 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 217 times.
434 || !args->GetU32(&count)) {
1003 ✗ return NFS4ERR_BADXDR;
1004 }
1005
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 if (!c->has_fh)
1006 ✗ return NFS4ERR_NOFILEHANDLE;
1007
1008 217 Object obj;
1009
1/2
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
217 uint32_t status = Resolve(c->fh, c->call->creds, &obj);
1010
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 if (status != NFS4_OK)
1011 ✗ return status;
1012
2/4
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 217 times.
217 if (obj.fh.kind == FileHandle::kAttrDir || S_ISDIR(obj.attr.mode))
1013 ✗ return NFS4ERR_ISDIR;
1014
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 217 times.
217 if (!S_ISREG(obj.attr.mode))
1015 ✗ return NFS4ERR_INVAL;
1016 {
1017
1/2
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
217 std::lock_guard<std::mutex> const lock(state_lock_);
1018
1/2
✓ Branch 1 taken 217 times.
✗ Branch 2 not taken.
217 status = CheckReadStateid(stateid, c->fh);
1019 217 }
1020
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 186 times.
217 if (status != NFS4_OK)
1021 31 return status;
1022
1023
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186 times.
186 if (count > options_.max_read)
1024 ✗ count = options_.max_read;
1025 186 std::string data;
1026
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 155 times.
186 if (obj.fh.kind == FileHandle::kNamedAttr) {
1027 31 std::string value;
1028 62 const int retval = backend_->GetXattr(obj.fh.ino, obj.xattr_name,
1029
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 c->call->creds, &value);
1030
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (retval != 0)
1031 ✗ return ErrnoToStatus(retval);
1032
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if (offset < value.size())
1033
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 data = value.substr(offset, count);
1034
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
31 body->PutBool(offset + data.size() >= value.size());
1035
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 body->PutOpaque(data);
1036 31 return NFS4_OK;
1037 31 }
1038
1039
1/2
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
155 if (count > 0) {
1040
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 const int retval = backend_->Read(obj.fh.ino, offset, count, &data);
1041
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 155 times.
155 if (retval != 0)
1042 ✗ return ErrnoToStatus(retval);
1043 }
1044 155 const bool eof = (data.size() < count)
1045
3/4
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 124 times.
155 || (offset + data.size() >= obj.attr.size);
1046
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 body->PutBool(eof);
1047
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 body->PutOpaque(data);
1048 155 return NFS4_OK;
1049 217 }
1050
1051
1052 372 uint32_t Nfs4Server::OpReaddir(XdrReader *args, Compound *c, XdrWriter *body) {
1053 uint64_t cookie;
1054 unsigned char verifier[kNfs4VerifierSize];
1055 uint32_t dircount, maxcount;
1056 372 std::vector<uint32_t> request;
1057
1/2
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
372 if (!args->GetU64(&cookie)
1058
2/4
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 372 times.
✗ Branch 4 not taken.
372 || !args->GetFixedOpaque(verifier, kNfs4VerifierSize)
1059
4/8
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 372 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 372 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 372 times.
✗ Branch 9 not taken.
372 || !args->GetU32(&dircount) || !args->GetU32(&maxcount)
1060
4/8
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 372 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 372 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 372 times.
744 || !DecodeBitmap(args, &request)) {
1061 ✗ return NFS4ERR_BADXDR;
1062 }
1063
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 if (!c->has_fh)
1064 ✗ return NFS4ERR_NOFILEHANDLE;
1065
1066 372 Object dir;
1067
1/2
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
372 uint32_t status = Resolve(c->fh, c->call->creds, &dir);
1068
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 if (status != NFS4_OK)
1069 ✗ return status;
1070
2/4
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 372 times.
372 if (dir.fh.kind == FileHandle::kNamedAttr || !S_ISDIR(dir.attr.mode))
1071 ✗ return NFS4ERR_NOTDIR;
1072
1073 372 XdrWriter my_verifier_w;
1074
2/4
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 372 times.
✗ Branch 5 not taken.
372 my_verifier_w.PutU64(backend_->Generation());
1075 372 const std::string &my_verifier = my_verifier_w.buffer();
1076
2/4
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 372 times.
372 if (cookie == 1 || cookie == 2)
1077 ✗ return NFS4ERR_BAD_COOKIE;
1078 744 if (cookie != 0
1079
6/6
✓ Branch 0 taken 279 times.
✓ Branch 1 taken 93 times.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 248 times.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 341 times.
372 && memcmp(verifier, my_verifier.data(), kNfs4VerifierSize) != 0) {
1080 31 return NFS4ERR_NOT_SAME;
1081 }
1082
1083 341 std::vector<DirEntry> entries;
1084
1/2
✓ Branch 1 taken 341 times.
✗ Branch 2 not taken.
341 status = ListChildren(dir, &entries);
1085
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 341 times.
341 if (status != NFS4_OK)
1086 ✗ return status;
1087
1088 // Cookies 0, 1 and 2 are reserved; entry i has cookie i + 3
1089
2/2
✓ Branch 0 taken 248 times.
✓ Branch 1 taken 93 times.
341 size_t index = (cookie == 0) ? 0 : static_cast<size_t>(cookie - 2);
1090
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 341 times.
341 if (index > entries.size())
1091 ✗ return NFS4ERR_BAD_COOKIE;
1092
1093
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 341 times.
341 const bool want_rdattr_error = IsSet(request.empty() ? NULL : &request[0],
1094 341 static_cast<unsigned>(request.size()),
1095 FATTR4_RDATTR_ERROR);
1096
1097
1/2
✓ Branch 2 taken 341 times.
✗ Branch 3 not taken.
341 body->PutFixedOpaque(my_verifier.data(), kNfs4VerifierSize);
1098 // Fixed part: verifier, end-of-list marker, eof flag
1099 341 const size_t fixed_size = kNfs4VerifierSize + 4 + 4;
1100 341 unsigned num_encoded = 0;
1101
2/2
✓ Branch 1 taken 1891 times.
✓ Branch 2 taken 62 times.
1953 for (; index < entries.size(); ++index) {
1102 1891 const DirEntry &e = entries[index];
1103
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 1891 times.
1891 if (!IsValidUtf8(e.name)) {
1104 ✗ Log(kLogLevelDebug, "skipping non-UTF-8 name in listing");
1105 ✗ continue;
1106 }
1107
1108 1891 XdrWriter entry;
1109
1/2
✓ Branch 1 taken 1891 times.
✗ Branch 2 not taken.
1891 entry.PutBool(true);
1110
1/2
✓ Branch 1 taken 1891 times.
✗ Branch 2 not taken.
1891 entry.PutU64(index + 3);
1111
1/2
✓ Branch 1 taken 1891 times.
✗ Branch 2 not taken.
1891 entry.PutString(e.name);
1112 1891 Object child;
1113
1/2
✓ Branch 1 taken 1891 times.
✗ Branch 2 not taken.
1891 const uint32_t child_status = LookupChild(dir, e.name, c->call->creds,
1114 &child);
1115
1/2
✓ Branch 0 taken 1891 times.
✗ Branch 1 not taken.
1891 if (child_status == NFS4_OK) {
1116
2/4
✗ Branch 1 not taken.
✓ Branch 2 taken 1891 times.
✓ Branch 5 taken 1891 times.
✗ Branch 6 not taken.
1891 EncodeFattr(child, request.empty() ? NULL : &request[0],
1117 1891 static_cast<unsigned>(request.size()), c->call->creds,
1118 &entry);
1119 ✗ } else if (want_rdattr_error) {
1120 ✗ uint32_t words[1] = {1u << FATTR4_RDATTR_ERROR};
1121 ✗ PutBitmap(words, 1, &entry);
1122 ✗ XdrWriter value;
1123 ✗ value.PutU32(child_status);
1124 ✗ entry.PutOpaque(value.buffer());
1125 ✗ } else {
1126 // Vanished between listing and lookup; skip it
1127 ✗ continue;
1128 }
1129
1130 1891 if (body->size() + entry.size() + fixed_size - kNfs4VerifierSize
1131
2/2
✓ Branch 0 taken 279 times.
✓ Branch 1 taken 1612 times.
1891 > maxcount) {
1132
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 248 times.
279 if (num_encoded == 0)
1133 31 return NFS4ERR_TOOSMALL;
1134 248 break;
1135 }
1136
1/2
✓ Branch 4 taken 1612 times.
✗ Branch 5 not taken.
1612 body->PutRaw(entry.buffer().data(), entry.size());
1137 1612 num_encoded++;
1138
6/8
✓ Branch 1 taken 1612 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 248 times.
✓ Branch 6 taken 1612 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 31 times.
✓ Branch 9 taken 248 times.
2170 }
1139
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 body->PutBool(false);
1140
1/2
✓ Branch 2 taken 310 times.
✗ Branch 3 not taken.
310 body->PutBool(index >= entries.size());
1141 310 return NFS4_OK;
1142 372 }
1143
1144
1145 31 uint32_t Nfs4Server::OpReadlink(Compound *c, XdrWriter *body) {
1146
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (!c->has_fh)
1147 ✗ return NFS4ERR_NOFILEHANDLE;
1148 31 Object obj;
1149
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const uint32_t status = Resolve(c->fh, c->call->creds, &obj);
1150
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (status != NFS4_OK)
1151 ✗ return status;
1152
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 if (obj.fh.kind != FileHandle::kObject || !S_ISLNK(obj.attr.mode))
1153 ✗ return S_ISDIR(obj.attr.mode) ? NFS4ERR_ISDIR : NFS4ERR_INVAL;
1154 31 std::string target;
1155
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const int retval = backend_->ReadLink(obj.fh.ino, &target);
1156
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (retval != 0)
1157 ✗ return ErrnoToStatus(retval);
1158
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 body->PutString(target);
1159 31 return NFS4_OK;
1160 31 }
1161
1162
1163 ✗ uint32_t Nfs4Server::OpSecinfo(XdrReader *args, Compound *c, XdrWriter *body) {
1164 ✗ std::string name;
1165 ✗ if (!args->GetString(&name, kNfs4OpaqueLimit))
1166 ✗ return NFS4ERR_BADXDR;
1167 ✗ if (!c->has_fh)
1168 ✗ return NFS4ERR_NOFILEHANDLE;
1169 ✗ Object dir, child;
1170 ✗ uint32_t status = Resolve(c->fh, c->call->creds, &dir);
1171 ✗ if (status != NFS4_OK)
1172 ✗ return status;
1173 ✗ status = LookupChild(dir, name, c->call->creds, &child);
1174 ✗ if (status != NFS4_OK)
1175 ✗ return status;
1176 // RFC 7530 16.31.4: SECINFO consumes the current file handle
1177 ✗ c->has_fh = false;
1178 ✗ body->PutU32(1);
1179 ✗ body->PutU32(kAuthSys);
1180 ✗ return NFS4_OK;
1181 }
1182
1183
1184 //------------------------------------------------------------------------------
1185 // Client and open state
1186
1187
1188 372 void Nfs4Server::PutStateid(const Stateid &stateid, XdrWriter *w) {
1189 372 w->PutU32(stateid.seqid);
1190 372 w->PutFixedOpaque(stateid.other.data(), kStateidOtherSize);
1191 372 }
1192
1193
1194 372 bool Nfs4Server::GetStateid(XdrReader *r, Stateid *stateid) {
1195 unsigned char other[kStateidOtherSize];
1196
1/2
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
372 if (!r->GetU32(&stateid->seqid)
1197
4/8
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 372 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 372 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 372 times.
372 || !r->GetFixedOpaque(other, kStateidOtherSize)) {
1198 ✗ return false;
1199 }
1200
1/2
✓ Branch 1 taken 372 times.
✗ Branch 2 not taken.
372 stateid->other.assign(reinterpret_cast<char *>(other), kStateidOtherSize);
1201 372 return true;
1202 }
1203
1204
1205 124 std::string Nfs4Server::NewStateidOther() {
1206 124 XdrWriter w;
1207
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 w.PutU32(boot_);
1208
1/2
✓ Branch 1 taken 124 times.
✗ Branch 2 not taken.
124 w.PutU64(next_stateid_++);
1209
1/2
✓ Branch 2 taken 124 times.
✗ Branch 3 not taken.
248 return w.buffer();
1210 124 }
1211
1212
1213 558 uint32_t Nfs4Server::CheckClient(uint64_t clientid) {
1214
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 527 times.
558 if (static_cast<uint32_t>(clientid >> 32) != boot_)
1215 31 return NFS4ERR_STALE_CLIENTID;
1216
1/2
✓ Branch 1 taken 527 times.
✗ Branch 2 not taken.
527 std::map<uint64_t, Client>::const_iterator const it = clients_.find(clientid);
1217
5/6
✓ Branch 3 taken 496 times.
✓ Branch 4 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 496 times.
✓ Branch 8 taken 31 times.
✓ Branch 9 taken 496 times.
527 if (it == clients_.end() || !it->second.confirmed)
1218 31 return NFS4ERR_STALE_CLIENTID;
1219 496 return NFS4_OK;
1220 }
1221
1222
1223 496 Nfs4Server::SeqidCheck Nfs4Server::CheckSeqid(const OwnerKey &owner,
1224 uint32_t seqid, uint32_t op,
1225 bool create_owner,
1226 uint32_t *status,
1227 std::string *body) {
1228
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 std::map<OwnerKey, OpenOwner>::iterator const it = owners_.find(owner);
1229
2/2
✓ Branch 2 taken 124 times.
✓ Branch 3 taken 372 times.
496 if (it == owners_.end()) {
1230
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if (!create_owner) {
1231 ✗ *status = NFS4ERR_BAD_SEQID;
1232 ✗ return kSeqidBad;
1233 }
1234 124 return kSeqidProcess;
1235 }
1236
2/2
✓ Branch 1 taken 341 times.
✓ Branch 2 taken 31 times.
372 if (seqid == it->second.seqid + 1)
1237 341 return kSeqidProcess;
1238
2/6
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
31 if (seqid == it->second.seqid && it->second.last_op == op) {
1239 ✗ *status = it->second.last_status;
1240 ✗ *body = it->second.last_body;
1241 ✗ return kSeqidReplay;
1242 }
1243 31 *status = NFS4ERR_BAD_SEQID;
1244 31 return kSeqidBad;
1245 }
1246
1247
1248 465 void Nfs4Server::RecordSeqid(const OwnerKey &owner, uint32_t seqid, uint32_t op,
1249 uint32_t status, const std::string &body) {
1250
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 465 times.
465 if (!SeqidAdvances(status))
1251 ✗ return;
1252 465 OpenOwner &o = owners_[owner];
1253 465 o.seqid = seqid;
1254 465 o.last_op = op;
1255 465 o.last_status = status;
1256 465 o.last_body = body;
1257 }
1258
1259
1260 186 uint32_t Nfs4Server::FindOpenState(
1261 const Stateid &stateid, bool exact_seqid,
1262 std::map<std::string, OpenState>::iterator *it) {
1263 186 XdrReader r(reinterpret_cast<const unsigned char *>(stateid.other.data()),
1264 372 stateid.other.size());
1265 uint32_t boot;
1266
1/2
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
186 r.GetU32(&boot);
1267
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 186 times.
186 if (boot != boot_)
1268 ✗ return NFS4ERR_STALE_STATEID;
1269
1/2
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
186 *it = open_states_.find(stateid.other);
1270
2/2
✓ Branch 2 taken 62 times.
✓ Branch 3 taken 124 times.
186 if (*it == open_states_.end())
1271 62 return NFS4ERR_BAD_STATEID;
1272 124 const uint32_t current = (*it)->second.seqid;
1273
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 124 times.
124 if (stateid.seqid > current)
1274 ✗ return NFS4ERR_BAD_STATEID;
1275
4/4
✓ Branch 0 taken 62 times.
✓ Branch 1 taken 62 times.
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 31 times.
124 if (exact_seqid && stateid.seqid < current)
1276 31 return NFS4ERR_OLD_STATEID;
1277 93 return NFS4_OK;
1278 }
1279
1280
1281 217 uint32_t Nfs4Server::CheckReadStateid(const Stateid &stateid,
1282 const FileHandle &fh) {
1283
1/2
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
217 const std::string zeros(kStateidOtherSize, '\0');
1284
1/2
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
217 const std::string ones(kStateidOtherSize, '\xff');
1285
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 124 times.
124 if ((stateid.seqid == 0 && stateid.other == zeros)
1286
5/8
✓ Branch 0 taken 124 times.
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 93 times.
✗ Branch 5 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 124 times.
✓ Branch 8 taken 93 times.
341 || (stateid.seqid == 0xffffffffu && stateid.other == ones)) {
1287 124 return NFS4_OK;
1288 }
1289 93 std::map<std::string, OpenState>::iterator it;
1290
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 const uint32_t status = FindOpenState(stateid, false, &it);
1291
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 62 times.
93 if (status == NFS4ERR_BAD_STATEID) {
1292 std::map<std::string, Delegation>::const_iterator const d =
1293
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 delegations_.find(stateid.other);
1294
3/6
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 31 times.
✗ Branch 10 not taken.
31 if (d != delegations_.end() && d->second.fh == fh)
1295 31 return NFS4_OK;
1296 }
1297
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (status != NFS4_OK)
1298 ✗ return status;
1299
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 31 times.
62 if (!(it->second.fh == fh))
1300 31 return NFS4ERR_BAD_STATEID;
1301 31 return NFS4_OK;
1302 217 }
1303
1304
1305 31 void Nfs4Server::DropClient(uint64_t clientid) {
1306
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 clients_.erase(clientid);
1307 struct Refreeze {
1308 31 explicit Refreeze(Nfs4Server *s) : server(s) { }
1309 31 ~Refreeze() { server->UpdateFrozenAttrs(); }
1310 Nfs4Server *server;
1311 31 } const refreeze(this);
1312 31 for (std::set<std::pair<uint64_t, uint64_t> >::iterator it =
1313 31 recalled_files_.begin();
1314
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 it != recalled_files_.end();) {
1315 ✗ if (it->first == clientid)
1316 ✗ recalled_files_.erase(it++);
1317 else
1318 ✗ ++it;
1319 }
1320 31 for (std::map<std::string, Delegation>::iterator it = delegations_.begin();
1321
1/2
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
31 it != delegations_.end();) {
1322 ✗ if (it->second.clientid == clientid)
1323 ✗ delegations_.erase(it++);
1324 else
1325 ✗ ++it;
1326 }
1327 31 for (std::map<std::string, OpenState>::iterator it = open_states_.begin();
1328
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 31 times.
62 it != open_states_.end();) {
1329
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if (it->second.owner.first == clientid)
1330
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 open_states_.erase(it++);
1331 else
1332 ✗ ++it;
1333 }
1334 31 for (std::map<OwnerKey, OpenOwner>::iterator it = owners_.begin();
1335
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 31 times.
62 it != owners_.end();) {
1336
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if (it->first.first == clientid)
1337
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 owners_.erase(it++);
1338 else
1339 ✗ ++it;
1340 }
1341 31 }
1342
1343
1344 155 void Nfs4Server::UpdateFrozenAttrs() {
1345 155 frozen_attrs_.clear();
1346 155 for (std::map<std::string, Delegation>::const_iterator it =
1347 155 delegations_.begin();
1348
2/2
✓ Branch 3 taken 62 times.
✓ Branch 4 taken 155 times.
217 it != delegations_.end();
1349 62 ++it) {
1350
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 if (it->second.recalled != 0)
1351
1/2
✓ Branch 3 taken 62 times.
✗ Branch 4 not taken.
62 frozen_attrs_[it->second.fh.ino] = it->second.attr;
1352 }
1353 155 num_frozen_attrs_ = frozen_attrs_.size();
1354 155 }
1355
1356
1357 310 void Nfs4Server::PutDelegation(uint64_t clientid, const FileHandle &fh,
1358 const Attr &attr, XdrWriter *w) {
1359
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 std::map<uint64_t, Client>::const_iterator const client = clients_.find(clientid);
1360
1/2
✓ Branch 0 taken 217 times.
✗ Branch 1 not taken.
217 bool grant = options_.delegations && fh.kind == FileHandle::kObject
1361
5/6
✓ Branch 0 taken 217 times.
✓ Branch 1 taken 93 times.
✓ Branch 5 taken 217 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 31 times.
✓ Branch 9 taken 186 times.
527 && client != clients_.end() && client->second.callback_ok;
1362 310 for (std::map<std::string, Delegation>::const_iterator it =
1363 310 delegations_.begin();
1364
4/6
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 279 times.
✗ Branch 5 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 310 times.
310 grant && it != delegations_.end();
1365 ✗ ++it) {
1366 // One delegation per client and file; none while a recall is going on
1367 ✗ if ((it->second.clientid == clientid && it->second.fh == fh)
1368 ✗ || it->second.recalled != 0) {
1369 ✗ grant = false;
1370 }
1371 }
1372
6/10
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 279 times.
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 31 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 310 times.
310 if (grant && recalled_files_.erase(std::make_pair(clientid, fh.ino)) > 0)
1373 ✗ grant = false;
1374
5/6
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 279 times.
✗ Branch 3 not taken.
✓ Branch 4 taken 31 times.
✓ Branch 5 taken 279 times.
✓ Branch 6 taken 31 times.
310 if (!grant || delegations_.size() >= options_.max_stateids) {
1375
1/2
✓ Branch 1 taken 279 times.
✗ Branch 2 not taken.
279 w->PutU32(OPEN_DELEGATE_NONE);
1376 279 return;
1377 }
1378
1379 31 Stateid stateid;
1380
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 stateid.other = NewStateidOther();
1381
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 Delegation &delegation = delegations_[stateid.other];
1382 31 delegation.clientid = clientid;
1383 31 delegation.fh = fh;
1384 31 delegation.attr = attr;
1385 31 stateid.seqid = delegation.seqid;
1386
1387
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 w->PutU32(OPEN_DELEGATE_READ);
1388
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 PutStateid(stateid, w);
1389
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 w->PutBool(false); // recall
1390 // permissions: none granted in advance, the client still asks ACCESS
1391
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 w->PutU32(0); // ACE4_ACCESS_ALLOWED_ACE_TYPE
1392
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 w->PutU32(0); // flags
1393
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 w->PutU32(0); // access mask
1394
2/4
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
31 w->PutString("EVERYONE@");
1395 31 }
1396
1397
1398 62 uint32_t Nfs4Server::OpDelegreturn(XdrReader *args) {
1399 62 Stateid stateid;
1400
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (!GetStateid(args, &stateid))
1401 ✗ return NFS4ERR_BADXDR;
1402
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::lock_guard<std::mutex> const lock(state_lock_);
1403 62 XdrReader r(reinterpret_cast<const unsigned char *>(stateid.other.data()),
1404 124 stateid.other.size());
1405 uint32_t boot;
1406
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 r.GetU32(&boot);
1407
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 62 times.
62 if (boot != boot_)
1408 ✗ return NFS4ERR_STALE_STATEID;
1409
3/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 31 times.
✓ Branch 4 taken 31 times.
62 if (delegations_.erase(stateid.other) == 0)
1410 31 return NFS4ERR_BAD_STATEID;
1411
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if (num_frozen_attrs_ > 0)
1412
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 UpdateFrozenAttrs();
1413 31 return NFS4_OK;
1414 62 }
1415
1416
1417 31 void Nfs4Server::StartMonitor() {
1418
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 if (!monitor_.joinable())
1419
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 monitor_ = std::thread(&Nfs4Server::MonitorLoop, this);
1420 31 }
1421
1422
1423 31 void Nfs4Server::MonitorLoop() {
1424 31 const unsigned kCallbackTimeoutMs = 5000;
1425
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 std::unique_lock<std::mutex> lock(state_lock_);
1426
1/2
✓ Branch 0 taken 93 times.
✗ Branch 1 not taken.
93 while (!monitor_stopping_) {
1427
1/2
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 monitor_cond_.wait_for(lock, std::chrono::seconds(1));
1428
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 62 times.
93 if (monitor_stopping_)
1429 31 break;
1430
1431 // Probe new callback paths
1432 62 std::vector<uint64_t> probes;
1433 62 probes.swap(pending_probes_);
1434 62 std::vector<std::pair<uint64_t, std::shared_ptr<CallbackClient> > > cbs;
1435
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 62 times.
93 for (unsigned i = 0; i < probes.size(); ++i) {
1436
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 std::map<uint64_t, Client>::iterator const c = clients_.find(probes[i]);
1437
3/6
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 31 times.
✗ Branch 9 not taken.
31 if (c != clients_.end() && c->second.callback)
1438
2/4
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
31 cbs.push_back(std::make_pair(probes[i], c->second.callback));
1439 }
1440
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 lock.unlock();
1441 62 std::vector<bool> probe_ok;
1442
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 62 times.
93 for (unsigned i = 0; i < cbs.size(); ++i)
1443
2/4
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
31 probe_ok.push_back(cbs[i].second->Null(kCallbackTimeoutMs));
1444
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 const uint64_t generation = backend_->Generation();
1445
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 lock.lock();
1446
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 62 times.
93 for (unsigned i = 0; i < cbs.size(); ++i) {
1447
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 std::map<uint64_t, Client>::iterator const c = clients_.find(cbs[i].first);
1448
3/6
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 31 times.
✗ Branch 10 not taken.
31 if (c != clients_.end() && c->second.callback == cbs[i].second) {
1449
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 c->second.callback_ok = probe_ok[i];
1450
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
62 Log(kLogLevelDebug, "callback path of client %" PRIx64 " %s",
1451
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
93 cbs[i].first, probe_ok[i] ? "works" : "is down");
1452 }
1453 }
1454
1455 // Recall everything when the exported tree changed; revoke what was not
1456 // returned within a lease period
1457 62 const time_t now = time(NULL);
1458 124 const bool changed = (last_generation_ != 0)
1459
3/4
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
62 && (generation != last_generation_);
1460 62 last_generation_ = generation;
1461 struct Recall {
1462 std::string other;
1463 uint32_t seqid;
1464 std::string fh;
1465 std::shared_ptr<CallbackClient> callback;
1466 };
1467 62 std::vector<Recall> recalls;
1468 62 for (std::map<std::string, Delegation>::iterator it = delegations_.begin();
1469
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 62 times.
93 it != delegations_.end();) {
1470 31 Delegation &d = it->second;
1471
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
31 if (d.recalled != 0 && now - d.recalled > options_.lease_time) {
1472 ✗ Log(kLogLevelWarn, "revoking unreturned delegation");
1473 ✗ delegations_.erase(it++);
1474 ✗ continue;
1475 }
1476
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 if (changed && d.recalled == 0) {
1477
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 std::map<uint64_t, Client>::iterator const c = clients_.find(d.clientid);
1478
3/6
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 8 not taken.
✓ Branch 9 taken 31 times.
31 if (c == clients_.end() || !c->second.callback) {
1479 ✗ delegations_.erase(it++);
1480 ✗ continue;
1481 }
1482 31 d.recalled = now;
1483
2/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
31 recalled_files_.insert(std::make_pair(d.clientid, d.fh.ino));
1484 31 Recall recall;
1485
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 recall.other = it->first;
1486 31 recall.seqid = d.seqid;
1487
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 recall.fh = d.fh.Encode();
1488 31 recall.callback = c->second.callback;
1489
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 recalls.push_back(recall);
1490 31 }
1491 31 ++it;
1492 }
1493
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 UpdateFrozenAttrs();
1494
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 31 times.
62 if (recalls.empty())
1495 31 continue;
1496
1497
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 lock.unlock();
1498
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 Log(kLogLevelInfo, "recalling %zu delegations", recalls.size());
1499 31 std::vector<std::string> failed;
1500 31 const time_t recall_start = time(NULL);
1501
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 31 times.
62 for (unsigned i = 0; i < recalls.size(); ++i) {
1502
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
62 const int result = recalls[i].callback->Recall(
1503 31 recalls[i].seqid, recalls[i].other, recalls[i].fh,
1504 kCallbackTimeoutMs);
1505
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (result != NFS4_OK) {
1506 ✗ Log(kLogLevelWarn, "delegation recall failed (%d)", result);
1507 ✗ failed.push_back(recalls[i].other);
1508 }
1509 }
1510
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
62 Log(kLogLevelInfo, "sent %zu recalls in %lds, %zu failed", recalls.size(),
1511 31 static_cast<long>(time(NULL) - recall_start), failed.size()); // NOLINT
1512
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 lock.lock();
1513
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
31 for (unsigned i = 0; i < failed.size(); ++i)
1514 ✗ delegations_.erase(failed[i]);
1515
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 UpdateFrozenAttrs();
1516
8/8
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 31 times.
✓ Branch 5 taken 31 times.
✓ Branch 6 taken 31 times.
✓ Branch 8 taken 31 times.
✓ Branch 9 taken 31 times.
✓ Branch 11 taken 31 times.
✓ Branch 12 taken 31 times.
155 }
1517 31 }
1518
1519
1520 62 size_t Nfs4Server::NumDelegations() {
1521
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::lock_guard<std::mutex> const lock(state_lock_);
1522 124 return delegations_.size();
1523 62 }
1524
1525
1526 186 size_t Nfs4Server::NumOpenStates() {
1527
1/2
✓ Branch 1 taken 186 times.
✗ Branch 2 not taken.
186 std::lock_guard<std::mutex> const lock(state_lock_);
1528 372 return open_states_.size();
1529 186 }
1530
1531
1532 155 uint32_t Nfs4Server::OpSetclientid(XdrReader *args, XdrWriter *body) {
1533 unsigned char verifier[kNfs4VerifierSize];
1534 155 std::string name, netid, addr;
1535 uint32_t cb_program, cb_ident;
1536
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetFixedOpaque(verifier, kNfs4VerifierSize);
1537
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetOpaque(&name, kNfs4OpaqueLimit);
1538
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetU32(&cb_program);
1539
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetString(&netid);
1540
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetString(&addr);
1541
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 args->GetU32(&cb_ident);
1542
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
155 if (!args->ok())
1543 ✗ return NFS4ERR_BADXDR;
1544
1/2
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 const std::string verf(reinterpret_cast<char *>(verifier), kNfs4VerifierSize);
1545
1546
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 std::lock_guard<std::mutex> const lock(state_lock_);
1547 155 uint64_t clientid = 0;
1548 155 for (std::map<uint64_t, Client>::iterator it = clients_.begin();
1549
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 155 times.
186 it != clients_.end();
1550 31 ++it) {
1551
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
62 if (it->second.name == name && it->second.confirmed
1552
3/6
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
62 && it->second.verifier == verf) {
1553 // Same client instance, e.g. callback update: keep the client id
1554 ✗ clientid = it->first;
1555 ✗ break;
1556 }
1557 }
1558
1/2
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
155 if (clientid == 0) {
1559 155 for (std::map<uint64_t, Client>::iterator it = clients_.begin();
1560
2/2
✓ Branch 2 taken 31 times.
✓ Branch 3 taken 155 times.
186 it != clients_.end();) {
1561
3/6
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 31 times.
31 if (it->second.name == name && !it->second.confirmed)
1562 ✗ clients_.erase(it++);
1563 else
1564 31 ++it;
1565 }
1566 155 clientid = (static_cast<uint64_t>(boot_) << 32) | next_clientid_++;
1567
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 Client &client = clients_[clientid];
1568
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 client.name = name;
1569
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 client.verifier = verf;
1570 }
1571
1572 155 XdrWriter confirm;
1573 310 confirm.PutU64((static_cast<uint64_t>(next_clientid_++) << 32)
1574
1/2
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 ^ static_cast<uint64_t>(time(NULL)));
1575
2/4
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
155 clients_[clientid].confirm = confirm.buffer();
1576
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 124 times.
155 if (options_.delegations) {
1577
2/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
62 clients_[clientid].callback.reset(
1578
2/4
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
31 new CallbackClient(cb_program, cb_ident, netid, addr));
1579
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 clients_[clientid].callback_ok = false;
1580 }
1581
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 body->PutU64(clientid);
1582
1/2
✓ Branch 3 taken 155 times.
✗ Branch 4 not taken.
155 body->PutFixedOpaque(confirm.buffer().data(), kNfs4VerifierSize);
1583 155 return NFS4_OK;
1584 155 }
1585
1586
1587 155 uint32_t Nfs4Server::OpSetclientidConfirm(XdrReader *args) {
1588 uint64_t clientid;
1589 unsigned char confirm[kNfs4VerifierSize];
1590
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 if (!args->GetU64(&clientid)
1591
4/8
✓ Branch 0 taken 155 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 155 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
✓ Branch 6 taken 155 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 155 times.
155 || !args->GetFixedOpaque(confirm, kNfs4VerifierSize)) {
1592 ✗ return NFS4ERR_BADXDR;
1593 }
1594
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 std::lock_guard<std::mutex> const lock(state_lock_);
1595
1/2
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
155 std::map<uint64_t, Client>::iterator const it = clients_.find(clientid);
1596 155 if (it == clients_.end()
1597
2/4
✓ Branch 1 taken 155 times.
✗ Branch 2 not taken.
✗ Branch 4 not taken.
✓ Branch 5 taken 155 times.
620 || it->second.confirm
1598
4/12
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
✓ Branch 5 taken 155 times.
✗ Branch 6 not taken.
✓ Branch 8 taken 155 times.
✗ Branch 9 not taken.
✗ Branch 11 not taken.
✓ Branch 12 taken 155 times.
✗ Branch 13 not taken.
✗ Branch 14 not taken.
✗ Branch 16 not taken.
✗ Branch 17 not taken.
465 != std::string(reinterpret_cast<char *>(confirm),
1599 kNfs4VerifierSize)) {
1600 ✗ return NFS4ERR_STALE_CLIENTID;
1601 }
1602
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 if (options_.delegations && it->second.callback
1603
5/6
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 124 times.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✓ Branch 8 taken 124 times.
186 && it->second.callback->valid()) {
1604
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 pending_probes_.push_back(clientid);
1605
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 StartMonitor();
1606 31 monitor_cond_.notify_all();
1607 }
1608
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 155 times.
155 if (it->second.confirmed)
1609 ✗ return NFS4_OK;
1610
1611 155 it->second.confirmed = true;
1612
1/2
✓ Branch 2 taken 155 times.
✗ Branch 3 not taken.
155 const std::string name = it->second.name;
1613 // A rebooted client replaces its previous incarnation and its state
1614 155 std::vector<uint64_t> previous;
1615 155 for (std::map<uint64_t, Client>::iterator i = clients_.begin();
1616
2/2
✓ Branch 2 taken 186 times.
✓ Branch 3 taken 155 times.
341 i != clients_.end();
1617 186 ++i) {
1618
5/6
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 155 times.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✓ Branch 8 taken 155 times.
186 if (i->first != clientid && i->second.name == name)
1619
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 previous.push_back(i->first);
1620 }
1621
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 155 times.
186 for (unsigned i = 0; i < previous.size(); ++i)
1622
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 DropClient(previous[i]);
1623 155 return NFS4_OK;
1624 155 }
1625
1626
1627 62 uint32_t Nfs4Server::OpRenew(XdrReader *args) {
1628 uint64_t clientid;
1629
2/4
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 62 times.
62 if (!args->GetU64(&clientid))
1630 ✗ return NFS4ERR_BADXDR;
1631
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 std::lock_guard<std::mutex> const lock(state_lock_);
1632
1/2
✓ Branch 1 taken 62 times.
✗ Branch 2 not taken.
62 return CheckClient(clientid);
1633 62 }
1634
1635
1636 496 uint32_t Nfs4Server::OpOpen(XdrReader *args, Compound *c, XdrWriter *body) {
1637 uint32_t seqid, share_access, share_deny, open_type, claim_type;
1638 uint64_t clientid;
1639 496 std::string owner, name;
1640
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetU32(&seqid);
1641
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetU32(&share_access);
1642
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetU32(&share_deny);
1643
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetU64(&clientid);
1644
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetOpaque(&owner, kNfs4OpaqueLimit);
1645
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetU32(&open_type);
1646
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 496 times.
496 if (!args->ok())
1647 ✗ return NFS4ERR_BADXDR;
1648
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 496 times.
496 if (open_type == OPEN4_CREATE) {
1649 // The create arguments are not needed: the file system is read-only
1650 ✗ return c->has_fh ? NFS4ERR_ROFS : NFS4ERR_NOFILEHANDLE;
1651 }
1652
2/4
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✓ Branch 4 taken 496 times.
496 if (!args->GetU32(&claim_type))
1653 ✗ return NFS4ERR_BADXDR;
1654
1/4
✓ Branch 0 taken 496 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
496 switch (claim_type) {
1655 496 case CLAIM_NULL:
1656
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 args->GetString(&name, kNfs4OpaqueLimit);
1657 496 break;
1658 ✗ case CLAIM_PREVIOUS: {
1659 uint32_t delegation_type;
1660 ✗ args->GetU32(&delegation_type);
1661 ✗ break;
1662 }
1663 ✗ case CLAIM_DELEGATE_CUR: {
1664 // The client converts local opens before returning a delegation
1665 ✗ Stateid delegation;
1666 ✗ GetStateid(args, &delegation);
1667 ✗ args->GetString(&name, kNfs4OpaqueLimit);
1668 ✗ break;
1669 }
1670 ✗ default:
1671 // State of a previous server instance is not kept
1672 ✗ return NFS4ERR_RECLAIM_BAD;
1673 }
1674
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 496 times.
496 if (!args->ok())
1675 ✗ return NFS4ERR_BADXDR;
1676
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 496 times.
496 if (!c->has_fh)
1677 ✗ return NFS4ERR_NOFILEHANDLE;
1678
1679
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
992 const OwnerKey owner_key(clientid, owner);
1680 992 std::string replay_body;
1681 uint32_t status;
1682 {
1683
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 std::lock_guard<std::mutex> const lock(state_lock_);
1684
1/2
✓ Branch 1 taken 496 times.
✗ Branch 2 not taken.
496 status = CheckClient(clientid);
1685
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 465 times.
496 if (status != NFS4_OK)
1686 31 return status;
1687
1/2
✓ Branch 1 taken 465 times.
✗ Branch 2 not taken.
465 const SeqidCheck check = CheckSeqid(owner_key, seqid, OP_OPEN, true,
1688 &status, &replay_body);
1689
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 434 times.
465 if (check == kSeqidBad)
1690 31 return status;
1691
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 434 times.
434 if (check == kSeqidReplay) {
1692 ✗ body->PutRaw(replay_body.data(), replay_body.size());
1693 ✗ return status;
1694 }
1695
2/2
✓ Branch 1 taken 434 times.
✓ Branch 2 taken 62 times.
496 }
1696
1697 868 Object target;
1698 868 Object dir;
1699
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
434 if (claim_type == CLAIM_NULL || claim_type == CLAIM_DELEGATE_CUR) {
1700
1/2
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
434 status = Resolve(c->fh, c->call->creds, &dir);
1701
1/2
✓ Branch 0 taken 434 times.
✗ Branch 1 not taken.
434 if (status == NFS4_OK)
1702
1/2
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
434 status = LookupChild(dir, name, c->call->creds, &target);
1703 } else {
1704 ✗ status = Resolve(c->fh, c->call->creds, &target);
1705 }
1706
2/2
✓ Branch 0 taken 403 times.
✓ Branch 1 taken 31 times.
434 if (status == NFS4_OK) {
1707
2/2
✓ Branch 0 taken 372 times.
✓ Branch 1 taken 31 times.
403 if ((share_access & OPEN4_SHARE_ACCESS_WRITE)
1708
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 || !(share_access & OPEN4_SHARE_ACCESS_READ)) {
1709 31 status = NFS4ERR_ROFS;
1710
1/2
✓ Branch 0 taken 372 times.
✗ Branch 1 not taken.
372 } else if (target.fh.kind == FileHandle::kAttrDir
1711
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 372 times.
372 || S_ISDIR(target.attr.mode)) {
1712 ✗ status = NFS4ERR_ISDIR;
1713
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 341 times.
372 } else if (S_ISLNK(target.attr.mode)) {
1714 31 status = NFS4ERR_SYMLINK;
1715
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 341 times.
341 } else if (!S_ISREG(target.attr.mode)) {
1716 ✗ status = NFS4ERR_INVAL;
1717
2/2
✓ Branch 1 taken 31 times.
✓ Branch 2 taken 310 times.
341 } else if (!(AccessFromMode(target.attr, c->call->creds) & ACCESS4_READ)) {
1718 31 status = NFS4ERR_ACCESS;
1719 }
1720 }
1721
1722 868 XdrWriter result;
1723
1/2
✓ Branch 1 taken 434 times.
✗ Branch 2 not taken.
868 std::lock_guard<std::mutex> const lock(state_lock_);
1724
2/2
✓ Branch 0 taken 310 times.
✓ Branch 1 taken 124 times.
434 if (status == NFS4_OK) {
1725 // Repeated opens of a file by the same owner share one stateid
1726 310 std::map<std::string, OpenState>::iterator it = open_states_.begin();
1727
2/2
✓ Branch 3 taken 217 times.
✓ Branch 4 taken 93 times.
310 for (; it != open_states_.end(); ++it) {
1728
3/6
✓ Branch 2 taken 217 times.
✗ Branch 3 not taken.
✓ Branch 6 taken 217 times.
✗ Branch 7 not taken.
✓ Branch 8 taken 217 times.
✗ Branch 9 not taken.
217 if (it->second.owner == owner_key && it->second.fh == target.fh)
1729 217 break;
1730 }
1731
2/2
✓ Branch 2 taken 93 times.
✓ Branch 3 taken 217 times.
310 if (it == open_states_.end()) {
1732
1/2
✗ Branch 1 not taken.
✓ Branch 2 taken 93 times.
93 if (open_states_.size() >= options_.max_stateids) {
1733 ✗ Log(kLogLevelWarn, "too many open NFS stateids (%u)", options_.max_stateids);
1734 ✗ return NFS4ERR_RESOURCE;
1735 }
1736
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 const std::string other = NewStateidOther();
1737
3/6
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 4 taken 93 times.
✗ Branch 5 not taken.
✓ Branch 7 taken 93 times.
✗ Branch 8 not taken.
93 it = open_states_.insert(std::make_pair(other, OpenState())).first;
1738
1/2
✓ Branch 2 taken 93 times.
✗ Branch 3 not taken.
93 it->second.owner = owner_key;
1739 93 it->second.fh = target.fh;
1740 93 }
1741 310 it->second.seqid++;
1742 310 it->second.access |= share_access;
1743
1744 310 Stateid stateid;
1745 310 stateid.seqid = it->second.seqid;
1746
1/2
✓ Branch 2 taken 310 times.
✗ Branch 3 not taken.
310 stateid.other = it->first;
1747
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 PutStateid(stateid, &result);
1748 // change_info4: nothing changes in a read-only directory
1749
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 result.PutBool(true);
1750
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 result.PutU64(0);
1751
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 result.PutU64(0);
1752 // No OPEN4_RESULT_CONFIRM: a confirm round trip buys nothing here
1753
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 result.PutU32(OPEN4_RESULT_LOCKTYPE_POSIX);
1754
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 result.PutU32(0); // attrset
1755
1/2
✓ Branch 0 taken 310 times.
✗ Branch 1 not taken.
310 if (claim_type == CLAIM_NULL)
1756
1/2
✓ Branch 1 taken 310 times.
✗ Branch 2 not taken.
310 PutDelegation(clientid, target.fh, target.attr, &result);
1757 else
1758 ✗ result.PutU32(OPEN_DELEGATE_NONE);
1759 310 c->fh = target.fh;
1760 310 }
1761
1/2
✓ Branch 2 taken 434 times.
✗ Branch 3 not taken.
434 RecordSeqid(owner_key, seqid, OP_OPEN, status, result.buffer());
1762
2/2
✓ Branch 0 taken 310 times.
✓ Branch 1 taken 124 times.
434 if (status == NFS4_OK)
1763
1/2
✓ Branch 4 taken 310 times.
✗ Branch 5 not taken.
310 body->PutRaw(result.buffer().data(), result.size());
1764 434 return status;
1765 496 }
1766
1767
1768 ✗ uint32_t Nfs4Server::OpOpenConfirm(XdrReader *args, Compound *c,
1769 XdrWriter *body) {
1770 ✗ Stateid stateid;
1771 uint32_t seqid;
1772 ✗ if (!GetStateid(args, &stateid) || !args->GetU32(&seqid))
1773 ✗ return NFS4ERR_BADXDR;
1774 ✗ if (!c->has_fh)
1775 ✗ return NFS4ERR_NOFILEHANDLE;
1776
1777 ✗ std::lock_guard<std::mutex> const lock(state_lock_);
1778 ✗ std::map<std::string, OpenState>::iterator it;
1779 ✗ uint32_t status = FindOpenState(stateid, true, &it);
1780 ✗ if (status != NFS4_OK)
1781 ✗ return status;
1782 ✗ const OwnerKey owner = it->second.owner;
1783 ✗ std::string replay_body;
1784 ✗ const SeqidCheck check = CheckSeqid(owner, seqid, OP_OPEN_CONFIRM, false,
1785 &status, &replay_body);
1786 ✗ if (check == kSeqidBad)
1787 ✗ return status;
1788 ✗ if (check == kSeqidReplay) {
1789 ✗ body->PutRaw(replay_body.data(), replay_body.size());
1790 ✗ return status;
1791 }
1792
1793 ✗ it->second.seqid++;
1794 ✗ Stateid result;
1795 ✗ result.seqid = it->second.seqid;
1796 ✗ result.other = it->first;
1797 ✗ XdrWriter w;
1798 ✗ PutStateid(result, &w);
1799 ✗ RecordSeqid(owner, seqid, OP_OPEN_CONFIRM, NFS4_OK, w.buffer());
1800 ✗ body->PutRaw(w.buffer().data(), w.size());
1801 ✗ return NFS4_OK;
1802 }
1803
1804
1805 ✗ uint32_t Nfs4Server::OpOpenDowngrade(XdrReader *args, Compound *c,
1806 XdrWriter *body) {
1807 ✗ Stateid stateid;
1808 uint32_t seqid, share_access, share_deny;
1809 ✗ if (!GetStateid(args, &stateid) || !args->GetU32(&seqid)
1810 ✗ || !args->GetU32(&share_access) || !args->GetU32(&share_deny)) {
1811 ✗ return NFS4ERR_BADXDR;
1812 }
1813 ✗ if (!c->has_fh)
1814 ✗ return NFS4ERR_NOFILEHANDLE;
1815
1816 ✗ std::lock_guard<std::mutex> const lock(state_lock_);
1817 ✗ std::map<std::string, OpenState>::iterator it;
1818 ✗ uint32_t status = FindOpenState(stateid, true, &it);
1819 ✗ if (status != NFS4_OK)
1820 ✗ return status;
1821 ✗ const OwnerKey owner = it->second.owner;
1822 ✗ std::string replay_body;
1823 ✗ const SeqidCheck check = CheckSeqid(owner, seqid, OP_OPEN_DOWNGRADE, false,
1824 &status, &replay_body);
1825 ✗ if (check == kSeqidBad)
1826 ✗ return status;
1827 ✗ if (check == kSeqidReplay) {
1828 ✗ body->PutRaw(replay_body.data(), replay_body.size());
1829 ✗ return status;
1830 }
1831
1832 ✗ XdrWriter w;
1833 ✗ if ((share_access & ~it->second.access) != 0) {
1834 ✗ status = NFS4ERR_INVAL;
1835 } else {
1836 ✗ it->second.access = share_access;
1837 ✗ it->second.seqid++;
1838 ✗ Stateid result;
1839 ✗ result.seqid = it->second.seqid;
1840 ✗ result.other = it->first;
1841 ✗ PutStateid(result, &w);
1842 ✗ status = NFS4_OK;
1843 }
1844 ✗ RecordSeqid(owner, seqid, OP_OPEN_DOWNGRADE, status, w.buffer());
1845 ✗ body->PutRaw(w.buffer().data(), w.size());
1846 ✗ return status;
1847 }
1848
1849
1850 93 uint32_t Nfs4Server::OpClose(XdrReader *args, Compound *c, XdrWriter *body) {
1851 uint32_t seqid;
1852 93 Stateid stateid;
1853
5/10
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 93 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 93 times.
✗ Branch 7 not taken.
✗ Branch 8 not taken.
✓ Branch 9 taken 93 times.
✗ Branch 10 not taken.
✓ Branch 11 taken 93 times.
93 if (!args->GetU32(&seqid) || !GetStateid(args, &stateid))
1854 ✗ return NFS4ERR_BADXDR;
1855
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 93 times.
93 if (!c->has_fh)
1856 ✗ return NFS4ERR_NOFILEHANDLE;
1857
1858 93 uint64_t forget_ino = 0;
1859 {
1860
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 std::lock_guard<std::mutex> const lock(state_lock_);
1861 93 std::map<std::string, OpenState>::iterator it;
1862
1/2
✓ Branch 1 taken 93 times.
✗ Branch 2 not taken.
93 uint32_t status = FindOpenState(stateid, true, &it);
1863
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 62 times.
93 if (status == NFS4ERR_BAD_STATEID) {
1864 // The stateid is gone after a CLOSE, so a retransmitted CLOSE is only
1865 // recognizable by the owner's cached reply
1866 31 for (std::map<OwnerKey, OpenOwner>::const_iterator o = owners_.begin();
1867
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
31 o != owners_.end();
1868 ✗ ++o) {
1869 31 const OpenOwner &owner = o->second;
1870
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 if (owner.last_op == OP_CLOSE && owner.seqid == seqid
1871
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 && owner.last_body.size() == 4 + kStateidOtherSize
1872
4/8
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
✗ Branch 6 not taken.
✓ Branch 7 taken 31 times.
✗ Branch 8 not taken.
62 && owner.last_body.compare(4, kStateidOtherSize, stateid.other)
1873 == 0) {
1874
1/2
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
31 body->PutRaw(owner.last_body.data(), owner.last_body.size());
1875 31 return owner.last_status;
1876 }
1877 }
1878 }
1879
2/2
✓ Branch 0 taken 31 times.
✓ Branch 1 taken 31 times.
62 if (status != NFS4_OK)
1880 31 return status;
1881
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 const OwnerKey owner = it->second.owner;
1882 31 std::string replay_body;
1883
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 const SeqidCheck check = CheckSeqid(owner, seqid, OP_CLOSE, false, &status,
1884 &replay_body);
1885
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (check == kSeqidBad)
1886 ✗ return status;
1887
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 31 times.
31 if (check == kSeqidReplay) {
1888 ✗ body->PutRaw(replay_body.data(), replay_body.size());
1889 ✗ return status;
1890 }
1891
1892 31 Stateid result;
1893 31 result.seqid = it->second.seqid + 1;
1894
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 result.other = it->first;
1895 31 const FileHandle fh = it->second.fh;
1896
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 open_states_.erase(it);
1897 31 bool still_open = false;
1898
1/2
✗ Branch 4 not taken.
✓ Branch 5 taken 31 times.
31 for (it = open_states_.begin(); it != open_states_.end(); ++it) {
1899 ✗ if (it->second.fh.ino == fh.ino) {
1900 ✗ still_open = true;
1901 ✗ break;
1902 }
1903 }
1904
2/4
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 if (!still_open && fh.kind == FileHandle::kObject)
1905 31 forget_ino = fh.ino;
1906
1907 31 XdrWriter w;
1908
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 PutStateid(result, &w);
1909
1/2
✓ Branch 2 taken 31 times.
✗ Branch 3 not taken.
31 RecordSeqid(owner, seqid, OP_CLOSE, NFS4_OK, w.buffer());
1910
1/2
✓ Branch 4 taken 31 times.
✗ Branch 5 not taken.
31 body->PutRaw(w.buffer().data(), w.size());
1911
4/6
✓ Branch 3 taken 31 times.
✗ Branch 4 not taken.
✓ Branch 6 taken 31 times.
✗ Branch 7 not taken.
✓ Branch 9 taken 31 times.
✓ Branch 10 taken 62 times.
93 }
1912
1/2
✓ Branch 0 taken 31 times.
✗ Branch 1 not taken.
31 if (forget_ino != 0)
1913
1/2
✓ Branch 1 taken 31 times.
✗ Branch 2 not taken.
31 backend_->Forget(forget_ino);
1914 31 return NFS4_OK;
1915 93 }
1916
1917 } // namespace nfs4
1918