[net.unix] improved IEEE 1003.1 mkdir, rmdir for UNIX System V

gwyn@BRL.ARPA (07/07/86)

#!/bin/sh
# Self-unpacking archive format.  To unbundle, sh this file.
echo 'dir_test.c' 1>&2
cat >'dir_test.c' <<'END OF dir_test.c'
/*
	dir_test -- mkdir, rmdir function test

	last edit:	86/07/05	D A Gwyn

	SCCS ID:	@(#)dir_test.c	1.1
*/

#include	<stdio.h>
#include	<string.h>

extern void	_exit();
extern int	access();

static char	Dirname[] = "/tmp/dir.tst";	/* test directory */

extern int	mkdir(), rmdir();


/*ARGSUSED*/
main( argc, argv )
	int	argc;
	char	*argv[];
	{
	char	buf[BUFSIZ];		/* for constructing strings */

	(void)system( strcat( strcpy( buf, "mkdir " ), Dirname ) );

	if ( access( Dirname, 0 ) != 0 )
		_exit( 1 );

	if ( mkdir( Dirname, 0777 ) == 0 )
		_exit( 2 );

	if ( rmdir( Dirname ) != 0 )
		_exit( 3 );

	(void)umask( 0200 );

	if ( mkdir( Dirname, 0777 ) != 0 )
		_exit( 4 );

	if ( access( Dirname, 0 ) != 0 )
		_exit( 5 );

	if ( access( Dirname, 02 ) == 0 )
		_exit( 6 );

	if ( access( Dirname, 05 ) != 0 )
		_exit( 7 );

	if ( rmdir( Dirname ) != 0 )
		_exit( 8 );

	return 0;
	}
END OF dir_test.c
echo 'mkdir.3' 1>&2
cat >'mkdir.3' <<'END OF mkdir.3'
.TH MKDIR 3V VMB
'\"	last edit:	86/07/05	D A Gwyn
'\"	SCCS ID:	@(#)mkdir.3	1.1
.SH NAME
mkdir \- create a directory
.SH SYNOPSIS
.B int mkdir(path,mode);
.br
.B char *path;
.br
.B int mode;
.SH DESCRIPTION
The
.I mkdir\^
function creates a new directory with name
.IR path\^ .
The mode of the new directory is initialized from
.IR mode\^ .
The protection part of the
.I mode\^
argument is modified by the process's file creation mask (see
.IR umask\^ (2)).
.P
The directory's owner
.SM ID
is set to the process's effective user ID.
Depending on the host system,
the directory group ID is set either to
the group ID of its parent directory
or to the process's effective group ID.
.P
The newly created directory is empty
except that it may contain entries for
``\s+2.\s0'' and ``\s+2..\s0''.
.SH "RETURN VALUE"
A return value of 0 indicates success.
A return value of \-1 indicated that an error has occurred
and an appropriate error code is stored in
.IR errno\^ .
No directory is created if the return value is \-1.
.SH NOTE
This function is compatible with the definition in the
IEEE 1003.1
standard.
.SH EXAMPLE
The following program is compiled by the command
.br
	$ \fIcc \|\-I/vld/include \|example.c \|/vld/lib/libVMB.a\fP
.sp
.P
	#include <stdio.h>
.br
	extern int	mkdir();
.br
	main( argc, argv )
.br
		int	argc;
.br
		char	*argv[\|];
.br
		{
.br
		if ( argc < 2 \|||\| mkdir( argv[1], 0777 ) != 0 )
.br
			{
.br
			(void) fputs( ``mkdir failed'', stderr );
.br
			exit( 1 );
.br
			}
.br
		return 0;
.br
		}
.SH FILES
/bin/mkdir		system directory creation utility
.br
/vld/lib/libVMB.a	VLD-VMB programmer support library
.SH "SEE ALSO"
mkdir(1), mknod(2), rmdir(3V), umask(2).
.SH AUTHOR
Douglas A. Gwyn, BRL/VLD-VMB
END OF mkdir.3
echo 'mkdir.c' 1>&2
cat >'mkdir.c' <<'END OF mkdir.c'
/*
	mkdir -- create a directory

	last edit:	86/07/05	D A Gwyn

	SCCS ID:	@(#)mkdir.c	1.1

	compatible with IEEE 1003.1

	returns zero iff operation succeeds
*/

#include	<errno.h>
#include	<fcntl.h>
#include	<signal.h>

extern void	_exit();
extern int	access(), chmod(), close(), execv(), fcntl(), open(),
		rmdir(), umask(), wait();

#ifndef	F_OK
#define	F_OK	0			/* access -- test existence */
#endif

