ftp.nice.ch/pub/next/unix/network/system/cap.5.0.s.tar.gz#/cap_5.0/applications/aufs/afpos.c

This is afpos.c in view mode; [Download] [Up]

/*
 * $Author: cck $ $Date: 88/05/25 14:37:02 $
 * $Header: afpos.c,v 1.83 88/05/25 14:37:02 cck Rel $
 * $Revision: 1.83 $
*/

/*
 * afpos.c - Appletalk Filing Protocol OS Interface. 
 *
 * AppleTalk package for UNIX (4.2 BSD).
 *
 * Copyright (c) 1986, 1987, 1988 by The Trustees of Columbia University in the
 * City of New York.
 *
 * Edit History:
 *
 *  March 1987     Schilit	Created.
 *  March 1988     CCKIM cleanup
 *
 */

/*
 * POSSIBLE DEFINES:
 *
 *  NONLXLATE
 *   Define to turn off translation of \n to \r on line at a time
 *    reads
 *  USECHOWN
 *   System allows user to use chown to give away file ownership
 * Following are used to get volume information
 *  USEGETMNT - DECs getmnt call.  Works for either Ultrix 1.2, 2.0 or
 *   2.2 (to be verified).  Call differs from 2.0 to 2.2
 *  USEQUOTA - base volume information on quota for user on file
 *   system if it exists (messed up by symlinks off the structure).
 *   This is for the "Melbourne" quota system as usually found in BSD
 *   systems.
 *  USESUNQUOTA - running with sun quota system.  Basically, just turns on 
 *   emulation of the Melbourne quota call
 *  USEUSTAT - not recommended.  returns information about file, but
 *   information doesn't tell us how much space is there -- only
 *   how much is free and we need both
 *  USESTATFS - statfs is the Sun NFS solution to volume information.
 *
 *  aux has a couple of "ifdefs".
 *   - one to set full BSD compatibility
 *   - one to protect against rename("file1","file1"): it will
 *     incorrectly unlink "file1" (period - nothing left afterwards)
 *
 *  OTHER: GGTYPE
 *   Some versions of unix return a gid_t array in getgroups instead of
 *    an int array.  For those, define GGTYPE to gid_t.  In particular,
 *    this is a problem with (at least some version of) "MORE/BSD"
 *    from Mt. Xinu. 
 *
 * System V defines
 *  NOLSTAT - no lstat call - don't try to figure out things with symlinks
 *  USERAND - use sysv rand call not bsd random
*/

#include <stdio.h>
#include <pwd.h>
#include <grp.h>
#include <errno.h>
#include <sys/param.h>
#ifndef _TYPES
# include <sys/types.h>		/* assume included by param.h */
#endif
#include <sys/file.h>
#include <sys/dir.h>
#include <sys/stat.h>
#include <netat/appletalk.h>
#include <sys/time.h>
#ifdef aux
# include <compat.h>
#endif

#ifdef USESUNQUOTA
# ifndef USEQUOTA
#  define USEQUOTA
# endif
#endif

#ifdef NEEDFCNTLDOTH
# include <fcntl.h>
#endif
#ifdef USESTRINGDOTH
# include <string.h>
#else
# include <strings.h>
#endif

#ifdef USEUSTAT
# include <ustat.h>
#endif

#ifdef USESTATFS
# include <sys/vfs.h>
#endif


#ifdef USEQUOTA
# ifndef USESUNQUOTA
#  include <sys/quota.h>
/* NOTE: If there is not sys/quota.h and there is a ufs/quota.h */
/* then you should probably define SUN_QUOTA -- especially if your */
/* NFS is based on the sun model */
# else
#  include <mntent.h>
#  include <ufs/quota.h>
# endif
# ifndef Q_GETDLIM
#  ifdef Q_GETQUOTA
#   define Q_GETDLIM Q_GETQUOTA
/* If you're here you've turned on quotas but aren't using the bsd
   or Sun quota systems -- add an else and whatever you need */
#  endif /*Q_GETQUOTA*/
# endif /* Q_GETDLIM */
#endif /* USEQUOTA */

/* assumes that ultrix 1.1 doesn't have getmnt or if it does, then it */
/* has limits.h and uname */
#ifdef USEGETMNT
# include <sys/mount.h>
/* the following assumes ultrix 1.2 or above */
/* well, do you really think dec would license their code to another */
/* vendor :-) */
# include <limits.h>
# include <sys/utsname.h>
#endif

#include <netat/afp.h>
#include <netat/afpcmd.h>		/* flags should be in misc  */
#include "afps.h"			/* common includes */
#include "afpvols.h"
#include "afppasswd.h"			/* in case we are using privates */
#include "afposncs.h"
#include "afpgc.h"

#ifdef MAXBSIZE
# define IOBSIZE MAXBSIZE	/* set to max buf entry size by if there */
#else
# ifdef BLKDEV_IOSIZE
#  define IOBSIZE BLKDEV_IOSIZE	/* set to std block device read size */
# else
#  define IOBSIZE BUFSIZ	/* use stdio bufsiz */
# endif
#endif

#define NILPWD ((struct passwd *) 0)

/* macro to test for directory file */

#define	S_ISDIR(mode)	(((mode) & S_IFMT) == S_IFDIR)

#define E_IOWN 0x80000000	/* owner of file */

#define I_RD 04			/* Internal (Unix) bits for read */
#define I_WR 02			/*  write */
#define I_EX 01			/*  execute (search) */

#define E_RD 02			/* External bits for read */
#define E_WR 04			/*  write */
#define E_SR 01			/*  search */

#define IS_OWNER 6		/* internal owner shift amount */
#define IS_GROUP 3		/* internal group shift amount */
#define IS_WORLD 0		/* internal world shift amount */

#define ES_USER 24		/* external user shift amount */
				/*  which is a calculated access based on: */
#define ES_WORLD 16		/* external world shift amount */
#define ES_GROUP 8		/* external group shift amount */
#define ES_OWNER 0		/* external owner shift amount */

/* Mask search (is unix execute) bits for world, group and owner */

#define IS_EX_WGO ((I_EX << IS_OWNER) | \
		   (I_EX << IS_GROUP) | \
		   (I_EX << IS_WORLD))

private char *usrnam,*usrdir;
private int usruid;
private int usrgid;
private int ngroups;
#ifndef GGTYPE
# define GGTYPE int
#endif
private GGTYPE groups[NGROUPS+1];
#ifdef USEGETMNT
# ifndef NOSTAT_ONE
/* no big deal if this changes, it just means you can't compile under */
/* ultrix 2.0/1.2 for ultrix 2.2 (you probably can't for 1.2 anyway) */
/* WARNING: not 100% sure that there aren't other problems preventing */
/* a compile from ultrix 2.0 from working on 2.2 */
#  define NOSTAT_ONE 4		/* as of ultrix 2.2 */
#  define ISOLDGETMNT 1
# else
#  define ISOLDGETMNT 0
# endif
private int oldgetmnt = ISOLDGETMNT; /* use new or old format getmnt */
				/* default is based on compiling system */
# ifndef NUMGETMNTBUF
#  define NUMGETMNTBUF 8	/* 8 before, let's not be conservative */
				/* because on systems with many file */
				/* systems we will get killed with 8 */
				/* Back off!!!!! struct fs_data is huge */
# endif
#endif

import int errno;

/*
 * AFPOS functions
 *
 *  Generally, a name like: OSXxxxx means that it is a "primary" function
 *   that accomplishes a afp command (not always though).
 *
*/

export int OSEnable();
export void tellaboutos();
export OSErr OSMapID();
export OSErr OSMapName();
export OSErr OSDelete();
private OSErr os_rmdir();
private OSErr os_delete();
export OSErr OSRename();
export OSErr OSMove();
private OSErr os_move();
export OSErr OSFlush();
export OSErr OSFlushFork();
export OSErr OSClose();
export OSErr OSRead();
export OSErr OSWrite();
export OSErr OSCreateDir();
private OSErr os_mkdir();
export OSErr OSCreateDir();
export OSErr OSFileDirInfo();
export OSErr OSDirInfo();	/* from old spec */
export OSErr OSFileInfo();	/* from old spec */
export void OSValidateDIDDirInfo();
export OSErr OSFileExists();
export OSErr OSSetFileDirParms();
export OSErr OSSetFileParms();
export OSErr OSSetDirParms();
export OSErr OSSetForklen();
private OSErr os_chmod();
private void os_chmod_all();
private u_short EtoIPriv();
private int OSInGroup();
private u_short EtoIAccess();
private dword ItoEPriv();
private dword ItoEAccess();
export OSErr OSAccess();
export OSErr OSVolInfo();
#ifdef USEGETMNT
private OSErr ultrix_volinfo();
#endif
export OSErr OSCopyFile();
private OSErr os_copy();
export OSErr OSCreateFile();
export OSErr OSOpenFile();
export boolean setguestid();
export boolean setpasswdfile();
export OSErr OSLoginRand();
export OSErr OSLogin();
export word OSRandom();
export sdword CurTime();	/* current time in internal form */
export char *tilde();		/* tilde expansion */
export char *logdir();		/* user login directory */
private OSErr unix_rmdir();
private OSErr unix_unlink();
private OSErr unix_rename();
private OSErr unix_open();
private OSErr unix_close();
private OSErr unix_mkdir();
private OSErr unix_create();
private OSErr unix_createo();
private OSErr unix_chown();
private OSErr unix_chmod();
private OSErr unix_stat();
private OSErr ItoEErr();
private int filemode();
private char *syserr();

/*
 * Enable OS Dependent functions
 *
 * For now: under AUX, set full BSD compatibility
 * under sun quota systems, build a mount table for use in quota call
 *
*/
export int
OSEnable()
{
#ifdef USEGETMNT
  struct utsname unames;
#endif

#ifdef USEGETMNT
  if (uname(&unames) >= 0) {
    /* don't think getmnt was available in ultrix 1.1.  If it was */
    /* then we assume it had uname too */
    if (strcmp(unames.sysname, "ULTRIX-32") == 0) {
      if (strcmp(unames.release, "2.0") == 0 ||
	  strcmp(unames.release, "1.2") == 0 ||
	  strcmp(unames.release, "1.1") == 0) {
	oldgetmnt = TRUE;
      } else oldgetmnt = FALSE;
    }
  }
#endif /* USEGETMNT */
#ifdef aux
/* ensure reliable signals, etc */
#define BSDCOMPAT COMPAT_BSDNBIO|COMPAT_BSDPROT|COMPAT_BSDSIGNALS|\
COMPAT_BSDTTY|COMPAT_EXEC|COMPAT_SYSCALLS
  setcompat(BSDCOMPAT);
#endif
#ifdef USESUNQUOTA
  build_mount_table();
#endif
}

