1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause 3 * 4 * Copyright (c) 2019 The FreeBSD Foundation 5 * 6 * This software was developed by BFF Storage Systems, LLC under sponsorship 7 * from the FreeBSD Foundation. 8 * 9 * Redistribution and use in source and binary forms, with or without 10 * modification, are permitted provided that the following conditions 11 * are met: 12 * 1. Redistributions of source code must retain the above copyright 13 * notice, this list of conditions and the following disclaimer. 14 * 2. Redistributions in binary form must reproduce the above copyright 15 * notice, this list of conditions and the following disclaimer in the 16 * documentation and/or other materials provided with the distribution. 17 * 18 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 19 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 20 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 21 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 22 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 23 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 24 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 25 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 26 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 27 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 * SUCH DAMAGE. 29 * 30 * $FreeBSD$ 31 */ 32 33 extern "C" { 34 #include <sys/file.h> 35 #include <fcntl.h> 36 } 37 38 #include "mockfs.hh" 39 #include "utils.hh" 40 41 /* This flag value should probably be defined in fuse_kernel.h */ 42 #define OFFSET_MAX 0x7fffffffffffffffLL 43 44 using namespace testing; 45 46 /* For testing filesystems without posix locking support */ 47 class Fallback: public FuseTest { 48 public: 49 50 void expect_lookup(const char *relpath, uint64_t ino, uint64_t size = 0) 51 { 52 FuseTest::expect_lookup(relpath, ino, S_IFREG | 0644, size, 1); 53 } 54 55 }; 56 57 /* For testing filesystems with posix locking support */ 58 class Locks: public Fallback { 59 virtual void SetUp() { 60 m_init_flags = FUSE_POSIX_LOCKS; 61 Fallback::SetUp(); 62 } 63 }; 64 65 class Fcntl: public Locks { 66 public: 67 void expect_setlk(uint64_t ino, pid_t pid, uint64_t start, uint64_t end, 68 uint32_t type, int err) 69 { 70 EXPECT_CALL(*m_mock, process( 71 ResultOf([=](auto in) { 72 return (in.header.opcode == FUSE_SETLK && 73 in.header.nodeid == ino && 74 in.body.setlk.fh == FH && 75 in.body.setlk.owner == (uint32_t)pid && 76 in.body.setlk.lk.start == start && 77 in.body.setlk.lk.end == end && 78 in.body.setlk.lk.type == type && 79 in.body.setlk.lk.pid == (uint64_t)pid); 80 }, Eq(true)), 81 _) 82 ).WillOnce(Invoke(ReturnErrno(err))); 83 } 84 void expect_setlkw(uint64_t ino, pid_t pid, uint64_t start, uint64_t end, 85 uint32_t type, int err) 86 { 87 EXPECT_CALL(*m_mock, process( 88 ResultOf([=](auto in) { 89 return (in.header.opcode == FUSE_SETLKW && 90 in.header.nodeid == ino && 91 in.body.setlkw.fh == FH && 92 in.body.setlkw.owner == (uint32_t)pid && 93 in.body.setlkw.lk.start == start && 94 in.body.setlkw.lk.end == end && 95 in.body.setlkw.lk.type == type && 96 in.body.setlkw.lk.pid == (uint64_t)pid); 97 }, Eq(true)), 98 _) 99 ).WillOnce(Invoke(ReturnErrno(err))); 100 } 101 }; 102 103 class Flock: public Locks { 104 public: 105 void expect_setlk(uint64_t ino, uint32_t type, int err) 106 { 107 EXPECT_CALL(*m_mock, process( 108 ResultOf([=](auto in) { 109 return (in.header.opcode == FUSE_SETLK && 110 in.header.nodeid == ino && 111 in.body.setlk.fh == FH && 112 /* 113 * The owner should be set to the address of 114 * the vnode. That's hard to verify. 115 */ 116 /* in.body.setlk.owner == ??? && */ 117 in.body.setlk.lk.type == type); 118 }, Eq(true)), 119 _) 120 ).WillOnce(Invoke(ReturnErrno(err))); 121 } 122 }; 123 124 class FlockFallback: public Fallback {}; 125 class GetlkFallback: public Fallback {}; 126 class Getlk: public Fcntl {}; 127 class SetlkFallback: public Fallback {}; 128 class Setlk: public Fcntl {}; 129 class SetlkwFallback: public Fallback {}; 130 class Setlkw: public Fcntl {}; 131 132 /* 133 * If the fuse filesystem does not support flock locks, then the kernel should 134 * fall back to local locks. 135 */ 136 TEST_F(FlockFallback, local) 137 { 138 const char FULLPATH[] = "mountpoint/some_file.txt"; 139 const char RELPATH[] = "some_file.txt"; 140 uint64_t ino = 42; 141 int fd; 142 143 expect_lookup(RELPATH, ino); 144 expect_open(ino, 0, 1); 145 146 fd = open(FULLPATH, O_RDWR); 147 ASSERT_LE(0, fd) << strerror(errno); 148 ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno); 149 leak(fd); 150 } 151 152 /* 153 * Even if the fuse file system supports POSIX locks, we must implement flock 154 * locks locally until protocol 7.17. Protocol 7.9 added partial buggy support 155 * but we won't implement that. 156 */ 157 TEST_F(Flock, local) 158 { 159 const char FULLPATH[] = "mountpoint/some_file.txt"; 160 const char RELPATH[] = "some_file.txt"; 161 uint64_t ino = 42; 162 int fd; 163 164 expect_lookup(RELPATH, ino); 165 expect_open(ino, 0, 1); 166 167 fd = open(FULLPATH, O_RDWR); 168 ASSERT_LE(0, fd) << strerror(errno); 169 ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno); 170 leak(fd); 171 } 172 173 /* Set a new flock lock with FUSE_SETLK */ 174 /* TODO: enable after upgrading to protocol 7.17 */ 175 TEST_F(Flock, DISABLED_set) 176 { 177 const char FULLPATH[] = "mountpoint/some_file.txt"; 178 const char RELPATH[] = "some_file.txt"; 179 uint64_t ino = 42; 180 int fd; 181 182 expect_lookup(RELPATH, ino); 183 expect_open(ino, 0, 1); 184 expect_setlk(ino, F_WRLCK, 0); 185 186 fd = open(FULLPATH, O_RDWR); 187 ASSERT_LE(0, fd) << strerror(errno); 188 ASSERT_EQ(0, flock(fd, LOCK_EX)) << strerror(errno); 189 leak(fd); 190 } 191 192 /* Fail to set a flock lock in non-blocking mode */ 193 /* TODO: enable after upgrading to protocol 7.17 */ 194 TEST_F(Flock, DISABLED_eagain) 195 { 196 const char FULLPATH[] = "mountpoint/some_file.txt"; 197 const char RELPATH[] = "some_file.txt"; 198 uint64_t ino = 42; 199 int fd; 200 201 expect_lookup(RELPATH, ino); 202 expect_open(ino, 0, 1); 203 expect_setlk(ino, F_WRLCK, EAGAIN); 204 205 fd = open(FULLPATH, O_RDWR); 206 ASSERT_LE(0, fd) << strerror(errno); 207 ASSERT_NE(0, flock(fd, LOCK_EX | LOCK_NB)); 208 ASSERT_EQ(EAGAIN, errno); 209 leak(fd); 210 } 211 212 /* 213 * If the fuse filesystem does not support posix file locks, then the kernel 214 * should fall back to local locks. 215 */ 216 TEST_F(GetlkFallback, local) 217 { 218 const char FULLPATH[] = "mountpoint/some_file.txt"; 219 const char RELPATH[] = "some_file.txt"; 220 uint64_t ino = 42; 221 struct flock fl; 222 int fd; 223 224 expect_lookup(RELPATH, ino); 225 expect_open(ino, 0, 1); 226 227 fd = open(FULLPATH, O_RDWR); 228 ASSERT_LE(0, fd) << strerror(errno); 229 fl.l_start = 10; 230 fl.l_len = 1000; 231 fl.l_pid = 0; 232 fl.l_type = F_RDLCK; 233 fl.l_whence = SEEK_SET; 234 fl.l_sysid = 0; 235 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); 236 leak(fd); 237 } 238 239 /* 240 * If the filesystem has no locks that fit the description, the filesystem 241 * should return F_UNLCK 242 */ 243 TEST_F(Getlk, no_locks) 244 { 245 const char FULLPATH[] = "mountpoint/some_file.txt"; 246 const char RELPATH[] = "some_file.txt"; 247 uint64_t ino = 42; 248 struct flock fl; 249 int fd; 250 pid_t pid = getpid(); 251 252 expect_lookup(RELPATH, ino); 253 expect_open(ino, 0, 1); 254 EXPECT_CALL(*m_mock, process( 255 ResultOf([=](auto in) { 256 return (in.header.opcode == FUSE_GETLK && 257 in.header.nodeid == ino && 258 in.body.getlk.fh == FH && 259 /* 260 * Though it seems useless, libfuse expects the 261 * owner and pid fields to be set during 262 * FUSE_GETLK. 263 */ 264 in.body.getlk.owner == (uint32_t)pid && 265 in.body.getlk.lk.pid == (uint64_t)pid && 266 in.body.getlk.lk.start == 10 && 267 in.body.getlk.lk.end == 1009 && 268 in.body.getlk.lk.type == F_RDLCK); 269 }, Eq(true)), 270 _) 271 ).WillOnce(Invoke(ReturnImmediate([=](auto in, auto& out) { 272 SET_OUT_HEADER_LEN(out, getlk); 273 out.body.getlk.lk = in.body.getlk.lk; 274 out.body.getlk.lk.type = F_UNLCK; 275 }))); 276 277 fd = open(FULLPATH, O_RDWR); 278 ASSERT_LE(0, fd) << strerror(errno); 279 fl.l_start = 10; 280 fl.l_len = 1000; 281 fl.l_pid = 42; 282 fl.l_type = F_RDLCK; 283 fl.l_whence = SEEK_SET; 284 fl.l_sysid = 42; 285 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); 286 287 /* 288 * If no lock is found that would prevent this lock from being created, 289 * the structure is left unchanged by this system call except for the 290 * lock type which is set to F_UNLCK. 291 */ 292 ASSERT_EQ(F_UNLCK, fl.l_type); 293 ASSERT_EQ(fl.l_pid, 42); 294 ASSERT_EQ(fl.l_start, 10); 295 ASSERT_EQ(fl.l_len, 1000); 296 ASSERT_EQ(fl.l_whence, SEEK_SET); 297 ASSERT_EQ(fl.l_sysid, 42); 298 299 leak(fd); 300 } 301 302 /* A different pid does have a lock */ 303 TEST_F(Getlk, lock_exists) 304 { 305 const char FULLPATH[] = "mountpoint/some_file.txt"; 306 const char RELPATH[] = "some_file.txt"; 307 uint64_t ino = 42; 308 struct flock fl; 309 int fd; 310 pid_t pid = getpid(); 311 pid_t pid2 = 1235; 312 313 expect_lookup(RELPATH, ino); 314 expect_open(ino, 0, 1); 315 EXPECT_CALL(*m_mock, process( 316 ResultOf([=](auto in) { 317 return (in.header.opcode == FUSE_GETLK && 318 in.header.nodeid == ino && 319 in.body.getlk.fh == FH && 320 /* 321 * Though it seems useless, libfuse expects the 322 * owner and pid fields to be set during 323 * FUSE_GETLK. 324 */ 325 in.body.getlk.owner == (uint32_t)pid && 326 in.body.getlk.lk.pid == (uint64_t)pid && 327 in.body.getlk.lk.start == 10 && 328 in.body.getlk.lk.end == 1009 && 329 in.body.getlk.lk.type == F_RDLCK); 330 }, Eq(true)), 331 _) 332 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 333 SET_OUT_HEADER_LEN(out, getlk); 334 out.body.getlk.lk.start = 100; 335 out.body.getlk.lk.end = 199; 336 out.body.getlk.lk.type = F_WRLCK; 337 out.body.getlk.lk.pid = (uint32_t)pid2;; 338 }))); 339 340 fd = open(FULLPATH, O_RDWR); 341 ASSERT_LE(0, fd) << strerror(errno); 342 fl.l_start = 10; 343 fl.l_len = 1000; 344 fl.l_pid = 0; 345 fl.l_type = F_RDLCK; 346 fl.l_whence = SEEK_SET; 347 fl.l_sysid = 0; 348 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); 349 EXPECT_EQ(100, fl.l_start); 350 EXPECT_EQ(100, fl.l_len); 351 EXPECT_EQ(pid2, fl.l_pid); 352 EXPECT_EQ(F_WRLCK, fl.l_type); 353 EXPECT_EQ(SEEK_SET, fl.l_whence); 354 EXPECT_EQ(0, fl.l_sysid); 355 leak(fd); 356 } 357 358 /* 359 * F_GETLK with SEEK_CUR 360 */ 361 TEST_F(Getlk, seek_cur) 362 { 363 const char FULLPATH[] = "mountpoint/some_file.txt"; 364 const char RELPATH[] = "some_file.txt"; 365 uint64_t ino = 42; 366 struct flock fl; 367 int fd; 368 pid_t pid = getpid(); 369 370 expect_lookup(RELPATH, ino, 1024); 371 expect_open(ino, 0, 1); 372 EXPECT_CALL(*m_mock, process( 373 ResultOf([=](auto in) { 374 return (in.header.opcode == FUSE_GETLK && 375 in.header.nodeid == ino && 376 in.body.getlk.fh == FH && 377 /* 378 * Though it seems useless, libfuse expects the 379 * owner and pid fields to be set during 380 * FUSE_GETLK. 381 */ 382 in.body.getlk.owner == (uint32_t)pid && 383 in.body.getlk.lk.pid == (uint64_t)pid && 384 in.body.getlk.lk.start == 500 && 385 in.body.getlk.lk.end == 509 && 386 in.body.getlk.lk.type == F_RDLCK); 387 }, Eq(true)), 388 _) 389 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 390 SET_OUT_HEADER_LEN(out, getlk); 391 out.body.getlk.lk.start = 400; 392 out.body.getlk.lk.end = 499; 393 out.body.getlk.lk.type = F_WRLCK; 394 out.body.getlk.lk.pid = (uint32_t)pid + 1; 395 }))); 396 397 fd = open(FULLPATH, O_RDWR); 398 ASSERT_LE(0, fd) << strerror(errno); 399 ASSERT_NE(-1, lseek(fd, 500, SEEK_SET)); 400 401 fl.l_start = 0; 402 fl.l_len = 10; 403 fl.l_pid = 42; 404 fl.l_type = F_RDLCK; 405 fl.l_whence = SEEK_CUR; 406 fl.l_sysid = 0; 407 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); 408 409 /* 410 * After a successful F_GETLK request, the value of l_whence is 411 * SEEK_SET. 412 */ 413 EXPECT_EQ(F_WRLCK, fl.l_type); 414 EXPECT_EQ(fl.l_pid, pid + 1); 415 EXPECT_EQ(fl.l_start, 400); 416 EXPECT_EQ(fl.l_len, 100); 417 EXPECT_EQ(fl.l_whence, SEEK_SET); 418 ASSERT_EQ(fl.l_sysid, 0); 419 420 leak(fd); 421 } 422 423 /* 424 * F_GETLK with SEEK_END 425 */ 426 TEST_F(Getlk, seek_end) 427 { 428 const char FULLPATH[] = "mountpoint/some_file.txt"; 429 const char RELPATH[] = "some_file.txt"; 430 uint64_t ino = 42; 431 struct flock fl; 432 int fd; 433 pid_t pid = getpid(); 434 435 expect_lookup(RELPATH, ino, 1024); 436 expect_open(ino, 0, 1); 437 EXPECT_CALL(*m_mock, process( 438 ResultOf([=](auto in) { 439 return (in.header.opcode == FUSE_GETLK && 440 in.header.nodeid == ino && 441 in.body.getlk.fh == FH && 442 /* 443 * Though it seems useless, libfuse expects the 444 * owner and pid fields to be set during 445 * FUSE_GETLK. 446 */ 447 in.body.getlk.owner == (uint32_t)pid && 448 in.body.getlk.lk.pid == (uint64_t)pid && 449 in.body.getlk.lk.start == 512 && 450 in.body.getlk.lk.end == 1023 && 451 in.body.getlk.lk.type == F_RDLCK); 452 }, Eq(true)), 453 _) 454 ).WillOnce(Invoke(ReturnImmediate([=](auto in __unused, auto& out) { 455 SET_OUT_HEADER_LEN(out, getlk); 456 out.body.getlk.lk.start = 400; 457 out.body.getlk.lk.end = 499; 458 out.body.getlk.lk.type = F_WRLCK; 459 out.body.getlk.lk.pid = (uint32_t)pid + 1; 460 }))); 461 462 fd = open(FULLPATH, O_RDWR); 463 ASSERT_LE(0, fd) << strerror(errno); 464 ASSERT_NE(-1, lseek(fd, 500, SEEK_SET)); 465 466 fl.l_start = -512; 467 fl.l_len = 512; 468 fl.l_pid = 42; 469 fl.l_type = F_RDLCK; 470 fl.l_whence = SEEK_END; 471 fl.l_sysid = 0; 472 ASSERT_NE(-1, fcntl(fd, F_GETLK, &fl)) << strerror(errno); 473 474 /* 475 * After a successful F_GETLK request, the value of l_whence is 476 * SEEK_SET. 477 */ 478 EXPECT_EQ(F_WRLCK, fl.l_type); 479 EXPECT_EQ(fl.l_pid, pid + 1); 480 EXPECT_EQ(fl.l_start, 400); 481 EXPECT_EQ(fl.l_len, 100); 482 EXPECT_EQ(fl.l_whence, SEEK_SET); 483 ASSERT_EQ(fl.l_sysid, 0); 484 485 leak(fd); 486 } 487 488 /* 489 * If the fuse filesystem does not support posix file locks, then the kernel 490 * should fall back to local locks. 491 */ 492 TEST_F(SetlkFallback, local) 493 { 494 const char FULLPATH[] = "mountpoint/some_file.txt"; 495 const char RELPATH[] = "some_file.txt"; 496 uint64_t ino = 42; 497 struct flock fl; 498 int fd; 499 500 expect_lookup(RELPATH, ino); 501 expect_open(ino, 0, 1); 502 503 fd = open(FULLPATH, O_RDWR); 504 ASSERT_LE(0, fd) << strerror(errno); 505 fl.l_start = 10; 506 fl.l_len = 1000; 507 fl.l_pid = getpid(); 508 fl.l_type = F_RDLCK; 509 fl.l_whence = SEEK_SET; 510 fl.l_sysid = 0; 511 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 512 leak(fd); 513 } 514 515 /* Clear a lock with FUSE_SETLK */ 516 TEST_F(Setlk, clear) 517 { 518 const char FULLPATH[] = "mountpoint/some_file.txt"; 519 const char RELPATH[] = "some_file.txt"; 520 uint64_t ino = 42; 521 struct flock fl; 522 int fd; 523 pid_t pid = getpid(); 524 525 expect_lookup(RELPATH, ino); 526 expect_open(ino, 0, 1); 527 expect_setlk(ino, pid, 10, 1009, F_UNLCK, 0); 528 529 fd = open(FULLPATH, O_RDWR); 530 ASSERT_LE(0, fd) << strerror(errno); 531 fl.l_start = 10; 532 fl.l_len = 1000; 533 fl.l_pid = 0; 534 fl.l_type = F_UNLCK; 535 fl.l_whence = SEEK_SET; 536 fl.l_sysid = 0; 537 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 538 leak(fd); 539 } 540 541 /* Set a new lock with FUSE_SETLK */ 542 TEST_F(Setlk, set) 543 { 544 const char FULLPATH[] = "mountpoint/some_file.txt"; 545 const char RELPATH[] = "some_file.txt"; 546 uint64_t ino = 42; 547 struct flock fl; 548 int fd; 549 pid_t pid = getpid(); 550 551 expect_lookup(RELPATH, ino); 552 expect_open(ino, 0, 1); 553 expect_setlk(ino, pid, 10, 1009, F_RDLCK, 0); 554 555 fd = open(FULLPATH, O_RDWR); 556 ASSERT_LE(0, fd) << strerror(errno); 557 fl.l_start = 10; 558 fl.l_len = 1000; 559 fl.l_pid = 0; 560 fl.l_type = F_RDLCK; 561 fl.l_whence = SEEK_SET; 562 fl.l_sysid = 0; 563 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 564 leak(fd); 565 } 566 567 /* l_len = 0 is a flag value that means to lock until EOF */ 568 TEST_F(Setlk, set_eof) 569 { 570 const char FULLPATH[] = "mountpoint/some_file.txt"; 571 const char RELPATH[] = "some_file.txt"; 572 uint64_t ino = 42; 573 struct flock fl; 574 int fd; 575 pid_t pid = getpid(); 576 577 expect_lookup(RELPATH, ino); 578 expect_open(ino, 0, 1); 579 expect_setlk(ino, pid, 10, OFFSET_MAX, F_RDLCK, 0); 580 581 fd = open(FULLPATH, O_RDWR); 582 ASSERT_LE(0, fd) << strerror(errno); 583 fl.l_start = 10; 584 fl.l_len = 0; 585 fl.l_pid = 0; 586 fl.l_type = F_RDLCK; 587 fl.l_whence = SEEK_SET; 588 fl.l_sysid = 0; 589 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 590 leak(fd); 591 } 592 593 /* Set a new lock with FUSE_SETLK, using SEEK_CUR for l_whence */ 594 TEST_F(Setlk, set_seek_cur) 595 { 596 const char FULLPATH[] = "mountpoint/some_file.txt"; 597 const char RELPATH[] = "some_file.txt"; 598 uint64_t ino = 42; 599 struct flock fl; 600 int fd; 601 pid_t pid = getpid(); 602 603 expect_lookup(RELPATH, ino, 1024); 604 expect_open(ino, 0, 1); 605 expect_setlk(ino, pid, 500, 509, F_RDLCK, 0); 606 607 fd = open(FULLPATH, O_RDWR); 608 ASSERT_LE(0, fd) << strerror(errno); 609 ASSERT_NE(-1, lseek(fd, 500, SEEK_SET)); 610 611 fl.l_start = 0; 612 fl.l_len = 10; 613 fl.l_pid = 0; 614 fl.l_type = F_RDLCK; 615 fl.l_whence = SEEK_CUR; 616 fl.l_sysid = 0; 617 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 618 619 leak(fd); 620 } 621 622 /* Set a new lock with FUSE_SETLK, using SEEK_END for l_whence */ 623 TEST_F(Setlk, set_seek_end) 624 { 625 const char FULLPATH[] = "mountpoint/some_file.txt"; 626 const char RELPATH[] = "some_file.txt"; 627 uint64_t ino = 42; 628 struct flock fl; 629 int fd; 630 pid_t pid = getpid(); 631 632 expect_lookup(RELPATH, ino, 1024); 633 expect_open(ino, 0, 1); 634 expect_setlk(ino, pid, 1000, 1009, F_RDLCK, 0); 635 636 fd = open(FULLPATH, O_RDWR); 637 ASSERT_LE(0, fd) << strerror(errno); 638 639 fl.l_start = -24; 640 fl.l_len = 10; 641 fl.l_pid = 0; 642 fl.l_type = F_RDLCK; 643 fl.l_whence = SEEK_END; 644 fl.l_sysid = 0; 645 ASSERT_NE(-1, fcntl(fd, F_SETLK, &fl)) << strerror(errno); 646 647 leak(fd); 648 } 649 650 /* Fail to set a new lock with FUSE_SETLK due to a conflict */ 651 TEST_F(Setlk, eagain) 652 { 653 const char FULLPATH[] = "mountpoint/some_file.txt"; 654 const char RELPATH[] = "some_file.txt"; 655 uint64_t ino = 42; 656 struct flock fl; 657 int fd; 658 pid_t pid = getpid(); 659 660 expect_lookup(RELPATH, ino); 661 expect_open(ino, 0, 1); 662 expect_setlk(ino, pid, 10, 1009, F_RDLCK, EAGAIN); 663 664 fd = open(FULLPATH, O_RDWR); 665 ASSERT_LE(0, fd) << strerror(errno); 666 fl.l_start = 10; 667 fl.l_len = 1000; 668 fl.l_pid = 0; 669 fl.l_type = F_RDLCK; 670 fl.l_whence = SEEK_SET; 671 fl.l_sysid = 0; 672 ASSERT_EQ(-1, fcntl(fd, F_SETLK, &fl)); 673 ASSERT_EQ(EAGAIN, errno); 674 leak(fd); 675 } 676 677 /* 678 * If the fuse filesystem does not support posix file locks, then the kernel 679 * should fall back to local locks. 680 */ 681 TEST_F(SetlkwFallback, local) 682 { 683 const char FULLPATH[] = "mountpoint/some_file.txt"; 684 const char RELPATH[] = "some_file.txt"; 685 uint64_t ino = 42; 686 struct flock fl; 687 int fd; 688 689 expect_lookup(RELPATH, ino); 690 expect_open(ino, 0, 1); 691 692 fd = open(FULLPATH, O_RDWR); 693 ASSERT_LE(0, fd) << strerror(errno); 694 fl.l_start = 10; 695 fl.l_len = 1000; 696 fl.l_pid = 0; 697 fl.l_type = F_RDLCK; 698 fl.l_whence = SEEK_SET; 699 fl.l_sysid = 0; 700 ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); 701 leak(fd); 702 } 703 704 /* 705 * Set a new lock with FUSE_SETLK. If the lock is not available, then the 706 * command should block. But to the kernel, that's the same as just being 707 * slow, so we don't need a separate test method 708 */ 709 TEST_F(Setlkw, set) 710 { 711 const char FULLPATH[] = "mountpoint/some_file.txt"; 712 const char RELPATH[] = "some_file.txt"; 713 uint64_t ino = 42; 714 struct flock fl; 715 int fd; 716 pid_t pid = getpid(); 717 718 expect_lookup(RELPATH, ino); 719 expect_open(ino, 0, 1); 720 expect_setlkw(ino, pid, 10, 1009, F_RDLCK, 0); 721 722 fd = open(FULLPATH, O_RDWR); 723 ASSERT_LE(0, fd) << strerror(errno); 724 fl.l_start = 10; 725 fl.l_len = 1000; 726 fl.l_pid = 0; 727 fl.l_type = F_RDLCK; 728 fl.l_whence = SEEK_SET; 729 fl.l_sysid = 0; 730 ASSERT_NE(-1, fcntl(fd, F_SETLKW, &fl)) << strerror(errno); 731 leak(fd); 732 } 733