| // SPDX-License-Identifier: GPL-2.0-or-later |
| /* |
| * Copyright (c) International Business Machines Corp., 2002 |
| * Copyright (c) 2015 Cyril Hrubis <chrubis@suse.cz> |
| * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. |
| * |
| * Robbie Williamson <robbiew@us.ibm.com> |
| * Roy Lee <roylee@andestech.com> |
| */ |
| /* |
| * Test Description: |
| * |
| * Tests truncate and mandatory record locking. |
| * |
| * Parent creates a file, child locks a region and sleeps. |
| * |
| * Parent checks that ftruncate before the locked region and inside the region |
| * fails while ftruncate after the region succeds. |
| * |
| * Parent wakes up child, child exits, lock is unlocked. |
| * |
| * Parent checks that ftruncate now works in all cases. |
| * |
| */ |
| |
| #include <stdio.h> |
| #include <errno.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/mount.h> |
| #include <unistd.h> |
| #include <stdlib.h> |
| #include <sys/statvfs.h> |
| |
| #include "tst_test.h" |
| |
| #define RECLEN 100 |
| #define MNTPOINT "mntpoint" |
| #define TESTFILE MNTPOINT"/testfile" |
| |
| static int len = 8 * 1024; |
| static int recstart, reclen; |
| |
| static void ftruncate_expect_fail(int fd, off_t offset, const char *msg) |
| { |
| TEST(ftruncate(fd, offset)); |
| |
| if (TST_RET == 0) { |
| tst_res(TFAIL, "ftruncate() %s succeeded unexpectedly", msg); |
| return; |
| } |
| |
| if (TST_ERR != EAGAIN) { |
| tst_res(TFAIL | TTERRNO, |
| "ftruncate() %s failed unexpectedly, expected EAGAIN", |
| msg); |
| return; |
| } |
| |
| tst_res(TPASS, "ftruncate() %s failed with EAGAIN", msg); |
| } |
| |
| static void ftruncate_expect_success(int fd, off_t offset, const char *msg) |
| { |
| struct stat sb; |
| |
| TEST(ftruncate(fd, offset)); |
| |
| if (TST_RET != 0) { |
| tst_res(TFAIL | TTERRNO, |
| "ftruncate() %s failed unexpectedly", msg); |
| return; |
| } |
| |
| SAFE_FSTAT(fd, &sb); |
| |
| if (sb.st_size != offset) { |
| tst_res(TFAIL, |
| "ftruncate() to %li bytes succeded but fstat() reports size %li", |
| (long)offset, (long)sb.st_size); |
| return; |
| } |
| |
| tst_res(TPASS, "ftruncate() %s succeded", msg); |
| } |
| |
| static void doparent(void) |
| { |
| int fd; |
| |
| TST_CHECKPOINT_WAIT(0); |
| |
| fd = SAFE_OPEN(TESTFILE, O_RDWR | O_NONBLOCK); |
| |
| ftruncate_expect_fail(fd, RECLEN, "offset before lock"); |
| ftruncate_expect_fail(fd, recstart + RECLEN/2, "offset in lock"); |
| ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); |
| |
| TST_CHECKPOINT_WAKE(0); |
| SAFE_WAIT(NULL); |
| |
| ftruncate_expect_success(fd, recstart + RECLEN/2, "offset in lock"); |
| ftruncate_expect_success(fd, recstart, "offset before lock"); |
| ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); |
| |
| SAFE_CLOSE(fd); |
| } |
| |
| void dochild(void) |
| { |
| int fd; |
| struct flock flocks; |
| |
| fd = SAFE_OPEN(TESTFILE, O_RDWR); |
| |
| tst_res(TINFO, "Child locks file"); |
| |
| flocks.l_type = F_WRLCK; |
| flocks.l_whence = SEEK_CUR; |
| flocks.l_start = recstart; |
| flocks.l_len = reclen; |
| |
| SAFE_FCNTL(fd, F_SETLKW, &flocks); |
| |
| TST_CHECKPOINT_WAKE_AND_WAIT(0); |
| |
| tst_res(TINFO, "Child unlocks file"); |
| |
| exit(0); |
| } |
| |
| static void verify_ftruncate(void) |
| { |
| int pid; |
| |
| if (tst_fill_file(TESTFILE, 0, 1024, 8)) |
| tst_brk(TBROK, "Failed to create test file"); |
| |
| SAFE_CHMOD(TESTFILE, 02666); |
| |
| reclen = RECLEN; |
| recstart = RECLEN + rand() % (len - 3 * RECLEN); |
| |
| pid = SAFE_FORK(); |
| |
| if (pid == 0) |
| dochild(); |
| |
| doparent(); |
| } |
| |
| static void setup(void) |
| { |
| /* |
| * Kernel returns EPERM when CONFIG_MANDATORY_FILE_LOCKING is not |
| * supported - to avoid false negatives, mount the fs first without |
| * flags and then remount it as MS_MANDLOCK |
| */ |
| if (mount(NULL, MNTPOINT, NULL, MS_REMOUNT|MS_MANDLOCK, NULL) == -1) { |
| if (errno == EPERM) { |
| tst_brk(TCONF, |
| "Mandatory lock not supported by this system"); |
| } else { |
| tst_brk(TBROK | TTERRNO, |
| "Remount with MS_MANDLOCK failed"); |
| } |
| } |
| } |
| |
| static struct tst_test test = { |
| .test_all = verify_ftruncate, |
| .setup = setup, |
| .needs_checkpoints = 1, |
| .forks_child = 1, |
| .mount_device = 1, |
| .needs_root = 1, |
| .mntpoint = MNTPOINT, |
| }; |