Home | History | Annotate | Download | only in fallocate
      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