void
tellaboutos()
{
  int haveflock;
  int havelockf;

  log("CONFIGURATION");
  getlockinfo(&haveflock, &havelockf);
  if (haveflock)
    log(" Configured: FLOCK");
  if (havelockf)
    log(" Configured: LOCKF");
#ifdef USEUSTAT
  log(" Configured: Volume space information: ustat");
#endif
#ifdef USESTATFS
  log(" Configured: Volume space information: statfs");
#endif
#ifdef USEGETMNT
  log(" Configured: Volume space information: getmnt");
  if (oldgetmnt)
    log("   using old style (Ultrix 1.2, 2.0) getmnt");
#endif
#ifdef USESUNQUOTA
  log(" Configured: SUN quota system");
#endif
#ifndef USESUNQUOTA
/* sunquota turns on usequota */
# ifdef USEQUOTA
  log(" Configured: Melbourne (BSD) quota system");
# endif
#endif
#ifdef USECHOWN
  log(" Configured: chown: system allows one to give away ownership of files");
#endif
  if (os_issmartunixfi())
    log(" Configured: reading unknown unix files to get finder information");
  log(" Configured translation tables are:");
  ncs_table_dump();
}

/*
 * OwnerID = 0 means that the folder is "unowned" or owned by
 * <any user>.  The owner bit of the User Rights summary is always 
 * set for such a folder.
 *
 * GroupID = 0 means that the folder has no group affiliation; hence
 * the group's access privileges (R, W, S) are ignored for such a
 * folder.
 *
*/

#define NOID (-1)			/* unused internal group/user id */

#define ItoEID(iid) ((sdword) (iid == 0 ? NOID : iid))
#define EtoIID(eid) ((int) (eid == NOID ? 0 : eid))

/*
 *
 * OSErr OSMapID(byte fcn,char *name,dword id)
 *
 *
 * This function is used to map a creator ID to a creator name, or
 * a group ID to a group name.
 *
 * The creator name/id is identical to the user name and UID under
 * Unix.
 *
 * Inputs:
 *   fcn	MapID_C for creator, MapID_G for group.
 *   id		The id to be mapped, either group ID or creator ID.	
 * 
 * Outputs:
 *  OSErr	Function result. 
 *  name	name corresponding to input ID.
 *
 *
 */

export OSErr
OSMapID(fcn,name,idd)
byte fcn;
char *name;
sdword idd;				/* 4 bytes */
{
  struct passwd *pwd;
  struct group *grp;
  int id = EtoIID(idd);			/* convert to internal id */

  if (DBOSI)
    printf("OSMapID fcn=%s id=%d\n",
	   ((fcn == MapID_C) ? "Creator" : "Group"),id);

  switch (fcn) {
  case MapID_C:
    pwd = getpwuid(id);
    if (pwd == NULL) 
      sprintf(name,"%08d",id);
    else
      strcpy(name,pwd->pw_name);
    break;
	  
  case MapID_G:
    grp = getgrgid(id);
    if (grp == NULL)
      sprintf(name,"%08d",id);
    else
      strcpy(name,grp->gr_name);
    break;
  default:
    return(aeParamErr);
  }
  return(noErr);
}

/*
 * OSErr OSMapName(byte fcn,char *name,sdword *id);
 *
 * This call is used to map a creator name to a creator ID or
 * a group name to a group id.
 *
 * The creator name/id is identical to the user name and UID under
 * Unix.
 *
 * Inputs:
 *  fcn		MapName_C for creator, MapName_G for group.
 *  name	Item to be mapped, either creator name or group name.
 *
 * Outputs:
 *  OSErr	Function result.
 *  id		ID corresponding to input name.
 *
 * 
 */

OSErr
OSMapName(fcn,name,eid)
byte fcn;
char *name;
sdword *eid;				/* 4 bytes */
{
  struct passwd *pwd;
  struct group *grp;
  int id;

  if (DBOSI)
    printf("OSMapName fcn=%s name=%s\n",
	   (fcn == MapName_C) ? "Creator" : "Group",name);

  switch (fcn) {
  case MapName_C:
    pwd = getpwnam(name); 
    if (pwd == NULL) 
      return(aeItemNotFound);
    id = pwd->pw_uid;			/* return user ID */
    break;

  case MapName_G:
    grp = getgrnam(name);		/* get group entry */
    if (grp == NULL)
      return(aeItemNotFound);
    id = grp->gr_gid;			/* return group ID */
    break;
  }
  *eid = ItoEID(id);			/* convert to external */
  return(noErr);
}

/*
 * return information about a particular user id.
 * if doself is true then return information about logged in user
 * AFP2.0
 *
*/
OSGetUserInfo(doself, userid, guirp)
boolean doself;
dword userid;
GetUserInfoReplyPkt *guirp;
{
  int bm = guirp->guir_bitmap;
  int usr = usruid;
  int grp = usrgid;
  struct passwd *pwd;

  if (!doself) {
    if ((pwd = (struct passwd *)getpwuid(userid)) == NULL)
      return(aeItemNotFound);
    usr = pwd->pw_uid;
    grp = pwd->pw_gid;
  }
  guirp->guir_userid = usr;
  guirp->guir_pgroup = grp;
  return(noErr);
}


/*
 * OSErr OSDelete(IDirP ipdir, idir ,char *file)
 *
 * OSDelete is used to delete a file or an empty directory.
 *
 * Inputs:
 *    parent directory
 *    directory id of directory (null if file)
 *    file name in parent directory
 *
 * Outputs, OSErr Function result:
 * 
 *  ParamErr		Bad path.  
 *  ObjectNotFound	Path does not point to an existing file or directory.
 *  DirNotEmpty		The directory is not empty.
 *  FileBusy		The file is open.
 *  AccessDenied	User does not have the rights (specified in AFP spec).
 *
 */
OSErr
OSDelete(ipdir,idir,file)
IDirP ipdir, idir;
char *file;
{
  int err;
  char path[MAXPATHLEN];

  OSfname(path,ipdir,file,F_DATA);	/* create data fork name */

  if (DBOSI)
    printf("OSDelete file=%s\n",path);

  if (idir) {
    err = os_rmdir(path,F_FNDR);	/* remove finder dir  */
    if (err != noErr)
      return(err);
    err = os_rmdir(path,F_RSRC);	/* delete resource directory */
    if (err != noErr)
      return(err);
    err = unix_rmdir(path);		/* delete the data file */
    if (err != noErr)
      return(err);
    (void) os_delete(ipdir,file,F_FNDR);	/* delete finder fork */
    /* remove the dirid */
    FModified(ipdir, file);
    Idrdirid(ipdir, idir);		/* idir is invalid after this point */
    return(noErr);			/* and return result */
  }

  err = unix_unlink(path);		/* rid the data file */
  if (err != noErr)
    return(err);
  (void) os_delete(ipdir,file,F_RSRC);	/* delete resource fork */
  (void) os_delete(ipdir,file,F_FNDR);	/* delete finder fork */
  FModified(ipdir, file);
  return(noErr);
}

/*
 * OSErr os_rmdir(char *dir, int typ)
 *
 * Delete a finder/resource directory as specified by type which
 * is either F_FNDR or F_RSRC.
 *
 * If a simple unix_rmdir fails because the directory is not empty
 * then scan the directory for "junk" files and remove those.
 *
 * Junk files are leftovers which exist in our finder/resource
 * directory but do not exist in the data directory.  They don't
 * usually occur under normal operation but cause a headache when
 * they do since the folder can stay locked after a delete error.
 *
*/
private OSErr
os_rmdir(dir,typ)
char *dir;
int typ;
{
  char path[MAXPATHLEN];		/* resource/finder path */
  char dpath[MAXPATHLEN];		/* data file path */
  DIR *dirp;
  struct direct *dp;
  struct stat stb;
  int pl,dpl,err;

  /* create the directory path for this rmdir... either fndr or rsrc dir */

  strcpy(path,dir);			/* local copy of dir name */
  if (typ == F_RSRC)
    strcat(path,RFDIR);			/* build resource directory name */
  else
    strcat(path,FIDIR);			/* build finder directory name */

  /* try deleting the directory */

  err = unix_rmdir(path);		/* try rmdir */
  if (err == aeObjectNotFound)		/* does not exist error? */
    err = noErr;			/* then ok by use */
  if (err == noErr ||			/* deleted ok? */
      err != aeDirNotEmpty)		/* or unknown cause? */
    return(err);			/* then return now */
  
  /* directory could not be deleted because it was not empty */
  /* delete dir entries which are not in the data directory and try again */

  strcpy(dpath,dir);			/* local copy of data dir name */
  dpl = strlen(dpath);			/* find length */
  dpath[dpl++] = '/';			/*  append slash */

  dirp = opendir(path);			/* open the fndr/rsrc dir... */
  if (dirp == NULL)			/* does not exist etc? */
    return(noErr);			/* then no directory */

  pl = strlen(path);			/* set length of fndr/rsrc dir */
  path[pl++] = '/';			/* add slash for file concats */

  for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {

    if (dp->d_namlen <= 2  &&
	dp->d_name[0] == '.' &&
	(dp->d_name[1] == '.' || dp->d_name[1] == '\0'))
      continue;				/* skip dot and dot dot */

    strcpy(dpath+dpl,dp->d_name);	/* compose name in data dir */
    if (stat(dpath,&stb) == 0) {	/* to see if it exists there */
      closedir(dirp);
      return(aeDirNotEmpty);		/* if so... dir really not empty */
    }
    
    strcpy(path+pl,dp->d_name);		/* otherwise use fndr/rsrc file name */
    if (DBOSI)
      printf("os_rmdir: cleaning %s\n",path);
    err = unix_unlink(path);		/*  and remove */
    if (err != noErr) {			/* if that failed... */
      closedir(dirp);
      return(err);			/*  then return now */
    }
  }
  closedir(dirp);			/* finished with directory  */
  path[pl-1] = '\0';			/* back to fndr/rsrc name */
  return(unix_rmdir(path));		/* try deleting now */
}

private OSErr
os_delete(pdir,file,typ)
IDirP pdir;
char *file;
int typ;
{
  char path[MAXPATHLEN];

  OSfname(path,pdir,file,typ);		/* build unix file name */
  return(unix_unlink(path));		/* do the work... */
}


/*
 * OSErr OSRename(IDirP pdir, char *from, char *to);
 *
 * OSRename is used to rename a file or directory.
 *
 * Inputs:
 *  pdir	parent directory id.
 *  from	name of file or directory to rename.
 *  to		new name for file or directory.
 *
 * Outputs:
 *  OSErr	Function result.
 *
 * Pass the work along to os_move, which handles the general case.
 *
 */

OSErr
OSRename(pdir,from,to)
IDirP pdir;				/* parent directory id */
char *from,*to;				/* from and to file names */
{
  return(os_move(pdir,from,pdir,to));	/* do the work */
}

/*
 * OSErr OSMove(IDirP fpdir, char *from, IDirP tpdir, char *to)
 *
 * OSMove is used to move a directory or file to another location on
 * a single volume (source and destination must be on the same volume).
 * The destination of the move is specified by provinding a pathname
 * that indicates the object's new parent directory.
 *
 * Inputs:
 *  fpdir	parent directory id of from.
 *  from	name of file or directory to rename.
 *  tpdir	parent directory id of to.
 *  to		new directory for file or directory.
 *
 * Outputs:
 *  OSErr	Function result.
 *
 * Create an internal directory id for the new directory name by
 * combining the parent dir id (tpdir), and directory name of the
 * destination (to) and call the general routine os_move().  Note
 * that the new name is the same as the old name and to specifies 
 * the destination directory.
 *
 */

