/*1* Copyright (c) 2000, 2001, 2002, 2003, 2004, 2005, 2008, 20092* The President and Fellows of Harvard College.3*4* Redistribution and use in source and binary forms, with or without5* modification, are permitted provided that the following conditions6* are met:7* 1. Redistributions of source code must retain the above copyright8* notice, this list of conditions and the following disclaimer.9* 2. Redistributions in binary form must reproduce the above copyright10* notice, this list of conditions and the following disclaimer in the11* documentation and/or other materials provided with the distribution.12* 3. Neither the name of the University nor the names of its contributors13* may be used to endorse or promote products derived from this software14* without specific prior written permission.15*16* THIS SOFTWARE IS PROVIDED BY THE UNIVERSITY AND CONTRIBUTORS ``AS IS'' AND17* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE18* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE19* ARE DISCLAIMED. IN NO EVENT SHALL THE UNIVERSITY OR CONTRIBUTORS BE LIABLE20* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL21* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS22* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)23* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT24* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY25* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF26* SUCH DAMAGE.27*/2829/*30* rmdirtest.c31*32* Tests file system synchronization and directory implementation by33* removing the current directory under itself and then trying to do34* things. It's ok for most of those things to fail, but the system35* shouldn't crash.36*/3738#include <sys/types.h>39#include <sys/stat.h>40#include <stdio.h>41#include <string.h>42#include <unistd.h>43#include <fcntl.h>44#include <errno.h>45#include <limits.h>46#include <err.h>474849static const char testdir[] = "testdir";50static char startpoint[PATH_MAX - sizeof(testdir)];5152/*53* Create the test directory, and change into it, remembering54* where we came from.55*/5657static58void59startup(void)60{61if (getcwd(startpoint, sizeof(startpoint))==NULL) {62err(1, "getcwd (not in test dir)");63}6465if (mkdir(testdir, 0775) < 0) {66err(1, "%s: mkdir", testdir);67}6869if (chdir(testdir) < 0) {70err(1, "%s: chdir", testdir);71}72}7374/*75* Remove the test directory.76*77* Note that even though it's the current directory, we can't do it78* with rmdir(".") - what that would try to do is remove the "." entry79* from the current directory, which is justifiably prohibited.80*/8182static83void84killdir(void)85{86char tmp[PATH_MAX];8788snprintf(tmp, sizeof(tmp), "%s/%s", startpoint, testdir);89if (rmdir(tmp)<0) {90err(1, "%s: rmdir", tmp);91}92}9394/*95* Leave the test directory and go back to where we came from, so we96* can try again.97*/9899static100void101finish(void)102{103if (chdir(startpoint)<0) {104err(1, "%s: chdir", startpoint);105}106}107108/*************************************************************/109110/*111* Basic test - just try removing the directory without doing anything112* evil.113*/114static115void116test1(void)117{118printf("Making %s\n", testdir);119startup();120121printf("Removing %s while in it\n", testdir);122killdir();123124printf("Leaving the test directory\n");125finish();126}127128/*129* Now do it while we also have the directory open.130*/131132static133void134test2(void)135{136int fd;137138printf("Now trying with the directory open...\n");139startup();140fd = open(".", O_RDONLY);141if (fd<0) {142err(1, ".: open");143}144killdir();145finish();146147/* close *after* leaving, just for excitement */148if (close(fd)<0) {149err(1, "removed %s: close", testdir);150}151}152153/*154* Now see if . and .. work after rmdir.155*/156157static158void159test3(void)160{161char buf[PATH_MAX];162int fd;163164printf("Checking if . exists after rmdir\n");165startup();166killdir();167168fd = open(".", O_RDONLY);169if (fd<0) {170switch (errno) {171case EINVAL:172case EIO:173case ENOENT:174break;175default:176err(1, ".");177break;178}179}180else {181close(fd);182}183184fd = open("..", O_RDONLY);185if (fd<0) {186switch (errno) {187case EINVAL:188case EIO:189case ENOENT:190break;191default:192err(1, "..");193break;194}195}196else {197warnx("..: openable after rmdir - might be bad");198close(fd);199}200201snprintf(buf, sizeof(buf), "../%s", testdir);202fd = open(buf, O_RDONLY);203if (fd<0) {204switch (errno) {205case EINVAL:206case EIO:207case ENOENT:208break;209default:210err(1, "%s", buf);211break;212}213}214else {215errx(1, "%s: works after rmdir", buf);216}217218finish();219}220221/*222* Now try to create files.223*/224225static226void227test4(void)228{229char buf[4096];230int fd;231232printf("Checking if creating files works after rmdir...\n");233startup();234killdir();235236fd = open("newfile", O_WRONLY|O_CREAT|O_TRUNC, 0664);237if (fd<0) {238switch (errno) {239case EINVAL:240case EIO:241case ENOENT:242break;243default:244err(1, "%s", buf);245break;246}247}248else {249warnx("newfile: creating files after rmdir works");250warnx("(this is only ok if the space gets reclaimed)");251252/*253* Waste a bunch of space so we'll be able to tell254*/255memset(buf, 'J', sizeof(buf));256write(fd, buf, sizeof(buf));257write(fd, buf, sizeof(buf));258write(fd, buf, sizeof(buf));259write(fd, buf, sizeof(buf));260close(fd);261}262263finish();264}265266/*267* Now try to create directories.268*/269270static271void272test5(void)273{274printf("Checking if creating subdirs works after rmdir...\n");275startup();276killdir();277278if (mkdir("newdir", 0775)<0) {279switch (errno) {280case EINVAL:281case EIO:282case ENOENT:283break;284default:285err(1, "mkdir in removed dir");286break;287}288}289else {290warnx("newfile: creating directories after rmdir works");291warnx("(this is only ok if the space gets reclaimed)");292293/*294* Waste a bunch of space so we'll be able to tell295*/296mkdir("newdir/t0", 0775);297mkdir("newdir/t1", 0775);298mkdir("newdir/t2", 0775);299mkdir("newdir/t3", 0775);300mkdir("newdir/t4", 0775);301mkdir("newdir/t5", 0775);302}303304finish();305}306307/*308* Now try listing the directory.309*/310static311void312test6(void)313{314char buf[PATH_MAX];315int fd, len;316317printf("Now trying to list the directory...\n");318startup();319fd = open(".", O_RDONLY);320if (fd<0) {321err(1, ".: open");322}323killdir();324325while ((len = getdirentry(fd, buf, sizeof(buf)-1))>0) {326if ((unsigned)len >= sizeof(buf)-1) {327errx(1, ".: getdirentry: returned invalid length");328}329buf[len] = 0;330if (!strcmp(buf, ".") || !strcmp(buf, "..")) {331/* these are allowed to appear */332continue;333}334errx(1, ".: getdirentry: returned unexpected name %s", buf);335}336if (len==0) {337/* EOF - ok */338}339else { /* len < 0 */340switch (errno) {341case EINVAL:342case EIO:343break;344default:345err(1, ".: getdirentry");346break;347}348}349350finish();351352/* close *after* leaving, just for excitement */353if (close(fd)<0) {354err(1, "removed %s: close", testdir);355}356}357358/*359* Try getcwd.360*/361static362void363test7(void)364{365char buf[PATH_MAX];366367startup();368killdir();369if (getcwd(buf, sizeof(buf))==NULL) {370switch (errno) {371case EINVAL:372case EIO:373case ENOENT:374break;375default:376err(1, "getcwd after removing %s", testdir);377break;378}379}380else {381errx(1, "getcwd after removing %s: succeeded (got %s)",382testdir, buf);383}384385finish();386}387388/**************************************************************/389390int391main(void)392{393test1();394test2();395test3();396test4();397test5();398test6();399test7();400401printf("Whew... survived.\n");402return 0;403}404405406