1 /* 2 * Copyright (c) 2015 Oracle and/or its affiliates. All Rights Reserved. 3 * 4 * This program is free software; you can redistribute it and/or 5 * modify it under the terms of the GNU General Public License as 6 * published by the Free Software Foundation; either version 2 of 7 * the License, or (at your option) any later version. 8 * 9 * This program is distributed in the hope that it would be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program. If not, see <http://www.gnu.org/licenses/>. 16 * 17 * Description: 18 * Test allocates a file with specified size then tests the following modes: 19 * FALLOC_FL_PUNCH_HOLE, FALLOC_FL_ZERO_RANGE and FALLOC_FL_COLLAPSE_RANGE. 20 * 21 * Author: Alexey Kodanev <alexey.kodanev (at) oracle.com> 22 */ 23 24 #define _GNU_SOURCE 25 26 #include <stdio.h> 27 #include <stdlib.h> 28 #include <errno.h> 29 #include <sys/stat.h> 30 #include <fcntl.h> 31 #include <unistd.h> 32 33 #include "tst_test.h" 34 #include "lapi/fallocate.h" 35 36 static int fd; 37 static size_t block_size; 38 static size_t buf_size; 39 40 #define MNTPOINT "fallocate" 41 #define FNAME MNTPOINT "/fallocate.txt" 42 #define NUM_OF_BLOCKS 3 43 44 static char *verbose; 45 46 static void get_blocksize(void) 47 { 48 struct stat file_stat; 49 50 SAFE_FSTAT(fd, &file_stat); 51 52 block_size = file_stat.st_blksize; 53 buf_size = NUM_OF_BLOCKS * block_size; 54 } 55 56 static size_t get_allocsize(void) 57 { 58 struct stat file_stat; 59 60 fsync(fd); 61 62 SAFE_FSTAT(fd, &file_stat); 63 64 return file_stat.st_blocks * 512; 65 } 66 67 static void fill_tst_buf(char buf[]) 68 { 69 /* fill the buffer with a, b, c, ... letters on each block */ 70 int i; 71 72 for (i = 0; i < NUM_OF_BLOCKS; ++i) 73 memset(buf + i * block_size, 'a' + i, block_size); 74 } 75 76 static void check_file_data(const char exp_buf[], size_t size) 77 { 78 char rbuf[size]; 79 80 tst_res(TINFO, "reading the file, compare with expected buffer"); 81 82 SAFE_LSEEK(fd, 0, SEEK_SET); 83 SAFE_READ(1, fd, rbuf, size); 84 85 if (memcmp(exp_buf, rbuf, size)) { 86 if (verbose) { 87 tst_res_hexd(TINFO, exp_buf, size, "expected:"); 88 tst_res_hexd(TINFO, rbuf, size, "but read:"); 89 } 90 tst_brk(TFAIL, "not expected file data"); 91 } 92 } 93 94 static void test01(void) 95 { 96 tst_res(TINFO, "allocate '%zu' bytes", buf_size); 97 98 if (fallocate(fd, 0, 0, buf_size) == -1) { 99 if (errno == ENOSYS || errno == EOPNOTSUPP) 100 tst_brk(TCONF, "fallocate() not supported"); 101 tst_brk(TFAIL | TERRNO, "fallocate() failed"); 102 } 103 104 char buf[buf_size]; 105 106 fill_tst_buf(buf); 107 108 SAFE_WRITE(1, fd, buf, buf_size); 109 110 tst_res(TPASS, "test-case succeeded"); 111 } 112 113 static void test02(void) 114 { 115 size_t alloc_size0 = get_allocsize(); 116 117 tst_res(TINFO, "read allocated file size '%zu'", alloc_size0); 118 tst_res(TINFO, "make a hole with FALLOC_FL_PUNCH_HOLE"); 119 120 if (tst_kvercmp(2, 6, 38) < 0) { 121 tst_brk(TCONF, 122 "FALLOC_FL_PUNCH_HOLE needs Linux 2.6.38 or newer"); 123 } 124 125 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 126 block_size, block_size) == -1) { 127 if (errno == EOPNOTSUPP) { 128 tst_brk(TCONF, 129 "FALLOC_FL_PUNCH_HOLE not supported"); 130 } 131 tst_brk(TFAIL | TERRNO, "fallocate() failed"); 132 } 133 134 tst_res(TINFO, "check that file has a hole with lseek(,,SEEK_HOLE)"); 135 off_t ret = lseek(fd, 0, SEEK_HOLE); 136 137 if (ret != (ssize_t)block_size) { 138 /* exclude error when kernel doesn't have SEEK_HOLE support */ 139 if (errno != EINVAL) { 140 tst_brk(TFAIL | TERRNO, 141 "fallocate() or lseek() failed"); 142 } 143 if (tst_kvercmp(3, 1, 0) < 0) { 144 tst_res(TINFO, "lseek() doesn't support SEEK_HOLE, " 145 "this is expected for < 3.1 kernels"); 146 } else { 147 tst_brk(TBROK | TERRNO, 148 "lseek() doesn't support SEEK_HOLE"); 149 } 150 } else { 151 tst_res(TINFO, "found a hole at '%ld' offset", ret); 152 } 153 154 size_t alloc_size1 = get_allocsize(); 155 156 tst_res(TINFO, "allocated file size before '%zu' and after '%zu'", 157 alloc_size0, alloc_size1); 158 if ((alloc_size0 - block_size) != alloc_size1) 159 tst_brk(TFAIL, "not expected allocated size"); 160 161 char exp_buf[buf_size]; 162 163 fill_tst_buf(exp_buf); 164 memset(exp_buf + block_size, 0, block_size); 165 166 check_file_data(exp_buf, buf_size); 167 168 tst_res(TPASS, "test-case succeeded"); 169 } 170 171 static void test03(void) 172 { 173 tst_res(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE"); 174 175 if (tst_kvercmp(3, 15, 0) < 0) { 176 tst_brk(TCONF, 177 "FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer"); 178 } 179 180 size_t alloc_size0 = get_allocsize(); 181 182 tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0); 183 184 if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size - 1, 185 block_size + 2) == -1) { 186 if (errno == EOPNOTSUPP) { 187 tst_brk(TCONF, 188 "FALLOC_FL_ZERO_RANGE not supported"); 189 } 190 tst_brk(TFAIL | TERRNO, "fallocate failed"); 191 } 192 193 /* The file hole in the specified range must be allocated and 194 * filled with zeros. Check it. 195 */ 196 size_t alloc_size1 = get_allocsize(); 197 198 tst_res(TINFO, "allocated file size before '%zu' and after '%zu'", 199 alloc_size0, alloc_size1); 200 if ((alloc_size0 + block_size) != alloc_size1) 201 tst_brk(TFAIL, "not expected allocated size"); 202 203 char exp_buf[buf_size]; 204 205 fill_tst_buf(exp_buf); 206 memset(exp_buf + block_size - 1, 0, block_size + 2); 207 208 check_file_data(exp_buf, buf_size); 209 210 tst_res(TPASS, "test-case succeeded"); 211 } 212 213 static void test04(void) 214 { 215 tst_res(TINFO, "collapsing file space with FALLOC_FL_COLLAPSE_RANGE"); 216 217 size_t alloc_size0 = get_allocsize(); 218 219 tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0); 220 221 if (fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, block_size, 222 block_size) == -1) { 223 if (errno == EOPNOTSUPP) { 224 tst_brk(TCONF, 225 "FALLOC_FL_COLLAPSE_RANGE not supported"); 226 } 227 tst_brk(TFAIL | TERRNO, "fallocate failed"); 228 } 229 230 size_t alloc_size1 = get_allocsize(); 231 232 tst_res(TINFO, "allocated file size before '%zu' and after '%zu'", 233 alloc_size0, alloc_size1); 234 if ((alloc_size0 - block_size) != alloc_size1) 235 tst_brk(TFAIL, "not expected allocated size"); 236 237 size_t size = buf_size - block_size; 238 char tmp_buf[buf_size]; 239 char exp_buf[size]; 240 241 fill_tst_buf(tmp_buf); 242 243 memcpy(exp_buf, tmp_buf, block_size); 244 memcpy(exp_buf + block_size, tmp_buf + 2 * block_size, 245 buf_size - block_size * 2); 246 247 exp_buf[block_size - 1] = exp_buf[block_size] = '\0'; 248 check_file_data(exp_buf, size); 249 250 tst_res(TPASS, "test-case succeeded"); 251 } 252 253 static void test05(void) 254 { 255 tst_res(TINFO, "inserting space with FALLOC_FL_INSERT_RANGE"); 256 257 size_t alloc_size0 = get_allocsize(); 258 259 tst_res(TINFO, "read current allocated file size '%zu'", alloc_size0); 260 261 if (fallocate(fd, FALLOC_FL_INSERT_RANGE, block_size, 262 block_size) == -1) { 263 if (errno == EOPNOTSUPP) { 264 tst_brk(TCONF, 265 "FALLOC_FL_INSERT_RANGE not supported"); 266 } 267 tst_brk(TFAIL | TERRNO, "fallocate failed"); 268 } 269 270 /* allocate space and ensure that it filled with zeroes */ 271 if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size, block_size) == -1) 272 tst_brk(TFAIL | TERRNO, "fallocate failed"); 273 274 size_t alloc_size1 = get_allocsize(); 275 276 tst_res(TINFO, "allocated file size before '%zu' and after '%zu'", 277 alloc_size0, alloc_size1); 278 if ((alloc_size0 + block_size) != alloc_size1) 279 tst_brk(TFAIL, "not expected allocated size"); 280 281 char exp_buf[buf_size]; 282 283 fill_tst_buf(exp_buf); 284 memset(exp_buf + block_size - 1, 0, block_size + 2); 285 286 check_file_data(exp_buf, buf_size); 287 288 tst_res(TPASS, "test-case succeeded"); 289 } 290 291 static void (*tcases[])(void) = { 292 test01, test02, test03, test04, test05 293 }; 294 295 static void run(unsigned int i) 296 { 297 tcases[i](); 298 } 299 300 static void setup(void) 301 { 302 fd = SAFE_OPEN(FNAME, O_RDWR | O_CREAT, 0700); 303 304 get_blocksize(); 305 } 306 307 static void cleanup(void) 308 { 309 if (fd > 0) 310 SAFE_CLOSE(fd); 311 } 312 313 static struct tst_option opts[] = { 314 {"v", &verbose, "-v Turns on verbose mode"}, 315 {NULL, NULL, NULL} 316 }; 317 318 static struct tst_test test = { 319 .options = opts, 320 .cleanup = cleanup, 321 .setup = setup, 322 .test = run, 323 .tcnt = ARRAY_SIZE(tcases), 324 .mount_device = 1, 325 .mntpoint = MNTPOINT, 326 .all_filesystems = 1, 327 .needs_tmpdir = 1, 328 .needs_root = 1, 329 }; 330