export OSErr
OSMove(fpdir,from,tpdir,to)
IDirP fpdir,tpdir;			/* from and to parent dirs */
char *from;				/* from file name */
char *to;				/*  to file name is dest directory */
{
  return(os_move(fpdir, from, tpdir, to));
}

/*
 * OSErr os_move(IDirP fpdir, char *from, IDirP tpdir, char *to)
 *
 * Move the file.
 *
 */
private OSErr
os_move(fpdir,from,tpdir,to)
IDirP fpdir,tpdir;			/* from and to parent dirs  */
char *from,*to;				/* from and to file names */
{
  char fpath[MAXPATHLEN];
  char tpath[MAXPATHLEN];
  struct stat stb;
  int err,cerr;
  int mo;

  OSfname(fpath,fpdir,from,F_DATA);	/* build data file name */
  OSfname(tpath,tpdir,to,F_DATA);	/* for from and to files */

  if (DBOSI)
    printf("OSRename from=%s, to=%s\n",fpath,tpath);

  if ((fpdir->flags & DID_FINDERINFO) && (tpdir->flags & DID_FINDERINFO) == 0)
    return(aeCantMove);
  if ((fpdir->flags & DID_RESOURCE) && (tpdir->flags & DID_RESOURCE) == 0)
    return(aeCantMove);

  /* must be able to stat destination */
  if ((err = unix_stat(pathstr(tpdir), &stb)) != noErr)
    return(err);
  mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid);

  if ((err = unix_stat(fpath,&stb)) != noErr)
    return(err);

  err = unix_rename(fpath,tpath);	/* give unix the args */
  if (err != noErr)			/* if an error on data files */
    return(err);			/*  then give up */

  if (!S_ISDIR(stb.st_mode)) {		/* directories have no rsrc fork */
    unix_chmod(tpath, mo);		/* file: try to reset protection */
    if (fpdir->flags & DID_RESOURCE) {
      OSfname(fpath,fpdir,from,F_RSRC);	/* build resource file names */
      OSfname(tpath,tpdir,to,F_RSRC);
      err = unix_rename(fpath,tpath);	/* give unix a shot at it */
      /* allow non-existant resource */
      if (err != noErr && err != aeObjectNotFound) { /* error on rename? */
	if (DBOSI)
	  printf("os_rename: failed %s for %s -> %s\n",
		 afperr(err),fpath,tpath);
	OSfname(fpath,fpdir,from,F_DATA); /* try to rebuild */
	OSfname(tpath,tpdir,to,F_DATA);
	cerr = unix_rename(tpath,fpath);	/* rename back to orignal */
	if (cerr != noErr && DBOSI)
	  printf("os_rename: cleanup failed\n");
	unix_chmod(tpath, stb.st_mode&0777); /* file:try to reset protection */
	return(err);
      }
    }
  }  

  if (fpdir->flags & DID_FINDERINFO) {
    OSfname(fpath,fpdir,from,F_FNDR);	/* build finder info file names */
    OSfname(tpath,tpdir,to,F_FNDR);
    err = unix_rename(fpath,tpath);	/* give unix a shot at it */
    if (err != noErr && DBOSI) {
      printf("os_rename: failed %s for %s -> %s\n", afperr(err),fpath,tpath);
    } else {
      if (!S_ISDIR(stb.st_mode))
	unix_chmod(tpath, mo);		/* file: try to reset protection */
      OSSetMacFileName(tpdir, to);
    }
  } else
    if (DBOSI)
      printf("os_rename: no finder info to rename\n");

  if (S_ISDIR(stb.st_mode))		/* if a directory then need to */
    Idmove(fpdir,from,tpdir,to);	/*  change internal structure */

  FModified(fpdir, from);	/* does an emodified */
  /* EModified(fpdir); */
  /* don't need to mark dest file as modified since mac won't let this */
  /* happen */
  EModified(tpdir);
  return(noErr);
}

    
/*
 * OSErr OSFlush(int vid)
 *
 * OSFlush is used to flush to disk any data relating to the specified
 * volume that has been modified by the user.
 *
 * The Unix system call sync is used.  We should probably be dumping out
 * internal volume buffers instead.
 *
 * Inputs:
 *  vid		Volume ID.
 *
 * Outputs:
 *  OSErr	Function Result.	 
 *
 */

/*ARGSUSED*/
export OSErr
OSFlush(vid)
int vid;
{
  import GCHandle *fich;	/* get FinderInfo cache */

  GCFlush(fich, NILDIR);	/* flush the finderinfo of bad data */
  FlushDeskTop(vid);
/*  sync();			/* this is probably a waste */
  /* above is a waste and slows down system unnecessarily */
  return(noErr);
}

/*
 * OSErr OSFlushFork(int fd)
 *
 * Forces the write to disk of any pending file activity.
 * 
 * Really intended for buffered OSWrites.
 *
 * Inputs:
 *  fd		File descriptor.
 *  
 * Outputs:
 *  OSErr	Function Result.
 *
 */

OSErr
OSFlushFork(fd)
int fd;
{
  if (DBOSI)
    printf("OSFlushFork fd=%d\n",fd);

  return(fsync(fd) == 0 ? noErr : aeMiscErr);
}


export OSErr
OSClose(fd)
int fd;
{
  return(unix_close(fd));		/* return OSErr */
}

/*
 * From the open file referenced by fd
 * at the offset offs
 * read "reqcnt" bytes
 * using the new line mask nlmsk
 * and the newline character nlchr
 * into the buffer r for at most rl bytes
 * keeping the file position in fpos
 * translating from lf to cr if unixtomac is nonzero
 *
*/
export OSErr
OSRead(fd,offs,reqcnt,nlmsk,nlchr,r,rl,fpos,trans_table_index)
int fd;
sdword offs,reqcnt;
byte nlmsk,nlchr;
byte *r;
int *rl;
sdword *fpos;
int trans_table_index;
{
  register char c;
  int cnt,i;

  /* want to probe for eof -- probably there */
  /* back off this.  If the request count is zero, then */
  /* don't tell them about EOF because zero length files */
  /* will not get xfered properly */
  if (reqcnt == 0) {
    *rl = 0;
    return(noErr);
  }

  if (offs != *fpos)
    lseek(fd,(off_t)offs,L_SET);

  if (OSTestLock(fd, reqcnt) != noErr) {
      return(aeLockErr);
  }

  cnt = read(fd,r,reqcnt);
  if (cnt < 0) {
    printf("OSRead: Error from read %s\n",syserr());
    return(aeParamErr);
  }

  if (cnt == 0)
    return(aeEOFErr);

  *fpos += cnt;

  /* under appleshare prior to version 2.0, nlmask was either 0 or 0xff */
  /* so no anding needed to be done (either use or not).  shouldn't hurt */
  /* to do it for previous versions though */
  if (nlmsk != 0) {
#ifndef NONLXLATE
    if (nlchr == ENEWLINE) {
      for (i=0; i < cnt; i++) {
	c = r[i] & nlmsk;
	if (c == ENEWLINE || c == INEWLINE)
	  break;
      }
      if (r[i] == INEWLINE)		/* if ended on internal newline */
	r[i] = ENEWLINE;		/*  then convert to external */
    } else
      for (i=0; i < cnt && (r[i]&nlmsk) != nlchr; i++)
	/* NULL */;
#else    
    for (i=0; i < cnt && (r[i]&nlmsk) != nlchr; i++)
      /* NULL */;
#endif NONLXLATE

    if (i < cnt)			/* found it? */
      cnt = i+1;			/* yes count is position plus 1 */
  }
  if (trans_table_index >= 0) {
    if (DBOSI)
      printf("FPRead: translating to macintosh according to: %s\n",
	     ncs_tt_name(trans_table_index));
    ncs_translate(NCS_TRANS_UNIXTOMAC, trans_table_index, r, cnt);
  }

  *rl = cnt;				/* store count of bytes read */
  if (cnt < reqcnt && nlmsk == 0)	/* less than request and no nlchr */
    return(aeEOFErr);			/*  means we found eof */
  return(noErr);			/* else no error.... */
}

/*
 * Write to the open file referenced by fd
 *   the write buffer is wbuf of length wlen
 *   do the write at offset offs
 *   keeping file position in fpos
 * flg - marks whether offs relative to beginning or end of file
 * unixtomax - if non-zero translate cr to lf on writes
 *
*/
OSWrite(fd,wbuf,wlen,offs,flg,fpos,trans_table_index)
int fd;
byte *wbuf;
sdword wlen,offs;
byte flg;
sdword *fpos;
int trans_table_index;
{
  int cnt;

  if (wlen == 0)			/* zero byte request? */
    return(noErr);			/*  yes... no work */

  if (flg == 0) {
    if (offs != *fpos)
      *fpos = lseek(fd,(off_t)offs,L_SET);
  } else
   *fpos = lseek(fd,(off_t)offs,L_XTND);

  if (OSTestLock(fd, wlen) != noErr) {
    return(aeLockErr);
  }
  if (trans_table_index >= 0) {
    if (DBOSI)
      printf("FPWrite: translating from macintosh according to: %s\n",
	     ncs_tt_name(trans_table_index));
    ncs_translate(NCS_TRANS_MACTOUNIX, trans_table_index, wbuf, wlen);
  }

  cnt = write(fd,wbuf,wlen);
  *fpos += cnt;
  if (cnt == wlen)
    return(noErr);
  if (DBOSI)
    printf("OSWrite: Error from write %s\n",syserr());
  return(ItoEErr(errno));
}

/*
 * OSErr OSCreateDir(char *path,dword *dirid)
 *
 * This function is used to create a new directory.
 *
 * Inputs:
 *  path	The directory to create.
 *
 * Outputs:
 *  OSErr	Function result.
 *  dirid	Directory ID of newly created directory.
 *
 */

private OSErr
os_mkdir(pdir,name,mode,typ)
IDirP pdir;				/* parent directory */
char *name;
int typ,mode;
{
  char path[MAXPATHLEN];
  OSErr err;
					/* want path/name/.resource */
					/* want path/name/.finderinfo */
					/* want path/name */

  OSfname(path, pdir, name, F_DATA);	/* get path/name */
  switch (typ) {
  case F_RSRC:
    strcat(path, "/");
    strcat(path, RFDIRFN);
    break;
  case F_FNDR:
    strcat(path, "/");
    strcat(path, FIDIRFN);
    break;
  }
  if (DBOSI)
    printf("os_mkdir: creating mode=o%o dir=%s\n",mode,path);

  err = unix_mkdir(path,mode);		/* let unix do the work */
  if (err != noErr)
    return(err);
  EModified(pdir);
  return(noErr);
}  


