summaryrefslogtreecommitdiffhomepage
path: root/test/syscalls/linux/flock.cc
blob: 10dad042ff0bade6fc66b40637690dd254b3b22e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
// Copyright 2018 The gVisor Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include <errno.h>
#include <sys/file.h>

#include <string>

#include "gtest/gtest.h"
#include "absl/time/clock.h"
#include "absl/time/time.h"
#include "test/syscalls/linux/file_base.h"
#include "test/syscalls/linux/socket_test_util.h"
#include "test/util/file_descriptor.h"
#include "test/util/temp_path.h"
#include "test/util/test_util.h"
#include "test/util/thread_util.h"
#include "test/util/timer_util.h"

namespace gvisor {
namespace testing {

namespace {

class FlockTest : public FileTest {};

TEST_F(FlockTest, InvalidOpCombinations) {
  // The operation cannot be both exclusive and shared.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_SH | LOCK_NB),
              SyscallFailsWithErrno(EINVAL));

  // Locking and Unlocking doesn't make sense.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_UN | LOCK_NB),
              SyscallFailsWithErrno(EINVAL));
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_UN | LOCK_NB),
              SyscallFailsWithErrno(EINVAL));
}

TEST_F(FlockTest, NoOperationSpecified) {
  // Not specifying an operation is invalid.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB),
              SyscallFailsWithErrno(EINVAL));
}

TEST_F(FlockTest, TestSimpleExLock) {
  // Test that we can obtain an exclusive lock (no other holders)
  // and that we can unlock it.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestSimpleShLock) {
  // Test that we can obtain a shared lock (no other holders)
  // and that we can unlock it.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestLockableAnyMode) {
  // flock(2): A shared or exclusive lock can be placed on a file
  // regardless of the mode in which the file was opened.
  const FileDescriptor fd = ASSERT_NO_ERRNO_AND_VALUE(
      Open(test_file_name_, O_RDONLY));  // open read only to test

  // Mode shouldn't prevent us from taking an exclusive lock.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0));

  // Unlock
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestUnlockWithNoHolders) {
  // Test that unlocking when no one holds a lock succeeeds.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestRepeatedExLockingBySameHolder) {
  // Test that repeated locking by the same holder for the
  // same type of lock works correctly.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestRepeatedExLockingSingleUnlock) {
  // Test that repeated locking by the same holder for the
  // same type of lock works correctly and that a single unlock is required.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDONLY));

  // Should be unlocked at this point
  ASSERT_THAT(flock(fd.get(), LOCK_NB | LOCK_EX), SyscallSucceedsWithValue(0));

  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestRepeatedShLockingBySameHolder) {
  // Test that repeated locking by the same holder for the
  // same type of lock works correctly.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestSingleHolderUpgrade) {
  // Test that a shared lock is upgradable when no one else holds a lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_SH),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_NB | LOCK_EX),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestSingleHolderDowngrade) {
  // Test single holder lock downgrade case.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestMultipleShared) {
  // This is a simple test to verify that multiple independent shared
  // locks will be granted.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // A shared lock should be granted as there only exists other shared locks.
  ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0));

  // Unlock both.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

/*
 * flock(2): If a process uses open(2) (or similar) to obtain more than one
 * descriptor for the same file, these descriptors are treated
 * independently by flock(). An attempt to lock the file using one of
 * these file descriptors may be denied by a lock that the calling process
 * has already placed via another descriptor.
 */
