MODULE PFILE
	(
	 MAIN = PFILE,
	 %TITLE'Second-level file protection'
	 IDENT = '1-2.0'
	) =

 BEGIN
  !++
  ! FACILITY:		User/management utilities
  !
  ! ABSTRACT:
  !
  !	This program allows files to be protected in such a way as to be able
  !	to circumvent even BYPASS.
  !
  ! ENVIRONMENT:	User and kernel mode
  !
  ! AUTHOR:		Ken A L Coar
  !
  ! MODIFIED BY:
  !
  !	KLC0116	Ken Coar	 2-JAN-1986 08:26
  !		Added documentation, cleaned up a little and made pretty
  !		for distribution. Fixed some more problems with wildcarding
  !		and related-file processing.
  !
  !	KLC0215	Ken Coar	23-FEB-1984
  !		Added wildcard processing and /LOG qualifier.
  !
  !	KLC0019	Ken Coar	15-MAR-1985 08:22
  !		Fixed RMS$_VER error (NAM blocks were overwriting themselves).
  !--

  %SBTTL'Declarations'

  !
  ! SWITCHES:
  !

  SWITCHES
   ADDRESSING_MODE (EXTERNAL = GENERAL, NONEXTERNAL = WORD_RELATIVE);

  !
  ! LINKAGES:
  !
  !	NONE.
  !

  !
  ! INCLUDE FILES:
  !

  LIBRARY 'SYS$LIBRARY:LIB';		! LIB plus STARLET
  LIBRARY 'KEN_LIBRARY:KENLIB';		! Local declarations

  !
  ! FORWARD ROUTINES:
  !
  !	NONE.
  !

  !
  ! EXTERNAL REFERENCES:
  !

  EXTERNAL ROUTINE
   CLI$GET_VALUE,
   CLI$PRESENT,
   ECS$MAXIMISE_ERROR,
   LIB$SYS_FAO,
   LIB_CLI_SIG_TO_RET;

  !
  ! MACROS:
  !
  !	NONE.
  !

  !
  ! EQUATED SYMBOLS:
  !
  !	NONE.
  !

  !
  ! FIELDS:
  !
  !	NONE.
  !

  !
  ! PSECTS:
  !

  RTL_PSECTS (FACILITY=LIB);

  !
  ! OWN STORAGE:
  !

  OWN
   MASK			:	BYTE INITIAL (0),
   NEWLEV		:	BYTE,
   DCHAN		:	WORD,
   ACLEVEL		:	BBLOCK [ATR$S_ACLEVEL],
   ATR			:	BBLOCK [12]
				PRESET(
					[ATR$W_SIZE] = ATR$S_ACLEVEL,
					[ATR$W_TYPE] = ATR$C_ACLEVEL,
					[ATR$L_ADDR] = ACLEVEL
				      ),
   FIB			:	BBLOCK [FIB$C_LENGTH]
				PRESET(
					[FIB$V_WRITE] = 1,
					[FIB$V_FINDFID] = 1
				      ),
   DFIB			:	DESCR (BUFFER=FIB,LENGTH=FIB$C_LENGTH),
   DFILE		:	DESCR (CLASS=DYNAMIC),
   DNEWPROT		:	DESCR (CLASS=DYNAMIC),
   DPROT		:	DESCR (CLASS=DYNAMIC),
   DEVNAM		:	BBLOCK [64],
   DDEVNAM		:	DESCR (BUFFER=DEVNAM),
   DVILST		:	$DVILST(
					(64, DEVNAM, DEVNAM, DDEVNAM)
				       ),
   RES			:	BBLOCK [NAM$C_MAXRSS],
   RRS			:	BBLOCK [NAM$C_MAXRSS],
   PES			:	BBLOCK [NAM$C_MAXRSS],
   PRS			:	BBLOCK [NAM$C_MAXRSS],
   DRS			:	DESCR (BUFFER=PRS),
   DES			:	DESCR (BUFFER=PES),
   RNAM			:	$NAM(
					ESS=NAM$C_MAXRSS,
					RSS=NAM$C_MAXRSS,
					ESA=RES,
					RSA=RRS
				    ),
   PNAM			:	$NAM(
					ESS=NAM$C_MAXRSS,
					RSS=NAM$C_MAXRSS,
					ESA=PES,
					RSA=PRS,
					RLF=RNAM
				    ),
   PFAB			:	$FAB(
					NAM=PNAM
				    ),
   RFAB			:	$FAB(
					FNA=PES,
					NAM=RNAM
				    );

  BIND
   KD_FACILITY		=	%ASCID'PFILE',
   KD_USER		=	%ASCID'U',
   KD_SUPER		=	%ASCID'S',
   KD_EXEC		=	%ASCID'E',
   KD_KERNEL		=	%ASCID'K';

