[comp.bugs.sys5] Alternative to access

storm@texas.dk (Kim F. Storm) (06/12/90)

In article <2077@polari.UUCP> 6sigma2@polari.UUCP (Brian Matthews) writes:
>In article <433@mtndew.UUCP> friedl@mtndew.UUCP (Stephen J. Friedl) writes:
>|Email will get a pretty detailed note on why
>|access(2) is evil.

del@thrush.mlb.semi.harris.com (Don Lewis) writes:
> Would using stat(2), and checking if the uids/gids match and looking
> at the permission bits have been better?  Should the effective or
> real ids have be used?

Here is the "file_exist()" function which I wrote to make all sorts
of file checking easy.  It is extracted from the nn distribution, so
it may need a little hacking to be used out of context:


/*
 *	(c) Copyright 1990, Kim Fabricius Storm.  All rights reserved.
 */

#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>


#ifdef HAVE_MULTIGROUP
#ifndef NGROUPS
#include <sys/param.h>
#endif
#ifndef GIDSET_TYPE
#define GIDSET_TYPE int
#endif
static int ngroups;
static GIDSET_TYPE gidset[NGROUPS];

static in_grplist(gid)
GIDSET_TYPE gid;
{
    int n;

    for (n = 0; n < ngroups; ++n)
	if (gidset[n] == gid) return 1;

    return 0;
}

#define group_access(gpid)	in_grplist((GIDSET_TYPE)(gpid))
#else
#define group_access(gid)	((gid) == group_id)
#endif


/*
 *	Test existence and access modes of a file or directory
 *
 *	mode is a string composed of the following characters
 *		f -- name is a file
 *		d -- name is a directory
 *		r -- name is readable
 *		w -- name is writeable
 *		x -- name is executeable/searchable
 *
 *	0 is returned if file does not exist or any of the requested
 *	permissions are not present; the actual cause is returned in
 *	errno (EACCESS, EISDIR, ENOTDIR).  
 *	Otherwise, the modification time of the file is returned.
 *
 *	Examples:
 *	file_exist("/usr", "dwr") 
 *		-- is /usr a directory, writeable AND readable?
 *
 *	file_exist(file, "fx")
 *		-- is file an executeable file?
 *
 *	file_exist(file, (char *)NULL)
 *		-- does file exist?
 */

time_t file_exist(name, mode)
char *name;
char *mode;
{
    static unsigned short user_id, group_id;
    static init = 1;
    struct stat statb;
    extern int errno;

    if (init) {
	user_id = geteuid();

#ifdef HAVE_MULTIGROUP
	ngroups = getgroups(NGROUPS, gidset);	/* Get users's group set */
	group_id = gidset[0];	/* not used, but just in case... */
#else
	group_id = getegid();
#endif
	init = 0;
    }
    if (stat(name, &statb)) return 0;

    if (mode == NULL) return statb.st_mtime;

    while (*mode) {
	switch (*mode++) {
	case 'd':
	    if ((statb.st_mode & S_IFMT) == S_IFDIR) continue;
	    errno = ENOTDIR;
	    return 0;
	case 'f':
	    if ((statb.st_mode & S_IFMT) == S_IFREG) continue;
	    if ((statb.st_mode & S_IFMT) == 0000000) continue;
	    if ((statb.st_mode & S_IFMT) == S_IFDIR) {
		errno = EISDIR;
		return 0;
	    }
	    break;
	case 'r':
	    if ((statb.st_mode & 0400) && statb.st_uid == user_id) continue;
	    if ((statb.st_mode & 0040) && group_access(statb.st_gid)) continue;
    	    if ((statb.st_mode & 0004)) continue;
	    break;
	case 'w':
	    if ((statb.st_mode & 0200) && statb.st_uid == user_id) continue;
	    if ((statb.st_mode & 0020) && group_access(statb.st_gid)) continue;
    	    if ((statb.st_mode & 0002)) continue;
	    break;
	case 'x':
	    if ((statb.st_mode & 0100) && statb.st_uid == user_id) continue;
	    if ((statb.st_mode & 0010) && group_access(statb.st_gid)) continue;
    	    if ((statb.st_mode & 0001)) continue;
	    break;
	}
	errno = EACCES;
	return 0;
    }

    /* all modes are ok */
    return statb.st_mtime;
}

-- 
Kim F. Storm  <storm@texas.dk>		No news is good news,
Texas Instruments A/S, Denmark		  but nn is better!