TEST_F(FlockTest, TestMultipleHolderSharedExclusive) {
  // This test will verify that an exclusive lock will not be granted
  // while a shared is held.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Verify We're unable to get an exlcusive lock via the second FD.
  // because someone is holding a shared lock.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Unlock
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestSharedLockFailExclusiveHolderNonblocking) {
  // This test will verify that a shared lock is denied while
  // someone holds an exclusive lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Verify we're unable to get an shared lock via the second FD.
  // because someone is holding an exclusive lock.
  ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Unlock
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

void trivial_handler(int signum) {}

TEST_F(FlockTest, TestSharedLockFailExclusiveHolderBlocking) {
  const DisableSave ds;  // Timing-related.

  // This test will verify that a shared lock is denied while
  // someone holds an exclusive lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Make sure that a blocking flock() call will return EINTR when interrupted
  // by a signal. Create a timer that will go off while blocking on flock(), and
  // register the corresponding signal handler.
  auto timer = ASSERT_NO_ERRNO_AND_VALUE(
      TimerCreate(CLOCK_MONOTONIC, sigevent_t{
                                       .sigev_signo = SIGALRM,
                                       .sigev_notify = SIGEV_SIGNAL,
                                   }));

  struct sigaction act = {};
  act.sa_handler = trivial_handler;
  ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds());

  // Now that the signal handler is registered, set the timer. Set an interval
  // so that it's ok if the timer goes off before we call flock.
  ASSERT_NO_ERRNO(
      timer.Set(0, itimerspec{
                       .it_interval = absl::ToTimespec(absl::Milliseconds(10)),
                       .it_value = absl::ToTimespec(absl::Milliseconds(10)),
                   }));

  ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallFailsWithErrno(EINTR));
  timer.reset();

  // Unlock
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderNonblocking) {
  // This test will verify that an exclusive lock is denied while
  // someone already holds an exclsuive lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Verify we're unable to get an exclusive lock via the second FD
  // because someone is already holding an exclusive lock.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Unlock
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestExclusiveLockFailExclusiveHolderBlocking) {
  const DisableSave ds;  // Timing-related.

  // This test will verify that an exclusive lock is denied while
  // someone already holds an exclsuive lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Make sure that a blocking flock() call will return EINTR when interrupted
  // by a signal. Create a timer that will go off while blocking on flock(), and
  // register the corresponding signal handler.
  auto timer = ASSERT_NO_ERRNO_AND_VALUE(
      TimerCreate(CLOCK_MONOTONIC, sigevent_t{
                                       .sigev_signo = SIGALRM,
                                       .sigev_notify = SIGEV_SIGNAL,
                                   }));

  struct sigaction act = {};
  act.sa_handler = trivial_handler;
  ASSERT_THAT(sigaction(SIGALRM, &act, NULL), SyscallSucceeds());

  // Now that the signal handler is registered, set the timer. Set an interval
  // so that it's ok if the timer goes off before we call flock.
  ASSERT_NO_ERRNO(
      timer.Set(0, itimerspec{
                       .it_interval = absl::ToTimespec(absl::Milliseconds(10)),
                       .it_value = absl::ToTimespec(absl::Milliseconds(10)),
                   }));

  ASSERT_THAT(flock(fd.get(), LOCK_EX), SyscallFailsWithErrno(EINTR));
  timer.reset();

  // Unlock
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestMultipleHolderSharedExclusiveUpgrade) {
  // This test will verify that we cannot obtain an exclusive lock while
  // a shared lock is held by another descriptor, then verify that an upgrade
  // is possible on a shared lock once all other shared locks have closed.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Verify we're unable to get an exclusive lock via the second FD because
  // a shared lock is held.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Verify that we can get a shared lock via the second descriptor instead
  ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0));

  // Unlock the first and there will only be one shared lock remaining.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));

  // Upgrade 2nd fd.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0));

  // Finally unlock the second
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestMultipleHolderSharedExclusiveDowngrade) {
  // This test will verify that a shared lock is not obtainable while an
  // exclusive lock is held but that once the first is downgraded that
  // the second independent file descriptor can also get a shared lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Verify We're unable to get a shared lock via the second FD because
  // an exclusive lock is held.
  ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Verify that we can downgrade the first.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  // Now verify that we can obtain a shared lock since the first was downgraded.
  ASSERT_THAT(flock(fd.get(), LOCK_SH | LOCK_NB), SyscallSucceedsWithValue(0));

  // Finally unlock both.
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

/*
 * flock(2): Locks created by flock() are associated with an open file table
 * entry. This means that duplicate file descriptors (created by, for example,
 * fork(2) or dup(2)) refer to the same lock, and this lock may be modified or
 * released using any of these descriptors. Furthermore, the lock is released
 * either by an explicit LOCK_UN operation on any of these duplicate descriptors
 * or when all such descriptors have been closed.
 */
TEST_F(FlockTest, TestDupFdUpgrade) {
  // This test will verify that a shared lock is upgradeable via a dupped
  // file descriptor, if the FD wasn't dupped this would fail.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup());

  // Now we should be able to upgrade via the dupped fd.
  ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  // Validate unlock via dupped fd.
  ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestDupFdDowngrade) {
  // This test will verify that a exclusive lock is downgradable via a dupped
  // file descriptor, if the FD wasn't dupped this would fail.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup());

  // Now we should be able to downgrade via the dupped fd.
  ASSERT_THAT(flock(dup_fd.get(), LOCK_SH | LOCK_NB),
              SyscallSucceedsWithValue(0));

  // Validate unlock via dupped fd
  ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestDupFdCloseRelease) {
  // flock(2): Furthermore, the lock is released either by an explicit LOCK_UN
  // operation on any of these duplicate descriptors, or when all such
  // descriptors have been closed.
  //
  // This test will verify that a dupped fd closing will not release the
  // underlying lock until all such dupped fds have closed.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup());

  // At this point we have ONE exclusive locked referenced by two different fds.
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Validate that we cannot get a lock on a new unrelated FD.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Closing the dupped fd shouldn't affect the lock until all are closed.
  dup_fd.reset();  // Closed the duped fd.

  // Validate that we still cannot get a lock on a new unrelated FD.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Closing the first fd
  CloseFile();  // Will validate the syscall succeeds.

  // Now we should actually be able to get a lock since all fds related to
  // the first lock are closed.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0));

  // Unlock.
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestDupFdUnlockRelease) {
  /* flock(2): Furthermore, the lock is released either by an explicit LOCK_UN
   * operation on any of these duplicate descriptors, or when all such
   * descriptors have been closed.
   */
  // This test will verify that an explict unlock on a dupped FD will release
  // the underlying lock unlike the previous case where close on a dup was
  // not enough to release the lock.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX | LOCK_NB),
              SyscallSucceedsWithValue(0));

  const FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup());

  // At this point we have ONE exclusive locked referenced by two different fds.
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Validate that we cannot get a lock on a new unrelated FD.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Explicitly unlock via the dupped descriptor.
  ASSERT_THAT(flock(dup_fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));

  // Validate that we can now get the lock since we explicitly unlocked.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceedsWithValue(0));

  // Unlock
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

