From:	CRDGW2::CRDGW2::MRGATE::"SMTP::CRVAX.SRI.COM::RELAY-INFO-VAX" 10-MAR-1992 00:10:27.97
To:	ARISIA::EVERHART
CC:	
Subj:	Re: VAX C: bug in access()

From:	RELAY-INFO-VAX@CRVAX.SRI.COM@SMTP@CRDGW2
To:	Everhart@Arisia@MRGATE

Received:  by crdgw1.ge.com (5.57/GE 1.123)
	 id AA12967; Mon, 9 Mar 92 23:25:49 EST
Received: From ucbvax.Berkeley.EDU ([128.32.133.1]) by CRVAX.SRI.COM with TCP; Mon,  9 MAR 92 20:16:22 PST
Received: by ucbvax.Berkeley.EDU (5.63/1.43)
	id AA06754; Mon, 9 Mar 92 20:01:58 -0800
Received: from USENET by ucbvax.Berkeley.EDU with netnews
	for info-vax@kl.sri.com (info-vax@kl.sri.com)
	(contact usenet@ucbvax.Berkeley.EDU if you have questions)
Date: 6 Mar 92 17:45:58 GMT
From: sdrc!scjones@uunet.uu.net  (Larry Jones)
Organization: SDRC, Cincinnati
Subject: Re: VAX C: bug in access()
Message-Id: <231@thor.sdrc.com>
References: <130854.29b23d67@mswe.dnet.ms.philips.nl>
Sender: info-vax-request@kl.sri.com
To: info-vax@kl.sri.com

In article <130854.29b23d67@mswe.dnet.ms.philips.nl>, hdavids@mswe.dnet.ms.philips.nl (Henk Davids) writes:
> There is a bug in the run-time routine access(): after many calls with
> a filename that contains a logical name which is defined as a search list,
> access returns -1 and perror then prints "too many open files".
> Many in this case is a few hundred - we've seen the problem appear between
> 200 and 400 calls.

Yep, I reported this many moons ago and also supplied a fix at the same;
wonder whatever happened to it.  In any case, here's a replacement that
fixes a bunch of other limitations as well:

/* NAME
 *    access - test for ACCESS to a file
 *       Written: 20-FEB-1988 23:14:00 - SCJONES
 *       Revised:  1-MAR-1989 08:32:12 - SCJONES
 *        Reason: Fix to account for ACLs, privs, etc.
 *       Revised: 20-FEB-1989 23:13:16 - SCJONES
 *        Reason: new routine - vaxcrtl version looses channels
 *
 * SYNOPSIS
 *    int access(file, mode)
 *       const char *file;
 *       int mode;
 *
 *    Inputs:
 *       file - name of file to check access to
 *       mode - access mode to check; logical or of:
 *                1 = execute, 2 = write, 4 = read
 *                (0 = check for existance only)
 *    Outputs:
 *       Returns 0 if access is allowed, -1 if not.
 *
 * DESCRIPTION
 *    Checks for the specified access to the file.  This is a rewrite of
 *    the standard vax11c version which does not correctly clean up the
 *    RMS context and thus can causes resource exhaustion.  Note that the
 *    standard version only checks the SOGW protection and ignores ACLs,
 *    rights lists, etc.  Also, it assumes groups 1 - 8 are system groups
 *    when it should use the SYSGEN parameter MAXSYSGRP and ignores
 *    privileges which give system access like SYSPRV and GRPPRV.  Existance
 *    checks are also faulty - the standard version returns failure if you
 *    don't have any access to the file, even though it exists.  This is all
 *    fixed in this version.
 */

#include "stdlib.h"
#include "atrdef.h"
#include "chpdef.h"
#include "descrip.h"
#include "fab.h"
#include "fibdef.h"
#include "iodef.h"
#include "jpidef.h"
#include "nam.h"
#include "rmsdef.h"
#include "ssdef.h"
#include "stddef.h"
#include "stsdef.h"

#ifndef lint
static char ACCESS_ID[] = "@(#)access.c	2.2";
#endif

struct itmlst
   {
   unsigned short size;
   unsigned short code;
   void *buffer;
   unsigned short *retlen;
   } ;

static char vname[NAM$C_MAXRSS + 1];

