[comp.unix.microport] SysV.3 mkdir for Release < 3

samperi@mancol.UUCP (Dominick Samperi) (03/24/88)

Here is a SystemV.3-compatible mkdir(1) that can be used with
Release < 3. It also has an effective group id option that is useful
to use in conjunction with setgid programs.

I'm placing it into the public domain.

Dominick Samperi, Manhattan College, NYC
    manhat!samperi@NYU.EDU           ihnp4!rutgers!nyu.edu!manhat!samperi
    philabs!cmcl2!manhat!samperi     ihnp4!rutgers!hombre!samperi
              (^ that's an ell)      uunet!swlabs!mancol!samperi

---- cut here ------------- cut here ------------ cut here ------------

/*
*	mkdir.c - System V.3-compatible /bin/mkdir, with
*		       effective group id option.
*
*		Author: Dominick Samperi, March 19, 1988.
*			nyu.edu!manhat!samperi
*			rutgers!hombre!samperi
*
*  Usage: mkdir [-pg] [-m mode] dirnames
*
*  -p => make intermediate directories, if necessary.
*  -g => use process effective group id instead of the real group id.
*  -m mode => set the mode of the created directory.
*
*  Installation:
*
*	cc -O mkdir.c -o mkdir
*	mv /bin/mkdir /bin/OLDmkdir
*	mv mkdir /bin/mkdir
*	chown root mkdir
*	chmod 4511 mkdir
*/

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#define TRUE   1
#define FALSE  0
#define not    !
#define is_dir(statp)	((statp->st_mode & S_IFMT) == S_IFDIR)
#define writable(statp, uid, gid) (uid == 0 || (statp->st_mode & 02) \
			|| (uid == statp->st_uid && (statp->st_mode & 0200)) \
			|| (gid == statp->st_gid && (statp->st_mode & 0020)))

int build = FALSE ;  /* Build intermediate directories if TRUE. */
int setmod = FALSE ; /* Set mode from command line.             */
int uid, gid ;       /* User and group ids for the directory.   */

main(argc, argv)
int argc ;
char *argv[] ;

/*
*  mkdir [-m mode] [-pg] dirnames
*/

{
	int i, gotname = FALSE, status = 0, mode = -1, mask ;
	char *p ;

	uid = getuid() ; gid = getgid() ;

	gotname = FALSE ; i = 1 ;
	while(i < argc && not gotname)
	    if(argv[i][0] == '-')
	    {
		p = argv[i] + 1 ;
		while(*p != '\0')
			switch(*p++)
			{
			    case 'm': /* Set directory mode. */
				if(sscanf(argv[i+1], "%o", &mode) != 1
				  || mode > 0777)
				{
					fprintf(stderr, "Bad mode\n") ;
					exit(1) ;
				}
				i++ ;
				break ;
			    case 'p': /* Build intermediate subdirectories. */
				build = TRUE ;
				break ;
			    case 'g': /* Use effective group id. */
				gid = getegid() ;
				break ;
			    default:
				fprintf(stderr, 
				"Bad option: -%c\n", argv[i][1]) ;
				exit(1) ;
			}
		i++ ;
	    }
	    else
		gotname = TRUE ;

	if(not gotname)
	{
		fprintf(stderr, "Usage: mkdir [-m mode] [-pg] dirnames\n") ;
		exit(0) ;
	}

	if(mode == -1)
		mode = 040777 ;  /* Use umask. */
	else
	{
		mode |= 040000 ;
		setmod = TRUE ;
	}

	for(; i < argc ; i++)
		if(mkdir(argv[i], mode) < 0)
			status = 1 ;
	exit(status) ;
}