#ifndef	NULL
#define	NULL	0
#endif

int
mkdir( path, mode )			/* returns 0 iff successful */
	char		*path;		/* name of new directory */
	int		mode;		/* desired access mode */
	{
	register int	pid;		/* process ID from fork() */
	register int	save;		/* temporary */

	save = errno;
	if ( path == NULL )		/* usually the best we can do */
		{
		errno = EFAULT;		/* path outside address space */
		return -1;
		}
	else if ( path[0] == '\0' )
		{
		errno = ENOENT;		/* path points to null string */
		return -1;
		}
	else if ( access( path, F_OK ) == 0 )
		{
		errno = EEXIST;		/* directory already exists */
		return -1;
		}
	else if ( errno == EACCES	/* search denied on prefix */
	       || errno == ENOTDIR	/* path component not dir */
		)
		return -1;
	errno = save;			/* access() changed errno */

	switch ( pid = fork() )
		{
	case -1:			/* can't fork; too bad */
		return -1;		/* report fork errno */

	case 0:				/* child process */
		{
		static char	*mkdir_args[] =	/* args for exec */
			{
			"/bin/mkdir",	/* system "mkdir" command */
			(char *)0,	/* directory name goes here */
			(char *)0	/* list terminator */
			};

		mkdir_args[1] = path;

		/* be careful to avoid "mkdir" I/O side-effects */
		(void)close( 0 );
		(void)close( 1 );
		(void)close( 2 );
		if ( open( "/dev/null", O_RDWR ) != 0
		  || fcntl( 0, F_DUPFD, 1 ) != 1
		  || fcntl( 0, F_DUPFD, 2 ) != 2
		  || execv( mkdir_args[0], mkdir_args ) != 0
		   )
			;		/* _exit in all cases */

		_exit( 255 );
		/*NOTREACHED*/
		}

	default:			/* parent process */
		{
		int		status;	/* child termination status */
		register int	w;	/* PID returned from wait() */
#ifdef	SIGCLD
		register void	(*oldcld)() = signal( SIGCLD, SIG_DFL );
					/* avoid wait on all children */
#endif

		while ( (w = wait( &status )) != pid
		     && (w != -1 || errno == EINTR)
		      )
			;		/* wait for proper child */

#ifdef	SIGCLD
		if ( oldcld != SIG_DFL )	/* restore catcher */
			(void)signal( SIGCLD, oldcld );
#endif

		if ( w == -1 || status != 0 )
			{
			errno = EACCES;	/* can't write on parent? */
			return -1;
			}
		}
		}

	save = umask( 0 );
	if ( chmod( path, mode & ~save ) != 0 )	/* "can't happen" */
		{
		(void)umask( save );
		(void)rmdir( path );	/* comply with spec */
		return -1;		/* report chmod | rmdir errno */
		}
	else	{
		(void)umask( save );
		return 0;		/* success */
		}
	}
END OF mkdir.c
echo 'rmdir.3' 1>&2
cat >'rmdir.3' <<'END OF rmdir.3'
.TH RMDIR 3V VMB
'\"	last edit:	86/07/05	D A Gwyn
'\"	SCCS ID:	@(#)rmdir.3	1.1
.SH NAME
rmdir \- remove a directory
.SH SYNOPSIS
.B int rmdir(path);
.br
.B char *path;
.SH DESCRIPTION
The
.I rmdir\^
function removes a directory whose name is given by
.IR path\^ .
The directory must not have any entries other than, possibly,
``\s+2.\s0'' and ``\s+2..\s0''.
.P
Depending on the host system,
it may be possible to remove the root
or current working directory.
.SH "RETURN VALUE"
A return value of 0 indicates success.
A return value of \-1 indicated that an error has occurred
and an appropriate error code is stored in
.IR errno\^ .
.SH NOTE
This function is compatible with the definition in the
IEEE 1003.1
standard.
.SH EXAMPLE
The following program is compiled by the command
.br
	$ \fIcc \|\-I/vld/include \|example.c \|/vld/lib/libVMB.a\fP
.sp
.P
	#include <stdio.h>
.br
	extern int	rmdir();
.br
	main( argc, argv )
.br
		int	argc;
.br
		char	*argv[\|];
.br
		{
.br
		if ( argc < 2 \|||\| rmdir( argv[1] ) != 0 )
.br
			{
.br
			(void) fputs( ``rmdir failed'', stderr );
.br
			exit( 1 );
.br
			}
.br
		return 0;
.br
		}
