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 "test.h" 34 #include "safe_macros.h" 35 #include "lapi/fallocate.h" 36 37 char *TCID = "fallocate04"; 38 int TST_TOTAL = 5; 39 40 static int fd; 41 static const char fname[] = "fallocate04.txt"; 42 static size_t block_size; 43 static size_t buf_size; 44 45 #define NUM_OF_BLOCKS 3 46 47 static int verbose; 48 static const option_t options[] = { 49 {"v", &verbose, NULL}, 50 {NULL, NULL, NULL} 51 }; 52 53 static void help(void) 54 { 55 printf(" -v Verbose\n"); 56 } 57 58 static void cleanup(void) 59 { 60 close(fd); 61 tst_rmdir(); 62 } 63 64 static void get_blocksize(void) 65 { 66 struct stat file_stat; 67 68 SAFE_FSTAT(cleanup, fd, &file_stat); 69 70 block_size = file_stat.st_blksize; 71 buf_size = NUM_OF_BLOCKS * block_size; 72 } 73 74 static size_t get_allocsize(void) 75 { 76 struct stat file_stat; 77 78 fsync(fd); 79 80 SAFE_FSTAT(cleanup, fd, &file_stat); 81 82 return file_stat.st_blocks * 512; 83 } 84 85 static void fill_tst_buf(char buf[]) 86 { 87 /* fill the buffer with a, b, c, ... letters on each block */ 88 int i; 89 90 for (i = 0; i < NUM_OF_BLOCKS; ++i) 91 memset(buf + i * block_size, 'a' + i, block_size); 92 } 93 94 static void setup(void) 95 { 96 tst_tmpdir(); 97 98 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); 99 100 get_blocksize(); 101 } 102 103 static void check_file_data(const char exp_buf[], size_t size) 104 { 105 char rbuf[size]; 106 107 tst_resm(TINFO, "reading the file, compare with expected buffer"); 108 109 SAFE_LSEEK(cleanup, fd, 0, SEEK_SET); 110 SAFE_READ(cleanup, 1, fd, rbuf, size); 111 112 if (memcmp(exp_buf, rbuf, size)) { 113 if (verbose) { 114 tst_resm_hexd(TINFO, exp_buf, size, "expected:"); 115 tst_resm_hexd(TINFO, rbuf, size, "but read:"); 116 } 117 tst_brkm(TFAIL, cleanup, "not expected file data"); 118 } 119 } 120 121 static void test01(void) 122 { 123 tst_resm(TINFO, "allocate '%zu' bytes", buf_size); 124 125 if (fallocate(fd, 0, 0, buf_size) == -1) { 126 if (errno == ENOSYS || errno == EOPNOTSUPP) 127 tst_brkm(TCONF, cleanup, "fallocate() not supported"); 128 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed"); 129 } 130 131 char buf[buf_size]; 132 133 fill_tst_buf(buf); 134 135 SAFE_WRITE(cleanup, 1, fd, buf, buf_size); 136 137 tst_resm(TPASS, "test-case succeeded"); 138 } 139 140 static void test02(void) 141 { 142 size_t alloc_size0 = get_allocsize(); 143 144 tst_resm(TINFO, "read allocated file size '%zu'", alloc_size0); 145 tst_resm(TINFO, "make a hole with FALLOC_FL_PUNCH_HOLE"); 146 147 if (tst_kvercmp(2, 6, 38) < 0) { 148 tst_brkm(TCONF, cleanup, 149 "FALLOC_FL_PUNCH_HOLE needs Linux 2.6.38 or newer"); 150 } 151 152 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 153 block_size, block_size) == -1) { 154 if (errno == EOPNOTSUPP) { 155 tst_brkm(TCONF, cleanup, 156 "FALLOC_FL_PUNCH_HOLE not supported"); 157 } 158 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed"); 159 } 160 161 tst_resm(TINFO, "check that file has a hole with lseek(,,SEEK_HOLE)"); 162 off_t ret = lseek(fd, 0, SEEK_HOLE); 163 164 if (ret != (ssize_t)block_size) { 165 /* exclude error when kernel doesn't have SEEK_HOLE support */ 166 if (errno != EINVAL) { 167 tst_brkm(TFAIL | TERRNO, cleanup, 168 "fallocate() or lseek() failed"); 169 } 170 if (tst_kvercmp(3, 1, 0) < 0) { 171 tst_resm(TINFO, "lseek() doesn't support SEEK_HOLE, " 172 "this is expected for < 3.1 kernels"); 173 } else { 174 tst_brkm(TBROK | TERRNO, cleanup, 175 "lseek() doesn't support SEEK_HOLE"); 176 } 177 } else { 178 tst_resm(TINFO, "found a hole at '%ld' offset", ret); 179 } 180 181 size_t alloc_size1 = get_allocsize(); 182 183 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 184 alloc_size0, alloc_size1); 185 if ((alloc_size0 - block_size) != alloc_size1) 186 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 187 188 char exp_buf[buf_size]; 189 190 fill_tst_buf(exp_buf); 191 memset(exp_buf + block_size, 0, block_size); 192 193 check_file_data(exp_buf, buf_size); 194 195 tst_resm(TPASS, "test-case succeeded"); 196 } 197 198 static void test03(void) 199 { 200 tst_resm(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE"); 201 202 if (tst_kvercmp(3, 15, 0) < 0) { 203 tst_brkm(TCONF, cleanup, 204 "FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer"); 205 } 206 207 size_t alloc_size0 = get_allocsize(); 208 209 tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0); 210 211 if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size - 1, 212 block_size + 2) == -1) { 213 if (errno == EOPNOTSUPP) { 214 tst_brkm(TCONF, cleanup, 215 "FALLOC_FL_ZERO_RANGE not supported"); 216 } 217 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 218 } 219 220 /* The file hole in the specified range must be allocated and 221 * filled with zeros. Check it. 222 */ 223 size_t alloc_size1 = get_allocsize(); 224 225 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 226 alloc_size0, alloc_size1); 227 if ((alloc_size0 + block_size) != alloc_size1) 228 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 229 230 char exp_buf[buf_size]; 231 232 fill_tst_buf(exp_buf); 233 memset(exp_buf + block_size - 1, 0, block_size + 2); 234 235 check_file_data(exp_buf, buf_size); 236 237 tst_resm(TPASS, "test-case succeeded"); 238 } 239 240 static void test04(void) 241 { 242 tst_resm(TINFO, "collapsing file space with FALLOC_FL_COLLAPSE_RANGE"); 243 244 size_t alloc_size0 = get_allocsize(); 245 246 tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0); 247 248 if (fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, block_size, 249 block_size) == -1) { 250 if (errno == EOPNOTSUPP) { 251 tst_brkm(TCONF, cleanup, 252 "FALLOC_FL_COLLAPSE_RANGE not supported"); 253 } 254 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 255 } 256 257 size_t alloc_size1 = get_allocsize(); 258 259 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 260 alloc_size0, alloc_size1); 261 if ((alloc_size0 - block_size) != alloc_size1) 262 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 263 264 size_t size = buf_size - block_size; 265 char tmp_buf[buf_size]; 266 char exp_buf[size]; 267 268 fill_tst_buf(tmp_buf); 269 270 memcpy(exp_buf, tmp_buf, block_size); 271 memcpy(exp_buf + block_size, tmp_buf + 2 * block_size, 272 buf_size - block_size * 2); 273 274 exp_buf[block_size - 1] = exp_buf[block_size] = '\0'; 275 check_file_data(exp_buf, size); 276 277 tst_resm(TPASS, "test-case succeeded"); 278 } 279 280 static void test05(void) 281 { 282 tst_resm(TINFO, "inserting space with FALLOC_FL_INSERT_RANGE"); 283 284 size_t alloc_size0 = get_allocsize(); 285 286 tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0); 287 288 if (fallocate(fd, FALLOC_FL_INSERT_RANGE, block_size, 289 block_size) == -1) { 290 if (errno == EOPNOTSUPP) { 291 tst_brkm(TCONF, cleanup, 292 "FALLOC_FL_INSERT_RANGE not supported"); 293 } 294 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 295 } 296 297 /* allocate space and ensure that it filled with zeroes */ 298 if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size, block_size) == -1) 299 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 300 301 size_t alloc_size1 = get_allocsize(); 302 303 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 304 alloc_size0, alloc_size1); 305 if ((alloc_size0 + block_size) != alloc_size1) 306 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 307 308 char exp_buf[buf_size]; 309 310 fill_tst_buf(exp_buf); 311 memset(exp_buf + block_size - 1, 0, block_size + 2); 312 313 check_file_data(exp_buf, buf_size); 314 315 tst_resm(TPASS, "test-case succeeded"); 316 } 317 318 int main(int argc, char *argv[]) 319 { 320 int lc; 321 322 tst_parse_opts(argc, argv, options, help); 323 324 setup(); 325 326 for (lc = 0; TEST_LOOPING(lc); ++lc) { 327 test01(); 328 test02(); 329 test03(); 330 test04(); 331 test05(); 332 } 333 334 cleanup(); 335 336 tst_exit(); 337 } 338