SysV.3 mkdir for Release < 3

Dominick Samperi samperi at mancol.UUCP
Thu Mar 24 05:58:00 AEST 1988


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 at 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 at NYU.EDU           ihnp4!rutgers!nyu.edu!manhat!samperi
    philabs!cmcl2!manhat!samperi     ihnp4!rutgers!hombre!samperi
              (^ that's an ell)      uunet!swlabs!mancol!samperi



More information about the Comp.unix.wizards mailing list