%SBTTL'KERNEL_MODIFY - Actually modify the file'
  GLOBAL ROUTINE KERNEL_MODIFY =
  !++
  ! FUNCTIONAL DESCRIPTION:
  !
  !	This routine is called to modify the access bits in the file header.
  !	It is called in kernel mode so that it cannot fail to access the file
  !	in order to modify the attributes.
  !
  ! CALLING SEQUENCE:
  !
  !	ret-status.wlc.v = $CMKRNL (ROUTIN=KERNEL_MODIFY)
  !
  ! FORMAL PARAMETERS:
  !
  !	NONE.
  !
  ! IMPLICIT INPUTS:
  !
  !	MASK		byte containing ones for the fields to be changed.
  !			Used to erase the old settings in those fields.
  !	NEWLEV		byte containing new access code, ORed into fields
  !			cleared via MASK.
  !	DCHAN		word containing channel assigned to the device.
  !	DFIB		descriptor for File Information Block which is
  !			associated with the file.
  !
  ! IMPLICIT OUTPUTS:
  !
  !	NONE.
  !
  ! COMPLETION STATUS:
  !
  !	SS$_NORMAL		successful completion
  !	other			some error from $QIOW
  !
  ! SIDE EFFECTS:
  !
  !	File access protection changed to reflect new field settings.
  !
  !--
   BEGIN
    LOCAL
     IOSTAT		:	_IOSB,
     STATUS		:	LONG;
    STATUS = $QIOW(
			CHAN=.DCHAN,
			EFN=32,
			FUNC=(IO$_ACCESS OR IO$M_ACCESS),
			IOSB=IOSTAT,
			P1=DFIB,
			P5=ATR
		  );
    IF .STATUS THEN STATUS = .IOSTAT [IOSB_W_STATUS];
    %CHECK ();
    ACLEVEL = .ACLEVEL AND (NOT .MASK);
    ACLEVEL = .ACLEVEL OR .NEWLEV;
    STATUS = $QIOW(
			CHAN=.DCHAN,
			EFN=32,
			FUNC=IO$_DEACCESS,
			IOSB=IOSTAT,
			P1=DFIB,
			P5=ATR
		  );
    IF .STATUS THEN STATUS = .IOSTAT [IOSB_W_STATUS];
    RETURN .STATUS;
   END;

%SBTTL'SETMASK - Build mask from command line'
  GLOBAL ROUTINE SETMASK(
				VALUE,
				OFFSET
			) =
  !++
  ! FUNCTIONAL DESCRIPTION:
  !
  !	This routine builds the access mask at the specified offset, which
  !	determines the access type. The new mask for that type is ORed into the
  !	existing mask.
  !
  ! CALLING SEQUENCE:
  !
  !	ret-status.wlc.v = SETMASK (value.rb.v, offset.rb.v)
  !
  ! FORMAL PARAMETERS:
  !
  !	VALUE		byte character indicating the mode. Passed by value.
  !	OFFSET		byte count of bit positions to shift before inserting
  !			new mask value. Passed by value.
  !
  ! IMPLICIT INPUTS:
  !
  !	MASK		byte containing ones for those bits to be replaced.
  !	NEWLEV		byte containing the current setting of the mode
  !			protection.
  !
  ! IMPLICIT OUTPUTS:
  !
  !	MASK		new field inserted into mask specification.
  !	NEWLEV		new mode protection.
  !
  ! COMPLETION STATUS:
  !
  !	SS$_NORMAL		successful completion
  !	SS$_BADPARAM		user specified something other than U, S, E,
  !				or K
  !
  ! SIDE EFFECTS:
  !
  !	NONE.
  !
  !--
   BEGIN
    LOCAL
     NEWBITS		:	LONG;
    MASK = .MASK OR %B'11'^.OFFSET;
    SELECTONE .VALUE OF
     SET
      ['U']		:	NEWBITS = %B'00000000';
      ['S']		:	NEWBITS = %B'00000001';
      ['E']		:	NEWBITS = %B'00000010';
      ['K']		:	NEWBITS = %B'00000011';
      [OTHERWISE]	:	RETURN SS$_BADPARAM;
     TES;
    NEWBITS = .NEWBITS ^ .OFFSET;
    NEWLEV = .NEWLEV OR .NEWBITS;
    RETURN SS$_NORMAL;
   END;

