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 "fallocate.h" 36 37 char *TCID = "fallocate04"; 38 int TST_TOTAL = 4; 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 SAFE_FSTAT(cleanup, fd, &file_stat); 79 80 return file_stat.st_blocks * 512; 81 } 82 83 static void fill_tst_buf(char buf[]) 84 { 85 /* fill the buffer with a, b, c, ... letters on each block */ 86 int i; 87 88 for (i = 0; i < NUM_OF_BLOCKS; ++i) 89 memset(buf + i * block_size, 'a' + i, block_size); 90 } 91 92 static void setup(void) 93 { 94 tst_tmpdir(); 95 96 fd = SAFE_OPEN(cleanup, fname, O_RDWR | O_CREAT, 0700); 97 98 get_blocksize(); 99 } 100 101 static void check_file_data(const char exp_buf[], size_t size) 102 { 103 char rbuf[size]; 104 105 tst_resm(TINFO, "reading the file, compare with expected buffer"); 106 107 SAFE_LSEEK(cleanup, fd, 0, SEEK_SET); 108 SAFE_READ(cleanup, 1, fd, rbuf, size); 109 110 if (memcmp(exp_buf, rbuf, size)) { 111 if (verbose) { 112 tst_resm_hexd(TINFO, exp_buf, size, "expected:"); 113 tst_resm_hexd(TINFO, rbuf, size, "but read:"); 114 } 115 tst_brkm(TFAIL, cleanup, "not expected file data"); 116 } 117 } 118 119 static void test01(void) 120 { 121 tst_resm(TINFO, "allocate '%zu' bytes", buf_size); 122 123 if (fallocate(fd, 0, 0, buf_size) == -1) { 124 if (errno == ENOSYS || errno == EOPNOTSUPP) 125 tst_brkm(TCONF, cleanup, "fallocate() not supported"); 126 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed"); 127 } 128 129 char buf[buf_size]; 130 131 fill_tst_buf(buf); 132 133 SAFE_WRITE(cleanup, 1, fd, buf, buf_size); 134 135 tst_resm(TPASS, "test-case succeeded"); 136 } 137 138 static void test02(void) 139 { 140 size_t alloc_size0 = get_allocsize(); 141 142 tst_resm(TINFO, "read allocated file size '%zu'", alloc_size0); 143 tst_resm(TINFO, "make a hole with FALLOC_FL_PUNCH_HOLE"); 144 145 if (tst_kvercmp(2, 6, 38) < 0) { 146 tst_brkm(TCONF, cleanup, 147 "FALLOC_FL_PUNCH_HOLE needs Linux 2.6.38 or newer"); 148 } 149 150 if (fallocate(fd, FALLOC_FL_PUNCH_HOLE | FALLOC_FL_KEEP_SIZE, 151 block_size, block_size) == -1) { 152 if (errno == EOPNOTSUPP) { 153 tst_brkm(TCONF, cleanup, 154 "FALLOC_FL_PUNCH_HOLE not supported"); 155 } 156 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate() failed"); 157 } 158 159 tst_resm(TINFO, "check that file has a hole with lseek(,,SEEK_HOLE)"); 160 off_t ret = lseek(fd, 0, SEEK_HOLE); 161 162 if (ret != (ssize_t)block_size) { 163 /* exclude error when kernel doesn't have SEEK_HOLE support */ 164 if (errno != EINVAL) { 165 tst_brkm(TFAIL | TERRNO, cleanup, 166 "fallocate() or lseek() failed"); 167 } 168 if (tst_kvercmp(3, 1, 0) < 0) { 169 tst_resm(TINFO, "lseek() doesn't support SEEK_HOLE, " 170 "this is expected for < 3.1 kernels"); 171 } else { 172 tst_brkm(TBROK | TERRNO, cleanup, 173 "lseek() doesn't support SEEK_HOLE"); 174 } 175 } else { 176 tst_resm(TINFO, "found a hole at '%ld' offset", ret); 177 } 178 179 size_t alloc_size1 = get_allocsize(); 180 181 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 182 alloc_size0, alloc_size1); 183 if ((alloc_size0 - block_size) != alloc_size1) 184 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 185 186 char exp_buf[buf_size]; 187 188 fill_tst_buf(exp_buf); 189 memset(exp_buf + block_size, 0, block_size); 190 191 check_file_data(exp_buf, buf_size); 192 193 tst_resm(TPASS, "test-case succeeded"); 194 } 195 196 static void test03(void) 197 { 198 tst_resm(TINFO, "zeroing file space with FALLOC_FL_ZERO_RANGE"); 199 200 if (tst_kvercmp(3, 15, 0) < 0) { 201 tst_brkm(TCONF, cleanup, 202 "FALLOC_FL_ZERO_RANGE needs Linux 3.15 or newer"); 203 } 204 205 size_t alloc_size0 = get_allocsize(); 206 207 tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0); 208 209 if (fallocate(fd, FALLOC_FL_ZERO_RANGE, block_size - 1, 210 block_size + 2) == -1) { 211 if (errno == EOPNOTSUPP) { 212 tst_brkm(TCONF, cleanup, 213 "FALLOC_FL_ZERO_RANGE not supported"); 214 } 215 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 216 } 217 218 /* The file hole in the specified range must be allocated and 219 * filled with zeros. Check it. 220 */ 221 size_t alloc_size1 = get_allocsize(); 222 223 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 224 alloc_size0, alloc_size1); 225 if ((alloc_size0 + block_size) != alloc_size1) 226 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 227 228 char exp_buf[buf_size]; 229 230 fill_tst_buf(exp_buf); 231 memset(exp_buf + block_size - 1, 0, block_size + 2); 232 233 check_file_data(exp_buf, buf_size); 234 235 tst_resm(TPASS, "test-case succeeded"); 236 } 237 238 static void test04(void) 239 { 240 tst_resm(TINFO, "collapsing file space with FALLOC_FL_COLLAPSE_RANGE"); 241 242 size_t alloc_size0 = get_allocsize(); 243 244 tst_resm(TINFO, "read current allocated file size '%zu'", alloc_size0); 245 246 if (fallocate(fd, FALLOC_FL_COLLAPSE_RANGE, block_size, 247 block_size) == -1) { 248 if (errno == EOPNOTSUPP) { 249 tst_brkm(TCONF, cleanup, 250 "FALLOC_FL_COLLAPSE_RANGE not supported"); 251 } 252 tst_brkm(TFAIL | TERRNO, cleanup, "fallocate failed"); 253 } 254 255 size_t alloc_size1 = get_allocsize(); 256 257 tst_resm(TINFO, "allocated file size before '%zu' and after '%zu'", 258 alloc_size0, alloc_size1); 259 if ((alloc_size0 - block_size) != alloc_size1) 260 tst_brkm(TFAIL, cleanup, "not expected allocated size"); 261 262 size_t size = buf_size - block_size; 263 char tmp_buf[buf_size]; 264 char exp_buf[size]; 265 266 fill_tst_buf(tmp_buf); 267 268 memcpy(exp_buf, tmp_buf, block_size); 269 memcpy(exp_buf + block_size, tmp_buf + size, block_size); 270 271 exp_buf[block_size - 1] = exp_buf[block_size] = '\0'; 272 check_file_data(exp_buf, size); 273 274 tst_resm(TPASS, "test-case succeeded"); 275 } 276 277 int main(int argc, char *argv[]) 278 { 279 int lc; 280 281 tst_parse_opts(argc, argv, options, help); 282 283 setup(); 284 285 for (lc = 0; TEST_LOOPING(lc); ++lc) { 286 test01(); 287 test02(); 288 test03(); 289 test04(); 290 } 291 292 cleanup(); 293 294 tst_exit(); 295 } 296