OSErr
OSCreateDir(pdir,name, newdirid)
IDirP pdir;				/* parent directory id */
char *name;				/* name of new directory */
IDirP *newdirid;
{
  char path[MAXPATHLEN];
  int err,mo;
  struct stat stb;

  if (DBOSI)
    printf("OSCreateDir: creating %s\n",name);

  err = unix_stat(pathstr(pdir),&stb);	/* get current protections */
  mo = stb.st_mode & 0777;

  err = os_mkdir(pdir,name,mo,F_DATA);	/* create the data directory */
  if (err != noErr)			/* if that failed... then */
    return(err);			/*  return the error */

  if (pdir->flags  & DID_FINDERINFO) {
    OSfname(path,pdir,name,F_FNDR);	/* create finderinfo for folder */
    err = unix_create(path,TRUE,mo);	/*  do delete if exists... */
    os_mkdir(pdir,name,mo,F_FNDR);	/* create the finderinfo directory */
  }
  if (pdir->flags & DID_RESOURCE)
    os_mkdir(pdir,name,mo,F_RSRC);	/* create the resource directory */
  *newdirid = Idndirid(pdir,name);	/* now install the directory */
  return(noErr);
}


/*
 * OSErr OSFileDirInfo(IDirP ipdir, char *fn,
 *			FileDirParms *fdp, int volid);
 *
 * Return information for file fn, existing in directory ipdir into
 * the fdp structure.
 *
 * fdp->fdp_dbitmap and fdp->fdp_fbitmap are set with the request
 * bitmaps so we don't necessarily need to fetch unwanted (costly)
 * items.
 *
 */
export OSErr 
OSFileDirInfo(ipdir,idir,fn,fdp,volid)
IDirP ipdir;				/* parent directory */
IDirP idir;				/* directory id if directory */
char *fn;				/* file name */
FileDirParm *fdp;			/* returned parms */
int volid;				/* volume */
{
  char path[MAXPATHLEN];
  struct stat buf;
  time_t sometime;

  OSfname(path,ipdir,fn,F_DATA);

  if (DBOSI)
    printf("OSFileDirInfo on %s\n",path);

  if (stat(path,&buf) != 0) {		/* file exists? */
    if (idir)				/* was in directory tree? */
      Idrdirid(ipdir, idir);		/* invalidate the entry then */
    return(aeObjectNotFound);		/* no... */
  }

  /* pick out the earliest date for mac creation time */
  sometime = (buf.st_mtime > buf.st_ctime) ? buf.st_ctime : buf.st_mtime;
  fdp->fdp_cdate = (sometime > buf.st_atime) ? buf.st_atime : sometime;
  /* pick the later of status change and modification for */
  /* mac modified */
  fdp->fdp_mdate = (buf.st_mtime < buf.st_ctime) ? buf.st_ctime : buf.st_mtime;

  fdp->fdp_bdate = 0;
  fdp->fdp_zero = 0;			/* zero the zero byte (?) */
/*  strcpy(fdp->fdp_lname,fn);		/* copy long name */
  if (idir == VolRootD(volid))
    strcpy(fdp->fdp_lname, VolName(volid));
  else
    ItoEName(fn,fdp->fdp_lname);

  if (S_ISDIR(buf.st_mode)) {		/* is this a directory? */
    fdp->fdp_flg = FDP_DIRFLG;		/* yes... */
    return(OSDirInfo(ipdir,fn,fdp,&buf,volid));	/* fill in */
  }
  fdp->fdp_flg = 0;			/* otherwise a file */
  return(OSFileInfo(ipdir,fn,fdp,&buf)); /* fill in */
}

  
export OSErr
OSDirInfo(ipdir,fn,fdp,buf,volid)
IDirP ipdir; 
char *fn;
FileDirParm *fdp;
struct stat *buf;
int volid;
{
  IDirP idirid;
  dword ItoEAccess();
  word bm;
  int nchild;

  fdp->fdp_dbitmap &= DP_AUFS_VALID; /* truncate to findable */
  bm = fdp->fdp_dbitmap;

  if (DBDIR)
    printf("OSDirInfo on %s bm=%x\n",fn,bm);

  if (bm & DP_ATTR)			/* skip attr if not requested */
    OSGetAttr(ipdir,fn,&fdp->fdp_attr);
  if (bm & DP_FINFO)			/* skip finfo if not requested */
    OSGetFNDR(ipdir,fn,fdp->fdp_finfo);

  fdp->fdp_parms.dp_parms.dp_ownerid = ItoEID(buf->st_uid);
  fdp->fdp_parms.dp_parms.dp_groupid = ItoEID(buf->st_gid);
  fdp->fdp_parms.dp_parms.dp_accright =
    ItoEAccess(buf->st_mode,buf->st_uid,buf->st_gid);

  idirid = Idndirid(ipdir,fn);	/* must validate the entry now */
  if (idirid == NILDIR)
    return(aeAccessDenied);
  InitDIDVol(idirid, volid);
#ifdef notdef
  /* should be done already - so don't do it here! */
  if ((idirid->flags & DID_VALID) == 0) /* info valid? */
    OSValidateDIDDirInfo(idirid); /* valid it then */
#endif
  if (bm & DP_CHILD) {
    nchild = OSEnumCount(idirid);
    if (nchild < 0)			/* error if less than zero */
      nchild = 0;			/*  probably no read... set to 0 */
    fdp->fdp_parms.dp_parms.dp_nchild = nchild;
  }
  fdp->fdp_parms.dp_parms.dp_dirid = ItoEdirid(idirid,volid);
  return(noErr);
}

/* 
 * validates a did by making sure:
 *  - idirid points to a directory
 *  - limits number of symbolic links we will follow in any given path!
 *    o note: overlapping volumes may cause us to follow too many
 *      symbolic links, but things should eventually catch up.
 * also checks if the .resource and .finderinfo directories exists
 *  (must not be symbolic links)
 * double check that parent exists
*/
export void
OSValidateDIDDirInfo(idirid)
IDirP idirid;
{
  char path[MAXPATHLEN];
  struct stat stb;
  IDirP pdir;
  int i;

  OSfname(path,idirid,"",F_DATA);
  if (stat(path, &stb) == 0) {
    if (S_ISDIR(stb.st_mode))
      idirid->flags |= DID_DATA;
#ifndef NOLSTAT
    if (lstat(path, &stb) != 0) { /* shouldn't fail! */
      idirid->flags = DID_VALID;
      return;
    }
    if ((pdir = Ipdirid(idirid)) != NILDIR) {	/* get parent */
      /* get count of symlinks of parent */
      i = ((pdir->flags & DID_SYMLINKS) >> DID_SYMLINKS_SHIFT);
      if ((stb.st_mode & S_IFMT) == S_IFLNK) {
	i++;			/* bump up count */
	if (i > DID_MAXSYMLINKS) {
	  idirid->flags = DID_VALID; /* toss it, too many links down */
	  return;
	}
      }
      /* really shouldn't need to mask it - means we are overinc'ed */
      idirid->flags |= ((i << DID_SYMLINKS_SHIFT) & DID_SYMLINKS);
    }
    /* don't follow symbolic links here! */
    OSfname(path,idirid,"",F_FNDR);
    if (lstat(path, &stb) == 0) 
      if (S_ISDIR(stb.st_mode))
	idirid->flags |= DID_FINDERINFO;
    OSfname(path,idirid,"",F_RSRC);
    if (lstat(path, &stb) == 0)  
      if (S_ISDIR(stb.st_mode))
	idirid->flags |= DID_RESOURCE;
#else
    /* no symolic links then */
    OSfname(path,idirid,"",F_FNDR);
    if (stat(path, &stb) == 0) 
      if (S_ISDIR(stb.st_mode))
	idirid->flags |= DID_FINDERINFO;
    OSfname(path,idirid,"",F_RSRC);
    if (stat(path, &stb) == 0)  
      if (S_ISDIR(stb.st_mode))
	idirid->flags |= DID_RESOURCE;
#endif
  }
  idirid->flags |= DID_VALID;
}

export OSErr
OSFileInfo(ipdir,fn,fdp,buf)
IDirP ipdir;
char *fn;
FileDirParm *fdp;
struct stat *buf;
{
  struct stat stb;
  char rpath[MAXPATHLEN];
  word bm;

  fdp->fdp_fbitmap &= FP_AUFS_VALID; /* truncate to aufs supported */
  bm = fdp->fdp_fbitmap;		/* fetch file bitmap */

  if (DBDIR)
    printf("OSFileInfo on %s bm=%x\n",fn,bm);

  if (bm & FP_ATTR)			/* skip attr if not requested */
    OSGetAttr(ipdir,fn,&fdp->fdp_attr);

  if (bm & FP_FINFO)			/* skip finfo if not requested */
    OSGetFNDR(ipdir,fn,fdp->fdp_finfo);

  fdp->fdp_parms.fp_parms.fp_fileno = buf->st_ino;
  fdp->fdp_parms.fp_parms.fp_dflen = buf->st_size;
  fdp->fdp_parms.fp_parms.fp_rflen = 0;

  if (bm & FP_RFLEN) {
    OSfname(rpath,ipdir,fn,F_RSRC);	/* convert to rsrc name */
    if (stat(rpath,&stb) != 0)		/* to figure size of resource fork */
      return(noErr);
    if (DBFIL)
      printf("OSFileInfo: %s size is %d\n", rpath,stb.st_size);
    fdp->fdp_parms.fp_parms.fp_rflen = stb.st_size;
  }
  return(noErr);
}

/*
 * simply check to see if a particular file exists
 *
*/
export OSErr
OSFileExists(ipdir, fn, type)
IDirP ipdir;
char *fn;
int type;
{
  char path[MAXPATHLEN];  
  struct stat stb;

  OSfname(path, ipdir, fn, type);
  return(unix_stat(path, &stb));
}

export OSErr
OSSetFileDirParms(ipdir,idir,fn,fdp)
IDirP ipdir;
IDirP idir;				/* directory id if dir */
char *fn;
FileDirParm *fdp;
{
  char path[MAXPATHLEN];
  struct stat stb;
  int err;

  OSfname(path,ipdir,fn,F_DATA);	/* unix file name */
  if ((err = unix_stat(path,&stb)) != noErr) {
    /* can't find it !!! */
    if (idir)
      Idrdirid(ipdir, idir);		/* remove from tree */
    return(err);
  }

  if (S_ISDIR(stb.st_mode))		/* if a directory */
    return(OSSetDirParms(ipdir,fn,fdp)); /* then set dir parms... */
  else					/* else set file parms */
    return(OSSetFileParms(ipdir,fn,fdp));
}

export OSErr
OSSetFileParms(ipdir,fn,fdp)
IDirP ipdir;
char *fn;
FileDirParm *fdp;
{
  word bm = fdp->fdp_fbitmap;

  if (bm & (FP_FINFO|FP_ATTR))
    OSSetFA(ipdir,fn,fdp->fdp_fbitmap,fdp);
  return(noErr);
}