mkdir(dirname, mode)
char *dirname ;
int mode ;
{
	char *root, *parent, *self, *p, *s ;
	int len, status, exists ;
	struct stat Stat_buf, *statp = &Stat_buf ;

	/* Remove trailing '/', if present. */
	len = strlen(dirname) ;
	if(dirname[len-1] == '/')
	{
		dirname[len-1] = '\0' ;
		len-- ;
	}

	/* Trying to create '/' ? */
	if(len == 0)
	{
		fprintf(stderr, "Cannot make directory: '/'\n") ;
		return(-1) ;
	}

	/* Does it already exist? */
	if((status = stat(dirname, statp)) == 0
	  && not is_dir(statp))
	{
		fprintf(stderr, "Not a directory: %s\n", dirname) ;
		return(-1) ;
	}
	else if(status == 0)
	{
		fprintf(stderr, "Directory already exists: %s\n", dirname) ;
		return(-1) ;
	}

	root   = (char *) malloc(strlen(dirname)) ; /* Immediate parent. */
	parent = (char *) malloc(strlen(dirname) + 4) ; /* dirname/..    */
	self   = (char *) malloc(strlen(dirname) + 3) ; /* dirname/.     */
	if((s = strrchr(dirname, '/')) != NULL)
	{
		if(s == dirname)
			strcpy(root, "/") ;
		else
		{
			strcpy(root, dirname) ;
			root[s - dirname] = '\0' ;
		}
	}
	else
		strcpy(root, "./") ;

	if((status = stat(root, statp)) == -1 && not build)
	{
		fprintf(stderr, "Couldn't access: %s\n", root) ;
		return(-1) ;
	}
	else if(status == 0 && not is_dir(statp))
	{
		fprintf(stderr, "Not a directory: %s\n", root) ;
		return(-1) ;
	}
	else if(status == 0 && not writable(statp, uid, gid))
	{
		fprintf(stderr, "No write access: %s\n", root) ;
		return(-1) ;
	}
	else if(status == 0) /* Root exists and is accessible. */
	{
		if(mknod(dirname, mode, 0) < 0)
		{
			fprintf(stderr, "Couldn't mknod: %s\n", dirname) ;
			return(-1) ;
		}
		strcpy(parent, dirname) ;
		strcat(parent, "/..") ;
		strcpy(self,   dirname) ;
		strcat(self,   "/.") ;
		if(link(root, parent) != 0 || link(dirname, self) != 0)
		{
			fprintf(stderr, "Couldn't create '.' and '..': %s\n",
					dirname) ;
			return(-1) ;
		}
		chown(dirname, uid, gid) ;
		if(setmod)
			chmod(dirname, mode) ;
	}
	else if(status == -1) /* No root, so we build. */
	{
		/* Skip over directories that already exist. */
		exists = TRUE ;
		p = strtok(dirname, "/") ;
		while(p && exists)
		{
			if((status = stat(dirname, statp)) == 0
			  && not is_dir(statp))
			{
				fprintf(stderr, "Not a directory: %s\n",
						dirname) ;
				return(-1) ;
			}
			else if(status == 0)
			{
				/* Check next larger path. */
				p = strtok(NULL, "/") ;
				if(p) p[-1] = '/' ;
			}
			else
				/* Doesn't exist, or inaccessible. */
				exists = FALSE ;	
		}

		/* Then make intermediate directories. */
		while(p)
		{
			if((status = stat(dirname, statp)) == 0
			  && not is_dir(statp))
			{
				fprintf(stderr, "Not a directory: %s\n",
						dirname) ;
				return(-1) ;
			}
			else if(status == 0 
			  && not writable(statp, uid, gid))
			{
				fprintf(stderr, "No write access: %s\n",
						dirname) ;
				return(-1) ;
			}
			else if(status == -1 
			  && mknod(dirname, mode, 0) < 0)
			{
				fprintf(stderr, "Couldn't access: %s\n",
						dirname) ;
				return(-1) ;
			}
			else if(status == -1)
			{
				/* mknod() ok, add '.' and '..' */
				if((s = strrchr(dirname, '/')) != NULL)
				{
					if(s == dirname)
						strcpy(root, "/") ;
					else
					{
						strcpy(root, dirname) ;
						root[s - dirname] = '\0' ;
					}
				}
				else
					strcpy(root, "./") ;
				strcpy(parent, dirname) ;
				strcat(parent, "/..") ;
				strcpy(self,   dirname) ;
				strcat(self,   "/.") ;
				if(link(root, parent) != 0
				  || link(dirname, self) != 0)
				{
					fprintf(stderr,
					"Couldn't make '.' and '..': %s\n",
					dirname) ;
					return(-1) ;
				}

				/* Set owner/group, and mode, if specified. */
				chown(dirname, uid, gid) ;
				if(setmod)
					chmod(dirname, mode) ;
			}
			p = strtok(NULL, "/") ;
			if(p) p[-1] = '/' ;
		}
	}
	free(root) ; free(parent) ; free(self) ;
}
-- 
Dominick Samperi, Manhattan College, NYC
    manhat!samperi@NYU.EDU           ihnp4!rutgers!nyu.edu!manhat!samperi
    philabs!cmcl2!manhat!samperi     ihnp4!rutgers!hombre!samperi
              (^ that's an ell)      uunet!swlabs!mancol!samperi