TEST_F(FlockTest, TestDupFdFollowedByLock) {
  // This test will verify that taking a lock on a file descriptor that has
  // already been dupped means that the lock is shared between both. This is
  // slightly different than than duping on an already locked FD.
  FileDescriptor dup_fd = ASSERT_NO_ERRNO_AND_VALUE(test_file_fd_.Dup());

  // Take a lock.
  ASSERT_THAT(flock(dup_fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds());

  // Now dup_fd and test_file_ should both reference the same lock.
  // We shouldn't be able to obtain a lock until both are closed.
  const FileDescriptor fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

  // Closing the first fd
  dup_fd.reset();  // Close the duped fd.

  // Validate that we cannot get a lock yet because the dupped descriptor.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB),
              SyscallFailsWithErrno(EWOULDBLOCK));

  // Closing the second fd.
  CloseFile();  // CloseFile() will validate the syscall succeeds.

  // Now we should be able to get the lock.
  ASSERT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds());

  // Unlock.
  ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceedsWithValue(0));
}

// NOTE: These blocking tests are not perfect. Unfortunately it's very hard to
// determine if a thread was actually blocked in the kernel so we're forced
// to use timing.
TEST_F(FlockTest, BlockingLockNoBlockingForSharedLocks) {
  // This test will verify that although LOCK_NB isn't specified
  // two different fds can obtain shared locks without blocking.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds());

  // kHoldLockTime is the amount of time we will hold the lock before releasing.
  constexpr absl::Duration kHoldLockTime = absl::Seconds(30);

  const DisableSave ds;  // Timing-related.

  // We do this in another thread so we can determine if it was actually
  // blocked by timing the amount of time it took for the syscall to complete.
  ScopedThread t([&] {
    MonotonicTimer timer;
    const FileDescriptor fd =
        ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

    // Only a single shared lock is held, the lock will be granted immediately.
    // This should be granted without any blocking. Don't save here to avoid
    // wild discrepencies on timing.
    timer.Start();
    ASSERT_THAT(flock(fd.get(), LOCK_SH), SyscallSucceeds());

    // We held the lock for 30 seconds but this thread should not have
    // blocked at all so we expect a very small duration on syscall completion.
    ASSERT_LT(timer.Duration(),
              absl::Seconds(1));  // 1000ms is much less than 30s.

    // We can release our second shared lock
    ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds());
  });

  // Sleep before unlocking.
  absl::SleepFor(kHoldLockTime);

  // Release the first shared lock. Don't save in this situation to avoid
  // discrepencies in timing.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds());
}