/*
 * OSSetDirParms
 * 
 *
*/
OSErr
OSSetDirParms(ipdir,fn,fdp)
IDirP ipdir;
char *fn;
FileDirParm *fdp;
{
  u_short EtoIAccess();
  char path[MAXPATHLEN];
  int own,grp,err;			/* owner and group ids */
  DirParm *dp = &fdp->fdp_parms.dp_parms;
  int flags;

  if (fdp->fdp_dbitmap & (DP_FINFO|DP_ATTR))
    OSSetFA(ipdir,fn,fdp->fdp_dbitmap,fdp);

  grp = own = -1;
  if (fdp->fdp_dbitmap & DP_CRTID)
    own = EtoIID(dp->dp_ownerid);    
  if (fdp->fdp_dbitmap & DP_GRPID)
    grp = EtoIID(dp->dp_groupid);

  flags = ipdir->flags;		/* should use to prevent overworking */
  if (own != -1 || grp != -1) {
    /* error recovery? do all just in case */
    OSfname(path,ipdir,fn,F_DATA);	/* change unix name */
    if ((err = unix_chown(path,own,grp)) != noErr)
      return(err);
    OSfname(path,ipdir,fn,F_RSRC);	/* change unix name */
    if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound)
      return(err);
    OSfname(path,ipdir,fn,F_FNDR);	/* create unix name */
    if ((err = unix_chown(path,own,grp)) != noErr && err != aeObjectNotFound)
      return(err);
    EModified(ipdir);
  }
    
  if (fdp->fdp_dbitmap & DP_ACCES) {
    u_short acc = EtoIAccess(dp->dp_accright);

    if ((err = os_chmod(ipdir,fn,acc,F_DATA)) != noErr)
      return(err);
    os_chmod_all(ipdir, fn, acc, F_DATA); /* change all file protections */
    os_chmod_all(ipdir, fn, acc, F_RSRC);
    os_chmod_all(ipdir, fn, acc, F_FNDR);
    if ((err = os_chmod(ipdir,fn,acc,F_RSRC)) != noErr &&
	err != aeObjectNotFound)
      return(err);
    if ((err = os_chmod(ipdir,fn,acc,F_FNDR)) != noErr &&
	err != aeObjectNotFound)
      return(err);
  }
  return(noErr);
}


/*
 * Set fork length specified the file handle
 * - careful about len - should be at least a signed double word
 *   on mac (4 bytes)
*/
export OSErr
OSSetForklen(fd, len)
int fd;
int len;
{
  int err;
  if (DBOSI)
    printf("OSSetForklen: truncating file on file descriptor %d to %d\n",
	   fd,len);
  if (ftruncate(fd, len) < 0) {
    err = errno;
    if (DBOSI)
      printf("OSSetForklen: error on truncate %s\n",syserr());
    return(ItoEErr(err));
  }
  return(noErr);
}

/*
 * OSErr os_chmod(IDirP idir, u_short mode, int typ)
 *
 * Directory id (idir), and type (typ) specify a directory name.
 * Internal access bits are mode.
 *
 * Change the protection of the directory to eacc.  
 *
 */
private OSErr
os_chmod(idir,fn,mode,typ)
IDirP idir;
char *fn;
u_short mode;
int typ;
{
  char path[MAXPATHLEN];
  int err;

  OSfname(path,idir,fn,typ);		/* convert unix name */
  if (DBOSI)
    printf("os_chmod: setting %o for %s\n",mode,path);

  err = unix_chmod(path,mode);		/* and set for the directory */
  if (err != noErr)
    return(err);

  EModified(idir);
  return(noErr);
}

/*
 * Change file protection for all files in directory
 *
 * Have to do because:
 * unix has file protection and AFP does not, change the protection
 * of each file in the directory as well.
 *
 * Do not change the protection of directories contained within
 * the directory... 
 *
*/
private void
os_chmod_all(idir,fn,mode,typ)
IDirP idir;
char *fn;
u_short mode;
int typ;
{
  char path[MAXPATHLEN];
  struct stat stb;
  struct direct *dp;
  DIR *dirp;
  int pl,err;

  OSfname(path,idir,fn,F_DATA);		/* convert unix name */
  pl = strlen(path);
  switch (typ) {
  case F_DATA:
    break;
  case F_RSRC:
    path[pl] = '/';
    strcpy(path+pl+1, RFDIRFN);
    break;
  case F_FNDR:
    path[pl] = '/';
    strcpy(path+pl+1, FIDIRFN);
    break;
  }
  if (DBOSI)
    printf("os_chmod: setting %o for %s\n",mode,path);

  dirp = opendir(path);
  if (dirp == NULL) {
    if (DBOSI)
      printf("os_chmod: opendir failed on %s\n",path);
    return;
  }

  pl = strlen(path);			/* length of the path */
  path[pl++] = '/';			/* add component terminator */
  for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
    strcpy(path+pl,dp->d_name);		/* create file name */
    if (stat(path,&stb) != 0) {		/* get the file status  */
      if (DBOSI)
	printf("os_chmod: stat failed for %s\n",path);
      continue;				/*  some error... */
    }
    if (S_ISDIR(stb.st_mode)) {
      if (dp->d_name[0] != '.')
	continue;
      /* was . or .. */
      if (dp->d_name[1] == '\0' ||
	  (dp->d_name[1] == '.' && dp->d_name[2] == '\0'))
	continue;		/* skip it */
      /* if not .resource or .finderinfo then skip */
      if (typ != F_DATA &&
	  strcmp(dp->d_name,RFDIRFN) != 0 && strcmp(dp->d_name, FIDIRFN) != 0)
	continue;
    }
    err = unix_chmod(path,mode);	/* set the mode for this file */
    /* ignore errors */
  }
  closedir(dirp);			/* close the directory */
}


private u_short
EtoIPriv(emode,eshift,ishift)
dword emode;
int eshift,ishift;
{
  u_short i = 0;

  emode = (emode >> eshift);
  if (emode & E_RD) i = I_RD | I_EX;
  if (emode & E_WR) i |= I_WR | I_EX;
/*  if (emode & E_SR) i |= I_EX; */
  return(i << ishift);  
}


/*
 * Is the current user in the group gid?
 *
*/
private int
OSInGroup(gid)
int gid;
{
  int i;
  int gtgid;

  /* if ngroups gets very large, do a binary search */
  for (i=0; i < ngroups; i++) {
    gtgid = groups[i];
    if (gtgid == gid)
      return(TRUE);
    if (gtgid > gid)		/* limiting case */
      return(FALSE);
  }
  return(FALSE);
}

/*
 * Get groups that a user is valid in
 *
 * Sort the array for later use
 *
*/
dogetgroups()
{
  int ng;
  int i,k,idxofmin;
  int t;

  if ((ng = getgroups(NGROUPS, groups)) < 0) {
    return(-1);
  }
  if (ng == 0)			/* not in any groups? */
    return(0);

  /* sort groups array */
  /* we assume n is very small, else possibly replace with quicker sort */
  /* n**2 performance (comparisons), but no more than n swaps */
  /* the array tends to be mostly sorted with the first element out */
  /* of place.  Don't use std quicksort -- under this assumption */
  /* it will give very poor performance on average (at much higher overhead) */
  /* NOTE: we could really speed this up if we knew that the  */
  /* array was sorted after the first "n" items, but with N so */
  /* small (usually less than 32), it's not a big deal */
  for (i = 0 ; i < ng; i++) {
    for ( idxofmin = i, k = i + 1; k < ng; k++) /* find (i+1)'th min */
      if (groups[k] < groups[idxofmin])
	idxofmin = k;
    if (i != idxofmin) {
      t = groups[idxofmin];
      groups[idxofmin] = groups[i];
      groups[i] = t;
    }
  }
  return(ng);
}

private u_short
EtoIAccess(emode)
dword emode;
{
  u_short imode;

  imode = (EtoIPriv(emode,ES_OWNER,IS_OWNER) |
	  EtoIPriv(emode,ES_GROUP,IS_GROUP) |
	  EtoIPriv(emode,ES_WORLD,IS_WORLD));

  if (DBOSI)
    printf("EtoIAccess: E=0x%x, I=o%o\n",emode,imode);
  return(imode);
}

private dword
ItoEPriv(imode,ishift,eshift)
u_short imode;
int ishift,eshift;
{
  dword e = 0;
  
  imode = (imode >> ishift);
  if (imode & I_RD) e = E_RD | E_SR;
  if (imode & I_WR) e |= E_WR;
/*  if (imode & I_EX) e |= E_SR; */
  return(e << eshift);
}

private dword
ItoEAccess(imode,uid,gid)
u_short imode;
int uid;
int gid;
{
  dword e = 0;

  if (usruid == 0)		/* root? */
    e |= ((E_RD|E_WR|E_SR)<<ES_USER);
  else if (usruid == uid)	/* but if the owner then */
    e |= ItoEPriv(imode,IS_OWNER,ES_USER)|E_IOWN; /* set owner bits */
  else if (OSInGroup(gid))		/* if in one of the groups */
    e |= ItoEPriv(imode,IS_GROUP,ES_USER); /* set group access as well */
  else e = ItoEPriv(imode,IS_WORLD,ES_USER); /* user rights = world */
  
  e |= (ItoEPriv(imode,IS_OWNER,ES_OWNER) | /* other access */
	ItoEPriv(imode,IS_GROUP,ES_GROUP) |
	ItoEPriv(imode,IS_WORLD,ES_WORLD));

  if (DBOSI)
    printf("ItoEAccess: I=o%o, E=0x%x\n",imode,e);
  return(e);
}

export OSErr
OSAccess(idir,fn,mode)
IDirP idir;
char *fn;
int mode;
{
  int imode = 0;
  int err;
  char path[MAXPATHLEN];

  if (mode & OFK_MWR)
    imode |= W_OK;
  if (mode & OFK_MRD)
    imode |= R_OK;

  OSfname(path,idir,fn,F_DATA);		/* create unix style filename */
  err = access(path,imode);
  if (err == 0)
    return(noErr);
  switch (errno) {
  case ENOTDIR:
    return(aeDirNotFound);
  case ENOENT:
    return(aeObjectNotFound);
  case EACCES:
    return(aeAccessDenied);
  }
  return(aeAccessDenied);
}

/*
 * OSErr OSVolInfo(VolPtr v)
 *
 * Update volume information for volume pointed to by v.
 * Returns error if path does not exist.
 *
 * Update:
 *
 *  v_attr	Read only flag (V_RONLY)
 *  v_cdate	creation date (of v_path)
 *  v_mdate	modification date (of v_path)
 *  v_size 	size of volume in bytes ????
 *  v_free	free bytes on volume  ????
 *
 */

