lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <C411BFA0-3C52-461E-8C15-3EE3F076AE71@dilger.ca>
Date:	Mon, 27 Jun 2011 12:32:17 -0600
From:	Andreas Dilger <adilger@...ger.ca>
To:	Josef Bacik <josef@...hat.com>
Cc:	linux-fsdevel@...r.kernel.org, linux-btrfs@...r.kernel.org,
	linux-kernel@...r.kernel.org, viro@...IV.linux.org.uk
Subject: Re: [PATCH] xfstests 255: add a seek_data/seek_hole tester

On 2011-06-27, at 12:02 PM, Josef Bacik wrote:
> This is a test to make sure seek_data/seek_hole is acting like it does on
> Solaris.  It will check to see if the fs supports finding a hole or not and will
> adjust as necessary.
> 
> diff --git a/src/seek-tester.c b/src/seek-tester.c
> new file mode 100644
> index 0000000..2b8c957
> --- /dev/null
> +++ b/src/seek-tester.c
> @@ -0,0 +1,473 @@
> +/*
> + * Copyright (C) 2011 Oracle.  All rights reserved.
> + * Copyright (C) 2011 Red Hat.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU General Public
> + * License v2 as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * General Public License for more details.
> + *
> + * You should have received a copy of the GNU General Public
> + * License along with this program; if not, write to the
> + * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
> + * Boston, MA 021110-1307, USA.
> + */
> +
> +#define _XOPEN_SOURCE 500
> +#include <sys/types.h>
> +#include <sys/stat.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <stdio.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <stdlib.h>
> +
> +#define SEEK_DATA	3
> +#define SEEK_HOLE	4

These should probably be "#ifndef SEEK_DATA" so that gcc doesn't complain
in the future when these are added to a standard header.