static int copy_str(str, typ)
   const char *str;
   int typ;
   {
   strcpy(vname, str);
   return 0;
   }

int access(file, mode)
   const char *file;
   int mode;
   {
   struct FAB fab;
   struct NAM nam;
   unsigned short iosb[4];
   unsigned long status;
   char r[NAM$C_MAXRSS];
   unsigned char *p;
   unsigned short chan;
   unsigned short ourgrp, ourusr;
   int ret;
   static char e[NAM$C_MAXRSS];
   static struct dsc$descriptor devdsc =
      { 0, DSC$K_DTYPE_T, DSC$K_CLASS_S, e };
   static struct fibdef fib;
   static struct dsc$descriptor fibdsc =
      { sizeof fib, DSC$K_DTYPE_T, DSC$K_CLASS_S, (char *)&fib };
   static unsigned short filepro;
   static unsigned long fileown, acllen;
   static struct atrdef atrlst[] =
      {
         { sizeof filepro, ATR$C_FPRO,      (unsigned long)&filepro },
         { sizeof fileown, ATR$C_UIC,       (unsigned long)&fileown },
         { sizeof acllen,  ATR$C_ACLLENGTH, (unsigned long)&acllen },
         { 0, 0, 0 }
      };
   static struct atrdef acllst[] =
      {
         { 0, ATR$C_READACL, NULL },
         { 0, 0, 0 }
      };
   static unsigned long flags;
   static unsigned long ouruic[2];
   static struct itmlst prolst[] =
      {
         { sizeof mode,    CHP$_ACCESS,    NULL,     NULL },
         { sizeof ouruic,  CHP$_ADDRIGHTS, ouruic,   NULL },
         { sizeof flags,   CHP$_FLAGS,     &flags,   NULL },
         { sizeof fileown, CHP$_OWNER,     &fileown, NULL },
         { sizeof filepro, CHP$_PROT,      &filepro, NULL },
         { 0,              CHP$_ACL,       NULL,     NULL },
         { 0,              CHP$_END,       NULL,     NULL }
      };
#define ACCESS 0
#define ACL    (sizeof prolst / sizeof prolst[0] - 2)
   static struct itmlst jpilst[] =
      {
         { sizeof ouruic, JPI$_UIC, ouruic, NULL },
         { 0, 0, 0, 0 }
      };
   static const pmap[8] = { 0, 4, 10, 14, 1, 5, 11, 15 };

   /* initialize */
   mode = pmap[mode & 7];     /* convert unix mode to vms protection */
   ret = -1;                  /* assume failure */

   /* convert unix file name to vms file name */
   if (SHELL$TO_VMS(file, copy_str, 0) <= 0)
      strcpy(vname, file);

   /* build rms control blocks, parse, and search */
   fab = cc$rms_fab;
   nam = cc$rms_nam;
   fab.fab$l_nam = &nam;
   fab.fab$l_fna = vname;
   fab.fab$b_fns = strlen(vname);
   nam.nam$l_esa = e;
   nam.nam$b_ess = sizeof e;
   nam.nam$l_rsa = r;
   nam.nam$b_rss = sizeof r;
   status = SYS$PARSE(&fab);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(fab.fab$l_sts, fab.fab$l_stv);
#endif
      goto done;
      }
   status = SYS$SEARCH(&fab);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(fab.fab$l_sts, fab.fab$l_stv);
#endif
      goto done;
      }
   /* from this point on, we need to clean up rms before returning */

   /* handle existance check */
   if (mode == 0) goto aok;

   /* assign a channel to the file */
   devdsc.dsc$w_length = nam.nam$b_esl;
   status = SYS$ASSIGN(&devdsc, &chan, 0, 0);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(status);
#endif
      goto cleanup;
      }

   /* get the file attributes */
   fib.fib$r_fid_overlay.fib$w_fid[0] = nam.nam$w_fid[0];
   fib.fib$r_fid_overlay.fib$w_fid[1] = nam.nam$w_fid[1];
   fib.fib$r_fid_overlay.fib$w_fid[2] = nam.nam$w_fid[2];
   fib.fib$l_aclctx = 0;
   fib.fib$r_acctl_overlay.fib$l_acctl = FIB$M_NOWRITE | FIB$M_NORECORD;
   status = SYS$QIOW(0, chan, IO$_ACCESS | IO$M_ACCESS, iosb, 0, 0,
         &fibdsc, 0, 0, 0, atrlst, 0);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(status);
