1239281Sgonzo/*-
2239281Sgonzo * Copyright (c) 2005 Robert N. M. Watson
3239281Sgonzo * All rights reserved.
4239281Sgonzo *
5239281Sgonzo * Redistribution and use in source and binary forms, with or without
6239281Sgonzo * modification, are permitted provided that the following conditions
7239281Sgonzo * are met:
8239281Sgonzo * 1. Redistributions of source code must retain the above copyright
9239281Sgonzo *    notice, this list of conditions and the following disclaimer.
10239281Sgonzo * 2. Redistributions in binary form must reproduce the above copyright
11239281Sgonzo *    notice, this list of conditions and the following disclaimer in the
12239281Sgonzo *    documentation and/or other materials provided with the distribution.
13239281Sgonzo *
14239281Sgonzo * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15239281Sgonzo * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16239281Sgonzo * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17239281Sgonzo * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18239281Sgonzo * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19239281Sgonzo * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20239281Sgonzo * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21239281Sgonzo * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22239281Sgonzo * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23239281Sgonzo * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24239281Sgonzo * SUCH DAMAGE.
25239281Sgonzo *
26239281Sgonzo * $FreeBSD$
27239281Sgonzo */
28239281Sgonzo
29239281Sgonzo#include <err.h>
30239281Sgonzo#include <errno.h>
31239281Sgonzo#include <fcntl.h>
32239281Sgonzo#include <limits.h>
33239281Sgonzo#include <stdio.h>
34239281Sgonzo#include <stdlib.h>
35239281Sgonzo#include <string.h>
36239281Sgonzo#include <unistd.h>
37239281Sgonzo
38239281Sgonzo/*
39239281Sgonzo * This regression test attempts to exercise two instances of uprintf(9) in
40239281Sgonzo * UFS: (1) when blocks are exhausted, and (2) when inodes are exhausted, in
41239281Sgonzo * order to attempt to trigger races in the uprintf(9) code.  The test
42239281Sgonzo * accepts a pointer to a path -- ideally, a very small UFS partition -- and
43239281Sgonzo * then proceeds to fill it in various ways.
44239281Sgonzo *
45239281Sgonzo * This tool assumes that it is alright to create, and delete, entries in the
46239281Sgonzo * directory with names of integer values.  Don't run this tool against a
47239281Sgonzo * directory that has files with names along those lines if you want to keep
48239281Sgonzo * the files.
49239281Sgonzo *
50239281Sgonzo * Suggested usage is:
51239281Sgonzo *
52239281Sgonzo * mdconfig -a -t malloc -s 512
53239281Sgonzo * newfs /dev/mdX
54239281Sgonzo * mount /dev/mdX /mnt
55239281Sgonzo * ufs_uprintf /mnt
56239281Sgonzo * umount /mnt
57239281Sgonzo * mdconfig -d -u X
58239281Sgonzo */
59239281Sgonzo
60239281Sgonzo#define	NUMTRIES	200
61239281Sgonzo
62239281Sgonzo/*
63239281Sgonzo * Fill up the disk, then generate NUMTRIES additional ENOSPC errors.
64239281Sgonzo */
65239281Sgonzo#define	BLOCKSIZE	1024
66239281Sgonzo#define	BLOCKS_FILENAME	"0"
67239281Sgonzostatic void
68239281Sgonzofill_blocks(void)
69239281Sgonzo{
70239281Sgonzo	char block[BLOCKSIZE];
71239281Sgonzo	ssize_t len;
72239281Sgonzo	int fd, i;
73239281Sgonzo
74239281Sgonzo	fd = open(BLOCKS_FILENAME, O_CREAT | O_TRUNC | O_RDWR, 0600);
75239281Sgonzo	if (fd < 0)
76239281Sgonzo		err(-1, "fill_blocks: open(%s)", BLOCKS_FILENAME);
77239281Sgonzo
78239281Sgonzo	/*
79239281Sgonzo	 * First step: fill the disk device.  Keep extending the file until
80239281Sgonzo	 * we hit our first error, and hope it is ENOSPC.
81239281Sgonzo	 */
82239281Sgonzo	bzero(block, BLOCKSIZE);
83239281Sgonzo	errno = 0;
84239281Sgonzo	while (1) {
85239281Sgonzo		len = write(fd, block, BLOCKSIZE);
86239281Sgonzo		if (len < 0)
87239281Sgonzo			break;
88239281Sgonzo		if (len != BLOCKSIZE) {
89239281Sgonzo			warnx("fill_blocks: write(%d) returned %zd",
90239281Sgonzo			    BLOCKSIZE, len);
91239281Sgonzo			close(fd);
92239281Sgonzo			(void)unlink(BLOCKS_FILENAME);
93239281Sgonzo			exit(-1);
94239281Sgonzo		}
95239281Sgonzo
96239281Sgonzo	}
97239281Sgonzo	if (errno != ENOSPC) {
98239281Sgonzo		warn("fill_blocks: write");
99239281Sgonzo		close(fd);
100239281Sgonzo		(void)unlink(BLOCKS_FILENAME);
101239281Sgonzo		exit(-1);
102239281Sgonzo	}
103239281Sgonzo
104239281Sgonzo	/*
105239281Sgonzo	 * Second step: generate NUMTRIES instances of the error by retrying
106239281Sgonzo	 * the write.
107239281Sgonzo	 */
108239281Sgonzo	for (i = 0; i < NUMTRIES; i++) {
109239281Sgonzo		len = write(fd, block, BLOCKSIZE);
110239281Sgonzo		if (len < 0 && errno != ENOSPC) {
111239281Sgonzo			warn("fill_blocks: write after ENOSPC");
112239281Sgonzo			close(fd);
113239281Sgonzo			(void)unlink(BLOCKS_FILENAME);
114239281Sgonzo			exit(-1);
115239281Sgonzo		}
116239281Sgonzo	}
117239281Sgonzo
118239281Sgonzo	close(fd);
119239281Sgonzo	(void)unlink(BLOCKS_FILENAME);
120239281Sgonzo}
121239281Sgonzo
122239281Sgonzo/*
123239281Sgonzo * Create as many entries in the directory as we can, then once we start
124239281Sgonzo * hitting ENOSPC, try NUMTRIES additional times.  Note that we don't be able
125239281Sgonzo * to tell the difference between running out of inodes and running out of
126239281Sgonzo * room to extend the directory, so this is just a best effort.
127239281Sgonzo */
128239281Sgonzostatic void
129239281Sgonzofill_inodes(void)
130239281Sgonzo{
131239281Sgonzo	char path[PATH_MAX];
132239281Sgonzo	int fd, i, max;
133239281Sgonzo
134239281Sgonzo	/*
135239281Sgonzo	 * First step, fill the directory.
136239281Sgonzo	 */
137239281Sgonzo	i = 0;
138239281Sgonzo	while (1) {
139239281Sgonzo		snprintf(path, PATH_MAX, "%d", i);
140239281Sgonzo		fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
141239281Sgonzo		if (fd < 0)
142239281Sgonzo			break;
143239281Sgonzo		close(fd);
144239281Sgonzo		i++;
145239281Sgonzo	}
146239281Sgonzo	max = i;
147239281Sgonzo	if (errno != ENOSPC) {
148239281Sgonzo		warn("fill_inodes: open(%s)", path);
149239281Sgonzo		goto teardown;
150239281Sgonzo	}
151239281Sgonzo
152239281Sgonzo	for (i = 0; i < NUMTRIES; i++) {
153239281Sgonzo		fd = open(path, O_CREAT | O_TRUNC | O_RDWR, 0600);
154239281Sgonzo		if (fd < 0 && errno != ENOSPC) {
155239281Sgonzo			warn("fill_inodes: open(%s) after ENOSPC", path);
156239281Sgonzo			goto teardown;
157239281Sgonzo		}
158239281Sgonzo		if (fd >= 0) {
159239281Sgonzo			warnx("fill_inodes: open(%s) after ENOSPC returned "
160239281Sgonzo			    " %d", path, fd);
161239281Sgonzo			close(fd);
162239281Sgonzo			goto teardown;
163239281Sgonzo		}
164239281Sgonzo	}
165239281Sgonzo
166239281Sgonzoteardown:
167239281Sgonzo	for (i = 0; i < max; i++) {
168239281Sgonzo		snprintf(path, PATH_MAX, "%d", i);
169239281Sgonzo		(void)unlink(path);
170239281Sgonzo	}
171239281Sgonzo}
172239281Sgonzo
173239281Sgonzoint
174239281Sgonzomain(int argc, char *argv[])
175239281Sgonzo{
176239281Sgonzo
177239281Sgonzo	if (argc != 2)
178239281Sgonzo		err(-1, "usage: ufs_uprintf /non_optional_path");
179239281Sgonzo
180239281Sgonzo	if (chdir(argv[1]) < 0)
181239281Sgonzo		err(-1, "chdir(%s)", argv[1]);
182239281Sgonzo
183239281Sgonzo	fill_blocks();
184239281Sgonzo
185239281Sgonzo	fill_inodes();
186239281Sgonzo
187239281Sgonzo	return (0);
188239281Sgonzo}
189239281Sgonzo