1// SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) International Business Machines Corp., 2002 4 * Copyright (c) 2015 Cyril Hrubis <chrubis@suse.cz> 5 * Copyright (c) 2019 FUJITSU LIMITED. All rights reserved. 6 * 7 * Robbie Williamson <robbiew@us.ibm.com> 8 * Roy Lee <roylee@andestech.com> 9 */ 10/* 11 * Test Description: 12 * 13 * Tests truncate and mandatory record locking. 14 * 15 * Parent creates a file, child locks a region and sleeps. 16 * 17 * Parent checks that ftruncate before the locked region and inside the region 18 * fails while ftruncate after the region succeds. 19 * 20 * Parent wakes up child, child exits, lock is unlocked. 21 * 22 * Parent checks that ftruncate now works in all cases. 23 * 24 */ 25 26#include <stdio.h> 27#include <errno.h> 28#include <sys/types.h> 29#include <sys/stat.h> 30#include <sys/mount.h> 31#include <unistd.h> 32#include <stdlib.h> 33#include <sys/statvfs.h> 34 35#include "tst_test.h" 36 37#define RECLEN 100 38#define MNTPOINT "mntpoint" 39#define TESTFILE MNTPOINT"/testfile" 40 41static int len = 8 * 1024; 42static int recstart, reclen; 43 44static void ftruncate_expect_fail(int fd, off_t offset, const char *msg) 45{ 46 TEST(ftruncate(fd, offset)); 47 48 if (TST_RET == 0) { 49 tst_res(TFAIL, "ftruncate() %s succeeded unexpectedly", msg); 50 return; 51 } 52 53 if (TST_ERR != EAGAIN) { 54 tst_res(TFAIL | TTERRNO, 55 "ftruncate() %s failed unexpectedly, expected EAGAIN", 56 msg); 57 return; 58 } 59 60 tst_res(TPASS, "ftruncate() %s failed with EAGAIN", msg); 61} 62 63static void ftruncate_expect_success(int fd, off_t offset, const char *msg) 64{ 65 struct stat sb; 66 67 TEST(ftruncate(fd, offset)); 68 69 if (TST_RET != 0) { 70 tst_res(TFAIL | TTERRNO, 71 "ftruncate() %s failed unexpectedly", msg); 72 return; 73 } 74 75 SAFE_FSTAT(fd, &sb); 76 77 if (sb.st_size != offset) { 78 tst_res(TFAIL, 79 "ftruncate() to %li bytes succeded but fstat() reports size %li", 80 (long)offset, (long)sb.st_size); 81 return; 82 } 83 84 tst_res(TPASS, "ftruncate() %s succeded", msg); 85} 86 87static void doparent(void) 88{ 89 int fd; 90 91 TST_CHECKPOINT_WAIT(0); 92 93 fd = SAFE_OPEN(TESTFILE, O_RDWR | O_NONBLOCK); 94 95 ftruncate_expect_fail(fd, RECLEN, "offset before lock"); 96 ftruncate_expect_fail(fd, recstart + RECLEN/2, "offset in lock"); 97 ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); 98 99 TST_CHECKPOINT_WAKE(0); 100 SAFE_WAIT(NULL); 101 102 ftruncate_expect_success(fd, recstart + RECLEN/2, "offset in lock"); 103 ftruncate_expect_success(fd, recstart, "offset before lock"); 104 ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); 105 106 SAFE_CLOSE(fd); 107} 108 109void dochild(void) 110{ 111 int fd; 112 struct flock flocks; 113 114 fd = SAFE_OPEN(TESTFILE, O_RDWR); 115 116 tst_res(TINFO, "Child locks file"); 117 118 flocks.l_type = F_WRLCK; 119 flocks.l_whence = SEEK_CUR; 120 flocks.l_start = recstart; 121 flocks.l_len = reclen; 122 123 SAFE_FCNTL(fd, F_SETLKW, &flocks); 124 125 TST_CHECKPOINT_WAKE_AND_WAIT(0); 126 127 tst_res(TINFO, "Child unlocks file"); 128 129 exit(0); 130} 131 132static void verify_ftruncate(void) 133{ 134 int pid; 135 136 if (tst_fill_file(TESTFILE, 0, 1024, 8)) 137 tst_brk(TBROK, "Failed to create test file"); 138 139 SAFE_CHMOD(TESTFILE, 02666); 140 141 reclen = RECLEN; 142 recstart = RECLEN + rand() % (len - 3 * RECLEN); 143 144 pid = SAFE_FORK(); 145 146 if (pid == 0) 147 dochild(); 148 149 doparent(); 150} 151 152static void setup(void) 153{ 154 /* 155 * Kernel returns EPERM when CONFIG_MANDATORY_FILE_LOCKING is not 156 * supported - to avoid false negatives, mount the fs first without 157 * flags and then remount it as MS_MANDLOCK 158 */ 159 if (mount(NULL, MNTPOINT, NULL, MS_REMOUNT|MS_MANDLOCK, NULL) == -1) { 160 if (errno == EPERM) { 161 tst_brk(TCONF, 162 "Mandatory lock not supported by this system"); 163 } else { 164 tst_brk(TBROK | TTERRNO, 165 "Remount with MS_MANDLOCK failed"); 166 } 167 } 168} 169 170static struct tst_test test = { 171 .needs_kconfigs = (const char *[]) { 172 "CONFIG_MANDATORY_FILE_LOCKING=y", 173 NULL 174 }, 175 .test_all = verify_ftruncate, 176 .setup = setup, 177 .needs_checkpoints = 1, 178 .forks_child = 1, 179 .mount_device = 1, 180 .needs_root = 1, 181 .mntpoint = MNTPOINT, 182}; 183