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