.SH FILES
/bin/rmdir		system directory creation utility
.br
/vld/lib/libVMB.a	VLD-VMB programmer support library
.SH "SEE ALSO"
mkdir(3V), remove(3V), rmdir(1).
.SH AUTHOR
Douglas A. Gwyn, BRL/VLD-VMB
END OF rmdir.3
echo 'rmdir.c' 1>&2
cat >'rmdir.c' <<'END OF rmdir.c'
/*
	rmdir -- remove a directory

	last edit:	86/07/05	D A Gwyn

	SCCS ID:	@(#)rmdir.c	1.1

	compatible with IEEE 1003.1

	returns zero iff operation succeeds
*/

#include	<errno.h>
#include	<fcntl.h>
#include	<signal.h>

extern void	_exit();
extern int	access(), close(), execv(), fcntl(), open(), wait();

#ifndef	F_OK
#define	F_OK	0			/* access -- test existence */
#endif

#ifndef	NULL
#define	NULL	0
#endif

int
rmdir( path )				/* returns 0 iff successful */
	char		*path;		/* name of directory */
	{
	register int	pid;		/* process ID from fork() */

	if ( path == NULL )		/* usually the best we can do */
		{
		errno = EFAULT;		/* path outside address space */
		return -1;
		}
	else if ( path[0] == '\0' )
		{
		errno = ENOENT;		/* path points to null string */
		return -1;
		}
	else if ( access( path, F_OK ) != 0 )
		return -1;		/* EACCES, ENOTDIR, ENOENT */

	switch ( pid = fork() )
		{
	case -1:			/* can't fork; too bad */
		return -1;		/* report fork errno */

	case 0:				/* child process */
		{
		static char	*rmdir_args[] =	/* args for exec */
			{
			"/bin/rmdir",	/* system "rmdir" command */
			(char *)0,	/* directory name goes here */
			(char *)0	/* list terminator */
			};

		rmdir_args[1] = path;

		/* be careful to avoid "rmdir" I/O side-effects */
		(void)close( 0 );
		(void)close( 1 );
		(void)close( 2 );
		if ( open( "/dev/null", O_RDWR ) != 0
		  || fcntl( 0, F_DUPFD, 1 ) != 1
		  || fcntl( 0, F_DUPFD, 2 ) != 2
		  || execv( rmdir_args[0], rmdir_args ) != 0
		   )
			;		/* _exit in all cases */

		_exit( 255 );
		/*NOTREACHED*/
		}

	default:			/* parent process */
		{
		int		status;	/* child termination status */
		register int	w;	/* PID returned from wait() */
#ifdef	SIGCLD
		register void	(*oldcld)() = signal( SIGCLD, SIG_DFL );
					/* avoid wait on all children */
#endif

		while ( (w = wait( &status )) != pid
		     && (w != -1 || errno == EINTR)
		      )
			;		/* wait for proper child */

#ifdef	SIGCLD
		if ( oldcld != SIG_DFL )	/* restore catcher */
			(void)signal( SIGCLD, oldcld );
#endif

		if ( w == -1 || status != 0 )
			{
			errno = EACCES;	/* can't write on parent? */
			return -1;
			}
		else
			return 0;	/* success */
		}
		}

	/*NOTREACHED*/
	}
END OF rmdir.c

ahby@meccts.UUCP (Shane P. McCarron) (07/08/86)

In article <1981@brl-smoke.ARPA> gwyn@BRL.ARPA writes:
>This function is compatible with the definition in the
>IEEE 1003.1

Since this standard is not finished, and has only been published in a
Trial Use Document, is this compatible with the trial use, or with the
in progress document being developed by the working committee.
-- 
Shane P. McCarron			UUCP	ihnp4!meccts!ahby
MECC Technical Services			ATT	(612) 481-3589

"Sinners can repent, but stupid is forever."

gwyn@brl-smoke.ARPA (Doug Gwyn ) (07/08/86)

In article <438@meccts.UUCP> ahby@meccts.UUCP (Shane P. McCarron) writes:
-In article <1981@brl-smoke.ARPA> gwyn@BRL.ARPA writes:
->This function is compatible with the definition in the
->IEEE 1003.1
-Since this standard is not finished, and has only been published in a
-Trial Use Document, is this compatible with the trial use, or with the
-in progress document being developed by the working committee.

Trial use, but I expect this interface won't change much.
Note that it is not a precisely conforming implementation,
since it cheats on some of the error conditions.  If
/bin/mkdir and /bin/rmdir return distinct exit status for
different failure modes, this could be improved.

Also note that, as Chris Torek pointed out, it would be
better to move the SIGCLD reset to before the fork().

I have a similar rename() implementation if anyone needs it
and can't figure out how to modify rmdir.c to get it.