TEST_F(FlockTest, BlockingLockFirstSharedSecondExclusive) {
  // This test will verify that if someone holds a shared lock any attempt to
  // obtain an exclusive lock will result in blocking.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_SH), SyscallSucceeds());

  // kHoldLockTime is the amount of time we will hold the lock before releasing.
  constexpr absl::Duration kHoldLockTime = absl::Seconds(2);

  const DisableSave ds;  // Timing-related.

  // We do this in another thread so we can determine if it was actually
  // blocked by timing the amount of time it took for the syscall to complete.
  ScopedThread t([&] {
    MonotonicTimer timer;
    const FileDescriptor fd =
        ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

    // This exclusive lock should block because someone is already holding a
    // shared lock. We don't save here to avoid wild discrepencies on timing.
    timer.Start();
    ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds());

    // We should be blocked, we will expect to be blocked for more than 1.0s.
    ASSERT_GT(timer.Duration(), absl::Seconds(1));

    // We can release our exclusive lock.
    ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds());
  });

  // Sleep before unlocking.
  absl::SleepFor(kHoldLockTime);

  // Release the shared lock allowing the thread to proceed.
  // We don't save here to avoid wild discrepencies in timing.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds());
}

TEST_F(FlockTest, BlockingLockFirstExclusiveSecondShared) {
  // This test will verify that if someone holds an exclusive lock any attempt
  // to obtain a shared lock will result in blocking.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds());

  // kHoldLockTime is the amount of time we will hold the lock before releasing.
  constexpr absl::Duration kHoldLockTime = absl::Seconds(2);

  const DisableSave ds;  // Timing-related.

  // We do this in another thread so we can determine if it was actually
  // blocked by timing the amount of time it took for the syscall to complete.
  ScopedThread t([&] {
    MonotonicTimer timer;
    const FileDescriptor fd =
        ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

    // This shared lock should block because someone is already holding an
    // exclusive lock. We don't save here to avoid wild discrepencies on timing.
    timer.Start();
    ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_SH), SyscallSucceeds());

    // We should be blocked, we will expect to be blocked for more than 1.0s.
    ASSERT_GT(timer.Duration(), absl::Seconds(1));

    // We can release our shared lock.
    ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds());
  });

  // Sleep before unlocking.
  absl::SleepFor(kHoldLockTime);

  // Release the exclusive lock allowing the blocked thread to proceed.
  // We don't save here to avoid wild discrepencies in timing.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds());
}

