[comp.os.minix] Bug in rmdir

Steinsbo%hsr.uninett@nac.no (Bjarne Steinsbo) (06/08/89)

Bug in `rm -r' revisited .. The bug is really in rmdir. Try making
two directories t1 and t2, `cd' to t1 and do `rmdir ../t2'. You will
get a t1 directory with no `.' and `..' entries and t2 will be left
unchanged.
Included is a new `rmdir' where this bug is removed (I hope :-)
I have not given this the extensive tests a SUID program deserves,
so please try it out. The bug is present in both the PC and Atari
versions of Minix, but Atari users will need to have upgraded libraries
to run this version.

I didn't make cdifs since the difs got larger than the program.

Please report any problems you might have.

Bjarne Steinsbo		<steinbo@hsr.uninett> or try
			<steinsbo%hsr.uninett@norunix.bitnet>

Where are you?
echo x - rmdir.c
sed '/^X/s///' > rmdir.c << '/'
X/* rmdir - remove a directory		Author: Adri Koppes
X * (modified by Paul Polderman)
X * (modified by Bjarne Steinsbo)
X */
X
X#include <signal.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <limits.h>
X
X#ifdef __GNUC__
X#ifdef ATARI_ST
X#include <std.h>
X#endif
X#endif
X
X#ifndef NULL
X#define NULL (char *) 0
X#endif
X
Xint     error = 0;
X
Xmain (argc, argv)
Xregister int argc;
Xregister char  **argv;
X{
X    if (argc < 2) {
X	prints ("Usage: rmdir dir ...\n");
X	exit (1);
X    }
X    signal (SIGHUP, SIG_IGN);
X    signal (SIGINT, SIG_IGN);
X    signal (SIGQUIT, SIG_IGN);
X    signal (SIGTERM, SIG_IGN);
X    while (--argc)
X	remove (*++argv);
X    if (error)
X	exit (1);
X}
X
Xextern char *rindex();
X
Xremove (dirname)
Xchar   *dirname;
X{
X    struct direct   d;
X    struct stat s,
X                cwd;
X    register int fd = 0;
X    register char *p;
X    char *abs_path();
X
X    if ((p = abs_path(dirname)) == NULL) {
X	stderr2("can't chdir ", dirname);
X	std_err("\n");
X	error++;
X	return;
X    } else dirname = p;
X
X    stat (dirname, &s);
X
X    stat (".", &cwd);
X    if ((s.st_ino == cwd.st_ino) && (s.st_dev == cwd.st_dev)) {
X	std_err ("rmdir: can't remove current directory\n");
X	error++;
X	return;
X    }
X
X    if (access (dirname, 6)) {
X	stderr2(dirname, " no permission\n");
X	error++;
X	return;
X    }
X    if ((fd = open (dirname, 0)) < 0) {
X	stderr2("can't read ", dirname);
X	std_err("\n");
X	error++;
X	return;
X    }
X    while (read (fd, (char *) & d, (int)sizeof (struct direct)) == sizeof (struct direct))
X    if (d.d_ino != 0)
X	if (strcmp (d.d_name, ".") && strcmp (d.d_name, "..")) {
X            stderr2(dirname, " not empty\n");
X	    close(fd);
X	    error++;
X	    return;
X	}
X    close (fd);
X    strcat (dirname, "/..");
X    for (p = dirname; *p; p++)	/* find end of dots */
X	;
X    unlink(dirname);		/* dirname/.. */
X    *(p - 1) = '\0';
X    unlink(dirname);		/* dirname/. */
X    *(p - 3) = '\0';
X    unlink(dirname);		/* dirname */
X}
X
Xstderr2(s1, s2)
Xchar *s1, *s2;
X{
X	std_err("rmdir: ");
X	std_err(s1);
X	std_err(s2);
X}
X
Xchar *abs_path(dir)		/* make absolute path */
Xchar *dir;
X{
X	static char fullpath[PATH_MAX];
X	char curpath[PATH_MAX];
X
X	getcwd(curpath,PATH_MAX);
X	if (chdir(dir) == 0) {
X		getcwd(fullpath,PATH_MAX);
X		chdir(curpath);
X		return fullpath;
X	} else return NULL;
X}
/