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 "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