export OSErr
OSVolInfo(path,v, bitmap)
char *path;
VolPtr v;
word bitmap;			/* bitmap of info needed */
{
  struct stat buf;
#ifdef USEQUOTA
  struct dqblk dqblk;
#endif
#ifdef USEUSTAT
  struct ustat ubuf;
#endif
#ifdef USESTATFS
  struct statfs fsbuf;
#endif
  time_t sometime;

  if (stat(path,&buf) != 0)	/* directory exists? */
    return(aeObjectNotFound);	/* no... */
  if (!(S_ISDIR(buf.st_mode)))	/* check for directory */  
    return(aeParamErr);		/* not a directory! */
    
  if (bitmap & VP_CDATE) {
    /* pick out the earliest date for mac creation time */
    sometime = (buf.st_mtime > buf.st_ctime) ? buf.st_ctime : buf.st_mtime;
    v->v_cdate = sometime > buf.st_atime ? buf.st_atime : sometime;
  }
  if (bitmap & VP_MDATE) {
    /* pick the later of status change and modification for */
    /* mac modified */
    v->v_mdate = (buf.st_mtime < buf.st_ctime) ? buf.st_ctime : buf.st_mtime;
  }
#ifdef notdef
  /* had it as v->v_mdate -- probably the reason we ifdef'ed it out */
  if (bitmap & VP_BDATE)
    v->v_bdate = 0;
#endif

  if (bitmap & VP_ATTR) {
#ifdef notdef
    /* don't really want this - causes problems when you have access */
    /* futher down the tree - should only come up locked iff the */
    /* tree is write locked (no (easy) way to tell because of symbolic */
    /* links and mount points) */
    if (access(v->v_path,W_OK) != 0) 	/* ok to write into directory? */
      v->v_attr |= V_RONLY;	/* no, set read-only */
    else
#endif
      v->v_attr &= ~V_RONLY;	/* clear read-only */
  }

  if ((bitmap & (VP_FREE|VP_SIZE)) == 0)
    return(noErr);		/* naught else to do */

  /* All the following is good and fine unless: (a) the volume */
  /* has symlinks off the volume or (b) there are mounted file systems */
  /* under the mac volume.  In those cases, returning this information */
  /* could be damaging because some "good" programs base what they */
  /* can do on the volume information --- but people really like this */
  /* information!!! so we take the trade off (even though it is one */
  /* of the most system dependent parts of aufs) */

  /* don't know how to calculate disk free and size without being su */
  /* (in the general case) */

  /* hard coded values of 512 for quotas and 1024 for ustat are bad */
  /* where are the "real" numbers defined? */

  /* careful on the ordering - these must go last and if you can possibly */
  /* define more than one then you must define in order you wish */
#ifdef USEQUOTA
  if (quota(Q_GETDLIM, usruid, buf.st_dev, &dqblk) == 0 &&
      dqblk.dqb_bhardlimit != 0) { /* make sure not unlimited */
    v->v_size = dqblk.dqb_bhardlimit*512;
    v->v_free = (dqblk.dqb_bhardlimit-dqblk.dqb_curblocks)*512;
    return(noErr);
  }
#endif
#ifdef USEGETMNT
  if (ultrix_volinfo(path, &buf, v) == noErr)
    return(noErr);
#endif
#ifdef USEUSTAT
  if (ustat(buf.st_dev, &ubuf) >= 0) {
    if (VP_SIZE & bitmap) {
      /* should do something better here */
      v->v_size = ubuf.f_tfree*1024;
    }
    v->v_free = ubuf.f_tfree*1024;
    return(noErr);
  }
#endif
#ifdef USESTATFS
  if (statfs(path, &fsbuf) >= 0) {
    v->v_size = fsbuf.f_bsize * fsbuf.f_blocks;
    /* limiting factor: cannot report on overutilization of a volume */
    v->v_free = (fsbuf.f_bavail < 0) ? 0 : fsbuf.f_bsize * fsbuf.f_bavail;
    return(noErr);
  }
#endif
  v->v_size = 0x1000000;		/* some random number */
  v->v_free = 0x1000000;		/* same random number */
  return(noErr);			/* all ok */
}

#ifdef USEGETMNT
/* get info on path using buf when there is ambiguity (ultrix 2.0 or before) */
/* fill in info on v */
private OSErr
ultrix_volinfo(path,buf,v)
char *path;
struct stat *buf;
VolPtr v;
{
  int context = 0;
  int i, num;
  u_int vfree;
  /* multiple buffers are wasteful when using Ultrix 2.2, but code is */
  /* good enough */
  struct fs_data buffer[NUMGETMNTBUF];
  struct fs_data *bp;
  int nbytes = sizeof(struct fs_data)*NUMGETMNTBUF;

  if (!oldgetmnt) {
    /* Ultrix 2.2 */
    /* use nostat_one -- we don't want to hang on nfs */
    /* return none if error or (0) - not found */
    nbytes = sizeof(struct fs_data);
    if ((num=getmnt(&context, buffer, nbytes, NOSTAT_ONE, path)) <= 0)
      return(-1);
    bp = buffer;
  } else {
    while ((num=getmnt(&context, buffer, nbytes)) > 0) {
      for (i = 0, bp = buffer; i < num; i++, bp++)
	if (buf->st_dev == bp->fd_req.dev)
	  goto found;
      /* context is set to zero if last call to getmnt returned */
      /* all file systems */
      if (context == 0)
	return(-1);
    }
    /* should never reach here, means getmnt returned an error */
    return(-1);			/* nothing found */
  }
found:
  /* "overflow" space if any - looks used */
  v->v_size = bp->fd_req.btot * 1024;
  /* bfreen must be "good" in that it cannot go below 0 when */
  /* out of space -- it is unsigned! */
  v->v_free = ((getuid() == 0) ? bp->fd_req.bfree : bp->fd_req.bfreen) * 1024;
  return(noErr);
}
#endif /* GETMNT */

#ifdef USESUNQUOTA

#ifndef MAXUFSMOUNTED
# ifdef NMOUNT
#  define MAXUFSMOUNTED NMOUNT	/* NMOUNT in param.h */
# else
#  define MAXUFSMOUNTED 32		/* arb. value */
# endif
#endif

private struct mount_points {
  char *mp_fsname;		/* name of "block" device */
  dev_t mp_dev;			/* device number */
} mount_points[MAXUFSMOUNTED];

private int num_mount_points = -1;

private struct mount_points *
find_mount_spec_intable(dev)
dev_t dev;
{
  int i;
  struct mount_points *mp;

  for (i = 0, mp = mount_points; i < num_mount_points; i++, mp++) {
    if (dev == mp->mp_dev)
      return(mp);
  }
  return(NULL);
}

/*
 * find the block special device...
 * try updating mount point table if possible
 *
 * returns name if it can, null o.w.
*/
private char *
find_mount_spec(dev)
{
  struct mount_points *mp;
  struct mntent *mtent;
  struct stat sbuf;
  FILE *mt;
  char *strdup();

  if ((mp = find_mount_spec_intable(dev)) != NULL) /* try once */
    return(mp->mp_fsname);
  
  if (num_mount_points < MAXUFSMOUNTED) {
    /* check to see if mounted */
    if ((mt = setmntent("/etc/mtab", "r")) == NULL) {
      if (DBOSI)
	log("/etc/mtab is read protected or nonexistant");
      return(NULL);
    }
    mp = NULL;
    while ((mtent = getmntent(mt)) != NULL) {
      if (stat(mtent->mnt_fsname, &sbuf) < 0)
	continue;
      if (dev != sbuf.st_rdev)
	continue;
      mp = mount_points+num_mount_points;
      num_mount_points++;
      mp->mp_fsname = strdup(mtent->mnt_fsname);
      mp->mp_dev = sbuf.st_rdev;
      break;
    }
    endmntent(mt);
  } else {
    /* table would overflow, try rebuilding */
    if (build_mount_table() < 0)	/* try rebuilding table */
      return(NULL);
    mp = find_mount_spec_intable(dev);
  }
  if (mp)
    return(mp->mp_fsname);
  if (DBOSI)
    log("Cannot find file system on device (%d,%d)\n", major(dev),minor(dev)); 
  return(NULL);			/* total failure */
}

/*
 * build a table of the various mounted file systems
 * using getmntent.  We want to use /etc/mtab, but
 * if we can't, then we use /etc/fstab to get some 
 * information anyway if this is the first time around (to
 * prevent constant rereads since /etc/fstab is pretty constant)
 *
*/
int
build_mount_table()
{
  FILE *mt;
  struct stat sbuf;
  struct mntent *mtent;
  struct mount_points *mp;
  char *strdup();

  if ((mt = setmntent("/etc/mtab", "r")) == NULL) {
    if (DBOSI)
      log("/etc/mtab is read protected or nonexistant");
    if (num_mount_points != 0)
      return(-1);
    if ((mt = setmntent("/etc/fstab", "r")) == NULL) {
      if (DBOSI)
	log("/etc/fstab is read protected or nonexistant");
      return(-1);
    }
  }

  /* free old info */
  if (num_mount_points) {
    for (mp = mount_points ; num_mount_points > 0; num_mount_points--, mp++)
      if (mp->mp_fsname)
	free(mp->mp_fsname);
  }
  num_mount_points = 0;	/* paranoia */

  mp  = mount_points;
  while ((mtent = getmntent(mt)) != NULL) {
    if (stat(mtent->mnt_fsname, &sbuf) < 0)
      continue;
    mp->mp_fsname = strdup(mtent->mnt_fsname);
    mp->mp_dev = sbuf.st_rdev;
    num_mount_points++, mp++;
    if (num_mount_points > MAXUFSMOUNTED) {
      log("Grrr.. too many mounted file systems for build_mount_table");
      break;
    }
  }
  endmntent(mt);
  if (num_mount_points == 0 && DBOSI)
    log("No mount points can be found");
  return(0);
}

/*
 * SunOS doesn't use the Melbourne quota system - it has its own
 * private one that uses quotactl instead of quota.  We emulate
 * quota here...
 *
*/
int
quota(cmd, uid, arg, addr)
int cmd, uid, arg;
caddr_t addr;
{
  char *fsname;

  switch (cmd) {
  case Q_QUOTAON:
  case Q_QUOTAOFF:
  case Q_SETQUOTA:
  case Q_GETQUOTA:
  case Q_SETQLIM:
  case Q_SYNC:
    break;
  default:
    errno = EINVAL;
    return(-1);
  }
  
  if ((fsname = find_mount_spec(arg)) == NULL) {
    errno = EPERM;
    return(-1);
  }
  return(quotactl(cmd, fsname, uid, addr));
}
#endif

export OSErr
OSCopyFile(spdir,sfile,dpdir,dfile)
IDirP spdir,dpdir;			/* source and destination parents */
char *sfile,*dfile;			/* source and destination file names */
{
  char spath[MAXPATHLEN];
  char dpath[MAXPATHLEN];
  struct stat stb;
  int mo;
  int err;

  OSfname(spath,spdir,sfile,F_DATA);	/* create unix style name for data */
  OSfname(dpath,dpdir,dfile,F_DATA);	/*  same for destination */

  if (DBOSI)
    printf("OSCopyFile: %s -> %s\n",spath,dpath);

  err = unix_stat(dpath,&stb);		/* see if destination exists... */
  if (err == noErr)			/* yes... it does */
    return(aeObjectExists);		/* return error... */

  /* get info on parent of destination */
  if ((err = unix_stat(pathstr(dpdir), &stb)) != noErr)
    return(err);
  mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid);
  err = os_copy(spath,dpath, mo);

  if (err != noErr && DBOSI)
    printf("OSCopyFile: DATA copy failed %s\n",afperr(err));

  if (err != noErr)
    return(err);

  OSfname(spath,spdir,sfile,F_RSRC);	/* create unix style name for rsrc */
  OSfname(dpath,dpdir,dfile,F_RSRC);	/*  same for destination */
  err = os_copy(spath,dpath,mo); /* do the copy... */
  /* allow object not found */
  if (err != noErr && err != aeObjectNotFound) { /* if failure.... */
    if (DBOSI)
      printf("OSCopyFile: RSRC copy failed %s\n",afperr(err));
    (void) os_delete(dpdir,dfile,F_DATA); /*  cleanup dest files */
    return(err);
  }

  OSfname(spath,spdir,sfile,F_FNDR);	/* create unix style name for fndr */
  OSfname(dpath,dpdir,dfile,F_FNDR);	/*  same for destination */
  err = os_copy(spath,dpath,mo); /* do the copy... */
  /* allow object not found */
  if (err != noErr && err != aeObjectNotFound) {
    if (DBOSI)
      printf("OSCopyFile: FNDR copy failed %s\n",afperr(err));
    (void) os_delete(dpdir,dfile,F_DATA); /* cleanup dest files */
    (void) os_delete(dpdir,dfile,F_RSRC); /* .... */
    return(err);
  }
  OSSetMacFileName(dpdir, dfile);

  FModified(dpdir, dfile);	/* mark as modified */
