1 /* 2 * Copyright (c) International Business Machines Corp., 2002 3 * Copyright (c) 2015 Cyril Hrubis <chrubis (at) suse.cz> 4 * 5 * Robbie Williamson <robbiew (at) us.ibm.com> 6 * Roy Lee <roylee (at) andestech.com> 7 * 8 * This program is free software; you can redistribute it and/or modify 9 * it under the terms of the GNU General Public License as published by 10 * the Free Software Foundation; either version 2 of the License, or 11 * (at your option) any later version. 12 * 13 * This program is distributed in the hope that it will be useful, 14 * but WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See 16 * the GNU General Public License for more details. 17 * 18 * You should have received a copy of the GNU General Public License 19 * along with this program; if not, write to the Free Software Foundation, 20 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 21 */ 22 23 /* 24 * Tests truncate and mandatory record locking. 25 * 26 * Parent creates a file, child locks a region and sleeps. 27 * 28 * Parent checks that ftruncate before the locked region and inside the region 29 * fails while ftruncate after the region succeds. 30 * 31 * Parent wakes up child, child exits, lock is unlocked. 32 * 33 * Parent checks that ftruncate now works in all cases. 34 */ 35 36 #include <signal.h> 37 #include <fcntl.h> 38 #include <unistd.h> 39 #include <stdio.h> 40 #include <errno.h> 41 #include <sys/wait.h> 42 #include <inttypes.h> 43 #include <sys/types.h> 44 #include <sys/stat.h> 45 #include <sys/mount.h> 46 #include <sys/statvfs.h> 47 48 #include "test.h" 49 #include "safe_macros.h" 50 51 #define RECLEN 100 52 #define MOUNT_DIR "dir/" 53 54 const char *TCID = "ftruncate04"; 55 int TST_TOTAL = 6; 56 57 static int len = 8 * 1024; 58 static char filename[80]; 59 60 static int recstart; 61 static int reclen; 62 63 static const char *device; 64 static const char *fs_type; 65 static int mount_flag; 66 67 static void dochild(void); 68 static void doparent(void); 69 static void setup(void); 70 static void cleanup(void); 71 72 int main(int ac, char **av) 73 { 74 int lc, pid; 75 76 tst_parse_opts(ac, av, NULL, NULL); 77 78 setup(); 79 80 #ifdef UCLINUX 81 maybe_run_child(&dochild, "sdd", filename, &recstart, &reclen); 82 #endif 83 84 for (lc = 0; TEST_LOOPING(lc); lc++) { 85 sprintf(filename, MOUNT_DIR"%s.%d.%d\n", TCID, getpid(), lc); 86 87 if (tst_fill_file(filename, 0, 1024, 8)) { 88 tst_brkm(TBROK, cleanup, 89 "Failed to create test file '%s'", filename); 90 } 91 92 SAFE_CHMOD(cleanup, filename, 02666); 93 94 reclen = RECLEN; 95 /* 96 * want at least RECLEN bytes BEFORE AND AFTER the 97 * record lock. 98 */ 99 recstart = RECLEN + rand() % (len - 3 * RECLEN); 100 101 if ((pid = FORK_OR_VFORK()) < 0) 102 tst_brkm(TBROK | TERRNO, cleanup, "fork() failed"); 103 104 if (pid == 0) { 105 #ifdef UCLINUX 106 if (self_exec(av[0], "sdd", filename, recstart, 107 reclen) < -1) { 108 tst_brkm(TBROK, cleanup, "self_exec() failed"); 109 } 110 #else 111 dochild(); 112 #endif 113 } 114 115 doparent(); 116 } 117 118 cleanup(); 119 tst_exit(); 120 } 121 122 static void ftruncate_expect_fail(int fd, off_t offset, const char *msg) 123 { 124 TEST(ftruncate(fd, offset)); 125 126 if (TEST_RETURN == 0) { 127 tst_resm(TFAIL, "ftruncate() %s succeeded unexpectedly", msg); 128 return; 129 } 130 131 if (TEST_ERRNO != EAGAIN) { 132 tst_resm(TFAIL | TTERRNO, 133 "ftruncate() %s failed unexpectedly, expected EAGAIN", 134 msg); 135 return; 136 } 137 138 tst_resm(TPASS, "ftruncate() %s failed with EAGAIN", msg); 139 } 140 141 static void ftruncate_expect_success(int fd, off_t offset, const char *msg) 142 { 143 struct stat sb; 144 145 TEST(ftruncate(fd, offset)); 146 147 if (TEST_RETURN != 0) { 148 tst_resm(TFAIL | TTERRNO, 149 "ftruncate() %s failed unexpectedly", msg); 150 return; 151 } 152 153 SAFE_FSTAT(cleanup, fd, &sb); 154 155 if (sb.st_size != offset) { 156 tst_resm(TFAIL, 157 "ftruncate() to %zu bytes succeded but fstat() reports size %zu", 158 offset, sb.st_size); 159 return; 160 } 161 162 tst_resm(TPASS, "ftruncate() %s succeded", msg); 163 } 164 165 static void doparent(void) 166 { 167 int fd; 168 169 /* Wait for child lock */ 170 TST_SAFE_CHECKPOINT_WAIT(cleanup, 0); 171 172 fd = SAFE_OPEN(cleanup, filename, O_RDWR | O_NONBLOCK); 173 174 ftruncate_expect_fail(fd, RECLEN, "offset before lock"); 175 ftruncate_expect_fail(fd, recstart + RECLEN/2, "offset in lock"); 176 ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); 177 178 /* wake child and wait for it to exit (to free record lock) */ 179 TST_SAFE_CHECKPOINT_WAKE(cleanup, 0); 180 SAFE_WAIT(NULL, NULL); 181 182 ftruncate_expect_success(fd, recstart + RECLEN/2, "offset in lock"); 183 ftruncate_expect_success(fd, recstart, "offset before lock"); 184 ftruncate_expect_success(fd, recstart + RECLEN, "offset after lock"); 185 186 SAFE_CLOSE(NULL, fd); 187 } 188 189 void dochild(void) 190 { 191 int fd; 192 struct flock flocks; 193 194 #ifdef UCLINUX 195 TST_CHECKPOINT_INIT(NULL); 196 #endif 197 198 fd = SAFE_OPEN(NULL, filename, O_RDWR); 199 200 tst_resm(TINFO, "Child locks file"); 201 202 flocks.l_type = F_WRLCK; 203 flocks.l_whence = SEEK_CUR; 204 flocks.l_start = recstart; 205 flocks.l_len = reclen; 206 207 if (fcntl(fd, F_SETLKW, &flocks) < 0) 208 tst_brkm(TFAIL, NULL, "child fcntl failed"); 209 210 TST_SAFE_CHECKPOINT_WAKE_AND_WAIT(NULL, 0); 211 212 tst_resm(TINFO, "Child unlocks file"); 213 214 tst_exit(); 215 } 216 217 static void setup(void) 218 { 219 struct statvfs fs; 220 221 srand(getpid()); 222 223 tst_tmpdir(); 224 225 SAFE_MKDIR(tst_rmdir, MOUNT_DIR, 0777); 226 227 TST_CHECKPOINT_INIT(tst_rmdir); 228 229 if (statvfs(".", &fs) == -1) 230 tst_brkm(TFAIL | TERRNO, tst_rmdir, "statvfs failed"); 231 232 if ((fs.f_flag & MS_MANDLOCK)) 233 return; 234 235 tst_resm(TINFO, "TMPDIR does not support mandatory locks"); 236 237 fs_type = tst_dev_fs_type(); 238 device = tst_acquire_device(cleanup); 239 240 if (!device) 241 tst_brkm(TCONF, cleanup, "Failed to obtain block device"); 242 243 /* the kernel returns EPERM when CONFIG_MANDATORY_FILE_LOCKING is not 244 * supported - to avoid false negatives, mount the fs first without 245 * flags and then remount it as MS_MANDLOCK */ 246 247 tst_mkfs(cleanup, device, fs_type, NULL, NULL); 248 SAFE_MOUNT(cleanup, device, MOUNT_DIR, fs_type, 0, NULL); 249 mount_flag = 1; 250 251 if (mount(NULL, MOUNT_DIR, NULL, MS_REMOUNT|MS_MANDLOCK, NULL) == -1) { 252 if (errno == EPERM) { 253 tst_brkm(TCONF, cleanup, "Mandatory locking (likely) " 254 "not supported by this system"); 255 } else { 256 tst_brkm(TBROK | TERRNO, cleanup, 257 "Remount with MS_MANDLOCK failed"); 258 } 259 } 260 } 261 262 static void cleanup(void) 263 { 264 if (mount_flag && tst_umount(MOUNT_DIR)) 265 tst_resm(TWARN | TERRNO, "umount(%s) failed", device); 266 267 if (device) 268 tst_release_device(device); 269 270 tst_rmdir(); 271 } 272