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