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