%SBTTL'PFILE - Main program'
  GLOBAL ROUTINE PFILE =
  !++
  ! FUNCTIONAL DESCRIPTION:
  !
  !	This is the main program. It parses the command line, and then finds
  !	all files specified and applies the new protection to them.
  !
  ! CALLING SEQUENCE:
  !
  !	Called by CLI as main entry point.
  !
  ! FORMAL PARAMETERS:
  !
  !	NONE.
  !
  ! IMPLICIT INPUTS:
  !
  !	Called from DCL.
  !
  ! IMPLICIT OUTPUTS:
  !
  !	NONE.
  !
  ! COMPLETION STATUS:
  !
  !	SS$_NORMAL		successful completion
  !	SS$_BADPARAM		mode other than U, S, E, or K was specified
  !
  ! SIDE EFFECTS:
  !
  !	File protections changed.
  !
  !--
   BEGIN
    LOCAL
     LOGIT		:	BYTE INITIAL (0),
     FINALSTATUS	:	LONG INITIAL (SS$_NORMAL),
     IOSTAT		:	_IOSB,
     STATUS		:	LONG;
    ENABLE LIB_CLI_SIG_TO_RET;
    LOGIT = CLI$PRESENT (%ASCID'LOG');
    !
    !	Build new value for access protection mask.
    !
    IF CLI$GET_VALUE (%ASCID'READ', DNEWPROT) THEN
     %CHECK (SETMASK (CH$RCHAR (.DNEWPROT [DSC$A_POINTER]), 0));
    IF CLI$GET_VALUE (%ASCID'WRITE', DNEWPROT) THEN
     %CHECK (SETMASK (CH$RCHAR (.DNEWPROT [DSC$A_POINTER]), 2));
    IF CLI$GET_VALUE (%ASCID'EXECUTE', DNEWPROT) THEN
     %CHECK (SETMASK (CH$RCHAR (.DNEWPROT [DSC$A_POINTER]), 4));
    IF CLI$GET_VALUE (%ASCID'DELETE', DNEWPROT) THEN
     %CHECK (SETMASK (CH$RCHAR (.DNEWPROT [DSC$A_POINTER]), 6));
    !
    !	Process all files on the command line.
    !
    WHILE CLI$GET_VALUE (%ASCID'P1', DFILE) DO
     BEGIN
      PFAB [FAB$B_FNS] = .DFILE [DSC$W_LENGTH];
      PFAB [FAB$L_FNA] = .DFILE [DSC$A_POINTER];
      %CHECK ($PARSE (FAB=PFAB));
      DES [DSC$W_LENGTH] = .PNAM [NAM$B_ESL];
      RFAB [FAB$B_FNS] = .PNAM [NAM$B_ESL];
      $PARSE (FAB=RFAB);
      RNAM [NAM$B_RSL] = .RNAM [NAM$B_ESL];
      CH$MOVE (.RNAM [NAM$B_ESL], RES, RRS);
      STATUS = $GETDVIW (DEVNAM=DES, IOSB=IOSTAT, ITMLST=DVILST, EFN=32);
      IF .STATUS THEN STATUS = .IOSTAT [IOSB_W_STATUS];
      %CHECK ();
      %CHECK ($ASSIGN (CHAN=DCHAN, DEVNAM=DDEVNAM));
      !
      !	Process all files that match the current value of P1, which may contain
      !	wildcards.
      !
      WHILE (STATUS = $SEARCH (FAB=PFAB)) NEQ RMS$_NMF DO
       BEGIN
        LOCAL
	 MESSAGE		:	VECTOR [6, LONG]
					PRESET(
						[0] = 3,
						[1] = SHR$_PROTECTED OR 3^16,
						[2] = 2,
						[3] = DRS,
						[4] = DPROT
					      );
        DRS [DSC$W_LENGTH] = .PNAM [NAM$B_RSL];
        IF NOT .STATUS THEN
	 BEGIN
	  MESSAGE [1] = SHR$_OPENIN OR 3^16;
	  IF .DRS [DSC$W_LENGTH] EQL 0 THEN
	   IF .DES [DSC$W_LENGTH] NEQ 0
	    THEN MESSAGE [3] = DES
	    ELSE MESSAGE [3] = DFILE;
	  MESSAGE [4] = .PFAB [FAB$L_STS];
	  MESSAGE [5] = .PFAB [FAB$L_STV];
	  MESSAGE [0] = 5;
	  $PUTMSG (FACNAM=KD_FACILITY, MSGVEC=MESSAGE);
	  FINALSTATUS = ECS$MAXIMISE_ERROR (FINALSTATUS, MESSAGE [1]);
	  EXITLOOP;
	 END;
	!
	!	Go change the protection.
	!
        CH$MOVE (6, PNAM [NAM$W_DID], FIB [FIB$W_DID]);
        CH$MOVE (6, PNAM [NAM$W_FID], FIB [FIB$W_FID]);
        STATUS = $CMKRNL (ROUTIN=KERNEL_MODIFY);
	IF NOT .STATUS THEN
	 BEGIN
	  MESSAGE [1] = SHR$_OPENIN OR 3^16;
	  MESSAGE [4] = .STATUS;
	  MESSAGE [0] = 4;
	 END;
	!
	!	Write out a message if he requested it, or if it is incumbent
	!	upon us to do so implicitly because we got an error.
	!
	IF .LOGIT OR NOT .STATUS THEN
	 BEGIN
	  IF .STATUS THEN
	   BEGIN
	    !
	    !	If we're here without an error, it's because he asked us to
	    !	log all changes. Format the new protection mask for him.
	    !
	    BBLOCK [MESSAGE [1],STS$V_SEVERITY] = STS$K_SUCCESS;
	    LIB$SYS_FAO(
			%ASCID'(R:!AS,W:!AS,E:!AS,D:!AS)',
			0,
			DPROT,
			(SELECTONE .ACLEVEL <0,2,0> OF
			  SET
			   [0]		:	KD_USER;
			   [1]		:	KD_SUPER;
			   [2]		:	KD_EXEC;
			   [3]		:	KD_KERNEL;
			  TES),
			(SELECTONE .ACLEVEL <2,2,0> OF
			  SET
			   [0]		:	KD_USER;
			   [1]		:	KD_SUPER;
			   [2]		:	KD_EXEC;
			   [3]		:	KD_KERNEL;
			  TES),
			(SELECTONE .ACLEVEL <4,2,0> OF
			  SET
			   [0]		:	KD_USER;
			   [1]		:	KD_SUPER;
			   [2]		:	KD_EXEC;
			   [3]		:	KD_KERNEL;
			  TES),
			(SELECTONE .ACLEVEL <6,2,0> OF
			  SET
			   [0]		:	KD_USER;
			   [1]		:	KD_SUPER;
			   [2]		:	KD_EXEC;
			   [3]		:	KD_KERNEL;
			  TES)
		       );
	   END;
	  $PUTMSG (FACNAM=KD_FACILITY, MSGVEC=MESSAGE);
	 END;
	FINALSTATUS = ECS$MAXIMISE_ERROR (FINALSTATUS, STATUS);
       END;
      %CHECK ($DASSGN (CHAN=.DCHAN));
     END;
    !
    !	If we had any problems at all, we let him know by returning the worst
    !	error we encountered.
    !
    RETURN .FINALSTATUS OR STS$M_INHIB_MSG;
   END;

 END
ELUDOM