#endif
      goto cleanup;
      }
   if (!(iosb[0] & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(iosb[0]);
#endif
      goto cleanup;
      }
   prolst[ACL].size = acllen;
   if (acllen == 0)
      prolst[ACL].code = CHP$_END;
   else
      {
      if ((p = calloc(acllen, 1)) == NULL)   /* allocate ACL buffer */
         {
#ifdef TEST
         LIB$SIGNAL(SS$_INSFMEM);
#endif
         }
      else
         {
         prolst[ACL].code = CHP$_ACL;   /* set up ACL entry */
         prolst[ACL].buffer = p;
         acllst[0].atr$w_size = ATR$S_READACL;
         for (;;)   /* read pieces of ACL */
            {
            if (acllst[0].atr$w_size > acllen) acllst[0].atr$w_size = acllen;
            acllst[0].atr$l_addr = (unsigned long)p;
            status = SYS$QIOW(0, chan, IO$_ACCESS, iosb, 0, 0,
                  &fibdsc, 0, 0, 0, acllst, 0);
            if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
               LIB$SIGNAL(status);
#endif
               break;
               }
            if (!(iosb[0] & STS$M_SUCCESS)) {
#ifdef TEST
               LIB$SIGNAL(iosb[0]);
#endif
               break;
               }
            if (!(status & fib.fib$l_acl_status)) {
#ifdef TEST
               LIB$SIGNAL(fib.fib$l_acl_status);
#endif
               break;
               }
            if (acllst[0].atr$w_size == acllen) break;  /* complete ACL */
            while (*p > 0)   /* find end of returned ACEs */
               {
               acllen -= *p;
               p += *p;
               }
            }
         }
      }

   /* that's all we needed, so close the channel */
   status = SYS$DASSGN(chan);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(status);
#endif
      }

   /* now test for the desired access */
   status = SYS$GETJPIW(0, 0, 0, jpilst, 0, 0, 0);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(status);
#endif
      }
   flags = 0;
   if (mode & 0x05) flags |= CHP$M_READ;    /* read or execute */
   if (mode & 0x0a) flags |= CHP$M_WRITE;   /* write or delete */
   prolst[ACCESS].buffer = &mode;
   status = SYS$CHKPRO(prolst);
   if (prolst[ACL].code == CHP$_ACL) free(prolst[ACL].buffer);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      if (status != SS$_NOPRIV) LIB$SIGNAL(status);
#endif
      goto cleanup;
      }

   /* access is good! */
aok:
   ret = 0;

   /* clean up rms - this is NOT DOCUMENTED but is required as rms has
    * internal context which does not get deassigned until $SEARCH
    * returns RMS$_NMF or we do something else with the NAM block.
    */
cleanup:
   nam.nam$l_wcc &= ~NAM$M_SVCTX;  /* don't save context, DESTROY it! */
   nam.nam$b_nop |= NAM$M_SYNCHK;  /* make it fast as possible */
   nam.nam$b_rss = nam.nam$b_rsl = nam.nam$b_ess = nam.nam$b_esl = 0;
   fab.fab$b_fns = fab.fab$b_dns = 0;
   status = SYS$PARSE(&fab);
   if (!(status & STS$M_SUCCESS)) {
#ifdef TEST
      LIB$SIGNAL(fab.fab$l_sts, fab.fab$l_stv);
#endif
      }

done:
   return ret;
   }

#ifdef TEST
#include <stdio.h>


main()
   {
   char buff[255];
   int mode;

   for (;;)
      {
      printf("Enter filespec, mode(r=4, w=2, x=1): ");
      if (scanf(" %[^,], %d", buff, &mode) != 2) break;
      printf("Access is %s\n\n", access(buff,mode) == 0 ? "allowed" : "denied");
      }
   }

#endif
----
Larry Jones, SDRC, 2000 Eastman Dr., Milford, OH  45150-2789  513-576-2070
Domain: scjones@sdrc.com  Path: uunet!sdrc!scjones  IBM: USSDR7DR at IBMMAIL
My life needs a rewind/erase button. -- Calvin