TEST_F(FlockTest, BlockingLockFirstExclusiveSecondExclusive) {
  // This test will verify that if someone holds an exclusive lock any attempt
  // to obtain another exclusive lock will result in blocking.
  ASSERT_THAT(flock(test_file_fd_.get(), LOCK_EX), SyscallSucceeds());

  // kHoldLockTime is the amount of time we will hold the lock before releasing.
  constexpr absl::Duration kHoldLockTime = absl::Seconds(2);

  const DisableSave ds;  // Timing-related.

  // We do this in another thread so we can determine if it was actually
  // blocked by timing the amount of time it took for the syscall to complete.
  ScopedThread t([&] {
    MonotonicTimer timer;
    const FileDescriptor fd =
        ASSERT_NO_ERRNO_AND_VALUE(Open(test_file_name_, O_RDWR));

    // This exclusive lock should block because someone is already holding an
    // exclusive lock.
    timer.Start();
    ASSERT_THAT(RetryEINTR(flock)(fd.get(), LOCK_EX), SyscallSucceeds());

    // We should be blocked, we will expect to be blocked for more than 1.0s.
    ASSERT_GT(timer.Duration(), absl::Seconds(1));

    // We can release our exclusive lock.
    ASSERT_THAT(flock(fd.get(), LOCK_UN), SyscallSucceeds());
  });

  // Sleep before unlocking.
  absl::SleepFor(kHoldLockTime);

  // Release the exclusive lock allowing the blocked thread to proceed.
  // We don't save to avoid wild discrepencies in timing.
  EXPECT_THAT(flock(test_file_fd_.get(), LOCK_UN), SyscallSucceeds());
}

TEST(FlockTestNoFixture, BadFD) {
  // EBADF: fd is not an open file descriptor.
  ASSERT_THAT(flock(-1, 0), SyscallFailsWithErrno(EBADF));
}

TEST(FlockTestNoFixture, FlockDir) {
  auto dir = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateDir());
  auto fd = ASSERT_NO_ERRNO_AND_VALUE(Open(dir.path(), O_RDONLY, 0000));
  EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds());
}

TEST(FlockTestNoFixture, FlockSymlink) {
  SKIP_IF(IsRunningWithVFS1());

  auto file = ASSERT_NO_ERRNO_AND_VALUE(TempPath::CreateFile());
  auto symlink = ASSERT_NO_ERRNO_AND_VALUE(
      TempPath::CreateSymlinkTo(GetAbsoluteTestTmpdir(), file.path()));

  auto fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open(symlink.path(), O_RDONLY | O_PATH, 0000));
  EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EBADF));
}

TEST(FlockTestNoFixture, FlockProc) {
  auto fd =
      ASSERT_NO_ERRNO_AND_VALUE(Open("/proc/self/status", O_RDONLY, 0000));
  EXPECT_THAT(flock(fd.get(), LOCK_EX | LOCK_NB), SyscallSucceeds());
}

TEST(FlockTestNoFixture, FlockPipe) {
  int fds[2];
  ASSERT_THAT(pipe(fds), SyscallSucceeds());

  EXPECT_THAT(flock(fds[0], LOCK_EX | LOCK_NB), SyscallSucceeds());
  // Check that the pipe was locked above.
  EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallFailsWithErrno(EAGAIN));

  EXPECT_THAT(flock(fds[0], LOCK_UN), SyscallSucceeds());
  EXPECT_THAT(flock(fds[1], LOCK_EX | LOCK_NB), SyscallSucceeds());

  EXPECT_THAT(close(fds[0]), SyscallSucceeds());
  EXPECT_THAT(close(fds[1]), SyscallSucceeds());
}

TEST(FlockTestNoFixture, FlockSocket) {
  int sock = socket(AF_UNIX, SOCK_STREAM, 0);
  ASSERT_THAT(sock, SyscallSucceeds());

  struct sockaddr_un addr =
      ASSERT_NO_ERRNO_AND_VALUE(UniqueUnixAddr(true /* abstract */, AF_UNIX));
  ASSERT_THAT(
      bind(sock, reinterpret_cast<struct sockaddr*>(&addr), sizeof(addr)),
      SyscallSucceeds());

  EXPECT_THAT(flock(sock, LOCK_EX | LOCK_NB), SyscallSucceeds());
  EXPECT_THAT(close(sock), SyscallSucceeds());
}

}  // namespace

}  // namespace testing
}  // namespace gvisor