#ifdef notdef
  EModified(dpdir);			/* destination dir is modified */
#endif
  return(noErr);
}

/*
 * OSErr os_copy(char *from, char *to, mo)
 *
 * Copy the file from, to the file to.  If "to" already exists then
 * overwrite.  File is created with mode "mo".
 *
 * Should probably lock the file!
 *
 */

private OSErr
os_copy(from, to, mo)
char *from,*to;
{
  int sfd,dfd,err;
  char iobuf[IOBSIZE];
  struct stat sstb;
  struct stat dstb;
  int i;

  if ((err = unix_stat(from,&sstb)) != noErr)
    return(err);

  if (S_ISDIR(sstb.st_mode)) {		/* dirs not allowed... */
    printf("os_copy: source is directory\n");
    return(aeObjectTypeErr);
  }

  if ((err=unix_open(from,0,&sfd)) != noErr) /* open source file */
    return(err);

  err = unix_stat(to,&dstb);	/* check on destination */
  if (err == noErr) {		/* file is there */
    if (sstb.st_dev == dstb.st_dev && sstb.st_ino == dstb.st_ino) {
      if (DBOSI)
	printf("os_copy: cannot copy to self\n");
      unix_close(sfd);
      return(aeParamErr);
    }
  } /* else ignore error from stat */

  err = unix_createo(to,TRUE,mo,&dfd);
  if (err != noErr) {
    printf("os_copy; create failed\n");
    (void) unix_close(sfd);
    if (err == aeObjectNotFound)	/* no destination? */
      err = aeParamErr;			/* then return this */
    return(err);
  }

  /* copy loop */
  for (i=0;;i++) {
    register int len;

    len = read(sfd,iobuf,IOBSIZE);
    if (len == 0)
      break;

    if (len < 0) {
      printf("os_copy: error during read\n");
      (void) unix_close(sfd);
      (void) unix_close(dfd);
      return(aeParamErr);		/* disk error */
    }

    if (write(dfd,iobuf,len) != len) {
      err = errno;			/* preserve error code */
      if (DBOSI)
	printf("os_copy: error on write %s\n",syserr());
      (void) unix_close(sfd);
      (void) unix_close(dfd);
      return(ItoEErr(err));
    }
    if (i % 5)
      abSleep(0, TRUE);
  }
  (void) unix_close(sfd);
  (void) unix_close(dfd);
  return(noErr);
}

export OSErr
OSCreateFile(pdir,file,delf)
IDirP pdir;
char *file;
int delf;			/* if want to delete existing file */
{
  char path[MAXPATHLEN];
  int err,derr,rerr,cerr,mo;
  struct stat stb;

  OSfname(path,pdir,file,F_DATA);	/* create data file name */

  if (DBOSI)
    printf("OSCreateFile: creating %s with %s\n",path,
	   (delf) ? "OverWrite" : "No OverWrite");
  
  err = unix_stat(pathstr(pdir),&stb);
  if (err != noErr)
    return(err);
  mo = filemode(stb.st_mode, stb.st_uid, stb.st_gid);

  /* should never get aeObjectExists if delf was true */
  OSfname(path,pdir,file,F_DATA);	/* create data fork */
  derr = unix_create(path,delf,mo);	/* using user delete flag */
  if (derr != noErr && derr != aeObjectExists) {
    if (DBOSI)
      printf("OSCreateFile: DATA fork create failed\n");
    /* previously under a conditional on delf, but not necessary */
    /* anymore because we don't get here if the object was already there */
    OSfname(path,pdir,file,F_DATA);
    cerr = unix_unlink(path);		/* clean up... */
    if (cerr != noErr && DBOSI)
      printf("OSCreateFile: cleanup failed\n");
    return(derr);
  }

  OSfname(path,pdir,file,F_RSRC);	/* try creating resource fork */
  rerr = unix_create(path,delf,mo);	/* ... */
  if (rerr != noErr && rerr != aeObjectExists && rerr != aeObjectNotFound) {
    if (DBOSI)
      printf("OSCreateFile: RSRC create failed\n");
    /* previously under a conditional on delf, but not necessary */
    /* anymore because we don't get here if the object was already there */
    OSfname(path,pdir,file,F_RSRC);
    cerr = unix_unlink(path);		/* clean up... */
    if (cerr != noErr && DBOSI)
      printf("OSCreateFile: cleanup failed\n");
    /* should we clean up data fork? */
    return(rerr);
  }

  OSfname(path,pdir,file,F_FNDR);	/* create finder fork */
  err = unix_create(path,TRUE,mo);
  /* ignore error here - exactly what should be done? */

  /* at this point, each had better be: aeObjectExists or noErr */
  if (rerr == aeObjectExists || derr == aeObjectExists)
    return(aeObjectExists);
  EModified(pdir);
  return(noErr);
}
  

export OSErr
OSOpenFork(pdir,file,mode,typ,fhdl)
IDirP pdir;				/* parent directory */
char *file;
word mode;
int typ,*fhdl;
{
  char path[MAXPATHLEN];
  char *ms;
  int mo;

  OSfname(path,pdir,file,typ);		/* expand name */

  if ((mode & ~(OFK_MRD|OFK_MWR)) != 0)
    if (DBOSI)
      printf("OSOpenFork: open mode bits are octal %o\n",mode);

  if ((mode & (OFK_MRD|OFK_MWR)) == (OFK_MRD|OFK_MWR)) {
    ms = "Read/Write";
    mo = O_RDWR;
  } else if (mode & OFK_MWR) {
    ms = "Write";
    mo = O_WRONLY;
  } else if (mode & OFK_MRD) {
    ms = "Read";
    mo = O_RDONLY;
  }

  if (DBOSI) 
    printf("OSOpenFork: Opening %s for %s\n",path,ms);

  return(unix_open(path,mo,fhdl));
}


private char *guestname = NULL;

export boolean
setguestid(nam)
char *nam;
{
  struct passwd *p;
  if ((p = getpwnam(nam)) == NULL) {
    log("Guest id %s NOT IN PASSWORD FILE",nam);
    log("Guest id %s NOT IN PASSWORD FILE",nam);
    return(FALSE);
  }
  if (p->pw_uid == 0) {
    log("Guest id %s is a root id!  NOT ALLOWED!", nam);
    log("Guest id %s is a root id!  NOT ALLOWED!", nam);
    log("Guest id %s is a root id!  NOT ALLOWED!", nam);
    return(FALSE);
  }
  if (p->pw_gid == 0) {
    log("Guest id %s is in group 0.  BE WARNED!", nam);
    log("Guest id %s is in group 0.  BE WARNED!", nam);
    log("Guest id %s is in group 0.  BE WARNED!", nam);
  }
  log("Guest id is %s, uid %d, primary gid %d",nam, p->pw_uid, p->pw_gid);
  guestname = nam;
  return(TRUE);
}

private boolean apasswdfile = FALSE;

export boolean
setpasswdfile(pw)
char *pw;
{

  if (desinit(0) < 0) {
    log("error: no des routines, so no aufs password file used");
    return(FALSE);
  }   
  apasswdfile = init_aufs_passwd(pw);
  return(apasswdfile);
}


export OSErr
OSLoginRand(nam)
char *nam;
{
  if (is_aufs_user(nam))
    return(noErr);
  return(aeParamErr);
}

export OSErr
OSLogin(nam,pwd,pwdother,uam)
char *nam,*pwd;
byte *pwdother;
int uam;
{
  struct passwd *p;
  boolean safedebug;
  byte encrypted[8];		/* 64 bits */
  byte passkey[8];		/* password is 8 bytes max */
  char *pass;

  safedebug = (DBOSI || (getuid() != 0 && geteuid() != 0));

  log("Login requested for %s (we are %srunning as root)",
      (uam == UAM_ANON) ? "<anonymous>" : nam,
      (getuid() == 0 || geteuid() == 0) ? "" : "not ");

  switch (uam) {
  case UAM_RANDNUM:
    if (!apasswdfile)
      return(aeBadUAM);
    if ((pass = user_aufs_passwd(nam)) == NULL)
      return(aeUserNotAuth);
    bzero(passkey,sizeof(passkey));		/* make sure zero */
    strncpy(passkey, pass, 8);
    dessetkey(passkey);
    /* copy the data to be encrypted */
    bcopy(pwdother, encrypted, sizeof(encrypted));
    endes(encrypted);
    if (bcmp(encrypted, pwd, 8) != 0)
      return(aeUserNotAuth);
    if ((p = aufs_unix_user(nam)) == NULL)
      return(aeUserNotAuth);
    usrgid = p->pw_gid;
    usruid = p->pw_uid;
    usrnam = (char *) malloc(strlen(p->pw_name)+1);
    strcpy(usrnam,p->pw_name);
    usrdir = (char *) malloc(strlen(p->pw_dir)+1);
    strcpy(usrdir,p->pw_dir);
    break;
  case UAM_ANON:
    if (guestname == NULL)
      return(aeParamErr);
    p = (struct passwd *)getpwnam(guestname);
    if (p == NILPWD) {
      log("Login: guest user not valid %s",guestname);
      return(aeParamErr);			/* unknown user */
    }  
    usrgid = p->pw_gid;
    usruid = p->pw_uid;
    usrnam = (char *) malloc(strlen(guestname)+1);
    strcpy(usrnam,guestname);
    usrdir = NULL;
    break;
  case UAM_CLEAR:
    if (!apasswdfile) {
      p = (struct passwd *) getpwnam(nam); /* user name */
      if (p == NILPWD) {
	log("Login: Unknown user %s",nam);
	return(aeParamErr);			/* unknown user */
      }  
      if (strcmp(crypt(pwd,p->pw_passwd),p->pw_passwd) != 0) {
	log("Login: Incorrect password for user %s",nam);
	if (!safedebug)
	  return(aeUserNotAuth);
      }
    } else {
      if ((p = aufs_unix_user(nam)) == NULL)
	return(aeUserNotAuth);
      if ((pass = user_aufs_passwd(nam)) == NULL)
	return(aeUserNotAuth);
      if (strcmp(pass,pwd) != 0)
	return(aeUserNotAuth);
    }
  
    usrgid = p->pw_gid;
    usruid = p->pw_uid;
    usrnam = (char *) malloc(strlen(p->pw_name)+1);
    strcpy(usrnam,p->pw_name);
    usrdir = (char *) malloc(strlen(p->pw_dir)+1);
    strcpy(usrdir,p->pw_dir);
    break;
  }

  if (!safedebug && setgid(usrgid) != 0) {
    log("Login: setgid failed for %s because %s",nam,syserr());
    return(aeUserNotAuth);
  }
  if ((getuid() == 0 || geteuid() == 0) && initgroups(usrnam, usrgid) < 0)
    log("OSLogin: initgroups failed for %s!: reason: %s",syserr(),usrnam);
  if ((ngroups = dogetgroups()) < 0) {
    log("OSLogin: getgroups failed for %s!: reason: %s",syserr(), usrnam);
    ngroups = 0;
  }

  if (!safedebug && setuid(usruid) != 0) {
    log("Login: setuid failed for %s because %s",nam,syserr());
    return(aeUserNotAuth);	/* or something */
  }

  log("Login: user %s, home directory %s",
      usrnam, usrdir == NULL ? "none" : usrdir);

  if (usrdir != NULL)
    VInit(usrnam,usrdir);	/* initialize volume stuff */
  return(noErr);
}

