Minix1.5/commands/rmdir.c

Compare this file to the similar file:
Show the results in this format:

/* rmdir - remove a directory		Author: Adri Koppes

/* (modified by Paul Polderman)
 * (modified by Bjarne Steinsbo)	Fixed "rmdir ../anything"
 *					Modified style to standard Minix
 *					Added some comments.
 */

#include <sys/types.h>
#include <fcntl.h>
#include <signal.h>
#include <sys/stat.h>
#include <sys/dir.h>
#include <limits.h>
#include <string.h>

int error = 0;

main(argc, argv)
register int argc;
register char **argv;
{
  if (argc < 2) {
	prints("Usage: rmdir dir ...\n");
	exit(1);
  }
  signal(SIGHUP, SIG_IGN);
  signal(SIGINT, SIG_IGN);
  signal(SIGQUIT, SIG_IGN);
  signal(SIGTERM, SIG_IGN);
  while (--argc) remove(*++argv);
  if (error) exit(1);
}


remove(dirname)
char *dirname;
{
  struct direct d;		/* buffer for reading directory */
  struct stat s, cwd;		/* buffers for `stat' call */
  int fd = 0;
  int sl = 0;
  int n;
  char dots[PATH_MAX];		/* scratch buffer for dirname */
  register char *p;

  /* Is the path name too long ? Check once and for all. */
  if (strlen(dirname) > PATH_MAX - 3) {	/* Need to append `/..' */
	stderr2("path name too long : ", dirname);
	std_err("\n");
	error++;
	return;
  }

  /* Does the file exist ? */
  if (stat(dirname, &s)) {
	stderr2(dirname, " doesn't exist\n");
	error++;
	return;
  }

  /* Is it a directory ? */
  if ((s.st_mode & S_IFMT) != S_IFDIR) {
	stderr2(dirname, " not a directory\n");
	error++;
	return;
  }

  /* If path ends in /., fix it (e.g., /usr/ast/. ==> /usr/ast). */
  while (1) {
	n = strlen(dirname);
	if (n > 2 && dirname[n-2] == '/' && dirname[n-1] == '.') 
		dirname[n-2] = 0;
	else
		break;
  }
  
  /* Are we trying to remove "." or ".." ? */
  if (p = strrchr(dirname, '/'))
	p++;
  else
	p = dirname;
  if (strcmp(p, ".") == 0 || strcmp(p, "..") == 0) {
	stderr2(dirname, " will not remove \".\" or \"..\"\n");
	error++;
	return;
  }

  /* Write permission in parent directory ? */
  strcpy(dots, dirname);
  while (dirname[fd])
	if (dirname[fd++] == '/') sl = fd;
  dots[sl] = '\0';
  if (*dots == '\0') strcpy(dots, ".");
  if (access(dots, 2)) {
	stderr2(dirname, " no permission\n");
	error++;
	return;
  }

  /* Are we trying to remove current directory ? */
  stat(".", &cwd);
  if ((s.st_ino == cwd.st_ino) && (s.st_dev == cwd.st_dev)) {
	std_err("rmdir: can't remove current directory\n");
	error++;
	return;
  }

  /* Is it possible to open the directory ? */
  if ((fd = open(dirname, O_RDONLY)) < 0) {
	stderr2("can't read ", dirname);
	std_err("\n");
	error++;
	return;
  }

  /* Is the directory empty ? (except "." and "..") */
  while(read(fd, (char *) &d, sizeof(struct direct)) == sizeof(struct direct)){
	if (d.d_ino != 0) {
		if (strcmp(d.d_name, ".") && strcmp(d.d_name, "..")) {
			stderr2(dirname, " not empty\n");
			close(fd);
			error++;
			return;
		}
	}
  }
  close(fd);

  /* Will the path name be invalidated when dirname/. or dirname/.. is
   * unlinked ? In that case, fix the path-name ! */
  strcpy(dots, dirname);
  patch_path(dots);

  /* OK, let's do the rmdir. */
  if (rmdir(dots) != 0) {
	stderr2("can't remove ", dots);
	std_err("\n");
	error++;
	return;
  }
}

stderr2(s1, s2)
char *s1, *s2;
{
  std_err("rmdir: ");
  std_err(s1);
  std_err(s2);
}

/* With s pointing to the first char in the next part of the pathname,
 * check if this part is empty (/), dot (./) or dotdot (../)
 */
#define IS_EMPTY(s)  (*(s) == '/')
#define IS_DOT(s)    (*(s) == '.' && *((s)+1) == '/')
#define IS_DOTDOT(s) (*(s) == '.' && *((s)+1) == '.' && *((s)+2) == '/')

patch_path(dir)
char *dir;
{
/* Check if the path name will be invalidated when `dirname/..' and
 * `dirname/.' is later unlinked. Return a (possibly) patched path.
 * Do this by cleaning the path up, i.e. removing unnecessary parts
 * of the path. `anypath/anything/../' , `anypath/./' and `anypath//'
 * are all considered equal to `anypath/' . This assumption will break
 * when symbolic links are (are they ?) introduced. Don't remove those
 * `../' parts that are essential to the path name.
 */

  register char *p;
  char *last;
  int level = 0;

  if (*dir == '/')		/* absolute ? */
	last = dir + 1;
  else
	last = dir;

  p = last;
  while (*p != '\0') {		/* clean up the path name */
	if (IS_EMPTY(p)) {	/* reduce `//' to `/' */
		StrCpy(p, p + 1);
		continue;
	}
	if (IS_DOT(p)) {	/* reduce `/./'  to `/' */
		StrCpy(p, p + 2);
		continue;
	}
	if (IS_DOTDOT(p)) {	/* reduce `/anything/../' to `/' */
		if (level > 0) {/* is it possible to reduce? */
			--level;
			StrCpy(last, p + 3);
			p = last;
			last -= 2;
			while (*last != '/' && last > dir) --last;
			if (*last == '/') last++;
		} else
			last = p += 3;
		continue;
	}
	last = p;
	level++;
	while (*p != '\0' && *p++ != '/');	/* get next part of path
						 * name */
  }
}

StrCpy(s, t)
register char *s, *t;
{
/* Overlapping copies are implemetation-dependent in strcpy, so we'll
 * use our own version.
 */

  while (*s++ = *t++)		/* do nothing */
	;
}