> +#define FS_NO_HOLES	(1 << 0)
> +#define QUIET		(1 << 1)
> +
> +static blksize_t alloc_size;
> +static unsigned flags = 0;
> +
> +static int get_io_sizes(int fd)
> +{
> +	struct stat buf;
> +	int ret;
> +
> +	ret = fstat(fd, &buf);
> +	if (ret)
> +		fprintf(stderr, "  ERROR %d: Failed to find io blocksize\n",
> +			errno);
> +
> +	/* st_blksize is typically also the allocation size */
> +	alloc_size = buf.st_blksize;
> +
> +	if (!(flags & QUIET))
> +		printf("Allocation size: %ld\n", alloc_size);
> +
> +	return ret;
> +}
> +
> +#define do_free(x)	do { if(x) free(x); } while(0);
> +
> +static void *do_malloc(size_t size)
> +{
> +	void *buf;
> +
> +	buf = malloc(size);
> +	if (!buf)
> +		fprintf(stderr, "  ERROR: Unable to allocate %ld bytes\n",
> +			(long)size);
> +
> +	return buf;
> +}
> +
> +static int do_truncate(int fd, off_t length)
> +{
> +	int ret;
> +
> +	ret = ftruncate(fd, length);
> +	if (ret)
> +		fprintf(stderr, "  ERROR %d: Failed to extend file "
> +			"to %ld bytes\n", errno, (long)length);
> +	return ret;
> +}
> +
> +static ssize_t do_pwrite(int fd, const void *buf, size_t count, off_t offset)
> +{
> +	ssize_t ret, written = 0;
> +
> +	while (count > written) {
> +		ret = pwrite(fd, buf + written, count - written, offset + written);
> +		if (ret < 0) {
> +			fprintf(stderr, "  ERROR %d: Failed to write %ld "
> +				"bytes\n", errno, (long)count);
> +			return ret;
> +		}
> +		written += ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static int do_lseek(int testnum, int subtest, int fd, int origin, off_t set,
> +		    off_t exp)
> +{
> +	off_t pos;
> +	int ret = -1;
> +
> +	pos = lseek(fd, set, origin);
> +
> +	if (pos != exp) {
> +		fprintf(stderr, "  ERROR in Test %d.%d: POS expected %ld, "
> +			"got %ld\n", testnum, subtest, (long)exp, (long)pos);
> +		goto out;
> +	}
> +
> +	if (pos == -1 && errno != ENXIO) {
> +		fprintf(stderr, "  ERROR in Test %d.%d: ERRNO expected %d, "
> +			"got %d\n", testnum, subtest, ENXIO, errno);
> +		goto out;
> +	}
> +
> +	ret = 0;
> +
> +out:
> +	return ret;
> +}
> +
> +static int get_flags(int fd)
> +{
> +	const char *buf = "ABCDEF";
> +	ssize_t written;
> +	off_t pos;
> +	int ret;
> +
> +	ret = do_truncate(fd, alloc_size * 2);
> +	if (ret)
> +		return ret;
> +
> +	written = do_pwrite(fd, buf, strlen(buf), 0);
> +	if (written)
> +		return -1;
> +
> +	pos = lseek(fd, 0, SEEK_HOLE);
> +	if (pos == alloc_size * 2) {
> +		if (!(flags & QUIET))
> +			printf("File system does not recognize holes, the only "
> +			       "hole found will be at the end.\n");
> +		flags |= FS_NO_HOLES;

This is a question that I've also had about compatibility with older
(well, every) Linux kernel that does not support SEEK_{HOLE,DATA}
today.

My reading of the existing generic_file_llseek() and default_llseek()
code, along with most filesystem-specific llseek() implementations is
that they will happily ignore the @whence parameter if it is not
known, and pretend like it is 0 (SEEK_SET), so they will just set the
position to the @offset parameter and return this value.  In that
case, the above "SEEK_HOLE" test would incorrectly fail on every
Linux kernel in existence today because the returned pos == 0.

Should applications call both SEEK_HOLE and SEEK_DATA with @offset=0,
and if they return the same values (which is normally impossible,
decide that the kernel does not support this SEEK_* functionality?

> +	} else if (pos == (off_t)-1) {
> +		fprintf(stderr, "SEEK_HOLE is not supported\n");
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +/* test hole data hole data */
> +static int test06(int fd, int testnum)
> +{
> +	int ret = 0;
> +	char *buf = NULL;
> +	int bufsz = alloc_size;
> +	int filsz = bufsz * 4;
> +	int off;
> +
> +	if (flags & FS_NO_HOLES)
> +		return 1;
> +
> +	/* HOLE - DATA - HOLE - DATA */
> +	/* Each unit is bufsz */
> +
> +	buf = do_malloc(bufsz);
> +	if (!buf)
> +		goto out;
> +	memset(buf, 'a', bufsz);
> +
> +	ret = do_pwrite(fd, buf, bufsz, bufsz);
> +	if (!ret)
> +		do_pwrite(fd, buf, bufsz, bufsz * 3);
> +	if (ret)
> +		goto out;
> +
> +	/* offset at the beginning */
> +	ret += do_lseek(testnum,  1, fd, SEEK_HOLE, 0, 0);
> +	ret += do_lseek(testnum,  2, fd, SEEK_HOLE, 1, 1);
> +	ret += do_lseek(testnum,  3, fd, SEEK_DATA, 0, bufsz);
> +	ret += do_lseek(testnum,  4, fd, SEEK_DATA, 1, bufsz);
> +
> +	/* offset around first hole-data boundary */
> +	off = bufsz;
> +	ret += do_lseek(testnum,  5, fd, SEEK_HOLE, off - 1, off - 1);
> +	ret += do_lseek(testnum,  6, fd, SEEK_DATA, off - 1, off);
> +	ret += do_lseek(testnum,  7, fd, SEEK_HOLE, off,     bufsz * 2);
> +	ret += do_lseek(testnum,  8, fd, SEEK_DATA, off,     off);
> +	ret += do_lseek(testnum,  9, fd, SEEK_HOLE, off + 1, bufsz * 2);
> +	ret += do_lseek(testnum, 10, fd, SEEK_DATA, off + 1, off + 1);
> +
> +	/* offset around data-hole boundary */
> +	off = bufsz * 2;
> +	ret += do_lseek(testnum, 11, fd, SEEK_HOLE, off - 1, off);
> +	ret += do_lseek(testnum, 12, fd, SEEK_DATA, off - 1, off - 1);
> +	ret += do_lseek(testnum, 13, fd, SEEK_HOLE, off,     off);
> +	ret += do_lseek(testnum, 14, fd, SEEK_DATA, off,     bufsz * 3);
> +	ret += do_lseek(testnum, 15, fd, SEEK_HOLE, off + 1, off + 1);
> +	ret += do_lseek(testnum, 16, fd, SEEK_DATA, off + 1, bufsz * 3);
> +
> +	/* offset around second hole-data boundary */
> +	off = bufsz * 3;
> +	ret += do_lseek(testnum, 17, fd, SEEK_HOLE, off - 1, off - 1);
> +	ret += do_lseek(testnum, 18, fd, SEEK_DATA, off - 1, off);
> +	ret += do_lseek(testnum, 19, fd, SEEK_HOLE, off,     filsz);
> +	ret += do_lseek(testnum, 20, fd, SEEK_DATA, off,     off);
> +	ret += do_lseek(testnum, 21, fd, SEEK_HOLE, off + 1, filsz);
> +	ret += do_lseek(testnum, 22, fd, SEEK_DATA, off + 1, off + 1);
> +
> +	/* offset around the end of file */
> +	off = filsz;
> +	ret += do_lseek(testnum, 23, fd, SEEK_HOLE, off - 1, filsz);
> +	ret += do_lseek(testnum, 24, fd, SEEK_DATA, off - 1, filsz - 1);
> +	ret += do_lseek(testnum, 25, fd, SEEK_HOLE, off, -1);
> +	ret += do_lseek(testnum, 26, fd, SEEK_DATA, off, -1);
> +	ret += do_lseek(testnum, 27, fd, SEEK_HOLE, off + 1, -1);
> +	ret += do_lseek(testnum, 28, fd, SEEK_DATA, off + 1, -1);
> +
> +out:
> +	do_free(buf);
> +	return ret;
> +}
> +
> +/* test file with data at the beginning and a hole at the end */
> +static int test05(int fd, int testnum)
> +{
> +	int ret = -1;
> +	char *buf = NULL;
> +	int bufsz = alloc_size;
> +	int filsz = bufsz * 4;
> +
> +	if (flags & FS_NO_HOLES)
> +		return 1;
> +
> +	/* DATA - HOLE */
> +	/* Each unit is bufsz */
> +
> +	buf = do_malloc(bufsz);
> +	if (!buf)
> +		goto out;
> +	memset(buf, 'a', bufsz);
> +
> +	ret = do_truncate(fd, filsz);
> +	if (!ret)
> +		ret = do_pwrite(fd, buf, bufsz, 0);
> +	if (ret)
> +		goto out;
> +
> +	/* offset at the beginning */
> +	ret += do_lseek(testnum,  1, fd, SEEK_HOLE, 0, bufsz);
> +	ret += do_lseek(testnum,  2, fd, SEEK_HOLE, 1, bufsz);
> +	ret += do_lseek(testnum,  3, fd, SEEK_DATA, 0, 0);
> +	ret += do_lseek(testnum,  4, fd, SEEK_DATA, 1, 1);
> +
> +	/* offset around data-hole boundary */
> +	ret += do_lseek(testnum,  5, fd, SEEK_HOLE, bufsz - 1, bufsz);
> +	ret += do_lseek(testnum,  6, fd, SEEK_DATA, bufsz - 1, bufsz - 1);
> +	ret += do_lseek(testnum,  7, fd, SEEK_HOLE, bufsz,     bufsz);
> +	ret += do_lseek(testnum,  8, fd, SEEK_DATA, bufsz,     -1);
> +	ret += do_lseek(testnum,  9, fd, SEEK_HOLE, bufsz + 1, bufsz + 1);
> +	ret += do_lseek(testnum, 10, fd, SEEK_DATA, bufsz + 1, -1);
> +
> +	/* offset around eof */
> +	ret += do_lseek(testnum, 11, fd, SEEK_HOLE, filsz - 1, filsz - 1);
> +	ret += do_lseek(testnum, 12, fd, SEEK_DATA, filsz - 1, -1);
> +	ret += do_lseek(testnum, 13, fd, SEEK_HOLE, filsz,     -1);
> +	ret += do_lseek(testnum, 14, fd, SEEK_DATA, filsz,     -1);
> +	ret += do_lseek(testnum, 15, fd, SEEK_HOLE, filsz + 1, -1);
> +	ret += do_lseek(testnum, 16, fd, SEEK_DATA, filsz + 1, -1);
> +
> +out:
> +	do_free(buf);
> +	return ret;
> +}
> +
> +/* test hole begin and data end */
> +static int test04(int fd, int testnum)
> +{
> +	int ret;
> +	char *buf = "ABCDEFGH";
> +	int bufsz = sizeof(buf);
> +	int holsz = alloc_size * 2;
> +	int filsz = holsz + bufsz;
> +
> +	if (flags & FS_NO_HOLES)
> +		return 1;
> +
> +	/* HOLE - DATA */
> +
> +	ret = do_pwrite(fd, buf, bufsz, holsz);
> +	if (ret)
> +		goto out;
> +
> +	/* offset at the beginning */
> +	ret += do_lseek(testnum,  1, fd, SEEK_HOLE, 0, 0);
> +	ret += do_lseek(testnum,  2, fd, SEEK_HOLE, 1, 1);
> +	ret += do_lseek(testnum,  3, fd, SEEK_DATA, 0, holsz);
> +	ret += do_lseek(testnum,  4, fd, SEEK_DATA, 1, holsz);
> +
> +	/* offset around hole-data boundary */
> +	ret += do_lseek(testnum,  5, fd, SEEK_HOLE, holsz - 1, holsz - 1);
> +	ret += do_lseek(testnum,  6, fd, SEEK_DATA, holsz - 1, holsz);
> +	ret += do_lseek(testnum,  7, fd, SEEK_HOLE, holsz,     filsz);
> +	ret += do_lseek(testnum,  8, fd, SEEK_DATA, holsz,     holsz);
> +	ret += do_lseek(testnum,  9, fd, SEEK_HOLE, holsz + 1, filsz);
> +	ret += do_lseek(testnum, 10, fd, SEEK_DATA, holsz + 1, holsz + 1);
> +
> +	/* offset around eof */
> +	ret += do_lseek(testnum, 11, fd, SEEK_HOLE, filsz - 1, filsz);
> +	ret += do_lseek(testnum, 12, fd, SEEK_DATA, filsz - 1, filsz - 1);
> +	ret += do_lseek(testnum, 13, fd, SEEK_HOLE, filsz,     -1);
> +	ret += do_lseek(testnum, 14, fd, SEEK_DATA, filsz,     -1);
> +	ret += do_lseek(testnum, 15, fd, SEEK_HOLE, filsz + 1, -1);
> +	ret += do_lseek(testnum, 16, fd, SEEK_DATA, filsz + 1, -1);
> +out:
> +	return ret;
> +}
> +
> +/* test full file */
> +static int test03(int fd, int testnum)
> +{
> +	char *buf = NULL;
> +	int bufsz = alloc_size + 100;
> +	int ret = -1;
> +
> +	buf = do_malloc(bufsz);
> +	if (!buf)
> +		goto out;
> +	memset(buf, 'a', bufsz);
> +
> +	ret = do_pwrite(fd, buf, bufsz, 0);
> +	if (ret)
> +		goto out;
> +
> +	/* offset at the beginning */
> +	ret += do_lseek(testnum,  1, fd, SEEK_HOLE, 0, bufsz);
> +	ret += do_lseek(testnum,  2, fd, SEEK_HOLE, 1, bufsz);
> +	ret += do_lseek(testnum,  3, fd, SEEK_DATA, 0, 0);
> +	ret += do_lseek(testnum,  4, fd, SEEK_DATA, 1, 1);
> +
> +	/* offset around eof */
> +	ret += do_lseek(testnum,  5, fd, SEEK_HOLE, bufsz - 1, bufsz);
> +	ret += do_lseek(testnum,  6, fd, SEEK_DATA, bufsz - 1, bufsz - 1);
> +	ret += do_lseek(testnum,  7, fd, SEEK_HOLE, bufsz,     -1);
> +	ret += do_lseek(testnum,  8, fd, SEEK_DATA, bufsz,     -1);
> +	ret += do_lseek(testnum,  9, fd, SEEK_HOLE, bufsz + 1, -1);
> +	ret += do_lseek(testnum, 10, fd, SEEK_DATA, bufsz + 1, -1);
> +
> +out:
> +	do_free(buf);
> +	return ret;
> +}
> +
> +/* test empty file */
> +static int test02(int fd, int testnum)
> +{
> +	int ret = 0;
> +
> +	ret += do_lseek(testnum, 1, fd, SEEK_DATA, 0, -1);
> +	ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 0, -1);
> +	ret += do_lseek(testnum, 3, fd, SEEK_HOLE, 1, -1);
> +
> +	return ret;
> +}
> +
> +/* test feature support */
> +static int test01(int fd, int testnum)
> +{
> +	int ret;
> +	char buf[] = "ABCDEFGH";
> +	int bufsz = sizeof(buf);
> +
> +	ret = do_pwrite(fd, buf, bufsz, 0);
> +	if (ret)
> +		goto out;
> +
> +	ret += do_lseek(testnum, 1, fd, SEEK_DATA, 0, 0);
> +	ret += do_lseek(testnum, 2, fd, SEEK_HOLE, 0, bufsz);
> +
> +out:
> +	return ret;
> +}
> +
> +struct testrec {
> +	int	test_num;
> +	int	(*test_func)(int fd, int testnum);
> +	char	*test_desc;
> +};
> +
> +struct testrec seek_tests[] = {
> +	{  1, test01, "Test basic support" },
> +	{  2, test02, "Test an empty file" },
> +	{  3, test03, "Test a full file" },
> +	{  4, test04, "Test file hole at beg, data at end" },
> +	{  5, test05, "Test file data at beg, hole at end" },
> +	{  6, test06, "Test file hole data hole data" },
> +};
> +
> +static int run_test(int fd, struct testrec *tr)
> +{
> +	int ret;
> +
> +	ret = tr->test_func(fd, tr->test_num);
> +	if (!(flags & QUIET))
> +		printf("%02d. %-50s\t%s\n", tr->test_num, tr->test_desc,
> +		       ret < 0 ? "FAIL" : (ret == 0 ? "SUCC" : "NOT RUN"));
> +	return ret;
> +}
> +
> +void print_help()
> +{
> +	printf("seek-test [-h] [-q] filename\n");
> +	printf("\t-h - this message\n");
> +	printf("\t-q - quiet, no output\n");
> +	printf("\tfilename - file to use for the test\n");
> +}
> +
> +int main(int argc, char **argv)
> +{
> +	int ret = -1;
> +	int i, fd = -1;
> +	int c;
> +	int numtests = sizeof(seek_tests) / sizeof(struct testrec);
> +
> +	while ((c = getopt(argc, argv, "qh")) != -1) {
> +		switch (c) {
> +		case 'q':
> +			flags |= QUIET;
> +			break;
> +		case 'h':
> +			print_help();
> +			exit(0);
> +		default:
> +			print_help();
> +			exit(1);
> +		}
> +	}
> +
> +	if (optind >= argc) {
> +		print_help();
> +		exit(1);
> +	}
> +
> +	fd = open(argv[optind], O_RDWR|O_CREAT|O_TRUNC, 0644);
> +	if (fd < 0) {
> +		fprintf(stderr, "Failed to open testfile: %d\n", errno);
> +		goto out;
> +	}
> +
> +	ret = get_io_sizes(fd);
> +	if (ret)
> +		goto out;
> +
> +	ret = get_flags(fd);
> +	if (ret)
> +		goto out;
> +
> +	for (i = 0; i < numtests; ++i) {
> +		ret = do_truncate(fd, 0);
> +		if (ret)
> +			goto out;
> +		run_test(fd, &seek_tests[i]);
> +	}
> +
> +out:
> +	if (fd > -1)
> +		close(fd);
> +	return ret;
> +}
> -- 
> 1.7.5.2
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-fsdevel" in
> the body of a message to majordomo@...r.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html


Cheers, Andreas





--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