/*
 * change password.
 *
 * nice idea, but not really workable right now.
 * best we could do is fork passwd
 * AFP2.0
 *
*/
OSChangePassword(nam, pwdold, pwdnew, uam)
char *nam;
byte *pwdold;
byte *pwdnew;
int uam;
{
  return(aeCallNotSupported);
}

export word 
OSRandom()
{
  static time_t t = 0;

  if (t == 0) {
    time(&t);
#ifdef USERAND
    srand(t);
#else
    srandom(t);
#endif
  }
#ifdef USERAND
  return((word) rand());
#else
  return((word) random());
#endif
}

sdword
CurTime()
{
  return(time(0));
}

/*
 * char *tilde(char *s)
 *
 * Expands a path starting with tilde, the same as the shell.
 * Returns the expanded path.
 *
 */
export char *
tilde(s)
char *s;
{
  static char path[MAXPATHLEN];
  char *sp,*logdir();
  
  if (*s != '~')			/* start with tilde? */
    return(s);				/* no, return original */
  s++;					/* skip over tilde */
  if (*s == '\0')			/* if nothing more, return */
    return(usrdir);

  if (*s == '/') {			/* case of ~/ */
    strcpy(path,usrdir);		/* use user's dir */
    strcat(path,s);			/*  and then the remainder */
    return(path);			/* return that */
  }

  if ((sp = index(s,'/')) == NULL)	/* check for slash */
    return(logdir(s));			/*  return ~john expanded */

  *sp = '\0';				/* otherwise tie off ~bill/mac */
  strcpy(path,logdir(s));		/* copy in the expanded ~bill */
  *sp = '/';				/* ... put back slash */
  strcat(path,sp);			/* append the remainder */
  return(path);				/* and return it */
}
  
export char *
logdir(user)
char *user;
{
  struct passwd *p;

  if (strcmp(user,usrnam) == 0)
    return(usrdir);		/* already know logged in user dir */
    
  p = (struct passwd *) getpwnam(user);
  if (p != NILPWD)
    return(p->pw_dir);
  return(NULL);
}
  
private OSErr
unix_rmdir(path)
char *path;
{
  if (DBUNX)
    printf("unix_rmdir: path=%s\n",path);

  if (rmdir(path) == 0)			/* and try to delete it */
    return(noErr);

  if (DBUNX)
    printf("unix_rmdir: failed %s\n",syserr());

  return(ItoEErr(errno));
}

private OSErr
unix_unlink(path)
char *path;
{
  if (DBUNX)
    printf("unix_unlink: path=%s\n",path);

  if (unlink(path) == 0)	/* remove the file */
    return(noErr);		/* no error */

  if (DBUNX)
    printf("unix_unlink: failed %s\n",syserr());

  return(ItoEErr(errno));
}  

private OSErr
unix_rename(from,to)
char *from,*to;
{
  if (DBUNX)
    printf("unix_rename: from %s to %s\n",from,to);

#ifdef aux
  if (strcmp(from, to) == 0)
    return(noErr);
#endif
  if (rename(from,to) == 0)
    return(noErr);

  if (DBUNX)
    printf("unix_rename: failed %s\n",syserr());

  return(ItoEErr(errno));
}

private OSErr
unix_open(path,mode,fd)
char *path;
int mode;
int *fd;
{

  *fd = open(path,mode);

  if (DBUNX)
    printf("unix_open: fd=%d, mode=%d, path=%s\n",*fd,mode,path);

  if ((*fd) > 0)
    return(noErr);

  if (DBUNX)
    printf("unix_open: failed %s\n",syserr());

  return(ItoEErr(errno));
}

private OSErr
unix_close(fd)
int fd;
{
  if (DBUNX)
    printf("unix_close: fd=%d\n",fd);

  if (close(fd) == 0)
    return(noErr);

  if (DBUNX)
    printf("unix_close: failed %s\n",syserr());

  return(ItoEErr(errno));		/* this would be a problem */
}

private OSErr
unix_mkdir(path,prot)
char *path;
int prot;				/* protection */
{
  if (DBUNX)
    printf("unix_mkdir: path = %s\n",path);

  if (mkdir(path,prot) == 0)
    return(noErr);

  if (DBUNX)
    printf("unix_mkdir: failed %s\n",syserr());

  return(ItoEErr(errno));
}

/*
 * OSErr unix_create(char *path, int delf, int mode)
 *
 * Create a file.
 *
 */

private OSErr
unix_create(path,delf,mode)
char *path;
int delf;
int mode;
{
  int fd,flg;

  if (DBUNX)
    printf("unix_create: delf=%d, mode=o%o, path=%s\n",delf,mode,path);

  flg = (delf) ? O_CREAT : O_CREAT | O_EXCL;

  if ((fd = open(path,flg,mode)) != -1) {
    (void) close(fd); 
    return(noErr);
  }

  if (DBUNX)
    printf("unix_create: failed %s\n",syserr());

  return(ItoEErr(errno));
}

/*
 * OSErr unix_createo(char *path, int delf, int mode, int *fd)
 *
 * Create a file and return the open file handle.
 *
 */

private OSErr
unix_createo(path,delf,mode,fd)
char *path;
int delf;
int mode;
int *fd;
{
  int flg;

  if (DBUNX)
    printf("unix_createo: delf=%d, path=%s\n",delf,path);

  flg = (delf) ? O_CREAT : O_CREAT | O_EXCL;
  flg |= O_RDWR;

  if ((*fd = open(path,flg,mode)) != -1)
    return(noErr);

  if (DBUNX)
    printf("unix_createo: failed %s\n",syserr());

  return(ItoEErr(errno));
}

private OSErr
unix_chown(path,own,grp)
char *path;
int own,grp;
{
  char gid[20];			/* big enough */
  int pid, npid;
  int status;
#ifndef USECHOWN
  struct stat stb;
  OSErr err;
#endif

  if (DBOSI) 
    printf("unix_chown: Attempting chown %s to owner %d, group %d\n",
	   path,own,grp);
#ifndef USECHOWN
  if (usruid != 0) {		/* not root, then do it hard way */
    if (DBOSI) 
      printf("unix_chown: skipping owern, chgrp %s to group %d\n",path,grp);
    if ((err = unix_stat(path, &stb)) != noErr)
      return(err);
    if (stb.st_gid == grp)	/* naught to do */
      return(noErr);
    sprintf(gid, "%d",grp);
    if ((pid=vfork()) == 0) {
      execl("/bin/chgrp","chgrp",gid,path, 0);
      _exit(1);			/* no false eofs */
    }
    while ((npid = wait(&status)) != pid) 
      /* NULL */;
    /* right half of status is non-zero if */
    /*  (a) stopped (&0xff == 0177) */
    /* or */
    /*  (b) signaled  (0x7f != 0) */
    /*  (c) coredumped (0x80 != 0) */
    if ((status & 0xff) != 0)
      return(aeAccessDenied);	/* oh well */
    /* retcode is leftmost 8 bits */
    if ((status>>8) != 0)
      return(aeAccessDenied);	/* oh well */
    return(noErr);
  }
#endif
  /* root can do what it pleases, so can any user on sysv */
  if (chown(path, own, grp) < 0)
    return(ItoEErr(errno));
  return(noErr);
}


private OSErr
unix_chmod(path,mode)
char *path;
u_short mode;
{
  if (DBUNX)
    printf("unix_chmod: mode=o%o path=%s\n",mode,path);

  if (chmod(path,(int) mode) == 0)
    return(noErr);

  if (DBUNX)
    printf("unix_chmod: failed %s\n",syserr());

  return(ItoEErr(errno));
}

private OSErr
unix_stat(path,stb)
char *path;
struct stat *stb;
{
  if (DBUNX)
    printf("unix_stat: path=%s\n",path);

  if (stat(path,stb) == 0)
    return(noErr);

  if (DBUNX)
    printf("unix_stat: failed %s\n",syserr());

  return(ItoEErr(errno));
}

/*
 * figure out the mode a file should have based on the uid, gid, and mode
 * of its parents.  Mainly for drop folders.
 *
 * really shouldn't have to do this -- instead change the owner
 * of the file -- however: (a) bsd doesn't allow and (b) must do after
 * all file operations because we don't have handle -- mucho work --
 * if we could.
 *
*/
private int
filemode(mode, uid, gid)
int mode, uid, gid;
{
  int mo = mode & 0777;		/* convert st_mode to mode */
  if (uid != usruid) {
    /* check for conditions that would mean drop folder for us */
    /* (but, don't accept a drop folder on basis of group that is */
    /* world viewable even though it really is a drop folder for us) */
    if ((mo & 04) == 0 && (mo & 040) == 0 && OSInGroup(gid))
      mo |= 0666;		/* let everyone else read/write */
    /* We need to do this because the poor person who get's the file */
    /* can't do anything with it otherwise */
  }
  return(mo);
}

private char *
syserr()
{
  extern char *sys_errlist[];
  extern int sys_nerr;
  static char buf[50];
  int serrno;

  serrno = errno;
  if (serrno < 0 || serrno > sys_nerr) {
    sprintf(buf,"Unknown error %d",serrno);
    return(buf);
  }
  return(sys_errlist[serrno]);
}

private OSErr
ItoEErr(num)
int num;
{
  switch (num) {
  case EPERM:				/* Not owner */
    return(aeAccessDenied);
  case ENOENT:				/* No such file or directory */
    return(aeObjectNotFound);
  case EACCES:				/* Permission denied  */
    return(aeAccessDenied);
  case EEXIST:				/* File exists */
    return(aeObjectExists);
  case ENOTDIR:				/* Not a directory */
    return(aeDirNotFound);
  case EISDIR:				/* Is a directory */
    return(aeObjectTypeErr);
  case ENFILE:				/* File table overflow */
    return(aeDiskFull);
  case EMFILE:				/* Too many files open */
    return(aeTooManyFilesOpen);
  case ETXTBSY:				/* Text file busy */
    return(aeFileBusy);
  case ENOSPC:				/* No space left on device */
    return(aeDiskFull);
  case EROFS:				/* read only file system */
    return(aeAccessDenied);
  case ENOTEMPTY:
    return(aeDirNotEmpty);
#ifdef EDQUOT
  case EDQUOT:
    return(aeDiskFull);
#endif EDQUOT
  default:
    if (DBUNX)
      printf("ItoEErr: Unknown unix error code %d\n",errno);
    return(aeMiscErr);
  }
}

  

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.