]> git.michaelhowe.org Git - packages/o/openafs.git/commitdiff
jafs-library-20020725
authorManuel Pereira <mpereira@us.ibm.com>
Fri, 26 Jul 2002 06:33:59 +0000 (06:33 +0000)
committerDerrick Brashear <shadow@dementia.org>
Fri, 26 Jul 2002 06:33:59 +0000 (06:33 +0000)
Java API work

45 files changed:
Makefile.in
configure.in
src/JAVA/classes/ErrorMessages.properties [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/ACL.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/AFSException.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/AFSFileException.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/AFSSecurityException.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Cell.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/ErrorTable.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/File.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/FileInputStream.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/FileOutputStream.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Group.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Key.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/PTSEntry.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Partition.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Process.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Server.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Token.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/User.java [new file with mode: 0644]
src/JAVA/classes/org/openafs/jafs/Volume.java [new file with mode: 0644]
src/JAVA/libjafs/ACL.c [new file with mode: 0644]
src/JAVA/libjafs/AdminToken.c [new file with mode: 0644]
src/JAVA/libjafs/Cell.c [new file with mode: 0644]
src/JAVA/libjafs/Exceptions.h [new file with mode: 0644]
src/JAVA/libjafs/File.c [new file with mode: 0644]
src/JAVA/libjafs/FileInputStream.c [new file with mode: 0644]
src/JAVA/libjafs/FileOutputStream.c [new file with mode: 0644]
src/JAVA/libjafs/Group.c [new file with mode: 0644]
src/JAVA/libjafs/Internal.c [new file with mode: 0644]
src/JAVA/libjafs/Internal.h [new file with mode: 0644]
src/JAVA/libjafs/JAFS_README [new file with mode: 0644]
src/JAVA/libjafs/Key.c [new file with mode: 0644]
src/JAVA/libjafs/Makefile.in [new file with mode: 0644]
src/JAVA/libjafs/Partition.c [new file with mode: 0644]
src/JAVA/libjafs/Process.c [new file with mode: 0644]
src/JAVA/libjafs/Server.c [new file with mode: 0644]
src/JAVA/libjafs/User.c [new file with mode: 0644]
src/JAVA/libjafs/UserToken.c [new file with mode: 0644]
src/JAVA/libjafs/Volume.c [new file with mode: 0644]
src/JAVA/libjafs/etc/CacheConfig [new file with mode: 0644]
src/JAVA/libjafs/etc/CacheConfig.100MB [new file with mode: 0644]
src/JAVA/libjafs/etc/CacheConfig.40MB [new file with mode: 0644]
src/TechNotes-JavaAPI [new file with mode: 0644]
src/config/Makefile.config.in

index b025d5e593dd12eb937136a9fb8a4d628c460116..ac65599557a7f135dac17e371574906a4e33d237 100644 (file)
@@ -58,7 +58,7 @@ dest_nolibafs: all_nolibafs dest_dirs
 dest_only_libafs: only_libafs dest_dirs
        $(MAKE) build TARGET=libafs COMPILE_PART2B=dest
 
-${TOP_INCDIR} ${TOP_INCDIR}/afs ${TOP_LIBDIR}:
+${TOP_INCDIR} ${TOP_INCDIR}/afs ${TOP_LIBDIR} ${TOP_JLIBDIR}:
        mkdir -p $@
 
 install_dirs: force
@@ -434,6 +434,20 @@ libadmin: libafsauthent bozo
                echo Not building MT libadmin for ${SYS_NAME} ;; \
        esac
 
+libjafs: libadmin
+       case ${SYS_NAME} in \
+       alpha_dux*|sgi_*|sun4x_*|rs_aix*|*linux*|hp_ux110) \
+       ${COMPILE_PART1} JAVA/libjafs  ${COMPILE_PART2} ;; \
+       *) \
+               echo Not building MT libjafs for ${SYS_NAME} ;; \
+       esac
+
+libjafsadm: libjafs
+
+jafs: libjafs
+
+jafsadm: libjafsadm
+
 finale: project cmd comerr afsd allrcmds butc tbutc @ENABLE_KERNEL_MODULE@ libuafs audit kauth log package \
        ptserver scout bu_utils ubik uss bozo vfsck volser \
        venus update xstat afsmonitor dauth rxdebug libafsrpc \
@@ -547,6 +561,7 @@ clean2:
        -${COMPILE_PART1} libadmin/cfg ${COMPILE_CLEAN}
        -${COMPILE_PART1} libadmin/test ${COMPILE_CLEAN}
        -${COMPILE_PART1} libadmin/samples ${COMPILE_CLEAN}
+       -${COMPILE_PART1} JAVA/libjafs ${COMPILE_CLEAN}
        -${COMPILE_PART1} finale ${COMPILE_CLEAN}
        -${COMPILE_PART1} mpp ${COMPILE_CLEAN}
        -${COMPILE_PART1} package ${COMPILE_CLEAN}
@@ -560,7 +575,7 @@ clean2:
        -${COMPILE_PART1} libuafs ${COMPILE_CLEAN}
        -(cd src/libafs; /bin/rm -rf afs afsint config rx)
        -(cd src/libuafs; /bin/rm -rf afs afsint config rx des)
-       -/bin/rm -rf ${TOP_INCDIR} ${TOP_LIBDIR}
+       -/bin/rm -rf ${TOP_INCDIR} ${TOP_LIBDIR} ${TOP_JLIBDIR}
        -/bin/rm -rf libafs_tree ${SYS_NAME}
 
  
@@ -620,6 +635,7 @@ distclean: clean
        src/libadmin/samples/Makefile \
        src/libadmin/test/Makefile \
        src/libadmin/vos/Makefile \
+       src/JAVA/libjafs/Makefile \
        src/libafs/Makefile \
        src/libafs/Makefile.common \
        src/libafs/MakefileProto.${MKAFS_OSTYPE} \
@@ -763,3 +779,5 @@ rcp: project rsh inetd
 allrcmds: project rcp rlogind
 
 
+
+
index 6df54c199b03b860562aef081764e996a16e85ae..eb2723f399d44f3095dd42fe03f320dbe93205fb 100644 (file)
@@ -50,6 +50,7 @@ src/fsprobe/Makefile \
 src/ftpd43+/Makefile \
 src/gtx/Makefile \
 src/inetd/Makefile \
+src/JAVA/libjafs/Makefile \
 src/kauth/test/Makefile \
 src/kauth/Makefile \
 src/libacl/test/Makefile \
diff --git a/src/JAVA/classes/ErrorMessages.properties b/src/JAVA/classes/ErrorMessages.properties
new file mode 100644 (file)
index 0000000..2f20124
--- /dev/null
@@ -0,0 +1,910 @@
+#-----------------------------------------------------------
+# Custom UFiler Error Codes
+#-----------------------------------------------------------
+UNKNOWN         = Unknown error.
+SPECIAL_CASE    = Special case error.
+GENERAL_FAILURE = Operation returned unsuccessful.
+
+E1000 = Data requested does not exist or is null.
+E1001 = User session is invalid
+E1002 = Session has expired, please login again
+E1003 = Operation aborted
+E1004 = Operation was forced to abort
+
+#-----------------------------------------------------------
+# Standard UNIX Error Codes
+#-----------------------------------------------------------
+E1   = Operation not permitted
+E2   = No such file or directory
+E3   = No such process
+E4   = Interrupted system call
+E5   = I/O error
+E6   = No such device or address
+E7   = Arg list too long
+E8   = Exec format error
+E9   = Bad file number
+E10  = No child processes
+E11  = Try again
+E12  = Out of memory
+E13  = Permission denied
+E14  = Bad address
+E15  = Block device required
+E16  = Device or resource busy
+E17  = File exists
+E18  = Cross-device link
+E19  = No such device
+E20  = Not a directory
+E21  = Is a directory
+E22  = Invalid argument
+E23  = File table overflow
+E24  = Too many open files
+E25  = Not a typewriter
+E26  = Text file busy
+E27  = File too large
+E28  = No space left on device
+E29  = Illegal seek
+E30  = Read-only file system
+E31  = Too many links
+E32  = Broken pipe
+E33  = Math argument out of domain of func
+E34  = Math result not representable
+E35  = Resource deadlock would occur
+E36  = File name too long
+E37  = No record locks available
+E38  = Function not implemented
+E39  = Directory not empty
+E40  = Too many symbolic links encountered
+E41  = Operation would block
+E42  = No message of desired type
+E43  = Identifier removed
+E44  = Channel number out of range
+E45  = Level 2 not synchronized
+E46  = Level 3 halted
+E47  = Level 3 reset
+E48  = Link number out of range
+E49  = Protocol driver not attached
+E50  = No CSI structure available
+E51  = Level 2 halted
+E52  = Invalid exchange
+E53  = Invalid request descriptor
+E54  = Exchange full
+E55  = No anode
+E56  = Invalid request code
+E57  = Invalid slot
+E58  = Dead lock
+E59  = Bad font file format
+E60  = Device not a stream
+E61  = No data available
+E62  = Timer expired
+E63  = Out of streams resources
+E64  = Machine is not on the network
+E65  = Package not installed
+E66  = Object is remote
+E67  = Link has been severed
+E68  = Advertise error
+E69  = Srmount error
+E70  = Communication error on send
+E71  = Protocol error
+E72  = Multihop attempted
+E73  = RFS specific error
+E74  = Not a data message
+E75  = Value too large for defined data type
+E76  = Name not unique on network
+E77  = File descriptor in bad state
+E78  = Remote address changed
+E79  = Can not access a needed shared library
+E80  = Accessing a corrupted shared library
+E81  = .lib section in a.out corrupted
+E82  = Attempting to link in too many shared libraries
+E83  = Cannot exec a shared library directly
+E84  = Illegal byte sequence
+E85  = Interrupted system call should be restarted
+E86  = Streams pipe error
+E87  = Too many users
+E88  = Socket operation on non-socket
+E89  = Destination address required
+E90  = Message too long
+E91  = Protocol wrong type for socket
+E92  = Protocol not available
+E93  = Protocol not supported
+E94  = Socket type not supported
+E95  = Operation not supported on transport endpoint
+E96  = Protocol family not supported
+E97  = Address family not supported by protocol
+E98  = Address already in use
+E99  = Cannot assign requested address
+E100 = Network is down
+E101 = Network is unreachable
+E102 = Network dropped connection because of reset
+E103 = Software caused connection abort
+E104 = Connection reset by peer
+E105 = No buffer space available
+E106 = Transport endpoint is already connected
+E107 = Transport endpoint is not connected
+E108 = Cannot send after transport endpoint shutdown
+E109 = Too many references: cannot splice
+E110 = Connection timed out
+E111 = Connection refused
+E112 = Host is down
+E113 = No route to host
+E114 = Operation already in progress
+E115 = Operation now in progress
+E116 = Stale NFS file handle
+E117 = Structure needs cleaning
+E118 = Not a XENIX named type file
+E119 = No XENIX semaphores available
+E120 = Is a named type file
+E121 = Remote I/O error
+E122 = Quota exceeded
+E123 = No medium found
+E124 = Wrong medium type
+#-----------------------------------------------------------
+# Error Codes for acfg_errors
+#-----------------------------------------------------------
+E70354688 = mysterious failure
+E70354689 = could not find entry
+E70354690 = do not know that information
+E70354691 = line appears before a cell has been defined
+E70354692 = syntax error
+E70354693 = a database file is missing
+E70354694 = no more entries
+#-----------------------------------------------------------
+# Error Codes for ktc_errors
+#-----------------------------------------------------------
+E11862784 = an unexpected error was encountered
+E11862785 = a buffer was too small for the response
+E11862786 = an invalid argument was passed in
+E11862787 = no such entry
+E11862788 = a pioctl failed
+E11862789 = AFS kernel pioctl doesn't exist
+E11862790 = unknown cell was passed to SetToken
+E11862791 = Cache Manager is not initialized / afsd is not running
+E11862792 = failed to send or receive session key via remote procedure call
+E11862793 = Cache Manager RPC server is not responding
+#-----------------------------------------------------------
+# Error Codes for boserr
+#-----------------------------------------------------------
+E39424 = process not active
+E39425 = no such entity
+E39426 = can't do operation now
+E39427 = entity already exists
+E39428 = failed to create entity
+E39429 = index out of range
+E39430 = you are not authorized for this operation
+E39431 = syntax error in create parameter
+E39432 = I/O error
+E39433 = network problem
+E39434 = unrecognized bnode type
+E39435 = kvno already used - have to remove existing kvno's before reuse
+E39436 = this function requires encrypted input, use a newer client program
+#-----------------------------------------------------------
+# Error Codes for butc_errs
+#-----------------------------------------------------------
+E156566272 = error in dump/restore process 
+E156566273 = ungraceful abort 
+E156566274 = the process has already been aborted
+E156566275 = unable to end dump/restore since work in progress
+E156566276 = some of the dump/restores were unsuccessful
+E156566277 = could not abort the process 
+E156566278 = the process was aborted by request
+E156566279 = scan tape resulted in failure
+E156566280 = No dump task with specified ID
+E156566281 = No tasks active
+E156566282 = the volume was not found on tape
+E156566283 = unexpected EOF encountered on tape
+E156566284 = missing file trailer on tape
+E156566285 = unexpected tape label
+E156566286 = tape was unusable
+E156566287 = corrupted volume header on tape
+E156566288 = internal error
+E156566289 = corruption in internal queue data structures
+E156566290 = memory allocation failure
+E156566291 = access denied
+E156566292 = tape requested to be skipped
+E156566293 = invalid task
+#-----------------------------------------------------------
+# Error Codes for butm_errs
+#-----------------------------------------------------------
+E156568832 = interface incompatible
+E156568833 = there is not an opened tape
+E156568834 = multiple simultaneous opens not permitted
+E156568835 = can't open tape
+E156568836 = error during tape close
+E156568837 = tape I/O error
+E156568838 = write operation on read-only tape
+E156568839 = operation inappropriate in this context
+E156568840 = read file ended before all data read
+E156568841 = write a zero length file
+E156568842 = end of tape
+E156568843 = problem reading configuration
+E156568844 = argument too long or out of range
+E156568845 = unexpected end of volume data
+E156568846 = appended tape label
+E156568847 = end of dump
+E156568848 = tape device error
+E156568849 = end-of-file marker
+E156568850 = unexpected tape datablock
+E156568851 = no label on tape
+E156568852 = cannot position within the file
+#-----------------------------------------------------------
+# Error Codes for butx_errs
+#-----------------------------------------------------------
+E156571648 = Version 1
+E156571649 = XBSA couldn't mount shared library
+E156571650 = XBSA handle already initialized
+E156571651 = XBSA invalid serverType specified
+E156571652 = XBSA invalid serverName specified
+E156571653 = XBSA invalid bsaObjectOwner specified
+E156571654 = XBSA invalid appObjectOwner specified
+E156571655 = XBSA invalid secToken specified
+E156571656 = XBSA invalid objectSpaceName specified
+E156571657 = XBSA invalid pathName specified
+E156571658 = XBSA invalid bufferSize specified
+E156571659 = XBSA invalid dataBuffer specified
+E156571660 = XBSA invalid lGName specified
+E156571661 = XBSA invalid objectDescription specified
+E156571662 = XBSA invalid objectInfo specified
+E156571663 = XBSA version mismatch
+E156571664 = XBSA unable to mount the XBSA library
+E156571665 = XBSA initialization of the XBSA interface failed
+E156571666 = XBSA begin transaction failed
+E156571667 = XBSA has not been initialized, no handle
+E156571668 = XBSA end transaction failed
+E156571669 = XBSA terminate session failed
+E156571670 = XBSA query object failed
+E156571671 = XBSA get object failed
+E156571672 = XBSA end data failed
+E156571673 = XBSA create object failed
+E156571674 = XBSA delete object failed
+E156571675 = XBSA send data failed
+E156571676 = XBSA get data failed
+E156571677 = XBSA get environment failed
+E156571678 = XBSA volume to delete not found
+#-----------------------------------------------------------
+# Error Codes for bucoord_errs
+#-----------------------------------------------------------
+E156288000 = Unacceptable user supplied argument
+E156288001 = Object has been updated
+E156288002 = Search matched more than one item
+E156288003 = Can't allocate working memory
+E156288004 = Can't get cell configuration information
+E156288005 = Specified item already exists
+E156288006 = Error in configuration parameters
+E156288007 = No such volume set
+E156288008 = No such volume entry
+E156288009 = Volume set already exists
+E156288010 = No such server
+E156288011 = No such partition
+E156288012 = Version number mismatch
+E156288013 = Lock has not been acquired
+E156288014 = Internal error
+E156288015 = No such host/port entry
+#-----------------------------------------------------------
+# Error Codes for budb_errs
+#-----------------------------------------------------------
+E156303872 = dump with specified id already exists
+E156303873 = no dump matching the id was found
+E156303874 = no dump matching the name was found
+E156303875 = no tape matching the name was found
+E156303876 = no volume matching the name was found
+E156303877 = entry doesn't exist
+E156303878 = reference to a tape not being used
+E156303879 = dump of database failed
+E156303880 = access to database denied
+E156303881 = incompatible version numbers
+E156303882 = argument too long or out of range
+E156303883 = sequence of operations incorrect
+E156303884 = inconsistent or unsupported flags bit combination
+E156303885 = requested list too large
+E156303886 = index to iterator function is out of range
+E156303887 = bad database block type
+E156303888 = lock is not set
+E156303889 = lock is held by another user
+E156303890 = attempt to lock a lock already held
+E156303891 = interface incompatible
+E156303892 = Ubik I/O error
+E156303893 = bad database address
+E156303894 = backup database is inconsistent
+E156303895 = internal error encountered in backup database server
+E156303896 = error reading cell database
+E156303897 = cell name not found
+E156303898 = database empty or corrupted
+E156303899 = Ubik ClientInit failed
+E156303900 = couldn't allocate entry
+E156303901 = can't allocate memory
+E156303902 = dump is not an initial dump
+E156303903 = reference to a dump not being used
+#-----------------------------------------------------------
+# Error Codes for cmd_errors
+#-----------------------------------------------------------
+E3359744 = More than the maximum number of parameters defined
+E3359745 = Internal parsing error
+E3359746 = Too many values specified after a CMD_SINGLE switch
+E3359747 = Too many parameters specified
+E3359748 = Impossibly few aguments specified
+E3359749 = unrecognized or ambiguous command name
+E3359750 = unrecognized or ambiguous switch name
+E3359751 = <unused>
+E3359752 = Insufficient required parameters provided
+E3359753 = Token too large
+#-----------------------------------------------------------
+# Error Codes for test1
+#-----------------------------------------------------------
+E11829760 = Can't read ticket file
+E11829761 = Can't find ticket or TGT
+E11829762 = TGT expired
+E11829763 = Can't decode authenticator
+E11829764 = Ticket expired
+E11829765 = Repeated request
+E11829766 = The ticket isn't for us
+E11829767 = Request is inconsistent
+E11829768 = Delta-T too big
+E11829769 = Incorrect net address
+E11829770 = Protocol version mismatch
+E11829771 = Invalid message type
+E11829772 = Message stream modified
+E11829773 = Message out of order
+E11829774 = Unauthorized request
+E11829775 = Current password is null
+E11829776 = Incorrect current password
+E11829777 = Protocol error
+E11829778 = Error returned by KDC
+E11829779 = Null ticket returned by KDC
+E11829780 = Retry count exceeded
+E11829781 = Can't send request
+#-----------------------------------------------------------
+# Error Codes for test2
+#-----------------------------------------------------------
+E1163220992 = foo
+E1163220993 = bar
+E1163220994 = meow
+#-----------------------------------------------------------
+# Error Codes for kaerrors
+#-----------------------------------------------------------
+E180480 = AuthServer database is inconsistent
+E180481 = user already exists
+E180482 = Ubik I/O error
+E180483 = couldn't allocate entry
+E180484 = user doesn't exist
+E180485 = database empty or corrupted
+E180486 = name or instance is too long or contains illegal characters
+E180487 = bad index used internally
+E180488 = caller not authorized
+E180489 = answer packet too short for result
+E180490 = password is incorrect
+E180491 = interface incompatible
+E180492 = argument out of range
+E180493 = administrative command called incorrectly
+E180494 = can't create session key
+E180495 = can't read password from terminal
+E180496 = illegal key: bad parity or weak
+E180497 = Ubik ClientInit failed
+E180498 = Ubik Call failed
+E180499 = AuthServer returned incorrect response
+E180500 = error reading cell database
+E180501 = cell name not found
+E180502 = too many Ubik security objects outstanding
+E180503 = too many keys were passed to the server's security object
+E180504 = authentication server was passed a bad ticket
+E180505 = unknown key version number
+E180506 = key cache invalidated by some key change
+E180507 = may not issue ticket for server
+E180508 = may not authenticate as this user
+E180509 = may not change your key
+E180510 = not allowed to create associate
+E180511 = can't find suitable ticket
+E180512 = operation not allowed for associate user
+E180513 = not a special AuthServer principal
+E180514 = server and client clocks are badly skewed
+E180515 = not allowed to recursively call set_password from get_time
+E180516 = Rx failed for some reason
+E180517 = zero length password is illegal
+E180518 = internal error encountered in kaserver
+E180519 = password has expired (KAPWEXPIRED)
+E180520 = it seems like a reused password (KAREUSED)
+E180521 = you changed it too recently; see your systems administrator (KATOOSOON)
+E180522 = ID is locked - see your system admin (KALOCKED)
+#-----------------------------------------------------------
+# Error Codes for afs_AdminBosErrors
+#-----------------------------------------------------------
+E16896 = the bos server name cannot be NULL
+E16897 = the bos server handle cannot be NULL
+E16898 = the bos server handle cannot be NULL
+E16899 = the bos server handle failed to pass the magic number test.  Most likely the server handle is invalid, or has been overwritten by mistake.
+E16900 = the bos server handle is invalid
+E16901 = the bos server handle does not reference a valid server
+E16902 = unable to establish a connection with the specified bos server machine
+E16903 = the process name cannot be NULL
+E16904 = the process cannot be NULL
+E16905 = the cron time cannot be NULL when creating a process of type cron
+E16906 = the cron time must be NULL when creating a process of type fs or simple
+E16907 = the process status cannot be NULL
+E16908 = the process type cannot be NULL
+E16909 = the process information cannot be NULL
+E16910 = the parameter cannot be NULL
+E16911 = the notifier cannot be NULL
+E16912 = the administrator name cannot be NULL
+E16913 = the key cannot be NULL
+E16914 = the key cannot be NULL
+E16915 = the host name cannot be NULL
+E16916 = the source file cannot be NULL
+E16917 = the destination file cannot be NULL
+E16918 = the new time cannot be NULL
+E16919 = the old time cannot be NULL
+E16920 = the backup time cannot be NULL
+E16921 = the restart time cannot be NULL
+E16922 = the log name cannot be NULL
+E16923 = the log buffer size cannot be NULL
+E16924 = the log data buffer cannot be NULL
+E16925 = the command cannot be NULL
+E16926 = the auxiliary process status cannot be NULL
+E16927 = the process status is invalid.  You can only set a process state to BOS_PROCESS_STOPPED or BOS_PROCESS_RUNNING
+E16928 = the process type retrieved from the bos server was invalid.
+E16929 = the executable source file could not be opened for reading.
+E16930 = unable to determine the size of the executable source file
+E16931 = the executable source file could not be read.
+E16932 = an error occurred transmitting the contents of the executable source file to the bos server.
+E16933 = the executable source file cannot be NULL.
+E16934 = the hour member of the time parameter must be between 0 and 23.
+E16935 = the minute member of the time parameter must be between 0 and 60.
+E16936 = the second member of the time parameter must be between 0 and 60.
+E16937 = the day member of the time parameter must be between 0 and 6.
+E16938 = unable to successfully read log file.
+E16939 = the cell handle does not contain a valid token.
+E16940 = the cell handle is not valid for vos requests.
+E16941 = a parition must be specified when salvaging a volume .
+E16942 = the log file could not be opened for writing.
+E16943 = the resulting salvage command is too long to pass to the bos server.
+E16944 = bos_ProcessCreate can't create fs processes, use bos_FSProcessCreate instead.
+E16945 = the file server executable path cannot be NULL.
+E16946 = the volume server executable path cannot be NULL.
+E16947 = the salvager executable path cannot be NULL.
+#-----------------------------------------------------------
+# Error Codes for afs_AdminCfgErrors
+#-----------------------------------------------------------
+E17920 = the specified configuration option is not yet supported
+E17921 = the host name parameter cannot be NULL
+E17922 = the host name parameter exceeds the maximum allowed length
+E17923 = the host handle reference parameter cannot be NULL
+E17924 = the host handle parameter cannot be NULL
+E17925 = the host handle parameter failed the magic number test; the handle is invalid or corrupted
+E17926 = the host handle parameter is marked as invalid
+E17927 = the host handle parameter contains a NULL host name reference
+E17928 = the host handle parameter contains a NULL cell handle
+E17929 = the host handle parameter contains a NULL cell name reference
+E17930 = the administrator principal parameter cannot be NULL
+E17931 = the administrator principal parameter exceeds the maximum allowed length
+E17932 = the password parameter cannot be an empty string
+E17933 = the configuration status reference parameter cannot be NULL
+E17934 = the cell name reference parameter cannot be NULL
+E17935 = the minimally required server configuration information is missing, unreadable, or invalid
+E17936 = the server is not configured in any cell
+E17937 = the server does not have any keys
+E17938 = the server's cell is not listed in the server's cell database
+E17939 = the server's cell database contains no database server entries for the server's cell
+E17940 = the cell name parameter cannot be NULL
+E17941 = the cell name parameter exceeds the maximum allowed length
+E17942 = the cell name parameter conflicts with the cell name contained in the host handle parameter
+E17943 = the database hosts parameter cannot be NULL
+E17944 = the database hosts parameter contains too many host names
+E17945 = unable to set the server cell information; most likely cause is an unknown database host name
+E17946 = the bosserver process is currently running on the server host
+E17947 = the vice partition table reference parameter cannot be NULL
+E17948 = the vice partition table entry count reference parameter cannot be NULL
+E17949 = unable to read the vice partition table
+E17950 = the partition name parameter cannot be NULL
+E17951 = the partition name parameter syntax is invalid
+E17952 = the device name parameter cannot be NULL
+E17953 = the device name parameter syntax is invalid
+E17954 = the vice partition table entry is invalid
+E17955 = unable to write to the vice partition table
+E17956 = the valid flag reference parameter cannot be NULL
+E17957 = the installed flag reference parameter cannot be NULL
+E17958 = the version number reference parameter cannot be NULL
+E17959 = the started flag reference parameter cannot be NULL
+E17960 = the minimally required client configuration information is missing, unreadable, or invalid
+E17961 = the client is not configured in any cell
+E17962 = the client's cell is not listed in the client's cell database
+E17963 = the client's cell database contains no database server entries for the client's cell
+E17964 = unable to determine which version of the AFS client is installed
+E17965 = unable to read the client's cell database
+E17966 = unable to update the client's cell database
+E17967 = the client's cell database contains the maximum number of entries for the specified cell; a new database host can not be added
+E17968 = failed to edit client's cell database
+E17969 = unable to set the client's current cell
+E17970 = the AFS bosserver control service is not configured or is improperly configured
+E17971 = the AFS bosserver control service is not prepared to accept a control request or is not responding
+E17972 = timed out waiting for the AFS bosserver control service to start or stop
+E17973 = cannot determine the status of the AFS bosserver control service
+E17974 = failed to set or clear the AFS server authentication flag
+E17975 = the bosserver-processes flag reference parameter cannot be NULL
+E17976 = the database-servers-configured flag reference parameter cannot be NULL
+E17977 = the fileserver-configured flag reference parameter cannot be NULL
+E17978 = the upserver-configured flag reference parameter cannot be NULL
+E17979 = the update client instance suffix cannot be NULL
+E17980 = the update client instance suffix exceeds the maximum allowed length
+E17981 = the update client's target server name cannot be NULL
+E17982 = the update client's import directory list cannot be NULL
+E17983 = the upclient-configured flag reference parameter cannot be NULL
+E17984 = unable to establish a connection with the specified Ubik voting service on the specified host
+E17985 = timed out waiting for one or more database servers to achieve quorum; common causes are time skew between database server machines and network connectivity problems
+E17986 = the server's cell database contains too many database server entries
+E17987 = the callback parameter cannot be NULL
+E17988 = the update count reference parameter cannot be NULL
+E17989 = the AFS server principal (afs) key can not be obtained from pre 3.5 database servers
+E17990 = the specified AFS server principal (afs) password is invalid; the password generates a key that fails a checksum comparison with the current AFS server principal (afs) key
+E17991 = the resolver is unable to retrieve host information from the default host database
+E17992 = the specfied host name resolves to a fully qualified name that exceeds the maximum allowed length
+E17993 = the AFS client service is not configured or is improperly configured
+E17994 = the AFS client service is not prepared to accept a control request or is not responding
+E17995 = timed out waiting for the AFS client service to start or stop
+E17996 = cannot determine the status of the AFS client service
+#-----------------------------------------------------------
+# Error Codes for afs_AdminClientErrors
+#-----------------------------------------------------------
+E19456 = the cell handle parameter cannot be NULL
+E19457 = the cell handle reference parameter cannot be NULL
+E19458 = the server handle parameter failed to pass the magic number test.  Most likely the server handle is invalid, or has been overwritten by mistake.
+E19459 = the cell handle is not valid.
+E19460 = the cell handle is not valid for authentication server requests
+E19461 = the cell handle authentication server pointer is NULL
+E19462 = the cell handle is not valid for protection server requests
+E19463 = the cell handle protection server pointer is NULL
+E19464 = the cell handle is not valid for volume server requests
+E19465 = the cell handle volume server pointer is NULL
+E19466 = the cell name parameter cannot be NULL
+E19467 = the token handle parameter cannot be NULL
+E19468 = the token handle is invalid
+E19469 = the token handle parameter failed to pass the magic number test.  Most likely the token handle is invalid, or has been overwritten by mistake
+E19470 = failed to create a new client security object for the token handle
+E19471 = unable to locate the location of AFS install
+E19472 = unable to initialize the AFS rpc component
+E19473 = unable to initialize the windows socket component
+E19474 = afsclient_Init must be called before calling any other afsclient function
+E19475 = an error occurred while trying to access the local client configuration information
+E19476 = the token handle was marked as containing valid tokens, but the actual tokens were invalid
+E19477 = the token handle must contain valid afs tokens for the cell
+E19478 = the directory parameter cannot be NULL
+E19479 = the volume name parameter cannot be NULL
+E19480 = unable to determine the parent of the given directory
+E19481 = the directory is not in AFS
+E19482 = the server did not match any known AFS servers
+E19483 = the rpc stat handle cannot be NULL
+E19484 = the cell handle does not contain kas tokens
+E19485 = there is no connection to the server
+E19486 = the client stat handle cannot be NULL
+E19487 = there is no connection to the client
+E19488 = the cell name pointer cannot be NULL
+E19489 = the client configuration pointer cannot be NULL
+E19490 = the rxdebug handle cannot be NULL
+E19491 = the rxdebug request timed out
+E19492 = the rxdebug request is not supported
+#-----------------------------------------------------------
+# Error Codes for afs_AdminCommonErrors
+#-----------------------------------------------------------
+E17152 = couldn't allocate memory necessary to fulfill request
+E17153 = insufficient privilege to complete operation
+E17154 = failed to initialize a mutex
+E17155 = failed to lock a mutex
+E17156 = failed to unlock a mutex
+E17157 = failed to destroy a mutex
+E17158 = failed to initialize a condition variable
+E17159 = failed to wait on a condition variable
+E17160 = failed to destroy a condition variable
+E17161 = failed to signal a condition variable
+E17162 = failed to initialize a thread attribute
+E17163 = failed to set thread detach state
+E17164 = failed to create a thread
+E17165 = failed to join a thread
+E17166 = the iterator has been marked terminated (most likely by calling done).  Next cannot be called after calling done.
+E17167 = the iterator has been marked completed.
+E17168 = the iterator parameter cannot be NULL.
+E17169 = the rpc specific data parameter cannot be NULL.
+E17170 = the iterator parameter failed to pass the magic number test.  Most likely the iterator is invalid, or has been overwritten by mistake.
+E17171 = the iterator parameter is marked invalid
+E17172 = the iterator parameter cannot be NULL nor can it point to NULL
+E17173 = the server name parameter cannot be NULL
+E17174 = the server address parameter cannot be NULL
+E17175 = the server name parameter cannot translated to an address
+E17176 = unable to determine the name of the local host
+E17177 = more data is available
+E17178 = failed to create a socket
+E17179 = the server type is invalid
+#-----------------------------------------------------------
+# Error Codes for afs_AdminKasErrors
+#-----------------------------------------------------------
+E19200 = the server handle parameter cannot be NULL
+E19201 = the server handle parameter failed to pass the magic number test.  Most likely the server handle is invalid, or has been overwritten by mistake.
+E19202 = the server handle parameter is marked invalid
+E19203 = the server handle parameter contains no servers
+E19204 = the cell handle and the server handle parameter cannot both be NULL
+E19205 = the cell handle and the server handle parameter cannot both be non-NULL
+E19206 = the authentication server handle parameter cannot be NULL
+E19207 = the from parameter cannot be NULL
+E19208 = the to parameter cannot be NULL
+E19209 = the server list parameter cannot be NULL
+E19210 = the server list parameter contains too many servers
+E19211 = the server handle parameter cannot be NULL nor can it point to NULL
+E19212 = the who parameter cannot be NULL
+E19213 = the password parameter cannot be NULL
+E19214 = the authentication server parameter cannot be NULL
+E19215 = the locked until parameter cannot be NULL
+E19216 = the principal parameter cannot be NULL
+E19217 = the key parameter cannot be NULL
+E19218 = the lock end time parameter cannot be NULL
+E19219 = the password expires parameter cannot be greater than 255
+E19220 = the failed password attempts parameter cannot be greater than 255
+E19221 = the failed password lock time parameter cannot be greater than 129600
+E19222 = the stats parameter cannot be NULL
+E19223 = the debug parameter cannot be NULL
+E19224 = the server list parameter didn't contain any servers
+E19225 = at least one principal field to set must be specified
+#-----------------------------------------------------------
+# Error Codes for afs_AdminMiscErrors
+#-----------------------------------------------------------
+E19712 = the directory parameter cannot be NULL.
+E19713 = the user parameter cannot be NULL.
+E19714 = the acl parameter cannot be NULL.
+E19715 = this interface does not support changing DFS acls.
+#-----------------------------------------------------------
+# Error Codes for afs_AdminPtsErrors
+#-----------------------------------------------------------
+E20480 = the protection server parameter cannot be NULL
+E20481 = the user name parameter cannot be NULL
+E20482 = the user name parameter is too long
+E20483 = the group name parameter cannot be NULL
+E20484 = the group name parameter is too long
+E20485 = failed to translate a name to an identifier
+E20486 = the new owner parameter cannot be NULL
+E20487 = the new owner parameter is too long
+E20488 = the new group parameter cannot be NULL
+E20489 = the new group id parameter cannot be positive
+E20490 = the target group parameter cannot be NULL
+E20491 = the target group parameter is too long
+E20492 = the group parameter cannot be NULL
+E20493 = the group membership list is longer than the maximum retrievable
+E20494 = the member name parameter cannot be NULL
+E20495 = the old name parameter cannot be NULL
+E20496 = the old name is too long
+E20497 = the new name parameter cannot be NULL
+E20498 = the new name is too long
+E20499 = PTS_GROUP_ANYUSER_ACCESS is an invalid value for the list delete parameter
+E20500 = PTS_GROUP_OWNER_ACCESS is an invalid value for the list groups owned parameter
+E20501 = PTS_USER_ANYUSER_ACCESS is an invalid value for the list groups owned parameter
+E20502 = the new entry parameter cannot be NULL
+E20503 = the user parameter cannot be NULL
+E20504 = the new group id parameter cannot be NULL
+E20505 = the maximum group id parameter cannot be NULL
+E20506 = the new user id parameter cannot be NULL
+E20507 = the maximum user id parameter cannot be NULL
+#-----------------------------------------------------------
+# Error Codes for afs_AdminUtilErrors
+#-----------------------------------------------------------
+E21760 = the server entry parameter cannot be NULL
+E21761 = the server name parameter cannot be NULL
+E21762 = the server address parameter cannot be NULL
+E21763 = the server address parameter cannot be NULL
+E21764 = the server name parameter cannot translated to an address
+E21765 = failed to open the client CellServDB file
+E21766 = the error text parameter cannot be NULL
+E21767 = the cell name parameter cannot be NULL
+E21768 = the rx connection parameter cannot be NULL
+E21769 = the rpc function parameter cannot be NULL
+E21770 = the rpc stats parameter cannot be NULL
+E21771 = the state parameter cannot be NULL
+E21772 = the rpc version parameter cannot be NULL
+E21773 = the rxdebug handle parameter cannot be null
+E21774 = the rxdebug version parameter cannot be null
+E21775 = the rxdebug stats parameter cannot be null
+#-----------------------------------------------------------
+# Error Codes for afs_AdminVosErrors
+#-----------------------------------------------------------
+E22016 = the volume server parameter cannot be NULL
+E22017 = the cell handle parameter cannot be NULL
+E22018 = the cell handle parameter failed to pass the magic number test.  Most likely the cell handle is invalid, or has been overwritten by mistake.
+E22019 = the cell handle parameter is marked as invalid.
+E22020 = the cell handle parameter is marked as invalid for volume server requests.
+E22021 = the cell handle does not contain valid AFS tokens.
+E22022 = the volume identifier parameter must be a read write volume in order to make a backup volume.
+E22023 = a backup volume already exists for volume identifier, but it exists on a server different from the one that holds volumeidentifier.
+E22024 = the server handle parameter cannot be NULL
+E22025 = the server handle parameter is marked as invalid
+E22026 = the server handle parameter failed the magic number test.  Most likely the server handle is invalid, or has been overwritten by mistake.
+E22027 = a connection with the server could not be established.
+E22028 = the partition parameter cannot be NULL
+E22029 = the partition parameter is too large
+E22030 = the volume name parameter is too long
+E22031 = the volume name parameter cannot be NULL
+E22032 = the volume name parameter cannot end with .readonly or .backup
+E22033 = the volume identifier parameter cannot be NULL
+E22034 = the volume identifier parameter cannot exceed VOLMAXPARTS
+E22035 = the volume identifier does not exist on the specified server and partition
+E22036 = the volume name is already in use.
+E22037 = the partition name parameter cannot be NULL
+E22038 = the partition name parameter must start with /vicep
+E22039 = the partition name parameter is too long.
+E22040 = the partition name parameter is too short.
+E22041 = the partition name parameter must be all lower case letters.
+E22042 = the partition identifier parameter cannot be NULL
+E22043 = the resulting partition identifier exceeds VOLMAXPARTS
+E22044 = the volume prefix parameter cannot be NULL when the exclude prefix is VOS_EXCLUDE
+E22045 = the server address parameter cannot be NULL
+E22046 = the server entry parameter cannot be NULL
+E22047 = the server transaction status parameter cannot be NULL
+E22048 = the vldb entry parameter cannot be NULL
+E22049 = at least one of the arguments must not be null - server handle, partition, volume identifier.
+E22050 = the new volume name parameter cannot be NULL
+E22051 = both the server handle and the paritiion parameters must be specified.
+E22052 = the dump file parameter cannot be NULL
+E22053 = an error occurred while trying to write more data to the dump file
+E22054 = an error occurred while trying to open dump file
+E22055 = the volume to be restored exists on a server different from the one specified and you specified an incremental restore.  If you wish to restore this volume, specify a full restore.
+E22056 = an error occurred while trying open restore file
+E22057 = an error occurred while trying close restore file
+E22058 = an error occurred while trying read the restore file
+E22059 = an error occurred while trying send the contents of the restore file
+E22060 = the volume name is too big
+E22061 = either the volume name or the volume identifier parameter must not be NULL
+E22062 = the volume parameter cannot be NULL
+E22063 = only read write volumes can be moved
+E22064 = only read write volumes can be released
+E22065 = only replicated volumes can be released
+E22066 = unable to create a backup volume because no read write volume exists
+E22067 = unable to create a backup volume because the vldb entry is invalid
+E22068 = unable to get server's address from vldb
+E22069 = skipping volume since read write is in a different location
+E22070 = there were no entries in the vldb retrieved.
+#-----------------------------------------------------------
+# Error Codes for pterror
+#-----------------------------------------------------------
+E267264 = Entry for name already exists
+E267265 = Entry for id already exists
+E267266 = Couldn't allocate an id for this entry
+E267267 = Couldn't read/write the database
+E267268 = User or group doesn't exist
+E267269 = Permission denied
+E267270 = No group specified
+E267271 = No user specified
+E267272 = Badly formed name (group prefix doesn't match owner?)
+E267273 = argument illegal or out of range
+E267274 = may not create more groups
+E267275 = database needs rebuilding
+E267276 = can't make owner an empty group
+E267277 = database is inconsistent
+E267278 = bad database address
+E267279 = too many elements in group
+E267280 = malloc failed to alloc enough memory
+#-----------------------------------------------------------
+# Error Codes for stress_errs
+#-----------------------------------------------------------
+E19059456 = process created, not yet started
+E19059457 = process running, no error
+E19059458 = arguments illegal or inconsistent
+E19059459 = incorrect input checksum
+E19059460 = incorrect output checksum
+E19059461 = unexpected number of bytes returned by rx_Read
+E19059462 = unexpected number of bytes sent by rx_Write
+E19059463 = connection unauthenticated
+E19059464 = unknown key version number
+E19059465 = incorrect client name/instance/cell
+E19059466 = increment operation produced wrong value
+E19059467 = clock on client and server too far apart
+E19059468 = couldn't make a new connection
+E19059469 = connection has unexpected call numbers
+E19059470 = failed to detect duplicate call
+E19059471 = failed to detect bad cksum
+E19059472 = whole connection is not in error
+E19059473 = idle connection is acting as a challenge oracle
+#-----------------------------------------------------------
+# Error Codes for rxkad_errs
+#-----------------------------------------------------------
+E19270400 = Security module structure inconsistent
+E19270401 = Packet too short for security challenge
+E19270402 = Security level negotiation failed
+E19270403 = Ticket length too long or too short
+E19270404 = packet had bad sequence number
+E19270405 = caller not authorized
+E19270406 = illegal key: bad parity or weak
+E19270407 = security object was passed a bad ticket
+E19270408 = ticket contained unknown key version number
+E19270409 = authentication expired
+E19270410 = sealed data inconsistent
+E19270411 = user data too long
+E19270412 = caller not authorized to use encrypted connections
+#-----------------------------------------------------------
+# Error Codes for uerrors
+#-----------------------------------------------------------
+E5376 = no quorum elected
+E5377 = not synchronization site (should work on sync site)
+E5378 = too many hosts
+E5379 = I/O error writing dbase or log
+E5380 = mysterious internal error
+E5381 = major synchronization error
+E5382 = file not found when processing dbase
+E5383 = bad lock range size (must be 1)
+E5384 = read error reprocessing log
+E5385 = problems with host name
+E5386 = bad operation for this transaction type
+E5387 = two commits or aborts done to transaction
+E5388 = operation done after abort (or commmit)
+E5389 = no servers appear to be up
+E5390 = premature EOF
+E5391 = error writing log file
+E5392 = unsupported address family -- bogus error
+E5393 = inconsistent cell name -- bogus error
+E5394 = security group bad or missing -- bogus error
+E5395 = server group name bad or missing -- bogus error
+E5396 = server uuid bad or missing -- bogus error
+E5397 = memory allocation failure -- bogus error
+E5398 = host not a member of server group -- bogus error
+E5399 = too many bindings per server -- bogus error
+E5400 = inconsistent principal name from binding -- bogus error
+E5401 = I/O error in ubik pipe -- bogus error
+E5402 = operation aborted to prevent dead lock (two sync sites?)
+E5403 = rpc runtime exception caught -- bogus error
+E5404 = vote thread pool queue operation failed -- bogus error
+E5405 = clock skew among servers too high -- bogus error
+E5406 = repeatedly failed to obtain ubik lock -- bogus error
+E5407 = permission denied for attempted operation -- bogus error
+E5408 = no space left on database device -- bogus error
+E5409 = invalid DB pathname -- bogus error
+E5410 = bad file descriptor -- bogus error
+E5411 = Reinitialize called before initialize
+E5412 = failed to initialize per client mutex
+E5413 = failed to destroy per client mutex
+#-----------------------------------------------------------
+# Error Codes for vl_errors
+#-----------------------------------------------------------
+E363520 = Volume Id entry exists in vl database
+E363521 = I/O related error
+E363522 = Volume name entry exists in vl database
+E363523 = Internal creation failure
+E363524 = No such entry
+E363525 = Vl database is empty
+E363526 = Entry is deleted (soft delete)
+E363527 = Volume name is illegal
+E363528 = Index is out of range
+E363529 = Bad volume type
+E363530 = Illegal server number (out of range)
+E363531 = Bad partition number
+E363532 = Run out of space for Replication sites
+E363533 = No such Replication server site exists
+E363534 = Replication site already exists
+E363535 = Parent R/W entry not found
+E363536 = Illegal Reference Count number
+E363537 = Vl size for attributes exceeded
+E363538 = Bad incoming vl entry
+E363539 = Illegal max volid increment
+E363540 = RO/BACK id already hashed
+E363541 = Vl entry is already locked
+E363542 = Bad volume operation code
+E363543 = Bad release lock type
+E363544 = Status report: last release was aborted
+E363545 = Invalid replication site server flag
+E363546 = No permission access
+E363547 = malloc(realloc) failed to alloc enough memory
+E363548 = Wrong vldb version
+E363549 = Index out of range
+E363550 = Servers have the same ip address
+E363551 = Illegal attribute mask value
+#-----------------------------------------------------------
+# Error Codes for volerr
+#-----------------------------------------------------------
+E1492325120 = internal error releasing transaction
+E1492325121 = unknown internal error
+E1492325122 = badly formatted dump
+E1492325123 = badly formatted dump(2)
+E1492325124 = could not attach volume
+E1492325125 = illegal partition
+E1492325126 = could not detach volume
+E1492325127 = insufficient privilege for volume operation
+E1492325128 = error from volume location database
+E1492325129 = bad volume name
+E1492325130 = volume moved
+E1492325131 = illegal volume operation
+E1492325132 = volume release failed
+E1492325133 = volume still in use by volserver
+E1492325134 = out of virtual memory in volserver
+E1492325135 = no such volume
+E1492325136 = more than one read/write volume
+E1492325137 = failed volume server operation
diff --git a/src/JAVA/classes/org/openafs/jafs/ACL.java b/src/JAVA/classes/org/openafs/jafs/ACL.java
new file mode 100644 (file)
index 0000000..10b1b96
--- /dev/null
@@ -0,0 +1,743 @@
+/*
+ * @(#)ACL.java        2.0 04/18/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.StringTokenizer;
+
+/**
+ * An abstract representation of AFS file and directory pathnames.
+ *
+ * This class is an extension of the standard Java File class with file-based 
+ * manipulation methods overridden by integrated AFS native methods.
+ *
+ * @version 2.0, 04/18/2001 - Completely revised class for efficiency.
+ */
+
+public class ACL implements Serializable, Comparable
+{
+  private ACL.Entry[] positiveEntries;
+  private ACL.Entry[] negativeEntries;
+
+  private String path;
+  public ACL(String path) throws AFSException
+  {
+    int numberPositiveEntries = 0;
+    int numberNegativeEntries = 0;
+    ACL.Entry aclEntry;
+    String buffer;
+
+    this.path = path;
+
+    StringTokenizer st = new StringTokenizer(getACLString(path), "\n\t");
+
+    buffer = st.nextToken();
+    numberPositiveEntries = new Integer(buffer).intValue();
+    positiveEntries = new ACL.Entry[numberPositiveEntries];
+
+    buffer = st.nextToken();
+    numberNegativeEntries = new Integer(buffer).intValue();
+    negativeEntries = new ACL.Entry[numberNegativeEntries];
+
+    for(int i = 0; i < numberPositiveEntries; i++)
+    {
+      aclEntry = new ACL.Entry();
+      aclEntry.setUser(st.nextToken());
+      aclEntry.setPermissions(new Integer(st.nextToken()).intValue());
+      positiveEntries[i] = aclEntry;
+    }
+
+    for(int i = 0; i < numberNegativeEntries; i++)
+    {
+      aclEntry = new ACL.Entry();
+      aclEntry.setUser(st.nextToken());
+      aclEntry.setPermissions(new Integer(st.nextToken()).intValue());
+      negativeEntries[i] = aclEntry;
+    }
+  }
+  public int getEntryCount()
+  {
+    return positiveEntries.length + positiveEntries.length;
+  }
+  public String getPath()
+  {
+    return path;
+  }
+  public ACL.Entry[] getPositiveEntries()
+  {
+    return positiveEntries;
+  }
+  public void addPositiveEntry(ACL.Entry entry) throws AFSException
+  {
+    int n = positiveEntries.length;
+    ACL.Entry[] e = new ACL.Entry[n + 1];
+    System.arraycopy(positiveEntries, 0, e, 0, n);
+    e[n] = entry;
+    positiveEntries = e;
+    setACLString(path, getFormattedString());
+  }
+  public void setPositiveEntries(ACL.Entry[] entries) throws AFSException
+  {
+    this.positiveEntries = entries;
+    setACLString(path, getFormattedString());
+  }
+  public ACL.Entry[] getNegativeEntries()
+  {
+    return negativeEntries;
+  }
+  public void addNegativeEntry(ACL.Entry entry) throws AFSException
+  {
+    int n = negativeEntries.length;
+    ACL.Entry[] e = new ACL.Entry[n + 1];
+    System.arraycopy(negativeEntries, 0, e, 0, n);
+    e[n] = entry;
+    negativeEntries = e;
+    setACLString(path, getFormattedString());
+  }
+  public void setNegativeEntries(ACL.Entry[] entries) throws AFSException
+  {
+    this.negativeEntries = entries;
+    setACLString(path, getFormattedString());
+  }
+
+  public void flush() throws AFSException
+  {
+    setACLString(path, getFormattedString());
+  }
+
+  private ACL.Entry[] getNonEmptyEntries(ACL.Entry[] entries)
+  {
+    ArrayList response = new ArrayList(entries.length);
+    for (int i = 0; i < entries.length; i++)
+    {
+      boolean isNonEmpty = entries[i].canRead()   ||
+                           entries[i].canLookup() ||
+                           entries[i].canWrite()  ||
+                           entries[i].canInsert() ||
+                           entries[i].canDelete() ||
+                           entries[i].canLock()   ||
+                           entries[i].canAdmin();
+      if (isNonEmpty) response.add(entries[i]);
+    }
+    if (response.size() == entries.length) return entries;
+    return (ACL.Entry[])response.toArray(new ACL.Entry[response.size()]);
+  }
+
+  private void entriesToString(ACL.Entry[] entries, StringBuffer response)
+  {
+    for (int i = 0; i < entries.length; i++)
+    {
+      this.entryToString((ACL.Entry)entries[i], response);
+    }
+  }
+
+  private void entryToString(ACL.Entry entry, StringBuffer response)
+  {
+    response.append(entry.getUser() + '\t' + entry.getPermissionsMask() + '\n');
+  }
+
+  /**
+   * Returns a ViceIoctl formatted String representation of this 
+   * <CODE>ACL</CODE>.
+   *
+   * @return a ViceIoctl formatted String representation of this 
+   *         <CODE>ACL</CODE>.
+   */
+  private String getFormattedString()
+  {
+    StringBuffer out = null;
+    ACL.Entry[] nonEmptyPos = this.getNonEmptyEntries(this.getPositiveEntries());
+    ACL.Entry[] nonEmptyNeg = this.getNonEmptyEntries(this.getNegativeEntries());
+
+    out = new StringBuffer(nonEmptyPos.length + "\n" + nonEmptyNeg.length + "\n");
+    this.entriesToString(nonEmptyPos, out);
+    this.entriesToString(nonEmptyNeg, out);
+
+    return out.toString();
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two ACL objects respective to their paths and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     acl    The ACL object to be compared to this ACL
+   *                   instance
+   * 
+   * @return    Zero if the argument is equal to this ACL's path, a
+   *           value less than zero if this ACL's path is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this ACL's path is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(ACL acl)
+  {
+    return this.getPath().compareTo(acl.getPath());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(ACL)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((ACL)obj);
+  }
+
+  /**
+   * Tests whether two <code>ACL</code> objects are equal, based on their 
+   * paths and permission bits.
+   *
+   * @param acl the ACL to test
+   * @return whether the specifed ACL is the same as this ACL
+   */
+  public boolean equals( ACL acl )
+  {
+    return ( (this.getPath().equals(acl.getPath())) &&
+             (positiveEntries.equals(acl.getPositiveEntries())) &&
+             (negativeEntries.equals(acl.getNegativeEntries())) );
+  }
+
+  /**
+   * Returns a String representation of this <CODE>ACL</CODE>
+   *
+   * @return a String representation of this <CODE>ACL</CODE>
+   */
+  public String toString()
+  {
+    ACL.Entry[] nonEmptyPos = this.getNonEmptyEntries(this.getPositiveEntries());
+    ACL.Entry[] nonEmptyNeg = this.getNonEmptyEntries(this.getNegativeEntries());
+
+    StringBuffer out = new StringBuffer("ACL for ");
+    out.append(path);
+    out.append("\n");
+    out.append("Positive Entries:\n");
+    for (int i = 0; i < nonEmptyPos.length; i++) {
+      out.append("  ");
+      out.append(nonEmptyPos[i].toString());
+    }
+    if (nonEmptyNeg.length > 0) {
+      out.append("Negative Entries:\n");
+      for (int i = 0; i < nonEmptyNeg.length; i++) {
+        out.append("  ");
+        out.append(nonEmptyNeg[i].toString());
+      }
+    }
+
+    return out.toString();
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Returns a formatted String representing the ACL for the specified path.
+   *
+   * The string format is in the form of a ViceIoctl and is as follows:
+   * printf("%d\n%d\n", positiveEntriesCount, negativeEntriesCount);
+   * printf("%s\t%d\n", userOrGroupName, rightsMask);
+   *
+   * @param   path     the directory path
+   * @returns a formatted String representing the ACL for the specified path.
+   * @throws an AFSException if an AFS or JNI exception is encountered.
+   */
+  private native String getACLString(String path) throws AFSException;
+
+  /**
+   * Sets the ACL in the file system according to this abstract representation.
+   *
+   * @param path       the directory path
+   * @param aclString  string representation of ACL to be set
+   * @throws an AFSException if an AFS or JNI exception is encountered.
+   */
+  private native void setACLString(String path, String aclString) throws AFSException;
+
+  /*====================================================================*/
+  /* INNER CLASSES  */
+  /*====================================================================*/
+
+  /**
+   * AFS ACL Entry Class.
+   *
+   * <p> Documentation reference: 
+   * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ772">Managing Access Control Lists</A>
+   *
+   * @version 2.0, 04/18/2001 - Completely revised class for efficiency.
+   * @version 3.0, 05/01/2002 - Converted class to an inner class.
+   */
+  public static final class Entry implements Serializable
+  {
+    /** ACL Mask read constant */
+    public static final int READ   = 1;
+    /** ACL Mask write constant */
+    public static final int WRITE  = 2;
+    /** ACL Mask insert constant */
+    public static final int INSERT = 4;
+    /** ACL Mask lookup constant */
+    public static final int LOOKUP = 8;
+    /** ACL Mask delete constant */
+    public static final int DELETE = 16;
+    /** ACL Mask lock constant */
+    public static final int LOCK   = 32;
+    /** ACL Mask administer constant */
+    public static final int ADMIN  = 64;
+  
+    private String username;
+  
+    private boolean r = false;
+    private boolean l = false;
+    private boolean i = false;
+    private boolean d = false;
+    private boolean w = false;
+    private boolean k = false;
+    private boolean a = false;
+  
+    /** 
+     * Constructs a new ACL entry with all permission bits set to <code>false</code>.
+     */
+    public Entry()
+    {
+    }
+    /** 
+     * Constructs a new ACL entry with all permission bits set to <code>false</code>
+     * and sets the associated user or group name.
+     *
+     * @param          user    The user or group name associated with this entry
+     */
+    public Entry(String user)
+    {
+      this.setUser(user);
+    }
+    /** 
+     * Constructs a new ACL entry setting each permission bit to its appropriate 
+     * value according to the <code>permissionsMask</code> specified.
+     *
+     * @see #canRead
+     * @see #canWrite
+     * @see #canInsert
+     * @see #canLookup
+     * @see #canDelete
+     * @see #canLock
+     * @see #canAdmin
+     * @param          permissionsMask An integer representation of the permissoin 
+     *                                rights of this entry
+     */
+    public Entry(int permissionsMask)
+    {
+      this.setPermissions(permissionsMask);
+    }
+    /** 
+     * Constructs a new ACL entry setting each permission bit to its appropriate 
+     * value according to the <code>permissionsMask</code> specified
+     * and sets the associated user or group name.
+     *
+     * @see #canRead
+     * @see #canWrite
+     * @see #canInsert
+     * @see #canLookup
+     * @see #canDelete
+     * @see #canLock
+     * @see #canAdmin
+     * @see #setUser
+     * @param          permissionsMask An integer representation of the permissoin 
+     *                                rights of this entry
+     * @param          user                    The username or group associated with this entry
+     */
+    public Entry(String user, int permissionsMask)
+    {
+      this.setUser(user);
+      this.setPermissions(permissionsMask);
+    }
+    /*-------------------------------------------------------------------------*/
+    /** 
+     * Set this entry's permission bits according to the value of the 
+     * <code>permissionsMask</code> specified.
+     *
+     * @see    #getPermissionsMask
+     * @param          permissionsMask An integer representation of the permissoin 
+     *                                rights of this entry
+     */
+    public void setPermissions(int permissionsMask)
+    {
+      if ((permissionsMask & READ) != 0) {
+          this.setRead(true);
+      }
+      if ((permissionsMask & LOOKUP) != 0) {
+          this.setLookup(true);
+      }
+      if ((permissionsMask & INSERT) != 0) {
+          this.setInsert(true);
+      }
+      if ((permissionsMask & DELETE) != 0) {
+          this.setDelete(true);
+      }
+      if ((permissionsMask & WRITE) != 0) {
+          this.setWrite(true);
+      }
+      if ((permissionsMask & LOCK) != 0) {
+          this.setLock(true);
+      }
+      if ((permissionsMask & ADMIN) != 0) {
+          this.setAdmin(true);
+      }
+    }
+    /** 
+     * Returns this entry's permission mask.
+     *
+     * <p> <B>Permission Mask</B><BR>
+     * 01 - READ  <BR>
+     * 02 - WRITE <BR>
+     * 04 - INSERT<BR>
+     * 08 - LOOKUP<BR>
+     * 16 - DELETE<BR>
+     * 32 - LOCK  <BR>
+     * 64 - ADMIN <BR>
+     *
+     * <p> Any combination of the above mask values would equate to a valid combination of 
+     * permission settings.  For example, if the permission mask was <B>11</B>, the ACL permissions
+     * would be as follows: <code>read</code> (1), <code>write</code> (2), and <code>lookup</code> (8).<BR>
+     * [1 + 2 + 8 = 11]
+     *
+     * @return An integer representation (mask) of the permissoin rights of this entry
+     */
+    public int getPermissionsMask()
+    {
+      int permissionsMask = 0;
+      if (canRead())   permissionsMask |= READ;
+      if (canWrite())  permissionsMask |= WRITE;
+      if (canInsert()) permissionsMask |= INSERT;
+      if (canLookup()) permissionsMask |= LOOKUP;
+      if (canDelete()) permissionsMask |= DELETE;
+      if (canLock())   permissionsMask |= LOCK;
+      if (canAdmin())  permissionsMask |= ADMIN;
+      return permissionsMask;
+    }
+    /** 
+     * Returns the user <B>or</B> group name associated with this ACL entry.
+     *
+     * @return String representation of the user or group name associated with this entry.
+     */
+    public String getUser()
+    {
+      return username;
+    }
+    /** 
+     * Sets the user <B>or</B> group name associated with this ACL entry.
+     *
+     * @param  user    representation of the user or group name associated with this entry.
+     */
+    public void setUser(String user)
+    {
+      username = user;
+    }
+    /** 
+     * <IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits <code>read</code> access.
+     *
+     * <p> This permission enables a user to read the contents of files in the directory 
+     * and to obtain complete status information for the files (read/retrieve the file 
+     * attributes).
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>File Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to files in 
+     * a directory, rather than the directory itself or its subdirectories. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>read</code> access of
+     *          files; <code>false</code> otherwise
+     */
+    public boolean canRead()
+    {
+      return r;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>read</code> access for files.
+     *
+     * @see #canRead
+     * @param  flag    boolean flag that denotes the permission bit for <code>read</code> access.
+     */
+    public void setRead(boolean flag)
+    {
+      r = flag;
+    }
+    /** 
+     * <IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits lookup access.
+     *
+     * <p> This permission functions as something of a gate keeper for access to the directory 
+     * and its files, because a user must have it in order to exercise any other permissions. 
+     * In particular, a user must have this permission to access anything in the directory's 
+     * subdirectories, even if the ACL on a subdirectory grants extensive permissions.
+     *
+     * <p> This permission enables a user to list the names of the files and subdirectories in 
+     * the directory (this does not permit read access to its respective entries), obtain 
+     * complete status information for the directory element itself, and examine the directory's 
+     * ACL.
+     *
+     * <p> This permission does not enable a user to read the contents of a file in the 
+     * directory.
+     *
+     * <p> Similarly, this permission does not enable a user to lookup the contents of, 
+     * obtain complete status information for, or examine the ACL of the subdirectory of 
+     * the directory. Those operations require the <code>lookup</code> permission on the ACL
+     * of the subdirectory itself.
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>Directory Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to the 
+     * directory itself. For example, the <code>insert</code> permission (see: {@link #canInsert})
+     * does not control addition of data to a file, but rather creation of a new file or 
+     * subdirectory. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>lookup</code> access for
+     *          directories; <code>false</code> otherwise
+     */
+    public boolean canLookup()
+    {
+      return l;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>lookup</code> access for directories.
+     *
+     * @see #canLookup
+     * @param  flag    boolean flag that denotes the permission bit for <code>lookup</code> access.
+     */
+    public void setLookup(boolean flag)
+    {
+      l = flag;
+    }
+    /** 
+     * <IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits <code>insert</code> access.
+     *
+     * <p> This permission enables a user to add new files to the directory, either by creating 
+     * or copying, and to create new subdirectories. It does not extend into any subdirectories,
+     * which are protected by their own ACLs.
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>Directory Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to the 
+     * directory itself. For example, the <code>insert</code> permission (see: {@link #canInsert})
+     * does not control addition of data to a file, but rather creation of a new file or 
+     * subdirectory. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>insert</code> access for
+     *          directories; <code>false</code> otherwise
+     */
+    public boolean canInsert()
+    {
+      return i;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>insert</code> access for directories.
+     *
+     * @see #canInsert
+     * @param  flag    boolean flag that denotes the permission bit for <code>insert</code> access.
+     */
+    public void setInsert(boolean flag)
+    {
+      i = flag;
+    }
+    /** 
+     * <IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits <code>delete</code> access.
+     *
+     * <p> This permission enables a user to remove files and subdirectories from the directory 
+     * or move them into other directories (assuming that the user has the <code>insert</code>
+     * (see: {@link #canInsert}) permission on the ACL of the other directories).
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>Directory Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to the 
+     * directory itself. For example, the <code>insert</code> permission (see: {@link #canInsert})
+     * does not control addition of data to a file, but rather creation of a new file or 
+     * subdirectory. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>delete</code> access for
+     *          directories; <code>false</code> otherwise
+     */
+    public boolean canDelete()
+    {
+      return d;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>delete</code> access for directories.
+     *
+     * @see #canDelete
+     * @param  flag    boolean flag that denotes the permission bit for <code>delete</code> rights.
+     */
+    public void setDelete(boolean flag)
+    {
+      d = flag;
+    }
+    /** 
+     * <IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits <code>write</code> access.
+     *
+     * <p> This permission enables a user to modify the contents of files in the directory 
+     * and to change their operating system specific mode bits. 
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>File Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to files in 
+     * a directory, rather than the directory itself or its subdirectories. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>write</code> access for
+     *          files; <code>false</code> otherwise
+     */
+    public boolean canWrite()
+    {
+      return w;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>write</code> access for files.
+     *
+     * @see #canWrite
+     * @param  flag    boolean flag that denotes the permission bit for <code>write</code> access.
+     */
+    public void setWrite(boolean flag)
+    {
+      w = flag;
+    }
+    /** 
+     * <IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits the <code>lock</code> authority.
+     *
+     * <p> This permission enables the user to run programs that issue system calls to 
+     * lock files in the directory. 
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="file.gif" ALT="File Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>File Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to files in 
+     * a directory, rather than the directory itself or its subdirectories. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>lock</code> authority for
+     *          files; <code>false</code> otherwise
+     */
+    public boolean canLock()
+    {
+      return k;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>lock</code> access for files.
+     *
+     * @see #canLock
+     * @param  flag    boolean flag that denotes the permission bit for <code>lock</code> rights.
+     */
+    public void setLock(boolean flag)
+    {
+      k = flag;
+    }
+    /** 
+     * <IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> Tests whether the ACL permits <code>administer</code> access.
+     *
+     * <p> This permission enables a user to change the directory's ACL. Members of the 
+     * <code>system:administrators</code> group implicitly have this permission on every 
+     * directory (that is, even if that group does not appear on the ACL). Similarly, the 
+     * owner of a directory implicitly has this permission on its ACL and those of all 
+     * directories below it that he or she owns. 
+     *
+     * <p><FONT COLOR="666699"><IMG SRC="folder.gif" ALT="Directory Permission" WIDTH="15" HEIGHT="15" BORDER="0"> <U><B>Directory Permission</B></U></FONT><BR>
+     * This permission is meaningful with respect to the 
+     * directory itself. For example, the <code>insert</code> permission (see: {@link #canInsert})
+     * does not control addition of data to a file, but rather creation of a new file or 
+     * subdirectory. 
+     *
+     * <p> Documentation reference: 
+     * <A HREF="http://www.transarc.com/Library/documentation/afs/3.6/unix/en_US/HTML/AdminGd/auagd020.htm#HDRWQ782">The AFS ACL Permissions</A>
+     *
+     * @return  <code>true</code> if and only if the ACL permits <code>administer</code> access for
+     *          directories; <code>false</code> otherwise
+     */
+    public boolean canAdmin()
+    {
+      return a;
+    }
+    /** 
+     * Sets the ACL permission to accomodate <code>administer</code> rights for directories.
+     *
+     * @see #canAdmin
+     * @param  flag    boolean flag that denotes the permission bit for <code>administer</code> rights.
+     */
+    public void setAdmin(boolean flag)
+    {
+      a = flag;
+    }
+
+    /////////////// custom override methods ////////////////////
+
+    /**
+     * Tests whether two <code>ACL.Entry</code> objects are equal, based on associated
+     * username and permission bits.
+     *
+     * @param entry the ACL.Entry to test
+     * @return whether the specifed ACL.Entry is the same as this ACL.Entry
+     */
+    public boolean equals( ACL.Entry entry )
+    {
+      return ( (this.getUser().equals( entry.getUser() )) &&
+               (this.getPermissionsMask() == entry.getPermissionsMask()) );
+    }
+  
+    /**
+     * Returns a String representation of this <CODE>ACL.Entry</CODE>
+     *
+     * @return a String representation of this <CODE>ACL.Entry</CODE>
+     */
+    public String toString()
+    {
+      StringBuffer out = new StringBuffer(username);
+      out.append("\t");
+      if (r) out.append("r");
+      if (l) out.append("l");
+      if (i) out.append("i");
+      if (d) out.append("d");
+      if (w) out.append("w");
+      if (k) out.append("k");
+      if (a) out.append("a");
+      out.append("\n");
+      return out.toString();
+    }
+
+  }
+}
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/AFSException.java b/src/JAVA/classes/org/openafs/jafs/AFSException.java
new file mode 100644 (file)
index 0000000..a250f9a
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * @(#)AFSException.java       2.0 01/04/16
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.Locale;
+
+/**
+ * An exception indicating that an error has occurred in the Java AFS 
+ * API, in the Java AFS JNI, or in the AFS file system.
+ *
+ * @version 1.0, 04/16/2001
+ * @see     java.lang.Exception
+ */
+public class AFSException extends Exception
+{
+  /**
+   * The AFS specific error number (code). 
+   * @see     #getErrorCode()
+   */
+  protected int errno;
+
+  /**
+   * Constructs an <code>AFSException</code> with the specified detail
+   * message. 
+   *
+   * @param   reason   the detail message.
+   */
+  public AFSException(String reason)
+  {
+    super(reason);
+  }
+  /**
+   * Constructs an <code>AFSException</code> with the specified error code. 
+   * This constructor will also generate the appropriate error message
+   * respective to the specified error code. 
+   *
+   * @param   errno   the AFS error number (error code).
+   */
+  public AFSException(int errno)
+  {
+    super(ErrorTable.getMessage( (errno == 0) ? ErrorTable.UNKNOWN : errno ));
+    this.errno = (errno == 0) ? ErrorTable.UNKNOWN : errno;
+  }
+  /**
+   * Constructs an <code>AFSException</code> with the specified detail message
+   * and specified error code.  In this constructor the specified detail message
+   * overrides the default AFS error message defined by the
+   * <code>ErrorTable</code> class.  Therefore, to retrieve the AFS specific 
+   * error message, you must use the <code>{@link #getAFSMessage}</code> method.
+   * The <code>{@link #getMessage}</code> method will return the message specified 
+   * in this constructor.
+   *
+   * @param   reason   the detail message.
+   * @param   errno    the AFS error number (error code).
+   */
+  public AFSException(String reason, int errno)
+  {
+    super(reason);
+    this.errno = errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS specific error number (code).  This code can be interpreted 
+   * by use of the <code>{@link ErrorTable}</code> class method 
+   * <code>{@link ErrorTable#getMessage(int)}</code>.
+   *
+   * @return  the AFS error code of this <code>AFSException</code> 
+   *          object. 
+   * @see     ErrorTable#getMessage(int)
+   */
+  public int getErrorCode() 
+  {
+    return errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage()
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno) : msg;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the locale specific error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage(Locale locale)
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno, locale) : msg;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message string defined by the <code>ErrorTable
+   * </code> class.  The message will be formatted according to the default
+   * Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage(Locale)
+   * @see        AFSException#AFSException(String, int)
+   * @see     ErrorTable#getMessage(int)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage() 
+  {
+    return ErrorTable.getMessage(errno);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message defined by the <code>ErrorTable</code>
+   * class.  The message will be formatted according to the specified Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   * @see        AFSException#AFSException(String, int)
+   * @see     ErrorTable#getMessage(int, Locale)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage(Locale locale)
+  {
+    return ErrorTable.getMessage(errno, locale);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns a string representation of this AFS Exception.
+   *
+   * <P> The message will be formatted according to the specified Locale.
+   *
+   * @return  the AFS error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage()
+   * @see     ErrorTable#getMessage(int)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String toString()
+  {
+    return "AFSException: Error Code: " + errno + "; Message: " +
+            getMessage();
+  }
+  /*-----------------------------------------------------------------------*/
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/AFSFileException.java b/src/JAVA/classes/org/openafs/jafs/AFSFileException.java
new file mode 100644 (file)
index 0000000..3d6c398
--- /dev/null
@@ -0,0 +1,195 @@
+/*
+ * @(#)AFSFileException.java   2.0 01/04/16
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.Locale;
+
+/**
+ * An exception indicating that a file related error has occured in the 
+ * Java AFS API, in the Java AFS JNI, or in the AFS file system.
+ *
+ * <P> This exception extends Java's <code>java.io.IOException</code>
+ * and is therefore often used as a substitution for
+ * {@link java.io.IOException}.
+ *
+ * @version 2.0, 04/16/2001
+ * @version 1.0, 05/25/2000
+ * @see     AFSException
+ * @see     java.io.IOException
+ */
+public class AFSFileException extends java.io.IOException
+{
+  /**
+   * The AFS specific error number (code). 
+   * @see     #getErrorCode()
+   */
+  protected int errno;
+
+  /**
+   * Constructs an <code>AFSFileException</code> with the specified detail
+   * message.
+   *
+   * @param   reason   the detail message.
+   */
+  public AFSFileException (String reason)
+  {
+    super(reason);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS specific error number (code).  This code can be interpreted 
+   * by use of the <code>{@link ErrorTable}</code> class method 
+   * <code>{@link ErrorTable#getMessage(int)}</code>.
+   *
+   * @return  the AFS error code of this <code>AFSException</code> 
+   *          object. 
+   * @see     ErrorTable#getMessage(int)
+   */
+  public int getErrorCode() 
+  {
+    return errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage()
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno) : msg;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the locale specific error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage(Locale locale)
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno, locale) : msg;
+  }
+  /**
+   * Constructs an <code>AFSFileException</code> with the specified error
+   * code. This constructor will also generate the appropriate error message
+   * respective to the specified error code. 
+   *
+   * @param   errno    the AFS error number (error code).
+   */
+  public AFSFileException (int errno)
+  {
+    super(ErrorTable.getMessage( (errno == 0) ? ErrorTable.UNKNOWN : errno ));
+    this.errno = (errno == 0) ? ErrorTable.UNKNOWN : errno;
+  }
+  /**
+   * Constructs an <code>AFSFileException</code> with the specified detail
+   * message and specified error code.  In this constructor the specified
+   * detail message overrides the default AFS error message defined by the
+   * <code>ErrorTable</code> class.  Therefore, to retrieve the AFS specific 
+   * error message, you must use the <code>{@link #getAFSMessage}
+   * </code> method. The <code>{@link #getMessage}</code> method will return
+   * the message specified in this constructor.
+   *
+   * @param   reason   the detail message.
+   * @param   errno    the AFS error number (error code).
+   * @see     #getAFSMessage()
+   */
+  public AFSFileException (String reason, int errno)
+  {
+    super( (reason == null) ? ErrorTable.getMessage( errno ) : reason );
+    this.errno = errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message string defined by the <code>ErrorTable
+   * </code> class.  The message will be formatted according to the default
+   * Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage(Locale)
+   * @see        AFSFileException#AFSFileException(String, int)
+   * @see     ErrorTable#getMessage(int)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage() 
+  {
+    return ErrorTable.getMessage(errno);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message defined by the <code>ErrorTable</code>
+   * class.  The message will be formatted according to the specified Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   * @see        AFSFileException#AFSFileException(String, int)
+   * @see     ErrorTable#getMessage(int, Locale)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage(Locale locale)
+  {
+    return ErrorTable.getMessage(errno, locale);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns a string representation of this AFS Exception.
+   *
+   * @return  the AFS error message string of this 
+   *          <code>AFSFileException</code> object. 
+   *            
+   * @see     #getMessage()
+   */
+  public String toString()
+  {
+    return "AFSFileException: Error Code: " + errno + "; Message: " +
+            getMessage();
+  }
+  /*-----------------------------------------------------------------------*/
+}
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/AFSSecurityException.java b/src/JAVA/classes/org/openafs/jafs/AFSSecurityException.java
new file mode 100644 (file)
index 0000000..2aacb40
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * @(#)AFSSecurityException.java       2.0 01/04/16
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.Locale;
+
+/**
+ * An exception indicating that a security related error has occured in the
+ * Java AFS API, in the Java AFS JNI, or in the AFS file system.
+ *
+ * @version 1.0, 04/16/2001
+ * @see     AFSException
+ */
+public class AFSSecurityException extends SecurityException
+{
+  /**
+   * The AFS specific error number (code). 
+   * @see     #getErrorCode()
+   */
+  protected int errno;
+
+  /**
+   * Constructs an <code>AFSSecurityException</code> with the specified detail
+   * message. 
+   *
+   * @param   reason   the detail message.
+   */
+  public AFSSecurityException (String reason)
+  {
+    super(reason);
+  }
+  /**
+   * Constructs an <code>AFSSecurityException</code> with the specified error
+   * code. This constructor will also generate the appropriate error message
+   * respective to the specified error code. 
+   *
+   * @param   errno    the AFS error number (error code).
+   */
+  public AFSSecurityException (int errno)
+  {
+    super(ErrorTable.getMessage( (errno == 0) ? ErrorTable.UNKNOWN : errno ));
+    this.errno = (errno == 0) ? ErrorTable.UNKNOWN : errno;
+  }
+  /**
+   * Constructs an <code>AFSFileException</code> with the specified detail
+   * message and specified error code.  In this constructor the specified
+   * detail message overrides the default AFS error message defined by the
+   * <code>ErrorTable</code> class.  Therefore, to retrieve the AFS specific 
+   * error message, you must use the <code>{@link #getAFSMessage}
+   * </code> method. The <code>{@link #getMessage}</code> method will return
+   * the message specified in this constructor.
+   *
+   * @param   reason   the detail message.
+   * @param   errno    the AFS error number (error code).
+   * @see     #getAFSMessage()
+   */
+  public AFSSecurityException (String reason, int errno)
+  {
+    super( (reason == null) ? ErrorTable.getMessage( errno ) : reason );
+    this.errno = errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS specific error number (code).  This code can be interpreted 
+   * by use of the <code>{@link ErrorTable}</code> class method 
+   * <code>{@link ErrorTable#getMessage(int)}</code>.
+   *
+   * @return  the AFS error code of this <code>AFSException</code> 
+   *          object. 
+   * @see     ErrorTable#getMessage(int)
+   */
+  public int getErrorCode() 
+  {
+    return errno;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage()
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno) : msg;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the locale specific error message string of this exception.
+   *
+   * @return  the error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   */
+  public String getMessage(Locale locale)
+  {
+    String msg = super.getMessage();
+    return (msg == null) ? ErrorTable.getMessage(errno, locale) : msg;
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message string defined by the <code>ErrorTable
+   * </code> class.  The message will be formatted according to the default
+   * Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   *            
+   * @see     #getAFSMessage(Locale)
+   * @see        AFSSecurityException#AFSSecurityException(String, int)
+   * @see     ErrorTable#getMessage(int)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage() 
+  {
+    return ErrorTable.getMessage(errno);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns the AFS error message defined by the <code>ErrorTable</code>
+   * class.  The message will be formatted according to the specified Locale.
+   *
+   * <P> This message is also available from this object's super class
+   * method <code>getMessage</code>. However, this method will always return
+   * the string message associated with the actual AFS error code/number
+   * specified, whereas the {@link #getMessage()} method will return the
+   * string message intended for this Exception object, which may be
+   * an overridden message defined in the constructor of this Exception.
+   *
+   * @return  the AFS error message string of this exception object. 
+   * @param   locale the locale for which this message will be displayed
+   *            
+   * @see     #getAFSMessage()
+   * @see        AFSSecurityException#AFSSecurityException(String, int)
+   * @see     ErrorTable#getMessage(int, Locale)
+   * @see     java.lang.Exception#getMessage()
+   */
+  public String getAFSMessage(Locale locale)
+  {
+    return ErrorTable.getMessage(errno, locale);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns a string representation of this AFS Exception.
+   *
+   * @return  the AFS error message string of this 
+   *          <code>AFSSecurityException</code> object. 
+   *            
+   * @see     #getMessage()
+   */
+  public String toString()
+  {
+    return "AFSSecurityException: Error Code: " + errno + "; Message: " +
+            getMessage();
+  }
+  /*-----------------------------------------------------------------------*/
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Cell.java b/src/JAVA/classes/org/openafs/jafs/Cell.java
new file mode 100644 (file)
index 0000000..c723bd6
--- /dev/null
@@ -0,0 +1,1874 @@
+/*
+ * @(#)Cell.java       1.0 6/29/2001
+ *
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.ArrayList;
+import java.util.GregorianCalendar;
+import java.util.Date;
+
+/**
+ * An abstract representation of an AFS cell.  It holds information about 
+ * the cell, such as what users, groups, and servers exist in the cell.
+ * <BR><BR>
+ *
+ * Constructing a <code>Cell</code> object does not mean a new cell is
+ * created in the AFS file system -- on the contrary, a <code>Cell</code>
+ * object must be a representation of an already existing AFS cell.  There
+ * is no way to create a new AFS cell through this API.  See 
+ * <a href="http://www.openafs.org">OpenAFS.org</a> for information on how
+ * to create a new cell.<BR><BR>
+ * 
+ * The construction of a <code>Cell</code> object acts as an entry point
+ * for authentication into the AFS system.  Thus, when you construct a 
+ * <code>Cell</code>, you must pass in an authenticated <code>Token</code>
+ * of a user in the AFS cell that the <code>Cell</code> represents.  You
+ * will be authenticated as the user represented by <code>token</code> and 
+ * you will only be allowed to perform actions that the user is
+ * authorized to perform.  You must construct a <code>Cell</code> before 
+ * attempting to construct any other object in this package, since the
+ * other objects all require a <code>Cell</code> object on construction,
+ * either directly or indirectly.<BR><BR>
+ *
+ * Note that to successfully construct a <code>Cell</code> object, the 
+ * code must be running on a machine with a running AFS client, and the
+ * cell this object is to represent must have an entry in the client's
+ * CellServDB file.<BR><BR>
+ * 
+ * Each <code>Cell</code> object has its own individual set of
+ * <code>Server</code>s, <code>User</code>s, and <code>Group</code>s.
+ * This represents the properties and attributes of an actual AFS cell.
+ *
+ * If an error occurs during a method call, an 
+ * <code>AFSException</code> will be thrown.  This class is the Java
+ * equivalent of errors thrown by AFS; see {@link AFSException}
+ * for a complete description.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Cell</code> object. It shows how a <code>Cell</code> can be used to 
+ * get an abstract representation of an AFS server, and how it can obtain an 
+ * array of <code>User</code> objects, each of which is an abstract 
+ * representation of an AFS user.<BR><BR>
+ * 
+ * <PRE>
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.Partition;
+ * import org.openafs.jafs.Server;
+ * import org.openafs.jafs.Token;
+ * import org.openafs.jafs.User;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   private Token token;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = cell.getServer(serverName);
+ *
+ *     User[] users = cell.getUsers();
+ *     ...
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Cell implements java.io.Serializable
+{
+  protected ArrayList users;
+  protected ArrayList userNames;
+  protected ArrayList groups;
+  protected ArrayList groupNames;
+  protected ArrayList servers;
+  protected ArrayList serverNames;
+
+  protected String name;
+  protected int cellHandle;
+  protected Token token;
+
+  protected int maxGroupID;
+  protected int maxUserID;
+
+  protected GregorianCalendar tokenExpiration;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <CODE>Cell</CODE> object instance given 
+   * the <code>Token</code> that should represents an authenticated user
+   * with administrative access.  In order to get full access to the cell, 
+   * it is best that the <code>Token</code> provided have administrative 
+   * privileges.
+   *
+   * @param token      the user's authenticated token
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public Cell( Token token ) 
+      throws AFSException
+  {
+    this.token = token;
+    this.name  = token.getCellName();
+
+    cellHandle = getCellHandle( name, token.getHandle() );
+
+    users = null;
+    userNames = null;
+    groups = null;
+    groupNames = null;
+    servers = null;
+    serverNames = null;
+    cachedInfo = false;
+    tokenExpiration = null;
+  }
+
+  /**
+   * Constructs a new <CODE>Cell</CODE> object instance given 
+   * the <code>Token</code> that should represents an authenticated user
+   * with administrative access.  In order to get full access to the cell, 
+   * it is best that the <code>Token</code> provided have administrative 
+   * privileges.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications.  It ensures all data member values are set 
+   * and available without calling back to the filesystem at the first 
+   * request for them.  Use the {@link #refresh()} method to address any 
+   * coherency concerns.
+   *
+   * @param token              the user's authenticated token
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction; otherwise members 
+   *                           will be set upon access, which is the default 
+   *                           behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Cell( Token token, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(token);
+    if (preloadAllMembers) refresh(true);
+  }
+
+  /**
+   * Refreshes the properties of this Cell object instance with values 
+   * from the AFS cell it represents.  All properties that have been 
+   * initialized and/or accessed will be renewed according to the values 
+   * of the AFS cell this Cell object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    this.refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Cell object instance with values 
+   * from the AFS cell it represents.  If <CODE>all</CODE> is <CODE>true</CODE>
+   * then <U>all</U> of the properties of this Cell object instance will be 
+   * set, or renewed, according to the values of the AFS cell it represents, 
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise 
+   *              renew all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if( all || (users != null) ) {
+        refreshUsers();
+    }
+    if( all || (userNames != null) ) {
+        refreshUserNames();
+    }
+    if( all || (groups != null) ) {
+        refreshGroups();
+    }
+    if( all || (groupNames != null) ) {
+        refreshGroupNames();
+    }
+    if( all || (servers != null) ) {
+        refreshServers();
+    }
+    if( all || (serverNames != null) ) {
+        refreshServerNames();
+    }
+    if( all || cachedInfo ) {
+        refreshInfo();
+    }
+  }
+
+  /**
+   * Obtains the expiration time of the token being used by this 
+   * <code>Cell</code> object.  Does not actually refresh the token; that is,
+   * once a token is obtained, its expiration time will not change.  This
+   * method is mostly for consistency with the other methods.  It is mainly 
+   * used for getting the token information once; after that, it need not
+   * be called again.
+   *
+   * @exception AFSException  If an error occurs in the native code     
+   */
+  private void refreshTokenExpiration() throws AFSException
+  {
+    long expTime;
+
+    expTime = token.getExpiration();
+
+    tokenExpiration = new GregorianCalendar();
+    long longTime = expTime*1000;
+    Date d = new Date( longTime );
+    tokenExpiration.setTime( d );
+  }
+
+  /**
+   * Sets all the information fields of this <code>Cell</code> object, 
+   * such as max group and user ids, to trheir most current values.
+   *
+   * @exception AFSException  If an error occurs in the native code     
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    maxGroupID = getMaxGroupID( cellHandle );
+    maxUserID = getMaxUserID( cellHandle );
+    cachedInfo = true;
+  }
+
+  /**
+   * Obtains the most current list of <code>User</code> objects of this cell.  
+   * Finds all users that currently have a kas and/or pts entry for this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshUsers() throws AFSException
+  {
+    User currUser;
+    users = new ArrayList();
+
+    // get kas entries
+    int iterationId = getKasUsersBegin( cellHandle );
+
+    currUser = new User( this );
+    boolean authorized = false;
+    int r = 1;
+    while( r != 0 ) {
+      try {
+        if (authorized) {
+          users.add( currUser );
+          currUser = new User( this );
+        }
+        r = getKasUsersNext( cellHandle, iterationId, currUser );
+        authorized = true;
+      } catch (AFSException e) {
+        System.err.println("ERROR Cell::refreshUsers():kas (User: " 
+                          + currUser.getName() + ") -> " + e.getMessage());
+        authorized = false;
+        //if (org.openafs.jafs.ErrorCodes.isPermissionDenied(e.getErrorCode())) 
+       //r = 0;
+      }
+    } 
+    getKasUsersDone( iterationId );
+
+    //take the union with the pts entries
+    iterationId = getPtsUsersBegin( cellHandle );
+    authorized = false;
+    r = 1;
+    while( r != 0 ) {
+      try {
+        if (authorized) {
+          if( !users.contains( currUser ) ) {
+            users.add( currUser );
+          }
+          currUser = new User( this );
+        }
+        r = getPtsOnlyUsersNext( cellHandle, iterationId, currUser );
+        authorized = true;
+      } catch (AFSException e) {
+        System.err.println("ERROR Cell::refreshUsers():pts (User: " 
+                          + currUser.getName() + ") -> " + e.getMessage());
+        authorized = false;
+        //if (org.openafs.jafs.ErrorCodes.isPermissionDenied(e.getErrorCode())) 
+       // r = 0;
+      }
+    } 
+    getPtsUsersDone( iterationId );
+
+  }
+
+  /**
+   * Obtains the most current list of user names of this cell.  Finds
+   * all users that currently have a kas and/or pts entry for this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshUserNames() throws AFSException
+  {
+    String currName;
+    userNames = new ArrayList();
+
+    // get kas entries
+    int iterationId = getKasUsersBegin( cellHandle );
+    while( ( currName = getKasUsersNextString( iterationId )) != null ) {
+      userNames.add( currName );
+    } 
+    getKasUsersDone( iterationId );
+    
+    //take the union with the pts entries
+    iterationId = Cell.getPtsUsersBegin( cellHandle );
+    while( ( currName = getPtsOnlyUsersNextString( iterationId, cellHandle ) ) 
+          != null ) {
+      if( !userNames.contains( currName ) ) {
+        userNames.add( currName );
+      }
+    } 
+    getPtsUsersDone( iterationId );
+  }
+
+
+  /**
+   * Obtains the most current list of <code>Group</code> objects of this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroups() throws AFSException
+  {
+    Group currGroup;
+
+    int iterationId = getGroupsBegin( cellHandle );
+    
+    groups = new ArrayList();
+    
+    currGroup = new Group( this );
+    boolean authorized = false;
+    int r = 1;
+    while( r != 0 ) {
+      try {
+        if (authorized) {
+          groups.add( currGroup );
+          currGroup = new Group( this );
+        }
+        r = getGroupsNext( cellHandle, iterationId, currGroup );
+        authorized = true;
+      } catch (AFSException e) {
+        System.err.println("ERROR Cell::refreshGroups() (Group: " 
+                          + currGroup.getName() + ") -> " + e.getMessage());
+        authorized = false;
+        //if (org.openafs.jafs.ErrorCodes.isPermissionDenied(e.getErrorCode())) 
+       // r = 0;
+      }
+    } 
+    Cell.getGroupsDone( iterationId );
+  }
+
+  /**
+   * Obtains the most current list of group names of this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupNames() throws AFSException
+  {
+    String currName;
+
+    int iterationId = getGroupsBegin( cellHandle );
+    
+    groupNames = new ArrayList();
+    while( ( currName = getGroupsNextString( iterationId ) ) != null ) {
+        groupNames.add( currName );
+    } 
+    getGroupsDone( iterationId );
+  }
+
+  /**
+   * Obtains the most current list of <code>Server</code> objects of this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshServers() throws AFSException
+  {
+    Server currServer;
+
+    int iterationId = getServersBegin( cellHandle );
+   
+    servers = new ArrayList();
+    
+    currServer = new Server( this );
+    boolean authorized = false;
+    int r = 1;
+    while( r != 0 ) {
+      try {
+        if (authorized) {
+          System.out.println("[Java] Cell::refreshServers() -> adding server: " 
+                            + currServer.getName());
+          servers.add( currServer );
+          currServer = new Server( this );
+        }
+        r = getServersNext( cellHandle, iterationId, currServer );
+System.out.println("[Java] Cell::refreshServers() -> r: " + r);
+        authorized = true;
+      } catch (AFSException e) {
+        System.err.println("ERROR Cell::refreshServers() (Server: " 
+                          + currServer.getName() + ") -> " + e.getMessage());
+        authorized = false;
+        //if (e.getErrorCode() == org.openafs.jafs.ErrorCodes.PERMISSION_DENIED) 
+        // r = 0;
+      }
+    } 
+    getServersDone( iterationId );
+  }
+
+  /**
+   * Obtains the most current list of server names of this cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshServerNames() 
+      throws AFSException
+  {
+    String currName;
+
+    int iterationId = getServersBegin( cellHandle );
+
+    serverNames = new ArrayList();
+    while( ( currName = getServersNextString( iterationId ) ) != null ) {
+        serverNames.add( currName );
+    } 
+    getServersDone( iterationId );
+  }
+
+  /**
+   * Unauthenticates this </code>Token</code> object associated with this 
+   * <code>Cell</code> and deletes all of its stored information.  This 
+   * method should only be called when this <code>Cell</code> or any of the
+   * objects constructed using this <code>Cell</code> will not be used 
+   * anymore.  Note that this does not delete the actual AFS cell that this 
+   * <code>Cell</code> object represents; it merely closes the 
+   * representation.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void close() throws AFSException
+  {
+    Cell.closeCell( cellHandle );
+    token.close();
+    users = null;
+    userNames = null;
+    groups = null;
+    groupNames = null;
+    servers = null;
+    serverNames = null;
+    cachedInfo = false;
+    tokenExpiration = null;
+  }
+
+  //////////////// ACCESSORS ////////////////////////
+
+  /**
+   * Retrieves the <CODE>User</CODE> object (which is an abstract 
+   * representation of an actual AFS user) designated by <code>name</code>.
+   * If a user by that name does not actually exist in AFS in the cell
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   * @param name    the name of the user to retrieve
+   * @return <CODE>User</CODE> designated by <code>name</code>.
+   */
+  public User getUser(String name) throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    User user = new User(name, this);
+    return user;
+  }
+
+  /**
+   * Returns the total number of users who are registered with KAS and PTS,
+   * without duplicates.  If a user has a KAS entry and not a PTS entry,
+   * it will still be counted.  Conversely, if a user has a PTS entry and
+   * not KAS, it too will be counted.  Effectively it is a non-duplicate
+   * union of KAS and PTS user entries.
+   * 
+   * <P>If the total list of users or user names have already been 
+   * collected (see {@link #getUsers()}), then the returning value will be 
+   * calculated based upon the current list.  Otherwise, KAS and PTS will be
+   * explicitly queried for the information.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>User</code> array of the users of the cell.
+   * @see #getUsers()
+   * @see #getUserNames()
+   */
+  public int getUserCount() throws AFSException
+  {
+    if( users != null ) {
+      return users.size();
+    } else if( userNames != null ) {
+      return userNames.size();
+    } else {
+      int k = getKasUserCount(cellHandle);
+      int p = getPtsOnlyUserCount(cellHandle);
+      return k + p;
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>User</code> objects 
+   * associated with this <code>Cell</code>, each of which are an abstract 
+   * representation of an actual user of the AFS cell.  After this method
+   * is called once, it saves the array of <code>User</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>User</code> array of the users of the cell.
+   */
+  public User[] getUsers() throws AFSException
+  {
+    if( users == null ) refreshUsers();
+    return (User[]) users.toArray( new User[users.size()] );
+  }
+
+  /**
+   * Returns an array containing a subset of the <code>User</code> objects
+   * associated with this <code>Cell</code>, each of which is an abstract
+   * representation of an actual AFS user of the AFS cell.  The subset
+   * is a point-in-time list of users (<code>User</code> objects
+   * representing AFS users) starting at the complete array's index of
+   * <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this cell's complete array of users.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getUserCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Note:</B> PTS-only users are collected before KAS users
+   * and therefore will always, if PTS-only users exist, be within the
+   * lowest range of this cell's complete list of users.  PTS and KAS
+   * users are joined in a non-duplicating union and are consequently
+   * treated as a single list of users, thus <code>startIndex</code>
+   * does not necessarily indicate the first KAS user.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 users within this cell
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   User[] users;
+   *   if (cell.getUserCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < cell.getUserCount()) {
+   *       users = cell.<B>getUsers</B>(index, length);
+   *       for (int i = 0; i < users.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     users = cell.getUsers();
+   *     for (int i = 0; i < users.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array 
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of users in this cell
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getUserCount()
+   * @see #getUserNames(int, int)
+   * @see #getUsers()
+   */
+  public User[] getUsers(int startIndex, int length) throws AFSException
+  {
+    User[] users  = new User[length];
+    User currUser = new User( this );
+    int ptsOnlyCount = getPtsOnlyUserCount(cellHandle);
+    int iterationID = 0;
+    int indexPTS = 0;
+    int indexKAS = 0;
+
+    if (startIndex < ptsOnlyCount) {
+      int i = 0;
+      iterationID = getPtsUsersBegin(cellHandle);
+      while( getPtsOnlyUsersNext( cellHandle, iterationID, currUser ) != 0 &&
+             indexPTS < length )
+      {
+        if (i >= startIndex) {
+          users[indexPTS] = currUser;
+          currUser = new User( this );
+          indexPTS++;
+        }
+      } 
+      getPtsUsersDone( iterationID );
+
+      if (indexPTS < length) {
+        startIndex = 0;
+        length -= indexPTS;
+      } else {
+        return users;
+      }
+    } else {
+      startIndex -= (ptsOnlyCount - 1);
+    }
+
+    iterationID = getKasUsersBeginAt( cellHandle, startIndex );
+    while( getKasUsersNext(cellHandle, iterationID, currUser ) != 0 && 
+           indexKAS < length )
+    {
+      users[indexKAS] = currUser;
+      currUser = new User( this );
+      indexKAS++;
+    } 
+    getKasUsersDone( iterationID );
+
+    if (indexKAS < length) {
+      User[] u = new User[indexKAS + indexPTS];
+      System.arraycopy(users, 0, u, 0, u.length);
+      return u;
+    } else {
+      return users;
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the names of users 
+   * associated with this <code>Cell</code>. After this method
+   * is called once, it saves the array of <code>String</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * <P>This method is especially useful when managing iterations of
+   * large lists.  {@link #getUserCount()} can be used to determine if
+   * iteration management is practical.  In comparison to {@link #getUsers()},
+   * this method has yielded an average performance advantage of approximately
+   * 82% at 10K users; this statistic, however, strictly compares the response
+   * time of each method and understands that the {@link #getUsers()} method
+   * will return an array of populated <code>User</code> objects, whereas this
+   * method will return an array of <code>String</code> names.
+   * <BR><BR>
+   *
+   * @return an <code>String</code> array of the user names of the cell.
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public String[] getUserNames() throws AFSException
+  {
+    if( userNames == null ) refreshUserNames();
+    return (String[]) userNames.toArray( new String[userNames.size()] );
+  }
+
+  /**
+   * Returns an array containing a subset of the names of users
+   * associated with this <code>Cell</code>.  The subset
+   * is a point-in-time list of users (<code>String</code> names
+   * of AFS users) starting at the complete array's index of
+   * <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this cell's complete array of users.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getUserCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Note:</B> PTS-only users are collected before KAS users
+   * and therefore will always, if PTS-only users exist, be within the
+   * lowest range of this cell's complete list of users.  PTS and KAS
+   * users are joined in a non-duplicating union and are consequently
+   * treated as a single list of users, thus <code>startIndex</code>
+   * does not necessarily indicate the first KAS user.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 users within this cell
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   String[] users;
+   *   if (cell.getUserCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < cell.getUserCount()) {
+   *       users = cell.<B>getUserNames</B>(index, length);
+   *       for (int i = 0; i < users.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     users = cell.getUserNames();
+   *     for (int i = 0; i < users.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of user names in this cell
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getUserCount()
+   * @see #getUserNames()
+   * @see #getUsers(int, int)
+   */
+  public String[] getUserNames(int startIndex, int length) 
+    throws AFSException
+  {
+    String[] users  = new String[length];
+    String currUser;
+    int ptsOnlyCount = getPtsOnlyUserCount(cellHandle);
+    int iterationID = 0;
+    int indexPTS = 0;
+    int indexKAS = 0;
+
+    if (startIndex < ptsOnlyCount) {
+      int i = 0;
+      iterationID = getPtsUsersBegin(cellHandle);
+      while( (currUser = getPtsOnlyUsersNextString( iterationID, cellHandle ))
+             != null && indexPTS < length ) {
+        if (i >= startIndex) {
+          users[indexPTS] = currUser;
+          indexPTS++;
+        }
+      } 
+      getPtsUsersDone( iterationID );
+
+      if (indexPTS < length) {
+        startIndex = 0;
+        length -= indexPTS;
+      } else {
+        return users;
+      }
+    } else {
+      startIndex -= (ptsOnlyCount - 1);
+    }
+
+    iterationID = getKasUsersBeginAt( cellHandle, startIndex );
+    while( (currUser = getKasUsersNextString( iterationID )) != null &&
+            indexKAS < length ) {
+      users[indexKAS] = currUser;
+      indexKAS++;
+    } 
+    getKasUsersDone( iterationID );
+
+    if (indexKAS < length) {
+      String[] u = new String[indexKAS + indexPTS];
+      System.arraycopy(users, 0, u, 0, u.length);
+      return u;
+    } else {
+      return users;
+    }
+  }
+
+  /**
+   * Retrieves the <CODE>Group</CODE> object (which is an abstract 
+   * representation of an actual AFS group) designated by <code>name</code>.
+   * If a group by that name does not actually exist in AFS in the cell
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   * @param name the name of the group to retrieve
+   * @return <CODE>Group</CODE> designated by <code>name</code>.
+   */
+  public Group getGroup(String name) throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    Group group = new Group(name, this);
+    group.refresh(true);
+    return group;
+  }
+
+  /**
+   * Returns the total number of groups associated with this Cell.
+   * 
+   * <P>If the total list of groups or group names have already been 
+   * collected (see {@link #getGroups()}), then the returning value will be 
+   * calculated based upon the current list.  Otherwise, PTS will be
+   * explicitly queried for the information.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>User</code> array of the users of the cell.
+   * @see #getGroups()
+   * @see #getGroupNames()
+   */
+  public int getGroupCount() throws AFSException
+  {
+    if( groups != null ) {
+      return groups.size();
+    } else if( groupNames != null ) {
+      return groupNames.size();
+    } else {
+      return getGroupCount(cellHandle);
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Group</code> objects 
+   * associated with this <code>Cell</code>, each of which are an abstract 
+   * representation of an actual group of the AFS cell.  After this method
+   * is called once, it saves the array of <code>Group</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>Group</code> array of the groups of the cell.
+   */
+  public Group[] getGroups() throws AFSException
+  {
+    if( groups == null ) refreshGroups();
+    return (Group[]) groups.toArray( new Group[groups.size()] );
+  }
+
+  /**
+   * Returns an array containing a subset of the <code>Group</code> objects
+   * associated with this <code>Cell</code>, each of which is an abstract
+   * representation of an actual AFS group of the AFS cell.  The subset
+   * is a point-in-time list of groups (<code>Group</code> objects
+   * representing AFS groups) starting at the complete array's index of
+   * <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this cell's complete array of groups.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getGroupCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 groups within this cell
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   Group[] groups;
+   *   if (cell.getGroupCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < cell.getGroupCount()) {
+   *       groups = cell.<B>getGroups</B>(index, length);
+   *       for (int i = 0; i < groups.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     groups = cell.getGroups();
+   *     for (int i = 0; i < groups.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array 
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of groups in this cell
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getGroupCount()
+   * @see #getGroupNames(int, int)
+   * @see #getGroups()
+   */
+  public Group[] getGroups(int startIndex, int length) throws AFSException
+  {
+    Group[] groups  = new Group[length];
+    Group currGroup = new Group( this );
+    int i = 0;
+
+    int iterationID = getGroupsBeginAt( cellHandle, startIndex );
+
+    while( getGroupsNext( cellHandle, iterationID, currGroup ) != 0 
+          && i < length ) {
+      groups[i] = currGroup;
+      currGroup = new Group( this );
+      i++;
+    } 
+    getGroupsDone( iterationID );
+    if (i < length) {
+      Group[] v = new Group[i];
+      System.arraycopy(groups, 0, v, 0, i);
+      return v;
+    } else {
+      return groups;
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the names of groups
+   * associated with this <code>Cell</code>. After this method
+   * is called once, it saves the array of <code>String</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>String</code> array of the group names of the cell.
+   */
+  public String[] getGroupNames() throws AFSException
+  {
+    if( groupNames == null ) refreshGroupNames();
+    return (String[]) groupNames.toArray( new String[groupNames.size()] );
+  }
+
+  /**
+   * Returns an array containing a subset of the names of groups
+   * associated with this <code>Cell</code>.  The subset
+   * is a point-in-time list of groups (<code>String</code> names
+   * of AFS groups) starting at the complete array's index of
+   * <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this cell's complete array of groups.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getGroupCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 groups within this cell
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   String[] groups;
+   *   if (cell.getGroupCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < cell.getGroupCount()) {
+   *       groups = cell.<B>getGroupNames</B>(index, length);
+   *       for (int i = 0; i < groups.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     groups = cell.getGroupNames();
+   *     for (int i = 0; i < groups.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array 
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of group names in this cell
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getGroupCount()
+   * @see #getGroups(int, int)
+   * @see #getGroupNames()
+   */
+  public String[] getGroupNames(int startIndex, int length) 
+    throws AFSException
+  {
+    String[] groups  = new String[length];
+    String currGroup;
+    int i = 0;
+
+    int iterationID = getGroupsBeginAt( cellHandle, startIndex );
+
+    while( (currGroup = getGroupsNextString( iterationID )) != null &&
+            i < length )
+    {
+      groups[i] = currGroup;
+      i++;
+    } 
+    getGroupsDone( iterationID );
+    if (i < length) {
+      String[] v = new String[i];
+      System.arraycopy(groups, 0, v, 0, i);
+      return v;
+    } else {
+      return groups;
+    }
+  }
+
+  /**
+   * Retrieves the <CODE>Server</CODE> object (which is an abstract 
+   * representation of an actual AFS server) designated by <code>name</code>.
+   * If a group by that name does not actually exist in AFS in the cell
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   * @param name the name of the server to retrieve
+   * @return <CODE>Server</CODE> designated by <code>name</code>.
+   */
+  public Server getServer(String name) 
+      throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    Server server = new Server(name, this);
+    server.refresh(true);
+    return server;
+  }
+
+  /**
+   * Returns the total number of servers associated with this Cell.
+   * 
+   * <P>If the total list of servers or server names have already been 
+   * collected (see {@link #getServers()}), then the returning value will be 
+   * calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>User</code> array of the users of the cell.
+   * @see #getServers()
+   * @see #getServerNames()
+   */
+  public int getServerCount() throws AFSException
+  {
+    if( servers != null ) {
+      return servers.size();
+    } else if( serverNames != null ) {
+      return serverNames.size();
+    } else {
+      return getServerCount(cellHandle);
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Server</code> objects 
+   * associated with this <code>Cell</code>, each of which are an abstract 
+   * representation of an actual server of the AFS cell.  After this method
+   * is called once, it saves the array of <code>Server</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return an <code>Server</code> array of the servers of the cell.
+   */
+  public Server[] getServers() throws AFSException
+  {
+    if ( servers == null ) refreshServers();
+    return (Server[]) servers.toArray( new Server[servers.size()] );
+  }
+
+  /**
+   * Retrieves an array containing all of the names of servers
+   * associated with this <code>Cell</code>. After this method
+   * is called once, it saves the array of <code>String</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>String</code> array of the servers of the cell.
+   */
+  public String[] getServerNames() throws AFSException
+  {
+    if ( serverNames == null ) refreshServerNames();
+    return (String[]) serverNames.toArray( new String[serverNames.size()] );
+  }
+
+  /**
+   * Returns the maximum group ID that's been used within the cell.  
+   * The next auto-assigned group ID will be one less (more negative) 
+   * than this amount.   After this method is called once, it saves the 
+   * max group id and returns that id on subsequent calls, until the 
+   * {@link #refresh()} method is called and a more current id is obtained.
+   *
+   * @return an integer representing the maximum group ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getMaxGroupID() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return maxGroupID;
+
+  }
+
+  /**
+   * Returns the maximum user ID that's been used within the cell.  
+   * The next auto-assigned user ID will be one greater (more positive) 
+   * than this amount.   After this method is called once, it saves the 
+   * max user id and returns that id on subsequent calls, until the 
+   * {@link #refresh()} method is called and a more current id is obtained.
+   *
+   * @return an integer representing the maximum user ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getMaxUserID() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return maxUserID;
+  }
+
+  /**
+   * Returns the expiration time of the authentication token being used 
+   * by this <code>Cell</code> object.  After this time, this 
+   * <code>Cell</code> object will no longer be authorized to perform
+   * actions requiring administrative authority.
+   *
+   * @return expiration time of the token
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public GregorianCalendar getTokenExpiration() throws AFSException
+  {
+    if( tokenExpiration == null ) refreshTokenExpiration();
+    return tokenExpiration;
+  }
+
+  /**
+   * Returns the cell handle of this cell.
+   *
+   * @return the cell handle
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getCellHandle() throws AFSException
+  {
+    return cellHandle;
+  }
+
+  /**
+   * Returns the name of this cell.
+   *
+   * @return the cell name
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Sets the maximum group ID that's been used within the cell.  The next 
+   * auto-assigned group ID will be one less (more negative) than this amount.
+   *
+   * @param maxID an integer representing the maximum group ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void setMaxGroupID( int maxID ) throws AFSException
+  {
+    setMaxGroupID( cellHandle, maxID );
+    maxGroupID = maxID;
+  }
+
+  /**
+   * Sets the maximum user ID that's been used within the cell.  The next 
+   * auto-assigned user ID will be one greater (more positive) than this 
+   * amount.
+   *
+   * @param maxID an integer representing the maximum user ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void setMaxUserID( int maxID ) throws AFSException
+  {
+    setMaxUserID( cellHandle, maxID );
+    maxUserID = maxID;
+  }
+
+  /////////////// information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Cell</code>.  
+   * Contains the cell name followed by the names of its users and groups.
+   *
+   * @return    a <code>String</code> representation of this <code>Cell</code>
+   */
+  protected String getInfo()
+  {
+    String r = "Cell: " + name + "\n\n";
+    try {
+        r += "\tMax group ID: " + getMaxGroupID() + "\n";
+        r += "\tMax user ID: " + getMaxUserID() + "\n";
+        r += "\tToken expiration: " + getTokenExpiration().getTime() + "\n";
+    } catch( AFSException e ) {
+        return e.toString();
+    }
+
+    String[] servs;
+    String[] usrs;
+    String[] grps;
+    try {
+        usrs = getUserNames();
+        grps = getGroupNames();
+        servs = getServerNames();
+
+    } catch( Exception e ) {
+        return e.toString();
+    }
+
+    r += "--Users--\n";
+
+    for( int i = 0; i < usrs.length; i++ ) {
+
+        r += usrs[i] + "\n"; 
+    }
+
+    r += "\n--Groups--\n";
+
+    for( int i = 0; i < grps.length; i++ ) {
+
+        r += grps[i] + "\n"; 
+    }
+
+    r += "\n--Servers--\n";
+
+    for( int i = 0; i < servs.length; i++ ) {
+
+        r += servs[i] + "\n"; 
+    }
+
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the users of this <code>Cell</code>.
+   *
+   * @return    a <code>String</code> representation of the users
+   * @see       User#getInfo
+   */
+  protected String getInfoUsers() throws AFSException
+  {
+    String r;
+
+    r = "Cell: " + name + "\n\n";
+    r += "--Users--\n";
+
+    User usrs[] = getUsers();
+    for( int i = 0; i < usrs.length; i++ ) {
+        r += usrs[i].getInfo() + "\n";
+    }
+
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the groups of this <code>Cell</code>.
+   *
+   * @return    a <code>String</code> representation of the groups
+   * @see       Group#getInfo
+   */
+  protected String getInfoGroups() throws AFSException
+  {
+    String r;
+
+    r = "Cell: " + name + "\n\n";
+    r += "--Groups--\n";
+
+    Group grps[] = getGroups();
+    for( int i = 0; i < grps.length; i++ ) {
+        r += grps[i].getInfo() + "\n";
+    }
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the servers of this <code>Cell</code>.
+   *
+   * @return    a <code>String</code> representation of the servers
+   * @see       Server#getInfo
+   */
+  protected String getInfoServers() 
+      throws AFSException
+  {
+    String r;
+    r = "Cell: " + name + "\n\n";
+    r += "--Servers--\n";
+    Server[] servs = getServers();
+    for( int i = 0; i < servs.length; i++ ) {
+        r += servs[i].getInfo() + "\n";
+    }
+    return r;
+  }
+
+  /////////////// override methods ////////////////////
+
+  /**
+   * Tests whether two <code>Cell</code> objects are equal, based on their 
+   * names.  Does not test whether the objects are actually the same
+   * representational instance of the AFS cell.
+   *
+   * @param otherCell   the <code>Cell</code> to test
+   * @return whether the specifed user is the same as this user
+   */
+  public boolean equals( Cell otherCell )
+  {
+    return name.equals( otherCell.getName() );
+  }
+
+  /**
+   * Returns the name of this <CODE>Cell</CODE>
+   *
+   * @return the name of this <CODE>Cell</CODE>
+   */
+  public String toString()
+  {
+    return name;
+  }
+
+  /////////////// native methods Cell ////////////////////
+
+  /**
+   * Returns the total number of KAS users belonging to the cell denoted
+   * by <CODE>cellHandle</CODE>.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @return total count of KAS users
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getKasUserCount( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the kas users that belong to the cell.  
+   * Returns an iteration ID to be used by subsequent calls to 
+   * <code>getKasUsersNextString</code> (or <code>getKasUsersNext</code>) 
+   * and <code>getKasUsersDone</code>.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getKasUsersBegin( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the KAS users, starting at
+   * <code>startIndex</code>, that belong to the cell.  
+   * Returns an iteration ID to be used by subsequent calls to 
+   * <code>getKasUsersNextString</code> (or <code>getKasUsersNext</code>) 
+   * and <code>getKasUsersDone</code>.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @param startIndex    the starting base-zero index
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getKasUsersBeginAt( int cellHandle,
+                                                  int startIndex )
+    throws AFSException;
+
+  /**
+   * Returns the next kas user of the cell.  Returns <code>null</code> if there
+   * are no more users.  Appends instance names to principal names as follows:
+   * <i>principal</i>.<i>instance</i>
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getKasUsersBegin
+   * @return the name of the next user of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getKasUsersNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next kas user object of the cell.  Returns 0 if there
+   * are no more users, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getKasUsersBegin
+   * @param theUser   a User object to be populated with the values of 
+   *                  the next kas user
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getKasUsersNext( int cellHandle,  
+                                              int iterationId, 
+                                              User theUser )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getKasUsersBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getKasUsersDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the total number of PTS users belonging to the cell denoted
+   * by <CODE>cellHandle</CODE>.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @return total number of PTS users
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getPtsUserCount( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Returns the total number of PTS users, belonging to the cell denoted
+   * by <CODE>cellHandle</CODE>, that are not in KAS.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @return total number of users that are in PTS and not KAS
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getPtsOnlyUserCount( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the pts users that belong to the cell.  
+   * Returns an iteration ID to be used by subsequent calls to 
+   * <code>getPtsUsersNextString</code> (or <code>getPtsUsersNext</code>) 
+   * and <code>getPtsUsersDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getPtsUsersBegin( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next pts user of the cell.  Returns <code>null</code> if 
+   * there are no more users.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getPtsUsersBegin
+   * @return the name of the next user of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getPtsUsersNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the next pts user (who is not a kas user) of the cell.  
+   * Returns <code>null</code> if there are no more users.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @param cellHandle   the cell handle to which these users will belong
+   * @see Cell#getPtsUsersBegin
+   * @return the name of the next pts user (not kas user) of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getPtsOnlyUsersNextString( int iterationId, 
+                                                           int cellHandle )
+    throws AFSException;
+
+  /**
+   * Fills the next pts user object of the cell.  Returns 0 if there
+   * are no more users, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getPtsUsersBegin
+   * @param theUser   a User object to be populated with the values of 
+   *                  the next pts user
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getPtsUsersNext( int cellHandle, int iterationId,
+                                              User theUser )
+    throws AFSException;
+
+  /**
+   * Fills the next pts user (who does not have a kas entry) object of 
+   * the cell.  Returns 0 if there are no more users, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getPtsUsersBegin
+   * @param theUser   a User object to be populated with the values of 
+   *                  the next pts (with no kas) user
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getPtsOnlyUsersNext( int cellHandle, 
+                                                  int iterationId, 
+                                                  User theUser )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getPtsUsersBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getPtsUsersDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the total number of groups belonging to the cell denoted
+   * by <CODE>cellHandle</CODE>.
+   *
+   * @param cellHandle    the handle of the cell to which the groups belong
+   * @return total number of groups
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getGroupCount( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the groups that belong to the cell.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getGroupsNextString</code> (or <code>getGroupsNext</code>) and 
+   * <code>getGroupsDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the groups belong
+   * @see Cell#getCellHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getGroupsBegin( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the groups that belong to the cell, starting
+   * with element index <code>startIndex</code>.  Returns an iteration ID to 
+   * be used by subsequent calls to <code>getGroupsNextString</code> 
+   * (or <code>getGroupsNext</code>) and <code>getGroupsDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the groups belong
+   * @param startIndex    the starting base-zero index
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getGroupsBeginAt( int cellHandle, 
+                                                int startIndex )
+    throws AFSException;
+
+  /**
+   * Returns the next group of the cell.  Returns <code>null</code> if there
+   * are no more groups.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getGroupsBegin
+   * @return the name of the next user of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getGroupsNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next group object of the cell.  Returns 0 if there
+   * are no more groups, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getGroupsBegin
+   * @param theGroup   a Group object to be populated with the values of 
+   *                   the next group
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getGroupsNext( int cellHandle, int iterationId, 
+                                            Group theGroup )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getGroupsBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getGroupsDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the total number of servers belonging to the cell denoted
+   * by <CODE>cellHandle</CODE>.
+   *
+   * @param cellHandle    the handle of the cell to which the servers belong
+   * @return total number of servers
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getServerCount( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the servers in the cell.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getServersNextString</code> and <code>getServersDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the servers belong
+   * @see Cell#getCellHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getServersBegin( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next server of the cell.  Returns <code>null</code> if there
+   * are no more servers.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getServersBegin
+   * @return the name of the next server of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getServersNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next server object of the cell.  Returns 0 if there are no 
+   * more servers, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @param theServer   a Server object to be populated with the values 
+   *                    of the next server 
+   * @see Cell#getServersBegin
+   * @return 0 if there are no more servers, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getServersNext( int cellHandle, int iterationId, 
+                                             Server theServer )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see Cell#getServersBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getServersDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the name of the cell.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @return the name of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getCellName( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Creates a mount point for a volume within the file system.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param directory    the full path of the place in the AFS file system 
+   *                     at which to mount the volume
+   * @param volumeName   the name of the volume to mount
+   * @param readWrite   whether or not this is to be a readwrite mount point
+   * @param forceCheck  whether or not to check if this volume name exists
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void createMountPoint( int cellHandle, 
+                                                String directory, 
+                                                String volumeName, 
+                                                boolean readWrite, 
+                                                boolean forceCheck )
+    throws AFSException;
+
+  /*
+   * Sets an ACL for a given place in the AFS file system.
+   *
+   * @param directory    the full path of the place in the AFS file system 
+   *                     for which to add an entry
+   * @param username   the name of the user or group for which to add an entry
+   * @param read    whether or not to allow read access to this user
+   * @param write    whether or not to allow write access to this user
+   * @param lookup    whether or not to allow lookup access to this user
+   * @param delete    whether or not to allow deletion access to this user
+   * @param insert    whether or not to allow insertion access to this user
+   * @param lock    whether or not to allow lock access to this user
+   * @param admin    whether or not to allow admin access to this user
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public static native void setACL( String directory, String username, 
+                                       boolean read, boolean write, 
+                                       boolean lookup, boolean delete, 
+                                       boolean insert, boolean lock,
+                                      boolean admin )
+    throws AFSException;
+
+  /**
+   * Gets the maximum group pts ID that's been used within a cell.   
+   * The next auto-assigned group ID will be one less (more negative) 
+   * than this value.
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @return an integer reresenting the max group id in a cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getMaxGroupID( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Sets the maximum group pts ID that's been used within a cell.  The next 
+   * auto-assigned group ID will be one less (more negative) than this value.
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param maxID an integer reresenting the new max group id in a cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void setMaxGroupID( int cellHandle, int maxID )
+    throws AFSException;
+
+  /**
+   * Gets the maximum user pts ID that's been used within a cell.   
+   * The next auto-assigned user ID will be one greater (more positive) 
+   * than this value.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @return an integer reresenting the max user id in a cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getMaxUserID( int cellHandle )
+    throws AFSException;
+
+  /**
+   * Sets the maximum user pts ID that's been used within a cell.  The next 
+   * auto-assigned user ID will be one greater (more positive) than this value.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param maxID an integer reresenting the new max user id in a cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void setMaxUserID( int cellHandle, int maxID )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the cell portion of the native library.
+   * This method should be called when no more <code>Cell</code> objects 
+   * are expected to be used.
+   */
+  protected static native void reclaimCellMemory();
+
+
+  /////////////// native methods jafs_Cell ////////////////////
+
+  /**
+   * Opens a cell for administrative use, based on the token provided.  
+   * Returns a cell handle to be used by other methods as a means of 
+   * authentication.
+   *
+   * @param cellName    the name of the cell for which to get the handle
+   * @param tokenHandle    a token handle previously returned by a call to 
+   * {@link Token#getHandle}
+   * @return a handle to the open cell
+   * @exception AFSException  If an error occurs in the native code
+   * @see Token#getHandle
+   */
+  protected static native int getCellHandle( String cellName, int tokenHandle )
+       throws AFSException;
+  /**
+   * Closes the given currently open cell handle.
+   *
+   * @param cellHandle   the cell handle to close
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void closeCell( int cellHandle ) 
+       throws AFSException;
+}
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/ErrorTable.java b/src/JAVA/classes/org/openafs/jafs/ErrorTable.java
new file mode 100644 (file)
index 0000000..431f51a
--- /dev/null
@@ -0,0 +1,203 @@
+/*
+ * @(#)ErrorTable.java 2.0 11/06/2000
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.ResourceBundle;
+import java.util.Locale;
+
+/**
+ * Static class for error code message management.
+ *
+ * <P>Simply translates all error codes returned by the AFS native library
+ * to literal string messages according to the defined locale.
+ *                                                                             
+ * @version 2.0, 11/06/2000
+ */
+public final class ErrorTable
+{
+  /* Undefined Error Constants */
+  public static final int UNKNOWN = -3;
+  public static final int SPECIAL_CASE = -2;
+  public static final int GENERAL_FAILURE = -1;
+
+  /* Java Application Error Constants */
+  public static final int NULL = 1000;
+  public static final int INVALID_SESSION = 1001;
+  public static final int EXPIRED_SESSION = 1002;
+  public static final int OPERATION_ABORTED = 1003;
+  public static final int FORCED_ABORT = 1004;
+
+  /* General UNIX Error Constants */
+  public static final int NOT_PERMITTED = 1;
+  public static final int NOT_FOUND = 2;
+  public static final int IO_ERROR = 5;
+  public static final int NO_DEVICE_ADDRESS = 6;
+  public static final int BAD_FILE = 9;
+  public static final int TRY_AGAIN = 11;
+  public static final int OUT_OF_MEMORY = 12;
+  public static final int PERMISSION_DENIED = 13;
+  public static final int BAD_ADDRESS = 14;
+  public static final int DEVICE_BUSY = 16;
+  public static final int FILE_EXISTS = 17;
+  public static final int NO_DEVICE = 19;
+  public static final int NOT_DIRECTORY = 20;
+  public static final int IS_DIRECTORY = 21;
+  public static final int INVALID_ARG = 22;
+  public static final int FILE_OVERFLOW = 23;
+  public static final int FILE_BUSY = 26;
+  public static final int NAME_TOO_LONG = 36;
+  public static final int DIRECTORY_NOT_EMPTY = 39;
+  public static final int CONNECTION_TIMED_OUT = 110;
+  public static final int QUOTA_EXCEEDED = 122;
+
+  /* AFS Error Constants */
+  public static final int BAD_USERNAME = 180486;
+  public static final int BAD_PASSWORD = 180490;
+  public static final int EXPIRED_PASSWORD = 180519;
+  public static final int SKEWED_CLOCK = 180514;
+  public static final int ID_LOCKED = 180522;
+  public static final int CELL_NOT_FOUND = 180501;
+  public static final int USERNAME_EXISTS = 180481;
+  public static final int USER_DOES_NOT_EXIST = 180484;
+
+  /* AFS Authentication Error Constants */
+  public static final int PRPERM      = 267269;
+  public static final int UNOACCESS   = 5407;
+  public static final int BZACCESS    = 39430;
+  public static final int KANOAUTH    = 180488;
+  public static final int RXKADNOAUTH = 19270405;
+
+  private static java.util.Hashtable bundles;
+
+  static
+  {
+    bundles = new java.util.Hashtable(2);
+    try {
+      bundles.put(Locale.US, 
+        ResourceBundle.getBundle("ErrorMessages", Locale.US));
+      bundles.put(Locale.SIMPLIFIED_CHINESE, 
+        ResourceBundle.getBundle("ErrorMessages", Locale.SIMPLIFIED_CHINESE));
+    } catch (Exception e) {
+      bundles.put(Locale.getDefault(),
+        ResourceBundle.getBundle("ErrorMessages"));
+    }
+  }
+
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Tests to identify if the return code is a "Permission Denied" error.
+   *
+   * <P> This method will qualify <CODE>errno</CODE> against:
+   * <LI><CODE>ErrorTable.PERMISSION_DENIED</CODE>
+   * <LI><CODE>ErrorTable.PRPERM</CODE>
+   * <LI><CODE>ErrorTable.UNOACCESS</CODE>
+   * <LI><CODE>ErrorTable.BZACCESS</CODE>
+   * <LI><CODE>ErrorTable.KANOAUTH</CODE>
+   * <LI><CODE>ErrorTable.RXKADNOAUTH</CODE>
+   *
+   * @param            errno           Error Code/Number
+   * @return   boolean If <CODE>errno</CODE> is a "Permission Denied"
+   *                          error.
+   */
+  public static boolean isPermissionDenied(int errno)
+  {
+    return (errno == PERMISSION_DENIED || errno == PRPERM ||
+            errno == UNOACCESS || errno == BZACCESS || errno == KANOAUTH
+            || errno == RXKADNOAUTH);
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns a String message representing the error code (number) provided.
+   *
+   * <P> If the error code provided is out of range of the library of defined
+   * error codes, this method will return <CODE>Error number [###] unknown
+   * </CODE>. If an exception is thrown, this method will return either: 
+   * <CODE>Unknown error</CODE>, <CODE>Special case error</CODE>, or 
+   * <CODE>Invalid error code: ###</CODE>.
+   *
+   * @param            errno           Error Code/Number
+   * @return   String  Interpreted error message derived from 
+   *                          <CODE>errno</CODE>.
+   */
+  public static String getMessage(int errno)
+  {
+    return getMessage(errno, Locale.getDefault());
+  }
+  /*-----------------------------------------------------------------------*/
+  /**
+   * Returns a String message, respective to the provided locale, representing
+   * the error code (number) provided.
+   *
+   * <P> If the error code provided is out of range of the library of defined
+   * error codes, this method will return <CODE>Error number [###] unknown
+   * </CODE>. If an exception is thrown, this method will return either: 
+   * <CODE>Unknown error</CODE>, <CODE>Special case error</CODE>, or 
+   * <CODE>Invalid error code: ###</CODE>.
+   *
+   * @param   errno   Error Code/Number
+   * @param   locale  Locale of to be used for translating the message.
+   * @return  String  Interpreted error message derived from 
+   *                  <CODE>errno</CODE>.
+   */
+  public static String getMessage(int errno, Locale locale)
+  {
+    String msg = "Error number [" + errno + "] unknown.";
+    try {
+       msg = getBundle(locale).getString("E" + errno);
+    } catch (Exception e) {
+      try {
+         if (errno == 0) {
+           msg = "";
+         } else if (errno == GENERAL_FAILURE) {
+           msg = getBundle(locale).getString("GENERAL_FAILURE");
+         } else if (errno == UNKNOWN) {
+           msg = getBundle(locale).getString("UNKNOWN");
+         } else if (errno == SPECIAL_CASE) {
+           msg = getBundle(locale).getString("SPECIAL_CASE");
+         } else {
+           System.err.println("ERROR in ErrorCode getMessage(): " + e);
+           msg = "Invaid error code: " + errno;
+         }
+      } catch (Exception e2) {
+        //INGORE
+      }
+    } finally {
+      return msg;
+    }
+  }
+  /*-----------------------------------------------------------------------*/
+  private static ResourceBundle getBundle(Locale locale) throws Exception
+  {
+    if (locale == null) return getBundle(Locale.getDefault());
+    ResourceBundle rb = (ResourceBundle)bundles.get(locale);
+    if (rb == null) {
+      rb = ResourceBundle.getBundle("ErrorMessages", locale);
+      bundles.put(locale, rb);
+    }
+    return rb;
+  }
+}
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/File.java b/src/JAVA/classes/org/openafs/jafs/File.java
new file mode 100644 (file)
index 0000000..fe58b2c
--- /dev/null
@@ -0,0 +1,942 @@
+/*
+ * @(#)File.java       1.3 10/12/2000
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.IOException;
+import java.util.ArrayList;
+
+/*****************************************************************************/
+/**
+ * 
+ * An abstract representation of AFS file and directory pathnames.
+ *
+ * This class is an extension of the standard Java File class with file-based 
+ * manipulation methods overridden by integrated AFS native methods.
+ *
+ * <p> Extension methods include:
+ *
+ * <ol>
+ * <li> <code>{@link #isMountPoint}</code>
+ * <li> <code>{@link #isLink}</code>
+ * <li> <code>{@link #isValidated}</code>
+ * <li> <code>{@link #validate}</code>
+ * <li> <code>{@link #refresh}</code>
+ * <li> <code>{@link #getErrorCode}</code>
+ * <li> <code>{@link #getErrorMessage}</code>
+ * </ol>
+ *
+ * <p> For performance optimization, all newly constructed <code>File</code> 
+ * objects are only validated once.  Furthermore, if an abstract pathname 
+ * denotes a symbolic-link, then the {@link #isLink} attribute is set 
+ * to true and the {@link #getTarget} field member is populated with 
+ * this symbolic-link's target resource. (see {@link #getTarget})
+ * 
+ * <p> If you are interested in validating the target resource, simply 
+ * call {@link #validate} before calling any of the attribute accessors. 
+ * This action will <code>stat</code> the target resource, identifying 
+ * its associated attributes and populating them in this objects field 
+ * members.
+ * 
+ * <p> Following is an example of how to construct a new AFS File Object:
+ * <p><blockquote><pre>
+ *     try {
+ *         File file = new File("/afs/mycell.com/proj/");
+ *         if (file.isDirectory()) {
+ *           System.out.println("This is a directory.");
+ *         } else if (file.isLink()) {
+ *           System.out.println("This is a symbolic-link.");
+ *           System.out.println("  Its target is: " + file.getTarget());
+ *           file.validate();
+ *           if (file.isFile()) {
+ *             System.out.println("  This object is now a file!");
+ *           } else if (file.isDirectory()) {
+ *             System.out.println("  This object is now a directory!");
+ *           } else if (file.isMountPoint()) {
+ *             System.out.println("  This object is now a volume mount point!");
+ *           }
+ *         } else if (file.isMountPoint()) {
+ *           System.out.println("This is a volume mount point.");
+ *         } else if (file.isFile()) {
+ *           System.out.println("This is file.");
+ *           System.out.println("  its size is: " + file.length());
+ *         }
+ *     } catch (AFSFileException ae) {
+ *         System.out.println("AFS Exception: " + ae.getMessage());
+ *         System.out.println("AFS Error Code: " + ae.getErrorCode());
+ *     } catch (Exception e) {
+ *         System.out.println("Exception: " + e.getMessage());
+ *         e.printStackTrace();
+ *     }
+ * </pre></blockquote>
+ *
+ * @version 2.0, 04/16/2001 - Completely revised class for efficiency.
+ * @version 1.3, 10/12/2000 - Introduced error code capture from native methods.
+ * @version 1.2, 05/30/2000
+ */
+public class File extends java.io.File implements Comparable
+{
+  private String path;
+  private String type;
+  private String target;
+
+  /** Each member is mutually exclusive */
+  private boolean isMountPoint = false;
+  private boolean isDirectory = false;
+  private boolean isFile = false;
+  private boolean isLink = false;
+
+  private boolean exists;
+  private long lastModified;
+  private long length;
+
+  private ACL.Entry acl;
+  private boolean validated = false;
+  private long dirHandle;
+  private int permissionsMask;
+  private int errno;
+
+  /**
+   * Creates a new <code>File</code> instance by converting the given
+   * pathname string into an abstract pathname and validating it against
+   * the file system.  If the given string is an empty string, then the 
+   * result is the empty abstract pathname; otherwise the abstract pathname 
+   * is <code>validated</code> to represent a qualified file object.
+   *
+   * @param   pathname  A pathname string
+   * @throws  NullPointerException
+   *          If the <code>pathname</code> argument is <code>null</code>
+   * @throws  AFSFileException
+   *          If the user constructing this AFS file object is denied
+   *          access to stat the file or simply a stat cannot be performed
+   *          on the file. The reason code and message will be available
+   *          from {@link org.openafs.jafs.AFSFileException#getErrorCode} and 
+   *          {@link org.openafs.jafs.AFSFileException#getMessage} respectively.
+   *          <p> This exception <U>will not</U> be thrown if the file does not
+   *          exist.  Rather, the {@link #exists} attribute will be set to
+   *          <code>false</code>.
+   * @see     #validate()
+   */
+  public File(String pathname) throws AFSFileException
+  {
+       super(pathname);
+       path = getAbsolutePath();
+       validated = setAttributes();
+       if (!validated) throw new AFSFileException(errno);
+  }
+  /**
+   * Creates a new <code>File</code> instance by converting the given
+   * pathname string into an abstract pathname.  If the given string is
+   * an empty string, then the result is the empty abstract pathname.
+   *
+   * <p> The abstract pathname will remain <B>abstract</B> unless the
+   * <code>validate</code> parameter is set to <code>true</code>. This
+   * means that the abstract pathname will <U>not</U> be <code>validated</code>
+   * and therefore the file object will not represent a qualified, attributed,
+   * AFS file resource.  Rather, this constructor provides a method by which
+   * you can construct a non-validated <code>File</code> object (one that
+   * does not contain the file's complete status information).
+   *
+   * <p> This constructor is useful for creating file objects of file/path names
+   * that you know exist, however are unauthorized to <code>validate</code> (or
+   * <code>stat</code> - to obtain complete status information). For example,
+   * if you are permitted to <code>lookup</code> (see: {@link #canLookup}) the
+   * contents of a given directory yet <U>not</U> permitted to <code>read</code>
+   * (see: {@link #canRead}), then this constructor would enable you to render the
+   * contents of the directory without validating each entry.
+   *
+   * <p> <B>Please note:</B> this is the only constructor that does not throw an AFSFileException.
+   *
+   * @param   pathname  A pathname string
+   * @param   validate  A boolean flag to indicate if this abstract path
+   *                           should be validated.
+   * @throws  NullPointerException
+   *          If the <code>pathname</code> argument is <code>null</code>
+   * @see        #File(String)
+   * @see     #validate()
+   */
+  public File(String pathname, boolean validate)
+  {
+       super(pathname);
+       path = getAbsolutePath();
+       if (validate) validated = setAttributes();
+  }
+  /**
+   * Creates a new <code>File</code> instance from a parent pathname string
+   * and a child pathname string and validates it against the file system.
+   *
+   * <p> If <code>parent</code> is <code>null</code> then the new
+   * <code>File</code> instance is created as if by invoking the
+   * single-argument <code>File</code> constructor on the given
+   * <code>filename</code> string (child pathname).
+   *
+   * <p> Otherwise the <code>parent</code> pathname string is taken to denote
+   * a directory, and the <code>filename</code> string is taken to
+   * denote either a directory or a file. The directory or file will then be
+   * <code>validated</code> to represent a qualified file object.
+   *
+   * @param   parent   The parent pathname string
+   * @param   filename This file's pathname string (child of specified parent)
+   * @throws  NullPointerException
+   *          If <code>child</code> is <code>null</code>
+   * @throws  AFSFileException
+   *          If the user constructing this AFS file object is denied
+   *          access to stat the file or simply a stat cannot be performed
+   *          on the file. The reason code and message will be available
+   *          from {@link org.openafs.jafs.AFSFileException#getErrorCode} and 
+   *          {@link org.openafs.jafs.AFSFileException#getMessage} respectively.
+   *          <p> This exception <U>will not</U> be thrown if the file does not
+   *          exist.  Rather, the {@link #exists} attribute will be set to
+   *          <code>false</code>.
+   * @see     #validate()
+   */
+  public File(String parent, String filename) throws AFSFileException
+  {
+       super(parent, filename);
+       path = getAbsolutePath();
+       validated = setAttributes();
+       if (!validated) throw new AFSFileException(errno);
+  }
+  /**
+   * Creates a new <code>File</code> instance from a parent pathname string
+   * and a child pathname string.
+   *
+   * <p> If <code>parent</code> is <code>null</code> then the new
+   * <code>File</code> instance is created as if by invoking the
+   * single-argument <code>File</code> constructor on the given
+   * <code>filename</code> string (child pathname).
+   *
+   * <p> Otherwise the <code>parent</code> pathname string is taken to denote
+   * a directory, and the <code>filename</code> string is taken to
+   * denote either a directory or a file.
+   *
+   * <p> The abstract pathname will remain <B>abstract</B> unless the
+   * <code>validate</code> parameter is set to <code>true</code>. This
+   * means that the abstract pathname will <U>not</U> be <code>validated</code>
+   * and therefore the file object will not represent a qualified, attributed,
+   * AFS file resource.  Rather, this constructor provides a method by which
+   * you can construct a non-validated <code>File</code> object (one that
+   * does not contain the file's complete status information).
+   *
+   * <p> This constructor is useful for creating file objects of file/path names
+   * that you know exist, however are unauthorized to <code>validate</code> (or
+   * <code>stat</code> - to obtain complete status information). For example,
+   * if you are permitted to <code>lookup</code> (see: {@link #canLookup}) the
+   * contents of a given directory yet <U>not</U> permitted to <code>read</code>
+   * (see: {@link #canRead}), then this constructor would enable you to render the
+   * contents of the directory without validating each entry.
+   *
+   * @param   parent   The parent pathname string
+   * @param   filename This file's pathname string (child of specified parent)
+   * @param   validate  A boolean flag to indicate if this abstract path
+   *                           should be validated.
+   * @throws  NullPointerException
+   *          If <code>child</code> is <code>null</code>
+   * @throws  AFSFileException
+   *          If the user constructing this AFS file object is denied
+   *          access to stat the file or simply a stat cannot be performed
+   *          on the file. The reason code and message will be available
+   *          from {@link org.openafs.jafs.AFSFileException#getErrorCode} and 
+   *          {@link org.openafs.jafs.AFSFileException#getMessage} respectively.
+   *          <p> This exception <U>will not</U> be thrown if the file does not
+   *          exist.  Rather, the {@link #exists} attribute will be set to
+   *          <code>false</code>.
+   * @see        #File(String, String)
+   * @see     #validate()
+   */
+  public File(String parent, String filename, boolean validate) throws AFSFileException
+  {
+       super(parent, filename);
+       path = getAbsolutePath();
+       if (validate) {
+         validated = setAttributes();
+         if (!validated) throw new AFSFileException(errno);
+       }
+  }
+  /**
+   * Creates a new <code>File</code> instance from a parent abstract
+   * pathname and a child pathname string and validates it against the file system.
+   *
+   * <p> If <code>parent</code> is <code>null</code> then the new
+   * <code>File</code> instance is created as if by invoking the
+   * single-argument <code>File</code> constructor on the given
+   * <code>filename</code> string (child pathname).
+   *
+   * <p> Otherwise the <code>parent</code> abstract pathname is taken to
+   * denote a directory, and the <code>filename</code> string is taken
+   * to denote either a directory or a file.  The directory or file will then be
+   * <code>validated</code> to represent a qualified file object.
+   *
+   * @param   parent   The parent abstract pathname
+   * @param   filename This file's pathname string (child of specified parent)
+   * @param   validate  A boolean flag to indicate if this abstract path
+   *                           should be validated.
+   * @throws  NullPointerException
+   *          If <code>child</code> is <code>null</code>
+   * @throws  AFSFileException
+   *          If the user constructing this AFS file object is denied
+   *          access to stat the file or simply a stat cannot be performed
+   *          on the file. The reason code and message will be available
+   *          from {@link org.openafs.jafs.AFSFileException#getErrorCode} and 
+   *          {@link org.openafs.jafs.AFSFileException#getMessage} respectively.
+   *          <p> This exception <U>will not</U> be thrown if the file does not
+   *          exist.  Rather, the {@link #exists} attribute will be set to
+   *          <code>false</code>.
+   * @see     #validate()
+   */
+  public File(File parent, String filename) throws AFSFileException
+  {
+       super(parent, filename);
+       path = getAbsolutePath();
+       validated = setAttributes();
+       if (!validated) throw new AFSFileException(errno);
+  }
+  /**
+   * Creates a new <code>File</code> instance from a parent abstract
+   * pathname and a child pathname string.
+   *
+   * <p> If <code>parent</code> is <code>null</code> then the new
+   * <code>File</code> instance is created as if by invoking the
+   * single-argument <code>File</code> constructor on the given
+   * <code>filename</code> string (child pathname).
+   *
+   * <p> Otherwise the <code>parent</code> abstract pathname is taken to
+   * denote a directory, and the <code>filename</code> string is taken
+   * to denote either a directory or a file.
+   *
+   * <p> The abstract pathname will remain <B>abstract</B> unless the
+   * <code>validate</code> parameter is set to <code>true</code>. This
+   * means that the abstract pathname will <U>not</U> be <code>validated</code>
+   * and therefore the file object will not represent a qualified, attributed,
+   * AFS file resource.  Rather, this constructor provides a method by which
+   * you can construct a non-validated <code>File</code> object (one that
+   * does not contain the file's complete status information).
+   *
+   * <p> This constructor is useful for creating file objects of file/path names
+   * that you know exist, however are unauthorized to <code>validate</code> (or
+   * <code>stat</code> - to obtain complete status information). For example,
+   * if you are permitted to <code>lookup</code> (see: {@link #canLookup}) the
+   * contents of a given directory yet <U>not</U> permitted to <code>read</code>
+   * (see: {@link #canRead}), then this constructor would enable you to render the
+   * contents of the directory without validating each entry.
+   *
+   * @param   parent   The parent abstract pathname
+   * @param   filename This file's pathname string (child of specified parent)
+   * @param   validate  A boolean flag to indicate if this abstract path
+   *                           should be validated.
+   * @throws  NullPointerException
+   *          If <code>child</code> is <code>null</code>
+   * @throws  AFSFileException
+   *          If the user constructing this AFS file object is denied
+   *          access to stat the file or simply a stat cannot be performed
+   *          on the file. The reason code and message will be available
+   *          from {@link org.openafs.jafs.AFSFileException#getErrorCode} and 
+   *          {@link org.openafs.jafs.AFSFileException#getMessage} respectively.
+   *          <p> This exception <U>will not</U> be thrown if the file does not
+   *          exist.  Rather, the {@link #exists} attribute will be set to
+   *          <code>false</code>.
+   * @see     #validate()
+   * @see        #File(File, String)
+   */
+  public File(File parent, String filename, boolean validate) throws AFSFileException
+  {
+       super(parent, filename);
+       path = getAbsolutePath();
+       if (validate) {
+         validated = setAttributes();
+         if (!validated) throw new AFSFileException(errno);
+       }
+  }
+
+  /*****************************************************************************/
+
+  /**
+   * Validates this abstract pathname as an attributed AFS file object.  
+   * This method will, if authorized, perform a <code>stat</code> on the
+   * actual AFS file and update its respective field members; defining
+   * this file object's attributes.
+   *
+   * @throws  AFSSecurityException
+   *          If an AFS exception occurs while attempting to stat and set this
+   *          AFS file object's attributes.
+   */
+  public void validate() throws AFSSecurityException
+  {
+       validated = setAttributes();
+       if (!validated) throw new AFSSecurityException(errno);
+  }
+  /**
+   * Tests whether the file denoted by this abstract pathname has
+   * been validated.
+   *
+   * <P> Validation is always attempted upon construction of the file object,
+   * therefore if this method returns false, then you are not permitted to 
+   * <code>validate</code> this file and consequently all attribute accessors
+   * will be invalid.
+   *
+   * <P> This method should return <code>true</code> even if this abstract 
+   * pathname does not exist. If this is abstract pathname does not exist then
+   * the <code>{@link #exists}</code> method should return false, however this
+   * implies that the attribute accessors are valid and accurate; thus implying
+   * successful validation.
+   *
+   * <P> This method is useful before calling any of the attribute accessors
+   * to ensure a valid response. 
+   *
+   * @return <code>true</code> if and only if the file denoted by this
+   *          abstract pathname has been validated during or after object construction;
+   *          <code>false</code> otherwise
+   */
+  public boolean isValidated()
+  {
+       return validated;
+  }
+  /**
+   * Refreshes this AFS file object by updating its attributes.
+   * This method currently provides the same functionality as
+   * {@link #validate}.
+   *
+   * @throws  AFSSecurityException
+   *          If an AFS exception occurs while attempting to stat and update this
+   *          AFS file object's attributes.
+   * @see     #validate()
+   */
+  public void refresh() throws AFSSecurityException
+  {
+       validate();
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Tests whether the file denoted by this abstract pathname is a
+   * directory.
+   *
+   * @return <code>true</code> if and only if the file denoted by this
+   *          abstract pathname exists <em>and</em> is a directory;
+   *          <code>false</code> otherwise
+   */
+  public boolean isDirectory() 
+  {
+       return (isDirectory || isMountPoint) ? true : false;
+  }
+  /**
+   * Tests whether the file denoted by this abstract pathname is a normal
+   * file.  A file is <em>normal</em> if it is not a directory and, in
+   * addition, satisfies other system-dependent criteria.  Any non-directory
+   * file created by a Java application is guaranteed to be a normal file.
+   *
+   * @return  <code>true</code> if and only if the file denoted by this
+   *          abstract pathname exists <em>and</em> is a normal file;
+   *          <code>false</code> otherwise
+   */
+  public boolean isFile() 
+  {
+       return isFile;
+  }
+  /**
+   * Tests whether the file denoted by this abstract pathname is an
+   * AFS Volume Mount Point.
+   *
+   * @return <code>true</code> if and only if the file denoted by this
+   *          abstract pathname exists <em>and</em> is a mount point;
+   *          <code>false</code> otherwise
+   */
+  public boolean isMountPoint() 
+  {
+       return isMountPoint;
+  }
+  /**
+   * Tests whether the file denoted by this abstract pathname is a
+   * symbolic-link.
+   *
+   * @return <code>true</code> if and only if the file denoted by this
+   *          abstract pathname exists <em>and</em> is a symbolic-link;
+   *          <code>false</code> otherwise
+   */
+  public boolean isLink()
+  {
+       return isLink;
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Tests whether the file denoted by this abstract pathname exists.
+   *
+   * @return  <code>true</code> if and only if the file denoted by this
+   *          abstract pathname exists; <code>false</code> otherwise
+   */
+  public boolean exists() 
+  {
+       return exists;
+  }
+  /**
+   * Returns the time that the file denoted by this abstract pathname was
+   * last modified.
+   *
+   * @return  A <code>long</code> value representing the time the file was
+   *          last modified, measured in milliseconds since the epoch
+   *          (00:00:00 GMT, January 1, 1970), or <code>0L</code> if the
+   *          file does not exist or if an I/O error occurs
+   */
+  public long lastModified() 
+  {
+       return lastModified;
+  }
+  /**
+   * Returns the length of the file denoted by this abstract pathname.
+   *
+   * @return  The length, in bytes, of the file denoted by this abstract
+   *          pathname, or <code>0L</code> if the file does not exist
+   */
+  public long length() 
+  {
+       return length;
+  }
+  /**
+   * Returns an abstract pathname string that represents the target resource of
+   * of this file, if it is a symbolic-link.
+   *
+   * <p> If this abstract pathname <B>does not</B> denote a symbolic-link, then this
+   * method returns <code>null</code>.  Otherwise a string is
+   * returned that represents the target resource of this symbolic-link.
+   *
+   * @return  A string representation of this symbolic-link's target resource.
+   * @see     #isLink()
+   */
+  public String getTarget()
+  {
+       return target;
+  }
+  /**
+   * Returns an array of strings naming the files and directories in the
+   * directory denoted by this abstract pathname.
+   *
+   * <p> If this abstract pathname does not denote a directory, then this
+   * method returns <code>null</code>.  Otherwise an array of strings is
+   * returned, one for each file or directory in the directory.  Names
+   * denoting the directory itself and the directory's parent directory are
+   * not included in the result.  Each string is a file name rather than a
+   * complete path.
+   *
+   * <p> There is no guarantee that the name strings in the resulting array
+   * will appear in any specific order; they are not, in particular,
+   * guaranteed to appear in alphabetical order.
+   *
+   * @return  An array of strings naming the files and directories in the
+   *          directory denoted by this abstract pathname.  The array will be
+   *          empty if the directory is empty.  Returns <code>null</code> if
+   *          this abstract pathname does not denote a directory, or if an
+   *          I/O error occurs.
+   */
+  public String[] list()
+  {
+       try {
+         if (isFile()) {
+           errno = ErrorTable.NOT_DIRECTORY;
+           return null;
+         }
+         ArrayList buffer = new ArrayList();
+         dirHandle = listNative(buffer);
+         if (dirHandle == 0) {
+           return null;
+         } else {
+           return (String[])buffer.toArray(new String[0]);
+         }
+       } catch (Exception e) {
+         System.out.println(e);
+         return null;
+       }
+  }
+  /**
+   * Returns an ArrayList object containing strings naming the files and 
+   * directories in the directory denoted by this abstract pathname.
+   *
+   * <p> If this abstract pathname does not denote a directory, then this
+   * method returns <code>null</code>.  Otherwise an array of strings is
+   * returned, one for each file or directory in the directory.  Names
+   * denoting the directory itself and the directory's parent directory are
+   * not included in the result.  Each string is a file name rather than a
+   * complete path.
+   *
+   * <p> There is no guarantee that the name strings in the resulting array
+   * will appear in any specific order; they are not, in particular,
+   * guaranteed to appear in alphabetical order.
+   *
+   * @return  An array of strings naming the files and directories in the
+   *          directory denoted by this abstract pathname.  The array will be
+   *          empty if the directory is empty.  Returns <code>null</code> if
+   *          this abstract pathname does not denote a directory, or if an
+   *          I/O error occurs.
+   * @throws  AFSSecurityException
+   *          If you are not authorized to list the contents of this directory
+   * @throws  AFSFileException
+   *          If this file object is not a <code>mount point</code>, <code>link
+   *          </code>, or <code>directory</code> <B>or</B> an unexpected AFS 
+   *          error occurs.
+   * @see     #list()
+   */
+  public ArrayList listArray() throws AFSFileException
+  {
+       try {
+         if (isFile()) throw new AFSFileException(ErrorTable.NOT_DIRECTORY);
+         ArrayList buffer = new ArrayList();
+         dirHandle = listNative(buffer);
+         if (dirHandle == 0) {
+           if (errno == ErrorTable.PERMISSION_DENIED) {
+               throw new AFSSecurityException(errno);
+           } else {
+               throw new AFSFileException(errno);
+           }
+         } else {
+           return buffer;
+         }
+       } catch (Exception e) {
+         System.out.println(e);
+         throw new AFSFileException(errno);
+       }
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Deletes the file or directory denoted by this abstract pathname.  If
+   * this pathname denotes a directory, then the directory must be empty in
+   * order to be deleted.
+   *
+   * @return  <code>true</code> if and only if the file or directory is
+   *          successfully deleted; <code>false</code> otherwise
+   */
+  public boolean delete()
+  {
+       try {
+         if(this.isDirectory()) {
+           return this.rmdir();
+         } else if(this.isFile() || this.isLink()) {
+           return this.rmfile();
+         }
+         return false;
+       } catch (Exception e) {
+         System.out.println(e);
+         return false;
+       }
+  }
+  /**
+   * Copies the file denoted by this abstract pathname to the destination
+   * file provided.  Then checks the newly copied file's size to
+   * test for file size consistency.
+   *
+   * @param  dest  The new abstract pathname for the named file
+   * 
+   * @return  <code>true</code> if and only if the file that was copied
+   *          reports the same file size (length) as that of this file;
+   *          <code>false</code> otherwise
+   *
+   * @throws  AFSFileException
+   *          If an I/O or AFS exception is encountered while copying the file.
+   */
+  public boolean copyTo(File dest) throws AFSFileException
+  {
+    FileInputStream fis  = new FileInputStream(this);
+    FileOutputStream fos = new FileOutputStream(dest);
+    byte[] buf = new byte[1024];
+    int i = 0;
+    while((i=fis.read(buf))!=-1) {
+      fos.write(buf, 0, i);
+    }
+    fis.close();
+    fos.close();
+    dest.validate();
+    return (dest.length() == this.length());
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Returns the permissions mask of the ACL for this object relative to the user accessing it.
+   *
+   * @return  the permissions mask of this object based upon the current user.
+   * @see     org.openafs.jafs.ACL.Entry#getPermissionsMask()
+   */
+  public int getPermissionsMask() 
+  {
+       return getRights();
+  }
+  /**
+   * Tests whether the user can administer the ACL (see: {@link org.openafs.jafs.ACL}
+   * of the directory denoted by this abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canAdmin
+   * @return  <code>true</code> if and only if the directory specified by this
+   *          abstract pathname exists <em>and</em> can be administered by the
+   *          current user; <code>false</code> otherwise
+   */
+  public boolean canAdmin()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canAdmin();
+  }
+  /**
+   * Tests whether the current user can delete the files or subdirectories of
+   * the directory denoted by this abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canDelete
+   * @return  <code>true</code> if and only if the directory specified by this
+   *          abstract pathname exists <em>and</em> permits deletion of its files
+   *          and subdirectories by the current user; <code>false</code> otherwise
+   */
+  public boolean canDelete()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canDelete();
+  }
+  /**
+   * Tests whether the current user can insert a file into the directory
+   * denoted by this abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canInsert
+   * @return  <code>true</code> if and only if the directory specified by this
+   *          abstract pathname exists <em>and</em> a file can be inserted by the
+   *          current user; <code>false</code> otherwise
+   */
+  public boolean canInsert()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canInsert();
+  }
+  /**
+   * Tests whether the current user can lock the file denoted by this 
+   * abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canLock
+   * @return  <code>true</code> if and only if the file specified by this
+   *          abstract pathname exists <em>and</em> can be locked by the
+   *          current user; <code>false</code> otherwise
+   */
+  public boolean canLock()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canLock();
+  }
+  /**
+   * Tests whether the current user can lookup the contents of the directory
+   * denoted by this abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canLookup
+   * @return  <code>true</code> if and only if the directory specified by this
+   *          abstract pathname exists <em>and</em> its contents can be listed by the
+   *          current user; <code>false</code> otherwise
+   */
+  public boolean canLookup()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canLookup();
+  }
+  /**
+   * Tests whether the current user can read the file denoted by this
+   * abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canRead
+   * @return  <code>true</code> if and only if the file specified by this
+   *          abstract pathname exists <em>and</em> can be read by the
+   *          current user; <code>false</code> otherwise
+   */
+  public boolean canRead()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canRead();
+  }
+  /**
+   * Tests whether the current user can modify to the file denoted by this
+   * abstract pathname.
+   *
+   * @see org.openafs.jafs.ACL.Entry#canWrite
+   * @return  <code>true</code> if and only if the file system actually
+   *          contains a file denoted by this abstract pathname <em>and</em>
+   *          the current user is allowed to write to the file;
+   *          <code>false</code> otherwise.
+   */
+  public boolean canWrite()
+  {
+       if (acl == null) acl = new ACL.Entry(getRights());
+       return acl.canWrite();
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Closes the directory denoted by this abstract pathname.
+   *
+   * @return  <code>true</code> if and only if the directory is
+   *          successfully closed; <code>false</code> otherwise
+   */
+  public boolean close()
+  {
+       if (dirHandle == 0) {
+         return false;
+       }
+       return closeDir(dirHandle);
+  }
+  /*-------------------------------------------------------------------------*/
+  /**
+   * Returns the AFS specific error number (code).  This code can be interpreted 
+   * by use of <code>{@link org.openafs.jafs.ErrorTable}</code> static class method 
+   * <code>{@link org.openafs.jafs.ErrorTable#getMessage}</code>
+   *
+   * @return  the AFS error code (number) associated with the last action performed
+   *          on this object.
+   * @see     org.openafs.jafs.ErrorTable#getMessage(int)
+   */
+  public int getErrorCode() 
+  {
+       return errno;
+  }
+  /**
+   * Returns the AFS error message string defined by the <code>{@link org.openafs.jafs.ErrorTable}</code>
+   * class.
+   *
+   * @return  the AFS error message string associated with the last action performed
+   *          on this object.
+   * @see     org.openafs.jafs.ErrorTable#getMessage(int)
+   */
+  public String getErrorMessage() 
+  {
+       return ErrorTable.getMessage(errno);
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two File objects relative to their filenames and <B>does not</B>
+   * compare their respective absolute paths.  Alphabetic case is significant in 
+   * comparing filenames.
+   *
+   * @param   file  The File object to be compared to this file's filename
+   * 
+   * @return  Zero if the argument is equal to this file's filename, a
+   *           value less than zero if this file's filename is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this file's filename is lexicographically
+   *           greater than the argument
+   *
+   * @since   JDK1.2
+   */
+  public int compareTo(File file) {
+    return this.getName().compareTo(file.getName());
+  }
+  /**
+   * Compares this file to another File object.  If the other object
+   * is an abstract pathname, then this function behaves like <code>{@link
+   * #compareTo(File)}</code>.  Otherwise, it throws a
+   * <code>ClassCastException</code>, since File objects can only be
+   * compared to File objects.
+   *
+   * @param   o  The <code>Object</code> to be compared to this abstract pathname
+   *
+   * @return  If the argument is an File object, returns zero
+   *          if the argument is equal to this file's filename, a value
+   *          less than zero if this file's filename is lexicographically
+   *          less than the argument, or a value greater than zero if this
+   *          file's filename is lexicographically greater than the
+   *          argument
+   *
+   * @throws  <code>ClassCastException</code> if the argument is not an
+   *             File object
+   *
+   * @see     java.lang.Comparable
+   * @since   JDK1.2
+   */
+  public int compareTo(Object o) throws ClassCastException 
+  { 
+    File file = (File)o;
+    return compareTo(file);
+  } 
+
+  /////////////// public native methods ////////////////////
+
+  /**
+   * Creates the directory named by this abstract pathname.
+   *
+   * @return  <code>true</code> if and only if the directory was
+   *          created; <code>false</code> otherwise
+   */
+  public native boolean mkdir();
+  /**
+   * Renames the file denoted by this abstract pathname.
+   *
+   * @param  dest  The new abstract pathname for the named file
+   * 
+   * @return  <code>true</code> if and only if the renaming succeeded;
+   *          <code>false</code> otherwise
+   *
+   * @throws  NullPointerException  
+   *          If parameter <code>dest</code> is <code>null</code>
+   */
+  public native boolean renameTo(File dest);
+  /**
+   * Performs a file <code>stat</code> on the actual AFS file and populates  
+   * this object's respective field members with the appropriate values.
+   * method will, if authorized, perform a <code>stat</code> on the
+   * actual AFS file and update its respective field members; defining
+   * this file object's attributes.
+   *
+   * <P><B>This method should not be used directly for refreshing or validating
+   * this AFS file object.  Please use {@link #validate} instead.</B>
+   *
+   * @return  <code>true</code> if and only if the current user is allowed to stat the file;
+   *          <code>false</code> otherwise.
+   * @see     #validate()
+   */
+  public native boolean setAttributes() throws AFSSecurityException;
+
+  /////////////// private native methods ////////////////////
+
+  /**
+   * List the contents of this directory.
+   *
+   * @return  the directory handle
+   */
+  private native long listNative(ArrayList buffer) throws AFSSecurityException;
+  /**
+   * Close the currently open directory using a previously obtained handle.
+   *
+   * @return  true if the directory closes without error
+   */
+  private native boolean closeDir(long dp) throws AFSSecurityException;
+  /**
+   * Removes/deletes the current directory.
+   *
+   * @return  true if the directory is removed without error
+   */
+  private native boolean rmdir() throws AFSSecurityException;
+  /**
+   * Removes/deletes the current file.
+   *
+   * @return  true if the file is removed without error
+   */
+  private native boolean rmfile() throws AFSSecurityException;
+  /**
+   * Returns the permission/ACL mask for this directory
+   *
+   * @return  permission/ACL mask
+   */
+  private native int getRights() throws AFSSecurityException;
+  /*-------------------------------------------------------------------------*/
+}
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/FileInputStream.java b/src/JAVA/classes/org/openafs/jafs/FileInputStream.java
new file mode 100644 (file)
index 0000000..ea7f65c
--- /dev/null
@@ -0,0 +1,167 @@
+/*
+ * @(#)FilterInputStream.java  1.0 00/10/10
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.InputStream;
+
+/**
+ * This class is a file input stream for files within AFS.  
+ * It is an input stream for reading data from a 
+ * <code>{@link org.openafs.jafs.File}</code>.
+ *
+ * @version 2.1, 08/03/2001
+ * @see org.openafs.jafs.File
+ * @see org.openafs.jafs.FileOutputStream
+ * @see java.io.FileInputStream
+ */
+public class FileInputStream extends InputStream
+{
+  /** Status indicator for the current state of this file input stream */
+  private int fileDescriptor;
+
+  /**
+   * Creates a <code>FileInputStream</code> by
+   * opening a connection to an actual AFS file,
+   * the file named by the path name <code>name</code>
+   * in the AFS file system.
+   *
+   * @param name the name of the file to read from
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file does not, or cannot be opened for any 
+   *                   other reason, including authorization.
+   */
+  public FileInputStream(String name) throws AFSFileException
+  {
+    this.fileDescriptor = this.openReadOnly(name);
+  }
+  /**
+   * Creates a <code>FileInputStream</code> by
+   * opening a connection to an actual AFS file,
+   * the file represented by file <code>file</code>
+   * in the AFS file system.
+   *
+   * @param file       an AFS file object representing a file to read from
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file does not, or cannot be opened for any 
+   *                   other reason, including authorization.
+   */
+  public FileInputStream(File file) throws AFSFileException
+  {
+    this(file.getPath());
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Reads the next byte of data from this input stream. The value 
+   * byte is returned as an <code>int</code> in the range 
+   * <code>0</code> to <code>255</code>. If no byte is available 
+   * because the end of the stream has been reached, the value 
+   * <code>-1</code> is returned. This method blocks until input data 
+   * is available, the end of the stream is detected, or an exception 
+   * is thrown. 
+   *
+   * <p>This method simply performs <code>in.read()</code> and returns 
+   * the result.
+   *
+   * @return   the next byte of data, or <code>-1</code> if the end of the
+   *           stream is reached.
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   * @see        java.io.FileInputStream#read
+   */
+  public int read() throws AFSFileException
+  {
+    byte[] bytes = new byte[1];
+    this.read(bytes, 0, 1);
+    return bytes[0];
+  }
+  /**
+   * Reads up to <code>b.length</code> bytes of data from this input
+   * stream into an array of bytes. This method blocks until some input
+   * is available.
+   *
+   * @param      b   the buffer into which the data is read.
+   * @return     the total number of bytes read into the buffer, or
+   *             <code>-1</code> if there is no more data because the end of
+   *             the file has been reached.
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  public int read(byte[] b) throws AFSFileException
+  {
+    return this.read(b, 0, b.length);
+  }
+
+  /////////////// public native methods ////////////////////
+
+  /**
+   * Reads up to <code>len</code> bytes of data from this input stream
+   * into an array of bytes. This method blocks until some input is
+   * available.
+   *
+   * @param      b     the buffer into which the data is read.
+   * @param      off   the start offset of the data.
+   * @param      len   the maximum number of bytes read.
+   * @return     the total number of bytes read into the buffer, or
+   *             <code>-1</code> if there is no more data because the end of
+   *             the file has been reached.
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  public native int read(byte[] b, int off, int len) throws AFSFileException;
+  /**
+   * Skips over and discards <code>n</code> bytes of data from the
+   * input stream. The <code>skip</code> method may, for a variety of
+   * reasons, end up skipping over some smaller number of bytes,
+   * possibly <code>0</code>. The actual number of bytes skipped is returned.
+   *
+   * @param      n   the number of bytes to be skipped.
+   * @return     the actual number of bytes skipped.
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  public native long skip(long n) throws AFSFileException;
+  /**
+   * Closes this file input stream and releases any system resources
+   * associated with the stream.
+   *
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  public native void close() throws AFSFileException;
+
+  /////////////// private native methods ////////////////////
+
+  /**
+   * Opens the specified AFS file for reading.
+   *
+   * @param name fileName of file to be opened
+   * @return    file descriptor
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  private native int openReadOnly(String fileName) throws AFSFileException;
+
+  /*-------------------------------------------------------------------------*/
+}
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/FileOutputStream.java b/src/JAVA/classes/org/openafs/jafs/FileOutputStream.java
new file mode 100644 (file)
index 0000000..95c92dd
--- /dev/null
@@ -0,0 +1,217 @@
+/*
+ * @(#)FilterOutputStream.java 1.0 00/10/10
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.OutputStream;
+
+/**
+ * This class is a file output stream for files within AFS.  
+ * It is an output stream for writing data to a 
+ * <code>{@link org.openafs.jafs.File}</code>.
+ *
+ * @version 2.1, 08/03/2001
+ * @see org.openafs.jafs.File
+ * @see org.openafs.jafs.FileInputStream
+ * @see java.io.FileOutputStream
+ */
+public class FileOutputStream extends OutputStream 
+{
+  /** Status indicator for the current state of this file output stream */
+  private int fileDescriptor;
+
+  /**
+   * Creates an output file stream to write to the AFS file with the 
+   * specified name.
+   * <p>
+   * If the file exists but is a directory rather than a regular file, does
+   * not exist but cannot be created, or cannot be opened for any other
+   * reason then a <code>AFSFileException</code> is thrown.
+   *
+   * @param name the name of the file to write to
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file exists but is a directory
+   *                   rather than a regular file, does not exist but cannot
+   *                   be created, or cannot be opened for any other reason, including
+   *                   authorization.
+   */
+  public FileOutputStream(String name) throws AFSFileException
+  {
+    this(name, false);
+  }
+  /**
+   * Creates an output file stream to write to the AFS file with the specified
+   * <code>name</code>.  If the second argument is <code>true</code>, then
+   * bytes will be written to the end of the file rather than the beginning.
+   * <p>
+   * If the file exists but is a directory rather than a regular file, does
+   * not exist but cannot be created, or cannot be opened for any other
+   * reason then a <code>AFSFileException</code> is thrown.
+   * 
+   * @param    name            the name of the file to write to
+   * @param    append  if <code>true</code>, then bytes will be written
+   *                           to the end of the file rather than the beginning
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file exists but is a directory
+   *                   rather than a regular file, does not exist but cannot
+   *                   be created, or cannot be opened for any other reason, including
+   *                   authorization.
+   */
+  public FileOutputStream(String name, boolean append) throws AFSFileException
+  {
+    if (append) {
+      fileDescriptor = this.openAppend(name);
+    } else {
+      fileDescriptor = this.openWrite(name);
+    }
+  }
+  /**
+   * Creates a file output stream to write to the AFS file represented by 
+   * the specified <code>File</code> object.
+   * <p>
+   * If the file exists but is a directory rather than a regular file, does
+   * not exist but cannot be created, or cannot be opened for any other
+   * reason then a <code>AFSFileException</code> is thrown.
+   *
+   * @param    file         the AFS file to be opened for writing.
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file exists but is a directory
+   *                   rather than a regular file, does not exist but cannot
+   *                   be created, or cannot be opened for any other reason, including
+   *                   authorization.
+   * @see    org.openafs.jafs.File#getPath()
+   */
+  public FileOutputStream(File file) throws AFSFileException
+  {
+    this(file.getPath(), false);
+  }
+  /**
+   * Creates a file output stream to write to the AFS file represented by 
+   * the specified <code>File</code> object.
+   * <p>
+   * If the file exists but is a directory rather than a regular file, does
+   * not exist but cannot be created, or cannot be opened for any other
+   * reason then a <code>AFSFileException</code> is thrown.
+   *
+   * @param file               the AFS file to be opened for writing.
+   * @param    append  if <code>true</code>, then bytes will be written
+   *                           to the end of the file rather than the beginning
+   * @exception        AFSFileException  If an AFS specific error occurs, 
+   *                   if the file exists but is a directory
+   *                   rather than a regular file, does not exist but cannot
+   *                   be created, or cannot be opened for any other reason, including
+   *                   authorization.
+   * @see    org.openafs.jafs.File#getPath()
+   */
+  public FileOutputStream(File file, boolean append) throws AFSFileException
+  {
+    this(file.getPath(), append);
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Writes the specified <code>byte</code> to this file output stream. 
+   * <p>
+   * Implements the abstract <tt>write</tt> method of <tt>OutputStream</tt>. 
+   *
+   * @param      b   the byte to be written.
+   * @exception  AFSFileException  if an error occurs.
+   */
+  public void write(int b) throws AFSFileException 
+  {
+    byte[] bytes = new byte[1];
+    bytes[0] = (byte) b;
+    this.write(bytes, 0, 1);
+  }
+  /**
+   * Writes <code>b.length</code> bytes from the specified byte array 
+   * to this file output stream. 
+   * <p>
+   * Implements the <code>write</code> method of three arguments with the 
+   * arguments <code>b</code>, <code>0</code>, and 
+   * <code>b.length</code>. 
+   * <p>
+   * Note that this method does not call the one-argument 
+   * <code>write</code> method of its underlying stream with the single 
+   * argument <code>b</code>. 
+   *
+   * @param    b   the data to be written.
+   * @exception  AFSFileException  if an error occurs.
+   * @see    #write(byte[], int, int)
+   * @see    java.io.FilterOutputStream#write(byte[], int, int)
+   */
+  public void write(byte[] b) throws AFSFileException 
+  {
+    this.write(b, 0, b.length);
+  }
+
+  /////////////// public native methods ////////////////////
+
+  /**
+   * Writes <code>len</code> bytes from the specified 
+   * <code>byte</code> array starting at offset <code>off</code> to 
+   * this file output stream. 
+   *
+   * @param b the data to be written
+   * @param off the start offset in the data
+   * @param len the number of bytes that are written
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   * @see    java.io.FilterOutputStream#write(int)
+   */
+  public native void write(byte[] b, int off, int len) throws AFSFileException;
+  /**
+   * Closes this file output stream and releases any system resources 
+   * associated with this stream. This file output stream may no longer 
+   * be used for writing bytes. 
+   *
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  public native void close()  throws AFSFileException;
+
+  /////////////// private native methods ////////////////////
+
+  /**
+   * Opens an AFS file, with the specified name, for writing.
+   *
+   * @param            filename name of file to be opened
+   * @return   file descriptor
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  private native int openWrite(String filename) throws AFSFileException;
+  /**
+   * Opens an AFS file, with the specified name, for appending.
+   *
+   * @param            filename name of file to be opened
+   * @return   file descriptor
+   * @exception  AFSFileException  if an I/O or other file related error occurs.
+   */
+  private native int openAppend(String filename) throws AFSFileException;
+
+  /*-------------------------------------------------------------------------*/
+}
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Group.java b/src/JAVA/classes/org/openafs/jafs/Group.java
new file mode 100644 (file)
index 0000000..3b93345
--- /dev/null
@@ -0,0 +1,1278 @@
+/*
+ * @(#)Group.java      1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.Vector;
+import java.util.Enumeration;
+import java.util.ArrayList;
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS group.  It holds information about 
+ * the group, such as what groups it owns.<BR><BR>
+ *
+ * Constructing an instance of a <code>Group</code> does not mean an actual 
+ * AFS group is created in a cell -- usually a <code>Group</code>
+ * object is a representation of an already existing AFS group.  If, 
+ * however, the <code>Group</code> is constructed with the name of a 
+ * group that does not exist in the cell represented by the provided 
+ * <code>Cell</code>, a new group with that name can be
+ * created in that cell by calling the {@link #create(String, int)} or
+ * {@link #create(String)} method. If such a group does already exist when 
+ * one of these methods is called, an exception will be thrown.<BR><BR>
+ *
+ * Each <code>Group</code> object has its own individual set of
+ * <code>Group</code>s that it owns and <code>User</code>s that belong
+ * to it.  These represents the properties and attributes 
+ * of an actual AFS group.
+ * <BR><BR>
+ *
+ * <!--Information on how member values are set-->
+ *
+ * Associated with an AFS group are many attributes, such as whether or not
+ * who is allowed to list the members of this group. The <code>Group</code> 
+ * class has many "set" methods to indicate values for these attributes (i.e. 
+ * {@link #setListMembership(int)}.  However, in order for these values to be 
+ * written to the actual AFS group, the {@link #flushInfo()} method needs to 
+ * be called.  This writes all user attributes set through this API to AFS.  
+ * This is done to minimize calls through JNI.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Group</code> object.  It lists the name and owner of a specified 
+ * group.
+ *
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Partition;
+ * import org.openafs.jafs.Group;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Group group;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String groupName  = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     group = new Group(groupName, cell);
+ *     
+ *     System.out.println("Owner of group " + group.getName() + " is " 
+ *                        + group.getOwnerName());
+ *     ...
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Group implements PTSEntry, Serializable, Comparable
+{
+  /**
+   * Only the owner of the group has access
+   */
+  public static final int GROUP_OWNER_ACCESS = 0;
+  /**
+   * Members of the group have access
+   */
+  public static final int GROUP_GROUP_ACCESS = 1;
+  /**
+   * Any user has access
+   */
+  public static final int GROUP_ANYUSER_ACCESS = 2;
+
+  protected Cell cell;
+  protected int cellHandle;
+  protected String name;
+  
+  protected int membershipCount;
+  protected int nameUID;
+  protected int ownerUID;
+  protected int creatorUID;
+
+  protected String owner;
+  protected String creator;
+
+  /**
+   * who is allowed to execute PTS examine for this group.  Valid values are:
+   * <ul>
+   * <li>{@link #GROUP_OWNER_ACCESS} -- only the owner has permission</li>
+   * <li>{@link #GROUP_GROUP_ACCESS} 
+   *     -- only members of the group have permission</li>
+   * <li>{@link #GROUP_ANYUSER_ACCESS} -- any user has permission</li></ul>
+   */
+  protected int listStatus;
+  /**
+   * who is allowed to execute PTS examine for this group.  Valid values are:
+   * <ul>
+   * <li>{@link #GROUP_OWNER_ACCESS} -- only the owner has permission</li>
+   * <li>{@link #GROUP_GROUP_ACCESS} 
+   *     -- only members of the group have permission</li>
+   * <li>{@link #GROUP_ANYUSER_ACCESS} -- any user has permission</li></ul>
+   */
+  protected int listGroupsOwned;
+  /**
+   * who is allowed to execute PTS listowned for this group.  Valid values are:
+   * <ul>
+   * <li>{@link #GROUP_OWNER_ACCESS} -- only the owner has permission</li>
+   * <li>{@link #GROUP_GROUP_ACCESS} 
+   *     -- only members of the group have permission</li>
+   * <li>{@link #GROUP_ANYUSER_ACCESS} -- any user has permission</li></ul>
+   */
+  protected int listMembership;
+  /**
+   * who is allowed to execute PTS adduser for this group.   Valid values are:
+   * <ul>
+   * <li>{@link #GROUP_OWNER_ACCESS} -- only the owner has permission</li>
+   * <li>{@link #GROUP_GROUP_ACCESS} 
+   *     -- only members of the group have permission</li>
+   * <li>{@link #GROUP_ANYUSER_ACCESS} -- any user has permission</li></ul>
+   */
+  protected int listAdd;
+  /**
+   * who is allowed to execute PTS removeuser for this group.   Valid 
+   * values are:
+   * <ul>
+   * <li>{@link #GROUP_OWNER_ACCESS} -- only the owner has permission</li>
+   * <li>{@link #GROUP_GROUP_ACCESS} 
+   *     -- only members of the group have permission</li>
+   * <li>{@link #GROUP_ANYUSER_ACCESS} -- any user has permission</li></ul>
+   */
+  protected int listDelete;
+
+  protected ArrayList members;
+  protected ArrayList memberNames;
+  protected ArrayList groupsOwned;
+  protected ArrayList groupsOwnedNames;
+
+  /**
+   * Whether or not the information fields of this group have been filled.
+   */
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <code>Group</code> object instance given the name 
+   * of the AFS group and the AFS cell, represented by 
+   * <CODE>cell</CODE>, to which it belongs.   This does not actually
+   * create a new AFS group, it just represents one.
+   * If <code>name</code> is not an actual AFS group, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String, int)} or {@link #create(String)}
+   * method is explicitly called to create it.  
+   *
+   * @param name      the name of the group to represent
+   * @param cell       the cell to which the group belongs.
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public Group( String name, Cell cell ) throws AFSException
+  {
+    this.name = name;
+    this.cell = cell;
+    cellHandle = cell.getCellHandle();
+
+    members = null;
+    memberNames = null;
+    groupsOwned = null;
+    groupsOwnedNames = null;
+    cachedInfo = false;
+  }
+
+  /**
+   * Constructs a new <code>Group</code> object instance given the name 
+   * of the AFS group and the AFS cell, represented by 
+   * <CODE>cell</CODE>, to which it belongs.   This does not actually
+   * create a new AFS group, it just represents one.
+   * If <code>name</code> is not an actual AFS group, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String, int)} or {@link #create(String)}
+   * method is explicitly called to create it.   Note that if the process
+   * doesn't exist and <code>preloadAllMembers</code> is true, an exception
+   * will be thrown.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications. It ensures all data member values are set and 
+   * available without calling back to the filesystem at the first request 
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param name               the name of the group to represent
+   * @param cell               the cell to which the group belongs.
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction;
+   *                           otherwise members will be set upon access, 
+   *                           which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Group( String name, Cell cell, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, cell);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Creates a blank <code>Group</code> given the cell to which the group
+   * belongs. Other methods cvan then be used to fill the fields of this 
+   * blank object.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param cell       the cell to which the group belongs.
+   */
+  Group( Cell cell ) throws AFSException
+  {
+    this( null, cell );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Creates the PTS entry for a new group in this cell.  Automatically assigns
+   * a group id.
+   *
+   * @param ownerName      the owner of this group
+   */    
+  public void create( String ownerName ) throws AFSException
+  {
+    this.create( ownerName, 0 );
+  }
+
+  /**
+   * Creates the PTS entry for a new group in this cell.
+   *
+   * @param ownerName      the owner of this group
+   * @param gid       the group id to assign to the new group
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void create( String ownerName, int gid ) throws AFSException
+  {
+    Group.create( cell.getCellHandle(), name, ownerName, gid );
+  }
+
+  /**
+   * Deletes the PTS entry for a group in this cell. Deletes this group 
+   * from the membership list of the user that belonged to it, but does not 
+   * delete the groups owned by this group.  Also nullifies the Java object.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void delete() throws AFSException
+  {
+    Group.delete( cell.getCellHandle(), name );
+
+    cell = null;
+    name = null;
+    owner = null;
+    creator = null;
+    members = null;
+    memberNames = null;
+    groupsOwned = null;
+    groupsOwnedNames = null;
+    try {
+      finalize();
+    } catch( Throwable t ) {
+      throw new AFSException( t.getMessage() );
+    }
+  }
+
+  /**
+   * Flushes the current information of this <code>Group</code> object to disk.
+   * This will update the information of the actual AFS group to match the 
+   * settings that have been modified in this <code>Group</code> object.  
+   * This function must be called before any changes made to the information 
+   * fields of this group will be seen by the AFS system.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void flushInfo() throws AFSException
+  {
+    Group.setGroupInfo( cell.getCellHandle(), name, this );
+  }
+
+  /**
+   * Add the specified member to this group. 
+   *
+   * @param userName    the <code>User</code> object to add
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void addMember( User theUser ) throws AFSException
+  {
+    String userName = theUser.getName(); 
+
+    Group.addMember( cell.getCellHandle(), name, userName );
+
+    // add to cache
+    if( memberNames != null ) {
+      memberNames.add( userName );
+    }
+    if( members != null ) {
+      members.add( new User( userName, cell ) );
+    }
+  }
+
+  /**
+   * Remove the specified member from this group.
+   * @param userName    the <code>User</code> object to remove
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void removeMember( User theUser ) throws AFSException
+  {
+    String userName = theUser.getName();
+    Group.removeMember( cell.getCellHandle(), name, userName );
+
+    // remove from cache
+    if( memberNames != null ) {
+      memberNames.remove( memberNames.indexOf(userName) );
+      memberNames.trimToSize();
+    }
+    if( members != null && members.indexOf(theUser) > -1) {
+      members.remove( members.indexOf(theUser) );
+      members.trimToSize();
+    }
+  }
+
+  /**
+   * Change the owner of this group.
+   *
+   * @param ownerName    the new owner <code>User</code> object
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void changeOwner( User theOwner ) throws AFSException
+  {
+    String ownerName = theOwner.getName();
+
+    Group.changeOwner( cell.getCellHandle(), name, ownerName );
+
+    if( cachedInfo ) {
+      owner = ownerName;
+    }
+  }
+
+  /**
+   * Change the owner of this group.
+   *
+   * @param ownerName    the new owner <code>Group</code> object
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void changeOwner( Group theOwner ) throws AFSException
+  {
+    String ownerName = theOwner.getName();
+    Group.changeOwner( cell.getCellHandle(), name, ownerName );
+    if( cachedInfo ) {
+      owner = ownerName;
+    }
+  }
+
+  /**
+   * Change the name of this group.
+   *
+   * @param newName    the new name for this group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void rename( String newName ) throws AFSException
+  {
+    Group.rename( cell.getCellHandle(), name, newName );
+    name = newName;
+  }
+
+  /**
+   * Refreshes the properties of this Group object instance with values from 
+   * the AFS group it represents.  All properties that have been initialized 
+   * and/or accessed will be renewed according to the values of the AFS group 
+   * this <code>Group</code> object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+      refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Group object instance with values from 
+   * the AFS group it represents.  If <CODE>all</CODE> is <CODE>true</CODE> 
+   * then <U>all</U> of the properties of this Group object instance will be 
+   * set, or renewed, according to the values of the AFS group it represents, 
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise renew 
+   *              all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if( all || cachedInfo ) {
+      refreshInfo();
+    }
+    if( all || groupsOwned != null ) {
+      refreshGroupsOwned();
+    }
+    if( all || groupsOwnedNames != null ) {
+      refreshGroupsOwnedNames();
+    }
+    if( all || members != null ) {
+      refreshMembers();
+    }
+    if( all || memberNames != null ) {
+      refreshMemberNames();
+    }
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Group</code> to reflect 
+   * the current state of the AFS group.  Does not refresh the members that 
+   * belong to the group, nor the groups the group owns.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    cachedInfo = true;
+    Group.getGroupInfo( cell.getCellHandle(), name, this );
+  }
+
+  /**
+   * Refreshes the current information about the <code>User</code> objects 
+   * belonging to this group.  Does not refresh the information fields of 
+   * the group or groups owned.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshMembers() throws AFSException
+  {
+    User currUser;
+
+    int iterationID = Group.getGroupMembersBegin( cell.getCellHandle(), name );
+
+    members = new ArrayList();
+
+    currUser = new User( cell );
+    while( Group.getGroupMembersNext( cellHandle, iterationID, currUser ) 
+          != 0 ) {
+      members.add( currUser );
+      currUser = new User( cell );
+    } 
+    
+    Group.getGroupMembersDone( iterationID );
+  }
+  
+  /**
+   * Refreshes the current information about the names of members belonging 
+   * to this group.  Does not refresh the information fields of the group 
+   * or groups owned.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshMemberNames() throws AFSException
+  {
+    String currName;
+    int iterationID = Group.getGroupMembersBegin( cell.getCellHandle(), name );
+
+    memberNames = new ArrayList();
+
+    while( ( currName = Group.getGroupMembersNextString( iterationID ) ) 
+          != null ) {
+      memberNames.add( currName );
+    } 
+    Group.getGroupMembersDone( iterationID );
+  }
+  
+  /**
+   * Refreshes the current information about the <code>Group</code> objects the
+   * group owns.  Does not refresh the information fields of the group or 
+   * members.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupsOwned() throws AFSException
+  {
+    Group currGroup;
+
+    int iterationID = User.getGroupsOwnedBegin( cell.getCellHandle(), name );
+
+    groupsOwned = new ArrayList();
+
+    currGroup = new Group( cell );
+    while( User.getGroupsOwnedNext( cellHandle, iterationID, currGroup ) 
+          != 0 ) {
+      groupsOwned.add( currGroup );
+      currGroup = new Group( cell );
+    } 
+    
+    User.getGroupsOwnedDone( iterationID );
+  }
+
+  /**
+   * Refreshes the current information about the names of groups the group 
+   * owns.  Does not refresh the information fields of the group or members.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupsOwnedNames() throws AFSException
+  {
+    String currName;
+
+    int iterationID = User.getGroupsOwnedBegin( cell.getCellHandle(), name );
+
+    groupsOwnedNames = new ArrayList();
+    while( ( currName = User.getGroupsOwnedNextString( iterationID ) ) 
+          != null ) {
+      groupsOwnedNames.add( currName );
+    } 
+    User.getGroupsOwnedDone( iterationID );
+  }
+
+  /**
+   * Adds an access control list entry for some AFS directory for this group.
+   *
+   * @param directory  the full path of the place in the AFS file system 
+   *                   for which to add an entry
+   * @param read  whether or not to allow read access to this user
+   * @param write  whether or not to allow write access to this user
+   * @param lookup  whether or not to allow lookup access to this user
+   * @param delete  whether or not to allow deletion access to this user
+   * @param insert  whether or not to allow insertion access to this user
+   * @param lock  whether or not to allow lock access to this user
+   * @param admin  whether or not to allow admin access to this user
+   * @exception AFSException  If an error occurs in the native code
+  public void setACL( String directory, boolean read, boolean write, boolean lookup, boolean delete, boolean insert, boolean lock, boolean admin )
+    throws AFSException
+  {
+    Cell.setACL( directory, name, read, write, lookup, delete, insert, lock, admin );
+  }
+   */
+
+  //////////////////////  accessors: ///////////////
+
+  /**
+   * Returns the name of this group.
+   *
+   * @return the name of this group
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns the numeric AFS id of this group.
+   *
+   * @return the AFS id of this group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getUID() throws AFSException
+  {
+    if( !cachedInfo ) {
+       refreshInfo();
+    }
+    return nameUID;
+  }
+
+  /**
+   * Returns the Cell this group belongs to.
+   *
+   * @return the Cell this group belongs to
+   */
+  public Cell getCell()
+  {
+    return cell;
+  }
+
+  /**
+   * Returns an array of the <code>User</code> object members of this group.
+   *
+   * @return      an array of the members of this group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public User[] getMembers() throws AFSException
+  {
+    if( members == null ) {
+      refreshMembers();
+    }
+    return (User[]) members.toArray( new User[members.size()] );
+  }
+
+  /**
+   * Returns an array of the member names of this group.
+   *
+   * @return      an array of the member names of this group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public String[] getMemberNames() throws AFSException
+  {
+    if( memberNames == null ) {
+      refreshMemberNames();
+    }
+    return (String[]) memberNames.toArray( new String[memberNames.size()] );
+  }
+
+  /**
+   * Returns an array of the <code>Group</code> objects this group owns.
+   *
+   * @return     an array of the <code>Groups</code> this group owns
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public Group[] getGroupsOwned() throws AFSException
+  {
+    if( groupsOwned == null ) {
+      refreshGroupsOwned();
+    }
+    return (Group[]) groupsOwned.toArray( new Group[groupsOwned.size()] );
+  }
+
+  /**
+   * Returns an array of the group names this group owns.
+   * Contains <code>String</code> objects.
+   *
+   * @return     an array of the group names this group owns
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public String[] getGroupsOwnedNames() throws AFSException
+  {
+    if( groupsOwnedNames == null ) {
+      refreshGroupsOwnedNames();
+    }
+    return (String[]) 
+       groupsOwnedNames.toArray(new String[groupsOwnedNames.size()] );
+  }
+
+  /**
+   * Returns the number of members of this group.
+   *
+   * @return the membership count
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getMembershipCount() throws AFSException
+  {
+    if( !cachedInfo ) {
+       refreshInfo();
+    }
+    return membershipCount;
+  }
+
+  /**
+   * PTS: Returns the owner of this group in the form of a {@link PTSEntry}.
+   *
+   * <P>The returning object could be either a {@link User} or {@link Group};
+   * to determine what type of object the {@link PTSEntry} represents,
+   * call the {@link PTSEntry#getType()} method.
+   *
+   * @return the owner of this group
+   * @exception AFSException  If an error occurs in the native code
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   * @see #refresh()
+   */
+  public PTSEntry getOwner() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if (owner == null) return null;
+    if (ownerUID > 0) {
+      return new User(owner, cell);
+    } else {
+      return new Group(owner, cell);
+    }
+  }
+
+  /**
+   * PTS: Returns the creator of this group in the form of a {@link PTSEntry}.
+   *
+   * <P>The returning object could be either a {@link User} or {@link Group};
+   * to determine what type of object the {@link PTSEntry} represents,
+   * call the {@link PTSEntry#getType()} method.
+   *
+   * @return the creator of this group
+   * @exception AFSException  If an error occurs in the native code
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   * @see #refresh()
+   */
+  public PTSEntry getCreator() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if (creator == null) return null;
+    if (creatorUID > 0) {
+      return new User(creator, cell);
+    } else {
+      return new Group(creator, cell);
+    }
+  }
+
+  /**
+   * Returns the type of {@link PTSEntry} this object represents.
+   *
+   * <P>This method will always return {@link PTSEntry#PTS_GROUP}.
+   *
+   * @return  the type of PTSEntry this object represents 
+              (will always return {@link PTSEntry#PTS_GROUP})
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   */
+  public short getType()
+  {
+    return PTSEntry.PTS_GROUP;
+  }
+
+  /**
+   * Returns who can list the status (pts examine) of this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the status listing permission
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getListStatus() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listStatus;
+  }
+
+  /**
+   * Returns who can list the groups owned (pts listowned) by this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code>
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the groups owned listing permission
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getListGroupsOwned() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listGroupsOwned;
+  }
+
+  /**
+   * Returns who can list the users (pts membership) that belong to this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the membership listing permission
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getListMembership() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listMembership;
+  }
+
+  /**
+   * Returns who can add members (pts adduser) to this group.
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the member adding permission
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getListAdd() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listAdd;
+  }
+
+  /**
+   * Returns who can delete members (pts removemember) from this group.
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the member deleting permission
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getListDelete() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listDelete;
+  }
+
+    ///////////////////  mutators: //////////////////////
+
+  /**
+   * Sets who can list the status (pts examine) of this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code>
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListStatus( int value ) throws AFSException
+  {
+    if( (value != Group.GROUP_OWNER_ACCESS) && 
+       (value != Group.GROUP_GROUP_ACCESS) && 
+       (value != Group.GROUP_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listStatus to " 
+                                           + value );
+    } else {
+       listStatus = value;
+    }
+  }
+
+  /**
+   * Sets who can list the groups owned (pts listowned) by this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListGroupsOwned( int value ) throws AFSException
+  {
+    if( (value != Group.GROUP_OWNER_ACCESS) && 
+       (value != Group.GROUP_GROUP_ACCESS) && 
+       (value != Group.GROUP_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listGroupsOwned to " 
+                                           + value );
+    } else {
+       listGroupsOwned = value;
+    }
+  }
+
+  /**
+   * Sets who can list the users (pts membership) that belong to this group.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListMembership( int value ) throws AFSException
+  {
+    if( (value != Group.GROUP_OWNER_ACCESS) && 
+       (value != Group.GROUP_GROUP_ACCESS) && 
+       (value != Group.GROUP_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listMembership to " 
+                                           + value );
+    } else {
+       listMembership = value;
+    }
+  }
+
+  /**
+   * Sets who can add members (pts adduser) to this group.
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an invalid value is provided
+   */
+  public void setListAdd( int value ) throws AFSException
+  {
+    if( (value != Group.GROUP_OWNER_ACCESS) && 
+       (value != Group.GROUP_GROUP_ACCESS) && 
+       (value != Group.GROUP_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listAdd to " + value );
+    } else {
+       listAdd = value;
+    }
+  }
+
+  /**
+   * Sets who can delete members (pts removemember) from this group.
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #GROUP_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #GROUP_GROUP_ACCESS}</code> 
+   *           -- only members of the group have permission</li>
+   * <li><code>{@link #GROUP_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an invalid value is provided
+   */
+  public void setListDelete( int value ) throws AFSException
+  {
+    if( (value != Group.GROUP_OWNER_ACCESS) && 
+       (value != Group.GROUP_GROUP_ACCESS) && 
+       (value != Group.GROUP_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listDelete to " 
+                                           + value );
+    } else {
+       listDelete = value;
+    }
+  }
+
+  /////////////// information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Group</code>. 
+   * Contains the information fields and members.
+   *
+   * @return a <code>String</code> representation of the <code>Group</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    try {
+       r = "Group: " + getName() + ", uid: " + getUID() + "\n";
+       r += "\towner: " + getOwner().getName() + ", uid: " + getOwner().getUID() + "\n";
+       r += "\tcreator: " + getCreator().getName() + ", uid: " 
+           + getCreator().getUID() + "\n";
+       r += "\tMembership count: " + getMembershipCount() + "\n";
+       r += "\tList status: " + getListStatus() + "\n";
+       r += "\tList groups owned: " + getListGroupsOwned() + "\n";
+       r += "\tList membership: " + getListMembership() + "\n";
+       r += "\tAdd members: " + getListAdd() + "\n";
+       r += "\tDelete members: " + getListDelete() + "\n";
+
+       r += "\tGroup members: \n";
+       String names[] = getMemberNames();
+       for( int i = 0; i < names.length; i++ ) {
+           r += "\t\t" + names[i] + "\n";
+       }
+
+       r += "\tOwns groups: \n";
+       names = getGroupsOwnedNames();
+       for( int i = 0; i < names.length; i++ ) {
+           r += "\t\t" + names[i] + "\n";
+       }
+       return r;
+    } catch ( AFSException e ) {
+       return e.toString();
+    }
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two Group objects respective to their names and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     group    The Group object to be compared to this Group instance
+   * 
+   * @return    Zero if the argument is equal to this Group's name, a
+   *           value less than zero if this Group's name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this Group's name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Group group)
+  {
+    return this.getName().compareTo(group.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Group)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Group)obj);
+  }
+
+  /**
+   * Tests whether two <code>Group</code> objects are equal, based on their 
+   * names.
+   *
+   * @param otherGroup   the Group to test
+   * @return whether the specifed Group is the same as this Group
+   */
+  public boolean equals( Group otherGroup )
+  {
+    return name.equals( otherGroup.getName() );
+  }
+
+  /**
+   * Returns the name of this <CODE>Group</CODE>
+   *
+   * @return the name of this <CODE>Group</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Creates the PTS entry for a new group.  Pass in 0 for the uid if PTS is to
+   * automatically assign the group id.
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param groupName      the name of the group to create
+   * @param ownerName      the owner of this group
+   * @param gid     the group id to assign to the group (0 to have one 
+   *                automatically assigned)
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void create( int cellHandle, String groupName, 
+                                      String ownerName, int gid )
+       throws AFSException;
+
+  /**
+   * Deletes the PTS entry for a group.  Deletes this group from the 
+   * membership list of the users that belonged to it, but does not delete 
+   * the groups owned by this group.
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param groupName      the name of the group to delete
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void delete( int cellHandle, String groupName )
+       throws AFSException;
+
+  /**
+   * Fills in the information fields of the provided <code>Group</code>.  
+   * Fills in values based on the current PTS information of the group.
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param name     the name of the group for which to get the information
+   * @param group     the <code>Group</code> object in which to fill in the 
+   *                  information
+   * @see Group
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getGroupInfo( int cellHandle, String name, 
+                                            Group group ) 
+       throws AFSException;
+
+  /**
+   * Sets the information values of this AFS group to be the parameter values.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param name     the name of the user for which to set the information
+   * @param theGroup   the group object containing the desired information
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void setGroupInfo( int cellHandle, String name, 
+                                            Group theGroup ) 
+       throws AFSException;
+
+  /**
+   * Begin the process of getting the users that belong to the group.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getGroupMembersNext</code> and <code>getGroupMembersDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param name          the name of the group for which to get the members
+   * @return an iteration ID
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native int getGroupMembersBegin( int cellHandle, 
+                                                   String name )
+       throws AFSException;
+
+  /**
+   * Returns the next members that belongs to the group.  Returns 
+   * <code>null</code> if there are no more members.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupMembersBegin
+   * @return the name of the next member
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native String getGroupMembersNextString( int iterationId ) 
+       throws AFSException;
+
+  /**
+   * Fills the next user object belonging to that group.  Returns 0 if there
+   * are no more users, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupMembersBegin
+   * @param theUser   a User object to be populated with the values of the 
+   *                  next user
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getGroupMembersNext( int cellHandle, 
+                                                  int iterationId, 
+                                                  User theUser )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupMembersBegin
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getGroupMembersDone( int iterationId )
+       throws AFSException;
+
+  /**
+   * Adds a user to the specified group. 
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param groupName          the name of the group to which to add a member
+   * @param userName      the name of the user to add
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void addMember( int cellHandle, String groupName, 
+                                         String userName )
+       throws AFSException;
+
+  /**
+   * Removes a user from the specified group. 
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param groupName          the name of the group from which to remove a 
+   *                           member
+   * @param userName      the name of the user to remove
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void removeMember( int cellHandle, String groupName, 
+                                            String userName )
+       throws AFSException;
+
+  /**
+   * Change the owner of the specified group. 
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param groupName          the name of the group of which to change the 
+   *                           owner
+   * @param ownerName      the name of the new owner
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void changeOwner( int cellHandle, String groupName, 
+                                           String ownerName )
+       throws AFSException;
+
+  /**
+   * Change the name of the specified group. 
+   *
+   * @param cellHandle    the handle of the cell to which the group belongs
+   * @see Cell#getCellHandle
+   * @param oldGroupName          the old name of the group
+   * @param newGroupName      the new name for the group
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void rename( int cellHandle, String oldGroupName, 
+                                      String newGroupName )
+       throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the group portion of the native 
+   * library.
+   * This method should be called when no more <code>Groups</code> are expected
+   * to be used.
+   */
+  protected static native void reclaimGroupMemory();
+}
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Key.java b/src/JAVA/classes/org/openafs/jafs/Key.java
new file mode 100644 (file)
index 0000000..ad20544
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * @(#)Key.java        1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS key.  It holds information about 
+ * the key, such as what its version is.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>Key</code> does not mean an actual 
+ * AFS key is created on a server -- usually a <code>Key</code>
+ * object is a representation of an already existing AFS key.  If, 
+ * however, the <code>Key</code> is constructed with the version number of a 
+ * key that does not exist on the server represented by the provided 
+ * <code>Server</code>, a new key with that version number can be
+ * created on that server by calling the {@link #create(String)} methods If
+ * such a key does already exist when this method is called, 
+ * an exception will be thrown.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Key</code> object. It obtains the list of <code>Key</code>s from
+ * a specified server, and prints the string representation of each key.
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Key;
+ * import org.openafs.jafs.Server;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server  = new Server(serverName, cell);
+ * 
+ *     System.out.println("Keys in Server " + server.getName() + ":");
+ *     Key[] keys = server.getKeys();
+ *     for (int i = 0; i < keys.length; i++) {
+ *       System.out.println(" -> " + keys[i] );
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Key implements Serializable, Comparable
+{
+  protected Server server;
+
+  protected int version;
+  protected long checkSum;
+  protected String encryptionKey;
+  protected int lastModDate;
+  protected int lastModMs;
+  
+  protected GregorianCalendar lastModDateDate;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <CODE>Key</CODE> object instance given the version of 
+   * the AFS key and the AFS server, represented by <CODE>server</CODE>, 
+   * to which it belongs.   This does not actually
+   * create a new AFS key, it just represents one.
+   * If <code>version</code> is not an actual AFS key, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String)}
+   * method is explicitly called to create it.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param version      the version of the key to represent
+   * @param server       the server to which the key belongs.
+   */
+  public Key( int version, Server server ) throws AFSException
+  {
+    this.server = server;
+    this.version = version;
+
+    lastModDateDate = null;
+
+    cachedInfo = false;
+  }
+
+  /**
+   * Constructs a new <CODE>Key</CODE> object instance given the version of 
+   * the AFS key and the AFS server, represented by <CODE>server</CODE>, 
+   * to which it belongs.    This does not actually
+   * create a new AFS key, it just represents one.
+   * If <code>version</code> is not an actual AFS key, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String)}
+   * method is explicitly called to create it. Note that if the key does not
+   * exist and <code>preloadAllMembers</code> is true, an exception will
+   * be thrown. 
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications. It ensures all data member values are set and 
+   * available without calling back to the filesystem at the first request 
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param version            the version of the key to represent 
+   * @param server             the server to which the key belongs.
+   * @param preloadAllMembers  true will ensure all object members are set 
+   *                           upon construction; otherwise members will be 
+   *                           set upon access, which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Key( int version, Server server, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(version, server);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Creates a blank <code>Key</code> given the server to which the key
+   * belongs.  This blank object can then be passed into other methods to 
+   * fill out its properties.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param server       the server to which the key belongs.
+   */
+  Key( Server server ) throws AFSException
+  {
+    this( -1, server );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Refreshes the properties of this Key object instance with values from 
+   * the AFS key it represents.  All properties that have been initialized 
+   * and/or accessed will be renewed according to the values of the AFS key 
+   * this Key object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Key object instance with values from 
+   * the AFS key it represents.  If <CODE>all</CODE> is <CODE>true</CODE> 
+   * then <U>all</U> of the properties of this Key object instance will be 
+   * set, or renewed, according to the values of the AFS key it represents, 
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise renew 
+   * all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if( all || cachedInfo ) {
+      refreshInfo();
+    }
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Key</code> to reflect the 
+   * current state of the AFS server key.  These inlclude the last 
+   * modification time, etc.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getKeyInfo( server.getBosHandle(), version, this );
+    cachedInfo = true;
+    lastModDateDate = null;
+  }
+
+  /**
+   * Creates a key with this <code>Key's</code> version number at the server,
+   * using the specified <code>String</code> for the key.
+   *
+   * @param keyString   the string to use for the encryption key
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void create( String keyString ) throws AFSException
+  {
+    create( server.getCell().getCellHandle(), server.getBosHandle(), version, 
+           keyString );
+  }
+
+  /**
+   * Removes the key with this <code>Key's</code> version number from 
+   * the server.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void delete( ) throws AFSException
+  {
+    delete( server.getBosHandle(), version );
+
+    encryptionKey = null;
+    cachedInfo = false;
+  }
+
+  //////////////// accessors:  ////////////////////////
+
+  /**
+   * Returns the version of this key in primitive form.
+   *
+   * @return the version number of this key
+   */
+  public int getVersion()
+  {
+    return version;
+  }
+
+  /**
+   * Returns the server this key is associated with.
+   *
+   * @return this key's server
+   */
+  public Server getServer()
+  {
+    return server;
+  }
+
+  /**
+   * Returns the encrypted key as a string in octal form. This is how AFS
+   * prints it out on the command line.  An example would be:
+   * '\040\205\211\241\345\002\023\211'.
+   *
+   * @return the encrypted key
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public String getEncryptionKey() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return encryptionKey;
+  }
+
+  /**
+   * Returns the check sum of this key.
+   *
+   * @return the check sum of this key
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getCheckSum() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return checkSum;
+  }
+
+  /**
+   * Returns the last modification date of this key.
+   *
+   * @return the last modification date of this key
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public GregorianCalendar getLastModDate() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if ( lastModDateDate == null && cachedInfo ) {
+         // make it into a date . . .
+         lastModDateDate = new GregorianCalendar();
+         long longTime = ((long) lastModDate)*1000;
+         Date d = new Date( longTime );
+         lastModDateDate.setTime( d );
+    }
+    return lastModDateDate;
+  }
+
+  /////////////// information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Key</code>.  
+   * Contains the information fields.
+   *
+   * @return a <code>String</code> representation of the <code>Key</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    try {
+       r = "Key version number: " + getVersion() + "\n";
+       r += "\tencrypted key: " + getEncryptionKey() + "\n";
+       r += "\tcheck sum: " + getCheckSum() + "\n";
+       r += "\tlast mod time: " + getLastModDate().getTime() + "\n";
+    } catch( Exception e ) {
+       return e.toString();
+    }
+    return r;
+  }
+
+  /////////////// override methods ////////////////////
+
+  /**
+   * Compares two Key objects respective to their key version and does not
+   * factor any other attribute.
+   *
+   * @param     key    The Key object to be compared to this Key instance
+   * 
+   * @return    Zero if the argument is equal to this Key's version, a
+   *            value less than zero if this Key's version is less than 
+   *            the argument, or a value greater than zero if this Key's 
+   *            version is greater than the argument
+   */
+  public int compareTo(Key key)
+  {
+    return (this.getVersion() - key.getVersion());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Key)
+   */
+  public int compareTo(Object obj)
+  {
+      return compareTo((Key)obj);
+  }
+
+  /**
+   * Tests whether two <code>Key</code> objects are equal, based on their 
+   * encryption key, version, and associated Server.
+   *
+   * @param otherKey   the Key to test
+   * @return whether the specifed Key is the same as this Key
+   */
+  public boolean equals( Key otherKey )
+  {
+    try {
+      return ( this.getEncryptionKey().equals(otherKey.getEncryptionKey()) ) &&
+             ( this.getVersion() == otherKey.getVersion() ) &&
+             ( this.getServer().equals(otherKey.getServer()) );
+    } catch (Exception e) {
+      return false;
+    }
+  }
+
+  /**
+   * Returns the name of this <CODE>Key</CODE>
+   *
+   * @return the name of this <CODE>Key</CODE>
+   */
+  public String toString()
+  {
+    try {
+      return getVersion() + " - " + getEncryptionKey() + " - " + getCheckSum();
+    } catch (Exception e) {
+      return e.toString();
+    }
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Fills in the information fields of the provided <code>Key</code>. 
+   *
+   * @param serverHandle    the bos handle of the server to which the key 
+   *                        belongs
+   * @see Server#getBosServerHandle
+   * @param version     the version of the key for which to get the information
+   * @param key     the <code>Key</code> object in which to fill in the 
+   *                information
+   * @see Server
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getKeyInfo( int serverHandle, int version, 
+                                          Key key ) 
+       throws AFSException;
+
+  /**
+   * Create a server key.
+   *
+   * @param cellHandle   the handle of the cell to which the server belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the bos handle of the server to which the key will 
+   *                      belong
+   * @see Server#getBosServerHandle
+   * @param versionNumber   the version number of the key to create (0 to 255)
+   * @param keyString     the <code>String</code> version of the key that will
+   *                      be encrypted
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void create( int cellHandle, int serverHandle, int versionNumber, String keyString )
+    throws AFSException;
+
+  /**
+   * Delete a server key.
+   *
+   * @param serverHandle  the bos handle of the server to which the key belongs
+   * @see Server#getBosServerHandle
+   * @param versionNumber   the version number of the key to remove (0 to 255)
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void delete( int serverHandle, int versionNumber )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the key portion of the native library.
+   * This method should be called when no more <code>Key</code> objects are 
+   * expected to be
+   * used.
+   */
+  protected static native void reclaimKeyMemory();
+
+}
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/PTSEntry.java b/src/JAVA/classes/org/openafs/jafs/PTSEntry.java
new file mode 100644 (file)
index 0000000..db3a291
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * @(#)PTSEntry.java   1.2 10/23/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+/**
+ * An interface representation of a PTS entry as it applies to
+ * AFS users and groups.  This interface is implemented in both
+ * {@link User} and {@link Group} object abstractions.
+ * <BR><BR>
+ *
+ *
+ * @version   1.0, 3/31/02
+ * @see User
+ * @see Group
+ */
+public interface PTSEntry
+{
+  /**
+   * Constant for {@link User} object implementers, 
+   * used with {@link #getType()}
+   */
+  public static final short PTS_USER  = 0;
+  /** 
+   * Constant for {@link Group} object implementers,
+   * used with {@link #getType()}
+   */
+  public static final short PTS_GROUP = 1;
+  /**
+   * Returns the Cell this PTS user or group belongs to.
+   *
+   * @return the Cell this PTS user or group belongs to
+   */
+  public Cell getCell();
+  /**
+   * Returns the creator of this PTS user or group.
+   *
+   * @return the creator of this PTS user or group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public PTSEntry getCreator() throws AFSException;
+  /**
+   * Returns the name of this PTS user or group.
+   *
+   * @return the name of this PTS user or group
+   */
+  public String getName();
+  /**
+   * Returns the owner of this PTS user or group.
+   *
+   * @return the owner of this PTS user or group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public PTSEntry getOwner() throws AFSException;
+  /**
+   * Returns the type of PTS entry the implementing object represents.
+   * 
+   * <P>Possible values are:<BR>
+   * <ul>
+   * <li><code>{@link #PTS_USER}</code> 
+   *           -- a {@link User} object</li>
+   * <li><code>{@link #PTS_GROUP}</code> 
+   *           -- a {@link Group} object</li>
+   * </ul>
+   *
+   * @return the name of this PTS user or group
+   */
+  public short getType();
+  /**
+   * Returns the numeric AFS id of this user or group.
+   *
+   * @return the AFS id of this user/group
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public int getUID() throws AFSException;
+}
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Partition.java b/src/JAVA/classes/org/openafs/jafs/Partition.java
new file mode 100644 (file)
index 0000000..98b0adc
--- /dev/null
@@ -0,0 +1,1052 @@
+/*
+ * @(#)Partition.java  1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+
+/**
+ * An abstract representation of an AFS partition.  It holds information about 
+ * the partition, such as what its total space is.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>Partition</code> does not mean 
+ * an actual AFS partition is created on a server -- on the contrary, 
+ * a <code>Partition</code> object must be a representation of an already 
+ * existing AFS partition.  There is no way to create a new AFS partition 
+ * through this API.<BR><BR>
+ *
+ * Each <code>Partition</code> object has its own individual set of
+ * <code>Volume</code>s. This represents the properties and attributes 
+ * of an actual AFS cell.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to obtain and use a 
+ * <code>Partition</code> object.  In this example, a list of the 
+ * <code>Partition</code> objects of a server are obtained, and the name
+ * and number of volumes is printed out for each one.<BR><BR>
+ * 
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Partition;
+ * import org.openafs.jafs.Server;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = new Server(serverName, cell);
+ * 
+ *     System.out.println("Partitions in Server " + server.getName() + ":");
+ *     Partition[] partitions = server.getPartitions();
+ *     for (int i = 0; i < partitions.length; i++) {
+ *       System.out.print("Partition " + partitions[i].getName());
+ *       System.out.print("hosts " + partitions[i].getVolumeCount());
+ *       System.out.print("volumes.\n");
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Partition implements Serializable, Comparable
+{
+  protected Cell cell;
+  protected Server server;
+
+  /* Populated by native method */
+  protected String name;
+
+  /* Populated by native method */
+  protected int id;
+
+  /* Populated by native method */
+  protected String deviceName;
+
+  /* Populated by native method */
+  protected int lockFileDescriptor;
+
+  /* Populated by native method */
+  protected int totalSpace;
+
+  /* Populated by native method */
+  protected int totalFreeSpace;
+
+  protected int totalQuota;
+
+  protected ArrayList volumes;
+  protected ArrayList volumeNames;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <code>Partition</code> object instance given the 
+   * name of the AFS partition and the AFS server, represented by 
+   * <CODE>server</CODE>, to which it belongs.   This does not actually
+   * create a new AFS partition, it just represents an existing one.
+   * If <code>name</code> is not an actual AFS partition, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object.
+   *
+   * @param name      the name of the partition to represent 
+   * @param server    the server on which the partition resides
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public Partition( String name, Server server ) throws AFSException
+  {
+    this.name = name;
+    this.server = server;
+    this.cell = server.getCell();
+    
+    id = -1;
+    
+    volumes = null;
+    volumeNames = null;
+    
+    cachedInfo = false;
+  }
+  
+  /**
+   * Constructs a new <CODE>Partition</CODE> object instance given the name 
+   * of the AFS partition and the AFS server, represented by 
+   * <CODE>server</CODE>, to which it belongs.   This does not actually
+   * create a new AFS partition, it just represents an existing one.
+   * If <code>name</code> is not an actual AFS partition, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications.  It ensures all data member values are set and 
+   * available without calling back to the filesystem at the first request 
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param name               the name of the partition to represent 
+   * @param server             the server to which the partition belongs.
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction;
+   *                           otherwise members will be set upon access, 
+   *                           which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Partition( String name, Server server, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, server);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Creates a blank <code>Server</code> given the cell to which the partition
+   * belongs and the server on which the partition resides.  This blank 
+   * object can then be passed into other methods to fill out its properties.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param cell       the cell to which the partition belongs.
+   * @param server     the server on which the partition resides
+   */
+  Partition( Server server ) throws AFSException
+  {
+    this( null, server );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Refreshes the properties of this Partition object instance with values 
+   * from the AFS partition
+   * it represents.  All properties that have been initialized and/or 
+   * accessed will be renewed according to the values of the AFS partition 
+   * this Partition object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Partition object instance with values 
+   * from the AFS partition it represents.  If <CODE>all</CODE> is 
+   * <CODE>true</CODE> then <U>all</U> of the properties of this Partition 
+   * object instance will be set, or renewed, according to the values of the 
+   * AFS partition it represents, disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties 
+   * that are currently set will be refreshed and properties that are not 
+   * set will remain uninitialized. See {@link #refresh()} for more 
+   * information.
+   *
+   * @param all   if true set or renew all object properties; otherwise 
+   * renew all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if (all || volumes != null) {
+      refreshVolumes();
+    }
+    if (all || volumeNames != null) {
+      refreshVolumeNames();
+    }
+    if (all || cachedInfo) {
+      refreshInfo();
+    }
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Partition</code> to 
+   * reflect the current state of the AFS partition. These include total
+   * free space, id, etc.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getPartitionInfo( cell.getCellHandle(), server.getVosHandle(), getID(), 
+                     this );
+    cachedInfo = true;
+  }
+
+  /**
+   * Obtains the most current list of <code>Volume</code> objects of this 
+   * partition.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshVolumes() throws AFSException
+  {
+    Volume currVolume;
+
+    int iterationID = getVolumesBegin( cell.getCellHandle(), 
+                                      server.getVosHandle(), getID() );
+
+    volumes = new ArrayList();
+       
+    currVolume = new Volume( this );
+    while( getVolumesNext( iterationID, currVolume ) != 0 ) {
+      volumes.add( currVolume );
+      currVolume = new Volume( this );
+    } 
+    getVolumesDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of volume names of this partition.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshVolumeNames() throws AFSException
+  {
+    String currName;
+
+    int iterationID = getVolumesBegin( cell.getCellHandle(), 
+                                      server.getVosHandle(), getID() );
+       
+    volumeNames = new ArrayList();
+       
+    while( ( currName = getVolumesNextString( iterationID ) ) != null ) {
+      volumeNames.add( currName );
+    } 
+    getVolumesDone( iterationID );
+  }
+
+  /**
+   * Syncs this partition to the VLDB.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void syncPartition() throws AFSException
+  {
+    server.syncServerWithVLDB( cell.getCellHandle(), server.getVosHandle(), 
+                              getID() );
+  }
+
+  /**
+   * Syncs the VLDB to this partition.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void syncVLDB() throws AFSException
+  {
+    server.syncVLDBWithServer( cell.getCellHandle(), server.getVosHandle(), 
+                              getID(), false );
+  }
+
+  /**
+   * Salvages (restores consistency to) this partition. Uses default values for
+   * most salvager options in order to simplify the API.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void salvage() throws AFSException
+  {
+    server.salvage( cell.getCellHandle(), server.getBosHandle(), name, null, 
+                   4, null, null, false, false, false, false, false, false );
+  }
+
+  //////////////// accessors:  ////////////////////////
+
+  /**
+   * Returns the name of this partition.
+   *
+   * @return the name of this partition
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns this partition's hosting server.
+   *
+   * @return this partition's server
+   */
+  public Server getServer()
+  {
+    return server;
+  }
+
+  /**
+   * Returns the number of volumes contained in this partition.
+   *
+   * <P>If the total list of volumes or volume names have already been
+   * collected (see {@link #getVolumes()}), then the returning value will
+   * be calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * <P> The product of this method is not saved, and is recalculated
+   * with every call.
+   *
+   * @return the number of volumes contained in this partition.
+   * @exception AFSException  If an error occurs in any 
+   *                               of the associated native methods
+   */
+  public int getVolumeCount() throws AFSException
+  {
+    if (volumes != null) {
+      return volumes.size();
+    } else if (volumeNames != null) {
+      return volumeNames.size();
+    } else {
+      return getVolumeCount( cell.getCellHandle(), 
+                                      server.getVosHandle(), getID() );
+    }
+  }
+
+  /**
+   * Retrieves the <CODE>Volume</CODE> object (which is an abstract 
+   * representation of an actual AFS volume of this partition) designated 
+   * by <code>name</code> (i.e. "root.afs", etc.).  If a volume by 
+   * that name does not actually exist in AFS on the partition
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   * @param name the name of the volume to retrieve
+   * @return <CODE>Volume</CODE> designated by <code>name</code>.
+   */
+  public Volume getVolume(String name) throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    Volume volume = new Volume(name, this);
+    return volume;
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Volume</code> objects 
+   * associated with this <code>Partition</code>, each of which is an 
+   * abstract representation of an actual AFS volume of the AFS partition.
+   * After this method is called once, it saves the array of 
+   * <code>Volume</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current list 
+   * is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a <code>Volume</code> array of the <code>Volume</code> 
+   *         objects of the partition.
+   * @see #refresh()
+   */
+  public Volume[] getVolumes() throws AFSException
+  {
+    if (volumes == null) refreshVolumes();
+    return (Volume[]) volumes.toArray( new Volume[volumes.size()] );
+  }
+
+  /**
+   * Returns an array containing a subset of the <code>Volume</code> objects
+   * associated with this <code>Partition</code>, each of which is an abstract
+   * representation of an actual AFS volume of the AFS partition.  The subset
+   * is a point-in-time list of volumes (<code>Volume</code> objects
+   * representing AFS volumes) starting at the complete array's index of
+   * <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this partition's complete array of volumes.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getVolumeCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 volumes within this partition
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   Volume[] volumes;
+   *   if (partition.getVolumeCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < partition.getVolumeCount()) {
+   *       volumes = partition.<B>getVolumes</B>(index, length);
+   *       for (int i = 0; i < volumes.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     volumes = partition.getVolumes();
+   *     for (int i = 0; i < volumes.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array 
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of volumes hosted by this partition
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getVolumeCount()
+   * @see #getVolumeNames(int, int)
+   * @see #getVolumes()
+   */
+  public Volume[] getVolumes(int startIndex, int length) throws AFSException
+  {
+    Volume[] volumes  = new Volume[length];
+    Volume currVolume = new Volume( this );
+    int i = 0;
+
+    int iterationID = getVolumesBeginAt( cell.getCellHandle(), 
+                                      server.getVosHandle(), getID(), startIndex );
+
+    while( getVolumesNext( iterationID, currVolume ) != 0 && i < length ) {
+      volumes[i] = currVolume;
+      currVolume = new Volume( this );
+      i++;
+    } 
+    getVolumesDone( iterationID );
+    if (i < length) {
+      Volume[] v = new Volume[i];
+      System.arraycopy(volumes, 0, v, 0, i);
+      return v;
+    } else {
+      return volumes;
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the names of volumes
+   * associated with this <code>Partition</code>. 
+   * After this method is called once, it saves the array of 
+   * <code>String</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * list is obtained.
+   *
+   * @return a <code>String</code> array of the volumes of the partition.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getVolumeNames() throws AFSException
+  {
+    if (volumeNames == null) refreshVolumeNames();
+    return (String []) volumeNames.toArray( new String[volumeNames.size() ] );
+  }
+
+  /**
+   * Returns an array containing a subset of the names of volumes
+   * associated with this <code>Partition</code>.  The subset is a 
+   * point-in-time list of volume names starting at the complete array's 
+   * index of <code>startIndex</code> and containing up to <code>length</code>
+   * elements.
+   *
+   * If <code>length</code> is larger than the number of remaining elements, 
+   * respective to <code>startIndex</code>, then this method will
+   * ignore the remaining positions requested by <code>length</code> and 
+   * return an array that contains the remaining number of elements found in 
+   * this partition's complete array of volume names.
+   *
+   * <P>This method is especially useful when managing iterations of very
+   * large lists.  {@link #getVolumeCount()} can be used to determine if
+   * iteration management is practical.
+   *
+   * <P>This method does not save the resulting data and therefore 
+   * queries AFS for each call.
+   *
+   * <P><B>Example:</B> If there are more than 50,000 volumes within this partition
+   * then only render them in increments of 10,000.
+   * <PRE>
+   * ...
+   *   String[] volumes;
+   *   if (partition.getVolumeCount() > 50000) {
+   *     int index = 0;
+   *     int length = 10000;
+   *     while (index < partition.getVolumeCount()) {
+   *       volumes = partition.<B>getVolumeNames</B>(index, length);
+   *       for (int i = 0; i < volumes.length; i++) {
+   *         ...
+   *       }
+   *       index += length;
+   *       ...
+   *     }
+   *   } else {
+   *     volumes = partition.getVolumeNames();
+   *     for (int i = 0; i < volumes.length; i++) {
+   *       ...
+   *     }
+   *   }
+   * ...
+   * </PRE>
+   *
+   * @param startIndex  the base zero index position at which the subset array 
+   *                    should start from, relative to the complete list of 
+   *                    elements present in AFS.
+   * @param length      the number of elements that the subset should contain
+   * @return a subset array of volume names hosted by this partition
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getVolumeCount()
+   * @see #getVolumes(int, int)
+   * @see #getVolumes()
+   */
+  public String[] getVolumeNames(int startIndex, int length) throws AFSException
+  {
+    String[] volumes  = new String[length];
+    String currName;
+    int i = 0;
+
+    int iterationID = getVolumesBeginAt( cell.getCellHandle(), 
+                                      server.getVosHandle(), getID(), startIndex );
+
+    while( ( currName = getVolumesNextString( iterationID ) ) != null && i < length ) {
+      volumes[i] = currName;
+      i++;
+    } 
+    getVolumesDone( iterationID );
+    if (i < length) {
+      String[] v = new String[i];
+      System.arraycopy(volumes, 0, v, 0, i);
+      return v;
+    } else {
+      return volumes;
+    }
+  }
+
+  /**
+   * Returns the id of this partition (i.e. "vicepa" = 0, etc.)
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the id of this partition
+   */
+  public int getID() throws AFSException
+  {
+    if (id == -1) id = translateNameToID( name );
+    return id;
+  }
+
+  /**
+   * Returns the device name of this partition (i.e. "hda5", etc.)
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the device name of this partition
+   * @see #refresh()
+   */
+  public String getDeviceName() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return deviceName;
+  }
+
+  /**
+   * Returns the lock file descriptor of this partition
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the lock file descriptor of this partition
+   * @see #refresh()
+   */
+  public int getLockFileDescriptor() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return lockFileDescriptor;
+  }
+
+  /**
+   * Returns the total space on this partition.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the total space on this partition
+   * @see #refresh()
+   */
+  public int getTotalSpace() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return totalSpace;
+  }
+
+  /**
+   * Returns the total free space on this partition.
+   * After this method is called once, it saves the total free space 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the total free space on this partition
+   * @see #refresh()
+   */
+  public int getTotalFreeSpace() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return totalFreeSpace;
+  }
+
+  /**
+   * Returns the total used space on this partition.
+   * After this method is called once, it saves the total used space 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the total used space on this partition
+   * @see #refresh()
+   */
+  public int getUsedSpace() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return (totalSpace - totalFreeSpace);
+  }
+
+  /**
+   * Returns the total combined quota of all volumes on this partition,
+   * unless a volume has configured an unlimited quota at which case an
+   * {@link AFSException} is thrown.
+   *
+   * <P>After this method is called once, it saves the value and returns 
+   * that value on subsequent calls, until the {@link #refresh()} 
+   * method is called and a more current value is obtained.
+   *
+   * @exception AFSException  If an error occurs while retrieving and 
+   *                               calculating, or a volume has an 
+   *                               unlimited quota.
+   * @return the total combined quota of all volumes on this partition
+   * @see #getTotalQuota(boolean)
+   * @see #hasVolumeWithUnlimitedQuota()
+   * @see Volume#getQuota()
+   */
+  public int getTotalQuota() throws AFSException
+  {
+    return getTotalQuota(false);
+  }
+
+  /**
+   * Returns the total combined quota of all volumes on this partition,
+   * ignoring volumes with unlimited quotas, if <CODE>
+   * ignoreUnlimitedQuotas</CODE> is <CODE>true</CODE>; otherwise an
+   * {@link AFSException} is thrown if a volume has an unlimited quota.
+   *
+   * <P>After this method is called once, it saves the value and returns 
+   * that value on subsequent calls, until the {@link #refresh()} 
+   * method is called and a more current value is obtained.
+   *
+   * @exception AFSException  If an error occurs while retrieving and 
+   *                               calculating, or a volume has an 
+   *                               unlimited quota.
+   * @return the total combined quota of all volumes on this partition
+   * @see #getTotalQuota()
+   * @see #hasVolumeWithUnlimitedQuota()
+   * @see Volume#getQuota()
+   * @see #refresh()
+   */
+  public int getTotalQuota(boolean ignoreUnlimitedQuotas) 
+    throws AFSException
+  {
+    if (volumes == null) refreshVolumes();
+    if (totalQuota == 0 || !ignoreUnlimitedQuotas) {
+      Volume[] volumes = getVolumes();
+      for (int i = 0; i < volumes.length; i++) {
+        try {
+          totalQuota += volumes[i].getQuota();
+        } catch (AFSException e) {
+          if (!ignoreUnlimitedQuotas) {
+            totalQuota = 0;
+            throw e;
+          }
+        }
+      }
+    }
+    return totalQuota;
+  }
+
+  /**
+   * Tests whether this partition contains a volume that has an unlimited
+   * quota configured.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return <CODE>true</CODE> if a contained volume's quota is configured
+   *         as unlimited; otherwise <CODE>false</CODE>.
+   * @see #getTotalQuota()
+   * @see #getTotalQuota(boolean)
+   * @see Volume#isQuotaUnlimited()
+   * @see Volume#getQuota()
+   * @see #refresh()
+   */
+  public boolean hasVolumeWithUnlimitedQuota() throws AFSException
+  {
+    if (volumes == null) refreshVolumes();
+    Volume[] volumes = getVolumes();
+    for (int i = 0; i < volumes.length; i++) {
+      if (volumes[i].isQuotaUnlimited()) return true;
+    }
+    return false;
+  }
+
+  /////////////// custom information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this 
+   * <code>Partition</code>.  Contains the information fields and a list of 
+   * volumes.
+   *
+   * @return a <code>String</code> representation of the <code>Partition</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    
+    try {
+       
+       r = "Partition: " + name + "\tid: " + getID() + "\n";
+       
+       r += "\tDevice name: " + getDeviceName() + "\n";
+       r += "\tLock file descriptor: " + getLockFileDescriptor() + "\n";
+       r += "\tTotal free space: " + getTotalFreeSpace() + " K\n";
+       r += "\tTotal space: " + getTotalSpace() + " K\n";
+
+       r += "\tVolumes:\n";
+
+       String vols[] = getVolumeNames();
+
+       for( int i = 0; i < vols.length; i++ ) {
+           r += "\t\t" + vols[i] + "\n";
+       }
+
+    } catch( Exception e ) {
+       return e.toString();
+    }
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the volumes of this <code>Partition</code>.
+   *
+   * @return    a <code>String</code> representation of the volumes
+   * @see       Volume#getInfo
+   */
+  protected String getInfoVolumes() throws AFSException
+  {
+       String r;
+
+       r = "Partition: " + name + "\n\n";
+       r += "--Volumes--\n";
+
+       Volume vols[] = getVolumes();
+       for( int i = 0; i < vols.length; i++ ) {
+           r += vols[i].getInfo() + "\n";
+       }
+       return r;
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two Partition objects respective to their names and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     partition    The Partition object to be compared to 
+   *                         this Partition instance
+   * 
+   * @return    Zero if the argument is equal to this Partition's name, a
+   *           value less than zero if this Partition's name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this Partition's name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Partition partition)
+  {
+    return this.getName().compareTo(partition.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Partition)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Partition)obj);
+  }
+
+  /**
+   * Tests whether two <code>Partition</code> objects are equal, 
+   * based on their names and hosting server.
+   *
+   * @param otherPartition   the Partition to test
+   * @return whether the specifed Partition is the same as this Partition
+   */
+  public boolean equals( Partition otherPartition )
+  {
+    return ( name.equals(otherPartition.getName()) ) &&
+           ( getServer().equals(otherPartition.getServer()) );
+  }
+
+  /**
+   * Returns the name of this <CODE>Partition</CODE>
+   *
+   * @return the name of this <CODE>Partition</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Fills in the information fields of the provided <code>Partition</code>.
+   *
+   * @param cellHandle    the handle of the cell to which the partition belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which the 
+   *                      partition resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition for which to get the 
+   *                    info
+   * @param thePartition   the {@link Partition Partition} object in which to 
+   *                       fill in the information
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getPartitionInfo( int cellHandle, 
+                                                int serverHandle, 
+                                                int partition, 
+                                                Partition thePartition ) 
+    throws AFSException;
+
+  /**
+   * Returns the total number of volumes hosted by this partition.
+   *
+   * @param cellHandle    the handle of the cell to which the partition belongs
+   * @param serverHandle  the vos handle of the server to which the partition 
+   *                      belongs
+   * @param partition   the numeric id of the partition on which the volumes 
+   *                    reside
+   * @return total number of volumes hosted by this partition
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   * @see Server#getVosServerHandle
+   */
+  protected static native int getVolumeCount( int cellHandle, 
+                                              int serverHandle, 
+                                              int partition )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the volumes on a partition.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getVolumesNext</code> and <code>getVolumesDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the partition belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server to which the partition 
+   *                      belongs
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volumes 
+   *                    reside
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getVolumesBegin( int cellHandle, 
+                                              int serverHandle, 
+                                              int partition )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the volumes on a partition.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getVolumesNext</code> and <code>getVolumesDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the partition belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server to which the partition 
+   *                      belongs
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volumes 
+   *                    reside
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getVolumesBeginAt( int cellHandle, 
+                                                int serverHandle, 
+                                                int partition, int index )
+    throws AFSException;
+
+  /**
+   * Returns the next volume of the partition.  Returns <code>null</code> 
+   * if there are no more volumes.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getVolumesBegin
+   * @return the name of the next volume of the server
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getVolumesNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next volume object of the partition.  Returns 0 if there
+   * are no more volumes, != 0 otherwise.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @param theVolume    the Volume object in which to fill the values 
+   *                     of the next volume
+   * @see #getVolumesBegin
+   * @return 0 if there are no more volumes, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getVolumesNext( int iterationId, 
+                                             Volume theVolume )
+    throws AFSException;
+
+  /**
+   * Fills the next volume object of the partition.  Returns 0 if there
+   * are no more volumes, != 0 otherwise.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @param theVolume    the Volume object in which to fill the values of the 
+   *                     next volume
+   * @see #getVolumesBegin
+   * @return 0 if there are no more volumes, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getVolumesAdvanceTo( int iterationId, 
+                                                  Volume theVolume, 
+                                                  int advanceCount )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getVolumesBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getVolumesDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Translates a partition name into a partition id
+   *
+   * @param name  the name of the partition in question
+   * @return   the id of the partition in question
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int translateNameToID( String name )
+    throws AFSException;
+
+  /**
+   * Translates a partition id into a partition name
+   *
+   * @param id  the id of the partition in question
+   * @return   the name of the partition in question
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String translateIDToName( int id )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the partition portion of the native 
+   * library. This method should be called when no more <code>Partition</code> 
+   * objects are expected to be
+   * used.
+   */
+  protected static native void reclaimPartitionMemory();
+}
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Process.java b/src/JAVA/classes/org/openafs/jafs/Process.java
new file mode 100644 (file)
index 0000000..b006db3
--- /dev/null
@@ -0,0 +1,953 @@
+/*
+ * @(#)Process.java    1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS process.  It holds information about 
+ * the server, such as what its state is.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>Process</code> does not mean an actual 
+ * AFS process is created on a server -- usually a <code>Process</code>
+ * object is a representation of an already existing AFS process.  If, 
+ * however, the <code>Process</code> is constructed with the name of a 
+ * process that does not exist in the server represented by the provided 
+ * <code>Server</code>, a new process with that name can be
+ * created on that server by calling one of the {@link #createSimple(String)},
+ * {@link #createFS(String)}, or {@link #createCron(String,String)} methods. If
+ * such a process does already exist when one of these methods are called, 
+ * an exception will be thrown.<BR><BR>
+ *
+ * <!--Information on how member values are set-->
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Process</code> object.  This example obtains the list of all
+ * <code>Process</code> objects on a particular server and prints out the
+ * name of each one along with its start time.<BR><BR>
+ *
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Process;
+ * import org.openafs.jafs.Server;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = new Server(serverName, cell);
+ * 
+ *     System.out.println("Processes in Server " + server.getName() + ":");
+ *     Process[] processes = server.getProcesss();
+ *     for (int i = 0; i < processes.length; i++) {
+ *       System.out.print("Process " + processes[i].getName());
+ *       System.out.print("was started: " + 
+ *                        processes[i].getStartTimeDate().getTime() + "\n");
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Process implements Serializable, Comparable
+{
+  /**
+   * Any standard type of process except for fs (such as kaserver, 
+   * upclientbin, etc.)
+   */
+  public static final int SIMPLE_PROCESS = 0;
+  
+  /**
+   * Combination of File Server, Volume Server, and Salvager processes
+   */
+  public static final int FS_PROCESS = 1;
+  
+  /**
+   * A process that should be restarted at a specific time either daily 
+   * or weekly.
+   */
+  public static final int CRON_PROCESS = 2;
+
+  /**
+   * Process execution state stopped
+   */
+  public static final int STOPPED = 0;
+
+  /**
+   * Process execution state running
+   */
+  public static final int RUNNING = 1;
+
+  /**
+   * Process execution state stopping
+   */
+  public static final int STOPPING = 2;
+
+  /**
+   * Process execution state starting
+   */
+  public static final int STARTING = 3;
+
+  protected String name;
+  protected Server server;
+  protected int serverHandle;
+
+  protected int type;
+  protected int state;
+  protected int goal;
+
+  protected long startTime;
+  protected long numberStarts;
+  protected long exitTime;
+  protected long exitErrorTime;
+  protected long errorCode;
+  protected long errorSignal;
+
+  protected boolean stateOk;
+  protected boolean stateTooManyErrors;
+  protected boolean stateBadFileAccess;
+
+  protected GregorianCalendar startTimeDate;
+  protected GregorianCalendar exitTimeDate;
+  protected GregorianCalendar exitErrorTimeDate;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <code>Process</code> object instance given the name 
+   * of the AFS process and the AFS server, represented by 
+   * <CODE>server</CODE>, to which it belongs.   This does not actually
+   * create a new AFS process, it just represents one.
+   * If <code>name</code> is not an actual AFS process, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless one of the {@link #createSimple(String)},
+   * {@link #createFS(String)}, or {@link #createCron(String,String)} 
+   * methods are explicitly called to create it.
+   *
+   * @param name      the name of the server to represent 
+   * @param server    the server on which the process resides
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public Process( String name, Server server ) throws AFSException
+  {
+    this.name = name;
+    this.server = server;
+    serverHandle = server.getBosHandle();
+       
+    startTimeDate = null;
+    exitTimeDate = null;
+    exitErrorTimeDate = null;
+
+    cachedInfo = false;
+  }
+
+  /**
+   * Constructs a new <CODE>Process</CODE> object instance given the name 
+   * of the AFS process and the AFS server, represented by 
+   * <CODE>server</CODE>, to which it belongs.  This does not actually
+   * create a new AFS process, it just represents one.
+   * If <code>name</code> is not an actual AFS process, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless one of the {@link #createSimple(String)},
+   * {@link #createFS(String)}, or {@link #createCron(String,String)} 
+   * methods are explicitly called to create it.  Note that if he process
+   * doesn't exist and <code>preloadAllMembers</code> is true, an exception
+   * will be thrown.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications. It ensures all data member values are set and 
+   * available without calling back to the filesystem at the first request 
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param name               the name of the process to represent 
+   * @param server             the server to which the process belongs.
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction; otherwise members will 
+   *                           be set upon access, which is the default 
+   *                           behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Process( String name, Server server, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, server);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Creates a blank <code>Process</code> given the server to which the process
+   * belongs.  This blank object can then be passed into other methods to fill
+   * out its properties.
+   *
+   * @param server       the server to which the process belongs.
+   * @exception AFSException      If an error occurs in the native code
+   */
+  Process( Server server ) throws AFSException
+  {
+    this( null, server );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Refreshes the properties of this Process object instance with values 
+   * from the AFS process it represents.  All properties that have been 
+   * initialized and/or accessed will be renewed according to the values of 
+   * the AFS process this Process object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Process object instance with values from
+   * the AFS process it represents.  If <CODE>all</CODE> is <CODE>true</CODE> 
+   * then <U>all</U> of the properties of this Process object instance will be
+   * set, or renewed, according to the values of the AFS process it represents,
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise renew 
+   *              all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if (all || cachedInfo) refreshInfo();
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Process</code> to reflect 
+   * the current state of the AFS process, such as the start time, the state,
+   * etc.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getProcessInfo( server.getBosHandle(), name, this );
+    cachedInfo = true;
+    startTimeDate = null;
+    exitTimeDate = null;
+    exitErrorTimeDate = null;
+  }
+
+  /**
+   * Creates this process as a simple process on the server.
+   *
+   * @param executionPath   the path to the process's executable
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void createSimple( String executionPath ) throws AFSException
+  {
+    create( server.getBosHandle(), name, SIMPLE_PROCESS, executionPath, null, 
+           null );
+  }
+
+  /**
+   * Creates this process as a file server process on the server.
+   *
+   * @param executionPath   the path to the process's executable
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void createFS( String executionPath ) throws AFSException
+  {
+    create( server.getBosHandle(), name, FS_PROCESS, executionPath, null, 
+           null );
+  }
+
+  /**
+   * Creates this process as a cron process on the server.
+   *
+   * @param executionPath   the path to the process's executable
+   * @param cronTime   a String representing the time a cron process is 
+   *                   to be run.  Acceptable formats are:<ul>
+   *                   <li>for daily restarts: "23:10" or "11:10 pm"</li>
+   *                   <li>for weekly restarts: "sunday 11:10pm" or 
+   *                       "sun 11:10pm"</li>
+   *                   </ul>
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void createCron( String executionPath, String cronTime ) 
+      throws AFSException
+  {
+    create( server.getBosHandle(), name, CRON_PROCESS, executionPath, 
+           cronTime, null );
+  }
+
+  /**
+   * Removes this process from the bos server
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void delete() throws AFSException
+  {
+    delete( server.getBosHandle(), name );
+  }
+
+  /**
+   * Stops this process.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void stop() throws AFSException
+  {
+    state = STOPPING;
+    goal = STOPPED;
+    stop( server.getBosHandle(), name );
+  }
+
+  /**
+   * Starts this process
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void start() throws AFSException
+  {
+    state = STARTING;
+    start( server.getBosHandle(), name );
+  }
+
+  /**
+   * Restarts this process
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void restart() throws AFSException
+  {
+    state = STARTING;
+    restart( server.getBosHandle(), name );
+  }
+
+  //////////////// accessors:  ////////////////////////
+
+  /**
+   * Returns the name of this process.
+   *
+   * @return the name of this process
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns the server hosting this process.
+   *
+   * @return this process' server
+   */
+  public Server getServer()
+  {
+    return server;
+  }
+
+  /**
+   * Returns the process type.  Possible values are:<ul>
+   *      <li>{@link #SIMPLE_PROCESS}</li>
+   *      <li>{@link #FS_PROCESS}</li>
+   *      <li>{@link #CRON_PROCESS}</li></ul>
+   *
+   * @return the process type
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public int getType() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return type;
+  }
+
+  /**
+   * Returns the process goal.  Possible values are:<ul>
+   *      <li>{@link #STOPPED}</li>
+   *      <li>{@link #RUNNING}</li>
+   *      <li>{@link #STARTING}</li>
+   *      <li>{@link #STOPPING}</li></ul>
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the process goal
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public int getGoal() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return goal;
+  }
+
+  /**
+   * Returns the process execution state.  Possible values are:<ul>
+   *      <li>{@link #STOPPED}</li>
+   *      <li>{@link #RUNNING}</li>
+   *      <li>{@link #STARTING}</li>
+   *      <li>{@link #STOPPING}</li></ul>
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the process execution state
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public int getState() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return state;
+  }
+
+  /**
+   * Returns the most recent start time of this process.  A 
+   * <code>null</code> value 
+   * indicates no start time.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the start time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getStartTime() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return startTime;
+  }
+
+  /**
+   * Returns the most recent start time of this process.  A <code>null</code> 
+   * value indicates no start time.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the start time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public GregorianCalendar getStartTimeDate() throws AFSException
+  {
+    if (!cachedInfo) {
+       refreshInfo();
+    }
+    if( startTimeDate == null && startTime != 0 ) {
+       // make it into a date . . .
+       startTimeDate = new GregorianCalendar();
+       long longTime = startTime * 1000;
+       Date d = new Date( longTime );
+       startTimeDate.setTime( d );
+    }
+    return startTimeDate;
+  }
+
+  /**
+   * Returns the number of starts of the process.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the number of starts
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getNumberOfStarts() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return numberStarts;
+  }
+
+  /**
+   * Returns the most recent exit time of this process.  A <code>null</code> 
+   * value indicates no exit time.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the exit time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getExitTime() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return exitTime;
+  }
+
+  /**
+   * Returns the most recent exit time of this process.  A <code>null</code> 
+   * value indicates no exit time
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the exit time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public GregorianCalendar getExitTimeDate() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if( exitTimeDate == null && exitTime != 0 ) {
+       // make it into a date . . .
+       exitTimeDate = new GregorianCalendar();
+       long longTime = exitTime*1000;
+       Date d = new Date( longTime );
+       exitTimeDate.setTime( d );
+    }
+    return exitTimeDate;
+  }
+
+  /**
+   * Returns the most recent time this process exited with an error.  A 
+   * <code>null</code> value indicates no exit w/ error time.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the exit w/ error time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getExitErrorTime() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return exitErrorTime;
+  }
+
+  /**
+   * Returns the most recent time this process exited with an error.  A <
+   * code>null</code> value indicates no exit w/ error time.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the exit w/ error time
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public GregorianCalendar getExitErrorTimeDate() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if (exitErrorTimeDate == null && exitErrorTime != 0) {
+       // make it into a date . . .
+       exitErrorTimeDate = new GregorianCalendar();
+       long longTime = exitErrorTime*1000;
+       Date d = new Date( longTime );
+       exitErrorTimeDate.setTime( d );
+    }
+    return exitErrorTimeDate;
+  }
+
+  /**
+   * Returns the error code of the process.  A value of 0 indicates 
+   * no error code.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the error code
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getErrorCode() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return errorCode;
+  }
+
+  /**
+   * Returns the error signal of the process.  A value of 0 indicates no 
+   * error signal.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the error signal
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public long getErrorSignal() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return errorSignal;
+  }
+
+  /**
+   * Returns whether or not the state of the process is ok.  A value of 
+   * <code>false</code> indicates there has been a core dump.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return whether or not the state is ok
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public boolean getStateOk() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return stateOk;
+  }
+
+  /**
+   * Returns whether or not the state of the process indicates too many errors.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return whether or not the state indicates too many errors
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public boolean getStateTooManyErrors() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return stateTooManyErrors;
+  }
+
+  /**
+   * Returns whether or not the state of the process indicates bad file access.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return whether or not the state indicates bad file access
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public boolean getStateBadFileAccess() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return stateBadFileAccess;
+  }
+
+  /////////////// custom information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Process</code>.
+   * Contains the information fields.
+   *
+   * @return a <code>String</code> representation of the <code>Process</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    try {
+       
+       r = "Process: " + name + "\n";
+       
+       r += "\ttype: ";
+       switch( getType() ) {
+       case SIMPLE_PROCESS:
+         r += "simple";
+         break;
+       case FS_PROCESS:
+         r += "fs";
+         break;
+       case CRON_PROCESS:
+         r += "cron";
+         break;
+       default:
+         r += "other - " + getType();
+       }
+       r += "\n";
+
+       r += "\tstate: ";
+       switch( getState() ) {
+       case STOPPED:
+         r += "stopped";
+         break;
+       case RUNNING:
+         r += "running";
+         break;
+       case STOPPING:
+         r += "stopping";
+         break;
+       case STARTING:
+         r += "starting";
+         break;
+       default:
+         r += "other - " + getState();
+       }
+       r += "\n";
+
+       r += "\tgoal: ";
+       switch( getGoal() ) {
+       case STOPPED:
+         r += "stopped";
+         break;
+       case RUNNING:
+         r += "running";
+         break;
+       case STOPPING:
+         r += "stopping";
+         break;
+       case STARTING:
+         r += "starting";
+         break;
+       default:
+         r += "other - " + getGoal();
+       }
+       r += "\n";
+
+       r += "\tstartTime: ";
+       if( getStartTime() == 0) {
+         r += "0";
+       } else {
+         r += getStartTimeDate().getTime(); 
+       }
+      r += "\n";
+
+       r += "\tnumberStarts: " + getNumberOfStarts() + "\n";
+
+       r += "\texitTime: ";
+       if( getExitTime() == 0 ) {
+         r += "0";
+       } else {
+         r += getExitTimeDate().getTime(); 
+       }
+      r += "\n";
+
+       r += "\texitErrorTime: ";
+       if( getExitErrorTimeDate() == null ) {
+         r += "0";
+       } else {
+         r += getExitErrorTimeDate().getTime(); 
+       }
+      r += "\n";
+
+       r += "\terrorCode: " + getErrorCode() + "\n";
+       r += "\terrorSignal: " + getErrorSignal() + "\n";
+       r += "\tstateOk: " + getStateOk() + "\n";
+       r += "\tstateTooManyErrors: " + getStateTooManyErrors() + "\n";
+       r += "\tstateBadFileAccess: " + getStateBadFileAccess() + "\n";
+
+    } catch( Exception e ) {
+       return e.toString();
+    }
+    return r;
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two Process objects respective to their names and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     process    The Process object to be compared to this Process 
+   *                       instance
+   * 
+   * @return    Zero if the argument is equal to this Process' name, a
+   *           value less than zero if this Process' name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this Process' name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Process process)
+  {
+    return this.getName().compareTo(process.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Process)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Process)obj);
+  }
+
+  /**
+   * Tests whether two <code>Process</code> objects are equal, based on their 
+   * names and hosting server.
+   *
+   * @param otherProcess   the Process to test
+   * @return whether the specifed Process is the same as this Process
+   */
+  public boolean equals( Process otherProcess )
+  {
+    return ( name.equals(otherProcess.getName()) ) &&
+           ( this.getServer().equals(otherProcess.getServer()) );
+  }
+
+  /**
+   * Returns the name of this <CODE>Process</CODE>
+   *
+   * @return the name of this <CODE>Process</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Fills in the information fields of the provided <code>Process</code>.
+   *
+   * @param cellHandle    the handle of the cell to which the process belongs
+   * @see Cell#getCellHandle
+   * @param processName     the instance name of the process  for which to get
+   *                        the information
+   * @param theProcess     the {@link Process Process} object in which to fill 
+   *                       in the information
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getProcessInfo( int cellHandle, 
+                                              String processName, 
+                                              Process theProcess ) 
+       throws AFSException;
+
+  /**
+   * Creates a processes on a server.
+   *
+   * @param serverHandle  the bos handle of the server to which the key will 
+   *                      belong
+   * @see Server#getBosServerHandle
+   * @param processName  the instance name to give the process.  See AFS 
+   *                     documentation for a standard list of instance names
+   * @param processType  the type of process this will be. 
+   *                     Acceptable values are:<ul>
+   *                     <li>{@link #SIMPLE_PROCESS}</li>
+   *                     <li>{@link #FS_PROCESS}</li>
+   *                     <li>{@link #CRON_PROCESS}</li></ul>
+   * @param executionPath  the execution path process to create
+   * @param cronTime   a String representing the time a cron process is to 
+   *                   be run.  Acceptable formats are:<ul>
+   *                   <li>for daily restarts: "23:10" or "11:10 pm"</li>
+   *                   <li>for weekly restarts: "sunday 11:10pm" or 
+   *                       "sun 11:10pm"</li>
+   *                   </ul>
+   *                   Can be <code>null</code> for non-cron processes.
+   * @param notifier   the execution path to a notifier program that should 
+   *                   be called when the process terminates.  Can be 
+   *                   <code>null</code>
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void create( int serverHandle, String processName, 
+                                      int processType, String executionPath, 
+                                      String cronTime, String notifier )
+    throws AFSException;
+
+  /**
+   * Removes a process from a server.
+   *
+   * @param serverHandle  the bos handle of the server to which the process 
+   *                      belongs
+   * @see Server#getBosServerHandle
+   * @param processName   the name of the process to remove
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void delete( int serverHandle, String processName )
+    throws AFSException;
+
+  /**
+   * Start this process.
+   *
+   * @param serverHandle  the bos handle of the server to which the process 
+   *                      belongs
+   * @see Server#getBosServerHandle
+   * @param processName   the name of the process to start
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void start( int serverHandle, String processName )
+    throws AFSException;
+
+  /**
+   * Retart this process.
+   *
+   * @param serverHandle  the bos handle of the server to which the process 
+   *                      belongs
+   * @see Server#getBosServerHandle
+   * @param processName   the name of the process to restart
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void restart( int serverHandle, String processName )
+    throws AFSException;
+
+  /**
+   * Stop this process.
+   *
+   * @param serverHandle  the bos handle of the server to which the process 
+   *                      belongs
+   * @see Server#getBosServerHandle
+   * @param processName   the name of the process to stop
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void stop( int serverHandle, String processName )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the process portion of the native 
+   * library. This method should be called when no more <code>Process</code> 
+   * objects are expected to be used.
+   */
+  protected static native void reclaimProcessMemory();
+}
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Server.java b/src/JAVA/classes/org/openafs/jafs/Server.java
new file mode 100644 (file)
index 0000000..de25385
--- /dev/null
@@ -0,0 +1,2351 @@
+/*
+ * @(#)Server.java     1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.Serializable;
+import java.text.DecimalFormat;
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
+/**
+ * An abstract representation of an AFS server.  It holds information about 
+ * the server, such as what its processes are.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>Server</code> does not mean an actual 
+ * AFS server is created and added to a cell -- on the contrary, a 
+ * <code>Server</code> object must be a representation of an already existing 
+ * AFS server.  There is no way to create a new AFS server through this API.  
+ * See <a href="http://www.openafs.org">OpenAFS.org</a> for information on how
+ * to create a new AFS server.<BR><BR>
+ *
+ * A <code>Server</code> object may represent either an AFS file server,
+ * an AFS database server, or both if the same machine serves both
+ * purposes.<BR><BR>
+ *
+ * Each <code>Server</code> object has its own individual set of
+ * <code>Partition</code>s, <code>Process</code>es, and <code>Key</code>s.
+ * This represents the properties and attributes of an actual AFS server.
+ * <BR><BR>
+ *    
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a Server 
+ * object.  This example constructs a <code>Server</code> using the 
+ * <code>Cell</code> representing teh AFS cell to which the server belongs, 
+ * and prints out the names of all the partitions residing on the server.
+ * <BR><BR>
+ *
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Partition;
+ * import org.openafs.jafs.Server;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = new Server(serverName, cell);
+ * 
+ *     System.out.println("Partitions in Server " + server.getName() + ":");
+ *     if( server.isFileServer() ) {
+ *       Partition[] partitions = server.getPartitions();
+ *       for (int i = 0; i < partitions.length; i++) {
+ *         System.out.println(" -> " + partitions[i]);
+ *       }
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Server implements Serializable, Comparable
+{
+  /**
+   * Used for binary restart time types.
+   */
+  private static final int RESTART_BINARY = 0;
+
+  /**
+   * Used for general restart time types.
+   */
+  private static final int RESTART_GENERAL = 1;
+
+  protected String name;
+  protected Cell cell;
+
+  protected int vosHandle;
+  protected int bosHandle;
+
+  protected boolean database;
+  protected boolean fileServer;
+
+  // these will be true if the machine is supposedly listed as a server 
+  // but that's wrong, or the machine is down 
+  protected boolean badFileServer;  
+  protected boolean badDatabase;
+
+  // String IP Address of address[0]
+  protected String[] ipAddresses;
+
+  protected ArrayList partitionNames;
+  protected ArrayList partitions;
+  protected ArrayList adminNames;
+  protected ArrayList admins;
+  protected ArrayList keys;
+  protected ArrayList processNames;
+  protected ArrayList processes;
+
+  // Storage information
+  protected int totalSpace;
+  protected int totalQuota;
+  protected int totalFreeSpace;
+  protected int totalUsedSpace;
+
+  protected ExecutableTime genRestartTime;
+  protected ExecutableTime binRestartTime;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <CODE>Server</CODE> object instance given the 
+   * name of the AFS server and the AFS cell, represented by 
+   * <CODE>cell</CODE>, to which it belongs.  This does not actually
+   * create a new AFS server, it just represents an existing one.
+   * If <code>name</code> is not an actual AFS server, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object.
+   *
+   * @param name  the name of the server to represent
+   * @param cell  the cell to which the server belongs.
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public Server( String name, Cell cell ) throws AFSException
+  {
+    this.name = name;
+    this.cell = cell;
+    
+    cachedInfo = false;
+
+    vosHandle = 0;
+    bosHandle = 0;
+
+    ipAddresses = new String[16];
+
+    partitionNames = null;
+    partitions = null;
+    adminNames = null;
+    admins = null;
+    keys = null;
+    processNames = null;
+    processes = null;
+  }
+
+  /**
+   * Constructs a new <CODE>Server</CODE> object instance given the name 
+   * of the AFS server and the AFS cell, represented by <CODE>cell</CODE>, 
+   * to which it belongs.   This does not actually
+   * create a new AFS server, it just represents an existing one.
+   * If <code>name</code> is not an actual AFS server, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications.  It ensures all data member values are set 
+   * and available without calling back to the filesystem at the first 
+   * request for them.  Use the {@link #refresh()} method to address any 
+   * coherency concerns.
+   *
+   * @param name               the name of the server to represent 
+   * @param cell               the cell to which the server belongs.
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction;
+   *                           otherwise members will be set upon access, 
+   *                           which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Server( String name, Cell cell, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, cell);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Constructs a blank <code>Server</code> object instance given the cell to 
+   * which the server belongs.  This blank object can then be passed into 
+   * other methods to fill out its properties.
+   *
+   * @param cell       the cell to which the server belongs.
+   * @exception AFSException      If an error occurs in the native code
+   */
+  Server( Cell cell ) throws AFSException
+  {
+    this( null, cell );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Refreshes the properties of this Server object instance with values 
+   * from the AFS server it represents.  All properties that have been 
+   * initialized and/or accessed will be renewed according to the values 
+   * of the AFS server this Server object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Server object instance with values 
+   * from the AFS server it represents.  If <CODE>all</CODE> is 
+   * <CODE>true</CODE> then <U>all</U> of the properties of this Server 
+   * object instance will be set, or renewed, according to the values of the 
+   * AFS server it represents, disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set 
+   * will remain uninitialized.
+   * See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; 
+   *              otherwise renew all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if ( all ) {
+      refreshProcesses();
+      refreshProcessNames();
+      refreshKeys();
+      refreshAdminNames();
+      refreshAdmins();
+      refreshPartitionNames();
+      refreshPartitions(all);
+      refreshInfo();
+      refreshGeneralRestart();
+      refreshBinaryRestart();
+    } else {
+      if ( processes      != null ) refreshProcesses();
+      if ( processNames   != null ) refreshProcessNames();
+      if ( keys           != null ) refreshKeys();
+      if ( adminNames     != null ) refreshAdminNames();
+      if ( admins         != null ) refreshAdmins();
+      if ( partitionNames != null ) refreshPartitionNames();
+      if ( partitions     != null ) refreshPartitions(all);
+      if ( genRestartTime != null ) refreshGeneralRestart();
+      if ( binRestartTime != null ) refreshBinaryRestart();
+      if ( cachedInfo )             refreshInfo();
+    }
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Server</code> to 
+   * reflect the current state of the AFS server.  These fields include
+   * the IP addresses and the fileserver types.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getServerInfo( cell.getCellHandle(), name, this );
+    cachedInfo = true;
+  }
+
+  /**
+   * Refreshes the general restart time fields of this <code>Server</code> 
+   * to reflect the current state of the AFS server.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshGeneralRestart() throws AFSException
+  {
+    if (genRestartTime == null) genRestartTime = new ExecutableTime();
+    getRestartTime( getBosHandle(), RESTART_GENERAL, genRestartTime );
+  }
+
+  /**
+   * Refreshes the binary restart time fields of this <code>Server</code> 
+   * to reflect the current state of the AFS server.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshBinaryRestart() throws AFSException
+  {
+    if (binRestartTime == null) binRestartTime = new ExecutableTime();
+    getRestartTime( getBosHandle(), RESTART_BINARY, binRestartTime );
+  }
+
+  /**
+   * Obtains the most current list of <code>Partition</code> objects 
+   * of this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshPartitions() throws AFSException
+  {
+    this.refreshPartitions(false);
+  }
+
+  /**
+   * Obtains the most current list of <code>Partition</code> objects of 
+   * this server.
+   *
+   * @param refreshVolumes force all volumes contained in each 
+   *                       partition to be refreshed.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshPartitions(boolean refreshVolumes) 
+      throws AFSException
+  {
+    if (!isFileServer() || isBadFileServer()) return;
+
+    Partition currPartition;
+
+    int iterationID = getPartitionsBegin( cell.getCellHandle(), 
+                                         getVosHandle() );
+    
+    partitions = new ArrayList();
+    
+    currPartition = new Partition( this );
+    while( getPartitionsNext( iterationID, currPartition ) != 0 ) {
+      //Only volumes are necessary since volume information 
+      //is populated at time of construction
+      if (refreshVolumes) currPartition.refreshVolumes();
+      partitions.add( currPartition );
+      currPartition = new Partition( this );
+    } 
+    getPartitionsDone( iterationID );
+    totalSpace = 0;
+    totalQuota = 0;
+    totalUsedSpace = 0;
+    totalFreeSpace = 0;
+  }
+
+  /**
+   * Obtains the most current list of partition names of this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshPartitionNames() throws AFSException
+  {
+    if (!isFileServer() || isBadFileServer()) return;
+
+    String currName;
+
+    int iterationID = getPartitionsBegin( cell.getCellHandle(), 
+                                         getVosHandle() );
+    
+    partitionNames = new ArrayList();
+    
+    while( ( currName = getPartitionsNextString( iterationID ) ) != null ) {
+        partitionNames.add( currName );
+    } 
+    getPartitionsDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of bos admin names of this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshAdminNames() throws AFSException
+  {
+    String currName;
+
+    int iterationID = getBosAdminsBegin( getBosHandle() );
+    
+    adminNames = new ArrayList();
+    
+    while( ( currName = getBosAdminsNextString( iterationID ) ) != null ) {
+        adminNames.add( currName );
+    } 
+    getBosAdminsDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of admin <code>User</code> objects of 
+   * this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshAdmins() throws AFSException
+  {
+    User currUser;
+
+    int iterationID = getBosAdminsBegin( getBosHandle() );
+       
+    admins = new ArrayList();
+       
+    currUser = new User( cell );
+    while( getBosAdminsNext( cell.getCellHandle(), iterationID, currUser ) 
+          != 0 ) {
+      admins.add( currUser );
+      currUser = new User( cell );
+    } 
+    getBosAdminsDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of <code>Key</code> objects of this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshKeys() throws AFSException
+  {
+    Key currKey;
+
+    int iterationID = getKeysBegin( getBosHandle() );
+    
+    keys = new ArrayList();
+    
+    currKey = new Key( this );
+    while( getKeysNext( iterationID, currKey ) != 0 ) {
+        keys.add( currKey );
+        currKey = new Key( this );
+    } 
+    getKeysDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of process names of this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshProcessNames() throws AFSException
+  {
+    String currName;
+
+    int iterationID = getProcessesBegin( getBosHandle() );
+    
+    processNames = new ArrayList();
+    
+    while( ( currName = getProcessesNextString( iterationID ) ) != null ) {
+        processNames.add( currName );
+    } 
+    getProcessesDone( iterationID );
+  }
+
+  /**
+   * Obtains the most current list of <code>Process</code> objects of 
+   * this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshProcesses() throws AFSException
+  {
+    Process currProcess;
+
+    int iterationID = getProcessesBegin( getBosHandle() );
+    
+    processes = new ArrayList();
+    
+    currProcess = new Process( this );
+    while( getProcessesNext( getBosHandle(), iterationID, currProcess ) 
+          != 0 ) {
+        processes.add( currProcess );
+        currProcess = new Process( this );
+    } 
+    getProcessesDone( iterationID );
+  }
+
+  /**
+   * Add a bos admin to the UserList file of this server, in order to
+   * given the AFS user represented by <code>admin</code> full bos
+   * administrative privileges on this server.
+   *
+   * @param admin   the admin to add
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void addAdmin( User admin ) throws AFSException
+  {
+    String adminName = admin.getName();
+    
+    addBosAdmin( getBosHandle(), adminName );
+    if ( adminNames != null ) {
+        adminNames.add( adminName );
+    }
+  }
+
+  /**
+   * Remove a bos admin from the UserList file of this server, in order to
+   * take away from the AFS user represented by <code>admin</code> bos
+   * administrative privileges on this machine.
+   *
+   * @param admin   the admin to remove
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void removeAdmin( User admin ) throws AFSException
+  {
+    String adminName = admin.getName();
+    
+    removeBosAdmin( getBosHandle(), adminName );
+    if ( adminNames != null ) {
+        adminNames.remove( adminNames.indexOf( adminName ) );
+        adminNames.trimToSize();
+    }
+  }
+
+  /**
+   * Syncs this server to the VLDB.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void syncServer() throws AFSException
+  {
+    syncServerWithVLDB( cell.getCellHandle(), getVosHandle(), -1 );
+  }
+
+  /**
+   * Syncs the VLDB to this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void syncVLDB() throws AFSException
+  {
+    syncVLDBWithServer( cell.getCellHandle(), getVosHandle(), -1, false );
+  }
+
+  /**
+   * Salvages (restores consistency to) this server. Uses default values for
+   * most salvager options in order to simplify the API.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void salvage() throws AFSException
+  {
+    salvage( cell.getCellHandle(), getBosHandle(), null, null, 4, null, null, 
+            false, false, false, false, false, false );
+  }
+
+  /**
+   * Starts up all bos processes on this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void startAllProcesses() throws AFSException
+  {
+    startAllProcesses( getBosHandle() );
+  }
+
+  /**
+   * Stops all bos processes on this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void stopAllProcesses() throws AFSException
+  {
+    stopAllProcesses( getBosHandle() );
+  }
+
+  /**
+   * Restarts all bos processes on this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void restartAllProcesses() throws AFSException
+  {
+    restartAllProcesses( getBosHandle(), false );
+  }
+
+  /**
+   * Restarts bos server and all bos processes on this server.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void restartBosServer() throws AFSException
+  {
+    restartAllProcesses( getBosHandle(), true );
+  }
+
+  /**
+   * Gets the contents of a log file, in one large <code>String</code>.  
+   * The log cannot be in AFS file space.
+   *
+   * @return a <code>String</code> containing the contents of the log file
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public String getLog( String logLocation ) throws AFSException
+  {
+    return getLog( getBosHandle(), logLocation );
+  }
+
+  /**
+   * Unauthenticates all server-related tokens that have been obtained by 
+   * this <code>Server</code> object, and shuts this server object down.
+   * This method should only be called when this <code>Server</code> or any 
+   * of the objects constructed using this <code>Server</code> will not be 
+   * used anymore.  Note that this does not effect the actual AFS server;
+   * it merely closes the representation.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void close() throws AFSException
+  {
+    if ( vosHandle != 0 ) {
+        closeVosServerHandle( vosHandle );
+    }
+    if ( bosHandle != 0 ) {
+        closeBosServerHandle( bosHandle );
+    }
+
+    cachedInfo = false;
+
+    vosHandle = 0;
+    bosHandle = 0;
+
+    partitionNames = null;
+    partitions = null;
+    adminNames = null;
+    admins = null;
+    keys = null;
+    processNames = null;
+    processes = null;
+  }
+
+  //////////////// accessors:  ////////////////////////
+
+  /**
+   * Returns the name of this server.
+   *
+   * @return the name of this server
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns the <code>Cell</code> object with which this <code>Server</code>
+   * was constructed.  It represents the actual AFS cell to which this
+   * server belongs.
+   *
+   * @return this server's cell
+   */
+  public Cell getCell()
+  {
+    return cell;
+  }
+
+  /**
+   * Returns the number of BOS administrators assigned to this server.
+   *
+   * <P>If the total list of admins or admin names have already been 
+   * collected (see {@link #getAdmins()}), then the returning value will
+   * be calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * <P> The product of this method is not saved, and is recalculated
+   * with every call.
+   *
+   * @return the number of admins on this server.
+   * @exception AFSException  If an error occurs 
+   *                               in any of the associated native methods
+   * @see #getAdmins()
+   * @see #getAdminNames()
+   */
+  public int getAdminCount() throws AFSException
+  {
+    if (adminNames != null) {
+      return adminNames.size();
+    } else if (admins != null) {
+      return admins.size();
+    } else {
+      return getBosAdminCount(getBosHandle());
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the admin <code>User</code> objects 
+   * associated with this <code>Server</code>, each of which are an abstract 
+   * representation of an actual bos administrator of the AFS server.  
+   * After this method is called once, it saves the array of 
+   * <code>User</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current list 
+   * is obtained.
+   *
+   * @return a <code>User</code> array of the admins of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public User[] getAdmins() throws AFSException
+  {
+    if ( admins == null ) refreshAdmins();
+    return (User[]) admins.toArray( new User[admins.size()] );
+  }
+
+  /**
+   * Retrieves an array containing all of the names of bos admins 
+   * associated with this <code>Server</code>. After this method
+   * is called once, it saves the array of <code>String</code>s and returns
+   * that saved array on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current list is obtained.
+   *
+   * @return a <code>String</code> array of the bos admin of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getAdminNames() throws AFSException
+  {
+    if ( adminNames == null ) refreshAdminNames();
+    return (String []) adminNames.toArray( new String[adminNames.size()] );
+  }
+
+  /**
+   * Returns the number of partitions on this server.
+   *
+   * <P>If the total list of partitions or partition names have already been 
+   * collected (see {@link #getPartitions()}), then the returning value will
+   * be calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * <P> The product of this method is not saved, and is recalculated
+   * with every call.
+   *
+   * @return the number of partitions on this server.
+   * @exception AFSException  If an error occurs 
+   *                               in any of the associated native methods
+   * @see #getPartitions()
+   * @see #getPartitionNames()
+   */
+  public int getPartitionCount() throws AFSException
+  {
+    if (partitionNames != null) {
+      return partitionNames.size();
+    } else if (partitions != null) {
+      return partitions.size();
+    } else {
+      return getPartitionCount(cell.getCellHandle(), getVosHandle());
+    }
+  }
+
+  /**
+   * Retrieves the <CODE>Partition</CODE> object (which is an abstract 
+   * representation of an actual AFS partition of this server) designated 
+   * by <code>name</code> (i.e. "/vicepa", etc.).  If a partition by 
+   * that name does not actually exist in AFS on the server
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @param name the name of the partition to retrieve
+   * @return <CODE>Partition</CODE> designated by <code>name</code>.
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   */
+  public Partition getPartition(String name) throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    if (isFileServer() && !isBadFileServer()) {
+      Partition partition = new Partition(name, this);
+      partition.refresh(true);
+      return partition;
+    } else {
+      //Throw "No such entry" error
+      throw new AFSException("Server is not a file server.", 363524);
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Partition</code> objects 
+   * associated with this <code>Server</code>, each of which are an abstract 
+   * representation of an actual AFS partition of the AFS server.  
+   * After this method is called once, it saves the array of 
+   * <code>Partition</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current list 
+   * is obtained.
+   *
+   * @return a <code>Partition</code> array of the <code>Partition</code> 
+   *         objects of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public Partition[] getPartitions() throws AFSException
+  {
+    if ( partitions == null ) refreshPartitions();
+    return (Partition []) 
+       partitions.toArray( new Partition[partitions.size()] );
+  }
+
+  /**
+   * Retrieves an array containing all of the names of partitions
+   * associated with this <code>Server</code> (i.e. "vicepa", etc.). 
+   * After this method is called once, it saves the array of 
+   * <code>String</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * list is obtained.
+   *
+   * @return a <code>String</code> array of the partitions of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getPartitionNames() throws AFSException
+  {
+    if ( partitionNames == null ) refreshPartitionNames();
+    return (String []) 
+       partitionNames.toArray( new String[partitionNames.size()] );
+  }
+
+  /**
+   * Retrieves the <CODE>Key</CODE> object (which is an abstract 
+   * representation of an actual AFS partition of this server) designated 
+   * by <code>nkeyVersion</code>.  If a key with 
+   * that version does not actually exist in AFS on the server
+   * represented by this object, <code>null</code> is returned.
+   *
+   * @param keyVersion the version of the key to retrieve
+   * @return <CODE>Key</CODE> designated by <code>keyVersion</code>.
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public Key getKey(int keyVersion) throws AFSException
+  {
+    try {
+      Key[] keys = this.getKeys();
+      for (int i = 0; i < keys.length; i++) {
+        if (keys[i].getVersion() == keyVersion) {
+          return keys[i];
+        }
+      }
+    } catch (Exception e) {
+      e.printStackTrace();
+    }
+    return null;
+  }
+
+  /**
+   * Returns the number of keys on this server.
+   *
+   * <P>If the total list of keys has already been 
+   * collected (see {@link #getKeys()}), then the returning value will
+   * be calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * <P> The product of this method is not saved, and is recalculated
+   * with every call.
+   *
+   * @return the number of keys on this server.
+   * @exception AFSException  If an error occurs 
+   *                               in any of the associated native methods
+   * @see #getKeys()
+   */
+  public int getKeyCount() throws AFSException
+  {
+    if (keys != null) {
+      return keys.size();
+    } else {
+      return getKeyCount(getBosHandle());
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Key</code> objects 
+   * associated with this <code>Server</code>, each of which are an abstract 
+   * representation of an actual AFS key of the AFS server.  
+   * After this method is called once, it saves the array of 
+   * <code>Key</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current list 
+   * is obtained.
+   *
+   * @return a <code>Key</code> array of the <code>Key</code> objects 
+   *         of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public Key[] getKeys() throws AFSException
+  {
+    if ( keys == null ) refreshKeys();
+    return (Key[]) keys.toArray( new Key[keys.size()] );
+  }
+
+  /**
+   * Retrieves the <CODE>Process</CODE> object (which is an abstract 
+   * representation of an actual AFS process of this server) designated 
+   * by <code>name</code> (i.e. "kaserver", etc.).  If a process by 
+   * that name does not actually exist in AFS on the server
+   * represented by this object, an {@link AFSException} will be
+   * thrown.
+   *
+   * @param name the name of the process to retrieve
+   * @return <CODE>Process</CODE> designated by <code>name</code>.
+   * @exception AFSException  If an error occurs in the native code
+   * @exception NullPointerException  If <CODE>name</CODE> is 
+   *                                  <CODE>null</CODE>.
+   */
+  public Process getProcess(String name) throws AFSException
+  {
+    if (name == null) throw new NullPointerException();
+    //if (isFileServer() && !isBadFileServer()) {
+      Process process = new Process(name, this);
+      process.refresh(true);
+      return process;
+    //}
+  }
+
+  /**
+   * Returns the number of processes hosted by this server.
+   *
+   * <P>If the total list of processes or process names have already been 
+   * collected (see {@link #getProcesses()}), then the returning value will
+   * be calculated based upon the current list.  Otherwise, AFS will be
+   * explicitly queried for the information.
+   *
+   * <P> The product of this method is not saved, and is recalculated
+   * with every call.
+   *
+   * @return the number of processes on this server.
+   * @exception AFSException  If an error occurs 
+   *                               in any of the associated native methods
+   * @see #getProcesses()
+   * @see #getProcessNames()
+   */
+  public int getProcessCount() throws AFSException
+  {
+    if (processNames != null) {
+      return processNames.size();
+    } else if (processes != null) {
+      return processes.size();
+    } else {
+      return getProcessCount(getBosHandle());
+    }
+  }
+
+  /**
+   * Retrieves an array containing all of the <code>Process</code> objects 
+   * associated with this <code>Server</code>, each of which are an abstract 
+   * representation of an actual AFS process of the AFS server.  
+   * After this method is called once, it saves the array of 
+   * <code>Process</code>es and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current list 
+   * is obtained.
+   *
+   * @return a <code>Process</code> array of the <code>Process</code> 
+   *         objects of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public Process[] getProcesses() throws AFSException
+  {
+    if ( processes == null ) refreshProcesses();
+    return (Process[]) processes.toArray( new Process[processes.size()] );
+  }
+
+  /**
+   * Retrieves an array containing all of the names of processes
+   * associated with this <code>Server</code> (i.e. "kaserver", etc.). 
+   * After this method is called once, it saves the array of 
+   * <code>String</code>s and returns that saved array on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * list is obtained.
+   *
+   * @return a <code>String</code> array of the processes of the server.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getProcessNames() throws AFSException
+  {
+    if ( processNames == null ) refreshProcessNames();
+    return (String[]) processNames.toArray( new String[processNames.size()] );
+  }
+
+  /**
+   * Returns whether or not this server is a database machine, meaning it runs
+   * processes such as the "kaserver" and "vlserver", and participates in 
+   * elections.
+   *
+   * @return whether or not this user this server is a database machine.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isDatabase() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return database;
+  }
+
+  /**
+   * Returns whether or not this server is a file server machine, meaning it
+   * runs the "fs" process and stores AFS volumes.
+   *
+   * @return whether or not this user this server is a file server machine.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isFileServer() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return fileServer;
+  }
+
+  /**
+   * Returns whether or not this server is a database machine AND 
+   * either it isn't in reality (e.g. it's incorrectly configured) 
+   * or it's currently down.
+   *
+   * @return whether or not this server is a database machine 
+   *         AND either it isn't in reality or it's currently down
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isBadDatabase() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return badDatabase;
+  }
+
+  /**
+   * Returns whether this machine thinks it's a file server AND 
+   * either it isn't in reality (e.g. it's incorrectly configured) 
+   * or it's currently down.
+   *
+   * @return whether or not this server is a file server machine AND 
+   *         either it isn't in reality or it's currently down
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isBadFileServer() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    return badFileServer;
+  }
+
+  /**
+   * Returns this server's IP address as a String.  It returns it in 
+   * dotted quad notation (i.e. 123.123.123.123).  
+   *
+   * @return this server's IP address as a String
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getIPAddresses() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    int n = 16;
+    for (int i = 0; i < n; i++) {
+      if (ipAddresses[i] == null) {
+        n = i;
+        break;
+      }
+    }
+    String[] addresses = new String[n];
+    System.arraycopy(ipAddresses, 0, addresses, 0, n);
+    return addresses;
+  }
+
+  /**
+   * Returns the BOS Server's general restart time in the form of an
+   * ExecutableTime object.  This is the time at which the bos server
+   * restarts itself and all running processes.  After this method
+   * is called once, it saves the time and returns
+   * that value on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current value is obtained.
+   *
+   * @return the general restart time
+   * @exception AFSException  If an error occurs in the native code
+   * @see Server.ExecutableTime
+   * @see #refresh()
+   */
+  public ExecutableTime getGeneralRestartTime() throws AFSException
+  {
+    if (genRestartTime == null) refreshGeneralRestart();
+    return genRestartTime;
+  }
+
+  /**
+   * Returns the BOS Server's binary restart time in the form of an
+   * ExecutableTime object.  This is the time at which all new or newly
+   * modified AFS binaries are restarted.  After this method
+   * is called once, it saves the time and returns
+   * that value on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current value is obtained.
+   *
+   * @return the binary restart time
+   * @exception AFSException  If an error occurs in the native code
+   * @see Server.ExecutableTime
+   * @see #refresh()
+   */
+  public ExecutableTime getBinaryRestartTime() throws AFSException
+  {
+    if (binRestartTime == null) refreshBinaryRestart();
+    return binRestartTime;
+  }
+
+  /**
+   * Returns the total space on this server (a sum of the space of all the 
+   * partitions associated with this server).  If this server is not a 
+   * file server, zero will be returned. After this method
+   * is called once, it saves the total space and returns
+   * that value on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current value is obtained.
+   *
+   * @return the total space on this server
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getTotalSpace() throws AFSException
+  {
+    if (partitions == null) refreshPartitions(true);
+    if (!isFileServer() || isBadFileServer()) return 0;
+    if (totalSpace == 0) {
+      Partition[] partitions = getPartitions();
+      for (int i = 0; i < partitions.length; i++) {
+        totalSpace += partitions[i].getTotalSpace();
+      }
+    }
+    return totalSpace;
+  }
+
+  /**
+   * Returns the total free space on this server (a sum of the free space of 
+   * all the partitions associated with this server).  If this server is not a 
+   * file server, zero will be returned. After this method
+   * is called once, it saves the total free space and returns
+   * that value on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current value is obtained.
+   *
+   * @return the total free space on this server
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getTotalFreeSpace() throws AFSException
+  {
+    if (partitions == null) refreshPartitions(true);
+    if (!isFileServer() || isBadFileServer()) return 0;
+    if (totalFreeSpace == 0) {
+      Partition[] partitions = getPartitions();
+      for (int i = 0; i < partitions.length; i++) {
+        totalFreeSpace += partitions[i].getTotalFreeSpace();
+      }
+    }
+    return totalFreeSpace;
+  }
+
+  /**
+   * Returns the total used space on this server (a sum of the used space of 
+   * all the partitions associated with this server).  If this server is not a 
+   * file server, zero will be returned. After this method
+   * is called once, it saves the total used space and returns
+   * that value on subsequent calls, until the {@link #refresh()} method
+   * is called and a more current value is obtained.
+   *
+   * @return the total space on this partition
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getTotalSpace()
+   * @see #getTotalFreeSpace()
+   */
+  public int getTotalUsedSpace() throws AFSException
+  {
+    if (totalUsedSpace == 0) {
+      totalUsedSpace = getTotalSpace() - getTotalFreeSpace();
+    }
+    return totalUsedSpace;
+  }
+
+  /**
+   * Returns this server's vos handle.
+   *
+   * @return this server's vos handle
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected int getVosHandle() throws AFSException
+  {
+    if ( vosHandle == 0 ) {
+      vosHandle = getVosServerHandle( cell.getCellHandle(), name );
+    }
+    return vosHandle;
+  }
+
+  /**
+   * Returns this server's bos handle.
+   *
+   * @return this server's bos handle
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected int getBosHandle() throws AFSException
+  {
+    if ( bosHandle == 0 ) {
+      bosHandle = getBosServerHandle( cell.getCellHandle(), name );
+    }
+    return bosHandle;
+  }
+
+  //////////////// mutators:  ////////////////////////
+
+  /**
+   * Sets the BOS general restart time.   This is the time at which the bos 
+   * server restarts itself and all running processes.
+   *
+   * @param executableTime  Executable time object that represents what 
+   * the BOS Server's general restart time should be. 
+   * @exception AFSException  If an error occurs in the native code
+   * @see Server.ExecutableTime
+   */
+  public void setGeneralRestartTime( ExecutableTime executableTime ) 
+      throws AFSException
+  {
+    this.setRestartTime( getBosHandle(), RESTART_GENERAL, executableTime );
+  }
+
+  /**
+   * Sets the BOS binary restart time.   This is the time at which all new 
+   * or newly modified AFS binaries are restarted.
+   *
+   * @param executableTime  Executable time object that represents what 
+   *                        the BOS Server's binary restart time should be.
+   * @exception AFSException  If an error occurs in the native code
+   * @see Server.ExecutableTime
+   */
+  public void setBinaryRestartTime( ExecutableTime executableTime ) 
+      throws AFSException
+  {
+    this.setRestartTime( getBosHandle(), RESTART_BINARY, executableTime );
+  }
+
+  /////////////// custom information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Server</code>.
+   * Contains the information fields and a list of partitions, admin, and 
+   * processes.
+   *
+   * @return a <code>String</code> representation of the <code>Server</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    try {
+    
+    r = "Server: " + name + "\n";
+
+    r += "\tdatabase: " + isDatabase() + "\t\tfileServer: " + 
+       isFileServer() + "\n";
+    r += "\tbad database: " + isBadDatabase() + "\tbad fileServer: " + 
+       isBadFileServer() + "\n";
+    //r += "\tAddress: " + getIPAddress()[0] + "\n";
+
+    // restart times:
+    r += "\tGeneral restart date: " + getGeneralRestartTime() + "\n";
+    r += "\tBinary restart date: " + getBinaryRestartTime() + "\n";
+    
+    if ( isFileServer() && !isBadFileServer() ) {
+      r += "\tPartitions:\n";
+      
+      String parts[] = getPartitionNames();
+      
+      for( int i = 0; i < parts.length; i++ ) {
+        r += "\t\t" + parts[i] + "\n";
+      }
+    }
+
+    if ( (isDatabase() && !isBadDatabase()) || 
+        (isFileServer() && !isBadFileServer()) ) {
+        r += "\tAdmins:\n";
+        
+        String ads[] = getAdminNames();
+    
+        for( int i = 0; i < ads.length; i++ ) {
+       r += "\t\t" + ads[i] + "\n";
+        }
+    }
+
+    if ( (isDatabase() && !isBadDatabase()) || 
+        (isFileServer() && !isBadFileServer()) ) {
+        r += "\tProcesses:\n";
+        
+        String pros[] = getProcessNames();
+    
+        for( int i = 0; i < pros.length; i++ ) {
+       r += "\t\t" + pros[i] + "\n";
+        }
+    }
+
+    } catch( Exception e ) {
+    return e.toString();
+    }
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the partitions of this <code>Server</code>.
+   *
+   * @return    a <code>String</code> representation of the partitions
+   * @see       Partition#getInfo
+   */
+  protected String getInfoPartitions() throws AFSException
+  {
+    String r;
+    r = "Server: " + name + "\n\n";
+    r += "--Partitions--\n";
+
+    Partition parts[] = getPartitions();
+
+    for( int i = 0; i < parts.length; i++ ) {
+        r += parts[i].getInfo() + "\n";
+    }
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the keys of this <code>Server</code>.
+   *
+   * @return    a <code>String</code> representation of the keys
+   * @see       Key#getInfo
+   */
+  protected String getInfoKeys() throws AFSException
+  {
+    String r;
+
+    r = "Server: " + name + "\n\n";
+    r += "--Keys--\n";
+
+    Key kys[] = getKeys();
+
+    for( int i = 0; i < kys.length; i++ ) {
+        r += kys[i].getInfo() + "\n";
+    }
+
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the processes of this <code>Server</code>.
+   *
+   * @return    a <code>String</code> representation of the processes
+   * @see       Process#getInfo
+   */
+  protected String getInfoProcesses() throws AFSException
+  {
+    String r;
+
+    r = "Server: " + name + "\n\n";
+    r += "--Processes--\n";
+
+    Process pros[] = getProcesses();
+
+    for( int i = 0; i < pros.length; i++ ) {
+        r += pros[i].getInfo() + "\n";
+    }
+    return r;
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two Server objects respective to their names and does not
+   * factor any other attribute.  Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     server    The Server object to be compared to this 
+   *                      Server instance
+   * 
+   * @return    Zero if the argument is equal to this Server's name, a
+   *           value less than zero if this Server's name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this Server's name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Server server)
+  {
+    return this.getName().compareTo(server.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Server)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Server)obj);
+  }
+
+  /**
+   * Tests whether two <code>Server</code> objects are equal, based on their 
+   * names and hosting Cell.
+   *
+   * @param otherServer   the Server to test
+   * @return whether the specifed Server is the same as this Server
+   */
+  public boolean equals( Server otherServer )
+  {
+    return ( name.equals(otherServer.getName()) ) &&
+           ( this.getCell().equals(otherServer.getCell()) );
+  }
+
+  /**
+   * Returns the name of this <CODE>Server</CODE>
+   *
+   * @return the name of this <CODE>Server</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Opens a server for administrative vos use, based on the cell handle 
+   * provided.  Returns a vos server handle to be used by other 
+   * methods as a means of identification.
+   *
+   * @param cellHandle    a cell handle previously returned by 
+   *                      a call to {@link #getCellHandle}
+   * @param serverName    the name of the server for which to retrieve 
+   *                      a vos handle
+   * @return a vos handle to the server
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getCellHandle
+   */
+  protected static native int getVosServerHandle( int cellHandle, 
+                                                 String serverName )
+       throws AFSException;
+
+  /**
+   * Closes the given currently open vos server handle.
+   *
+   * @param vosHandle   the vos server handle to close
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void closeVosServerHandle( int vosHandle ) 
+       throws AFSException; 
+
+  /**
+   * Opens a server for administrative bos use, based on the cell handle 
+   * provided.  Returns a bos server handle to be used by other methods 
+   * as a means of identification.
+   *
+   * @param cellHandle    a cell handle previously returned by a call 
+   *                      to {@link #getCellHandle}
+   * @param serverName    the name of the server for which to retrieve 
+   *                      a bos handle
+   * @return a bos handle to the server
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getCellHandle
+   */
+  protected static native int getBosServerHandle( int cellHandle, 
+                                                 String serverName )
+       throws AFSException;
+
+  /**
+   * Closes the given currently open bos server handle.
+   *
+   * @param bosHandle   the bos server handle to close
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void closeBosServerHandle( int bosHandle ) 
+       throws AFSException; 
+
+  /**
+   * Fills in the information fields of the provided <code>Server</code>. 
+   *
+   * @param cellHandle    the handle of the cell to which the server belongs
+   * @see Cell#getCellHandle
+   * @param name     the name of the server for which to get the information
+   * @param server     the <code>Server</code> object in which to fill in 
+   *                   the information
+   * @see Server
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getServerInfo( int cellHandle, String name, 
+                                             Server server ) 
+       throws AFSException;
+
+  /**
+   * Returns the total number of partitions hosted by the server denoted by
+   * <CODE>serverHandle</CODE>, if the server is a fileserver.
+   *
+   * @param cellHandle    the handle of the cell to which the server belongs
+   * @param serverHandle  the vos handle of the server to which the 
+   *                      partitions belong
+   * @return total number of partitions
+   * @exception AFSException  If an error occurs in the native code
+   * @see Cell#getCellHandle
+   * @see #getVosServerHandle
+   */
+  protected static native int getPartitionCount( int cellHandle, 
+                                                 int serverHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the partitions on a server.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getPartitionsNext</code> and <code>getPartitionsDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the server belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server to which the 
+   *                      partitions belong
+   * @see #getVosServerHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getPartitionsBegin( int cellHandle, 
+                                                 int serverHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next partition of the server.  Returns <code>null</code> 
+   * if there are no more partitions.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getPartitionsBegin
+   * @return the name of the next partition of the server
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getPartitionsNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next partition object of the server.  Returns 0 if there
+   * are no more partitions, != 0 otherwise
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @param thePartition   the Partition object in which to fill the 
+   *                       values of the next partition
+   * @see #getPartitionsBegin
+   * @return 0 if there are no more servers, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getPartitionsNext( int iterationId, 
+                                                Partition thePartition )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getPartitionsBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getPartitionsDone( int iterationId )
+    throws AFSException;
+  
+  /**
+   * Returns the total number of processes hosted by the server denoted by
+   * <CODE>serverHandle</CODE>.
+   *
+   * @param serverHandle  the vos handle of the server to which the 
+   *                      processes belong
+   * @return total number of processes
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getVosServerHandle
+   */
+  protected static native int getProcessCount( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the processes on a server.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getProcessesNext</code> and <code>getProcessesDone</code>.  
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      processes belong
+   * @see #getBosServerHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getProcessesBegin( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next process of the server.  Returns <code>null</code> 
+   * if there are no more processes.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getProcessesBegin
+   * @return the name of the next process of the cell
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getProcessesNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Fills the next process object of the server.  Returns 0 if there
+   * are no more processes, != 0 otherwise.
+   *
+   * @param serverHandle    the handle of the BOS server that hosts the process
+   * @see #getBosHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @param theProcess    the Process object in which to fill the 
+   *                      values of the next process
+   * @see #getProcessesBegin
+   * @return 0 if there are no more processes, != otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getProcessesNext( int serverHandle, 
+                                               int iterationId, 
+                                               Process theProcess )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getProcessesBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getProcessesDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the total number of keys hosted by the server denoted by
+   * <CODE>serverHandle</CODE>.
+   *
+   * @param serverHandle  the vos handle of the server to which the 
+   *                      keys belong
+   * @return total number of keys
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getVosServerHandle
+   */
+  protected static native int getKeyCount( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the keys of a server.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getKeysNext</code> and <code>getKeysDone</code>.  
+   *
+   * @param serverHandle  the bos handle of the server to which the keys belong
+   * @see #getBosServerHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getKeysBegin( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next key of the server.  Returns 0 if there
+   * are no more keys, != 0 otherwise.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @param theKey   a {@link Key Key} object, in which to fill in the
+   *                 properties of the next key.
+   * @see #getKeysBegin
+   * @return 0 if there are no more keys, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getKeysNext( int iterationId, Key theKey )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getKeysBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getKeysDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the total number of BOS administrators associated with the server 
+   * denoted by <CODE>serverHandle</CODE>.
+   *
+   * @param serverHandle  the vos handle of the server to which the 
+   *                      BOS admins belong
+   * @return total number of BOS administrators
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getVosServerHandle
+   */
+  protected static native int getBosAdminCount( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Begin the process of getting the bos amdinistrators on a server.  Returns 
+   * an iteration ID to be used by subsequent calls to 
+   * <code>getBosAdminsNext</code> and <code>getBosAdminsDone</code>.  
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      partitions belong
+   * @see #getBosServerHandle
+   * @return an iteration ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getBosAdminsBegin( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Returns the next bos admin of the server.  Returns <code>null</code> 
+   * if there are no more admins.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getBosAdminsBegin
+   * @return the name of the next admin of the server
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getBosAdminsNextString( int iterationId )
+    throws AFSException;
+
+  /**
+   * Returns the next bos admin of the server.  Returns 0 if there
+   * are no more admins, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which these admins belong
+   * @see #getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getBosAdminsBegin
+   * @param theUser   the user object in which to fill the values of this admin
+   * @return 0 if no more admins, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getBosAdminsNext( int cellHandle, 
+                                               int iterationId, User theUser )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see #getBosAdminsBegin
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void getBosAdminsDone( int iterationId )
+    throws AFSException;
+
+  /**
+   * Adds the given to name to the list of bos administrators on that server.
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      partitions belong
+   * @see #getBosServerHandle
+   * @param adminName   the name of the admin to add to the list
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void addBosAdmin( int serverHandle, 
+                                           String adminName )
+    throws AFSException;
+
+  /**
+   * Removes the given to name from the list of bos administrators on 
+   * that server.
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      partitions belong
+   * @see #getBosServerHandle
+   * @param adminName   the name of the admin to remove from the list
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void removeBosAdmin( int serverHandle, 
+                                              String adminName )
+    throws AFSException;
+
+  /**
+   * Salvages (restores consistency to) a volume, partition, or server
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see #getCellHandle
+   * @param serverHandle  the bos handle of the server on which the 
+   *                      volume resides
+   * @see #getBosServerHandle
+   * @param partitionName  the name of the partition to salvage, 
+   *                       can be <code>null</code> only if volName is 
+   *                       <code>null</code>
+   * @param volName  the name of the volume to salvage, 
+   *                 can be <code>null</code>
+   * @param numSalvagers   the number of salvager processes to run in parallel
+   * @param tempDir   directory to place temporary files, can be 
+   *                  <code>null</code>
+   * @param logFile    where salvager log will be written, can be 
+   *                   <code>null</code>
+   * @param inspectAllVolumes   whether or not to inspect all volumes, 
+   *                            not just those marked as active at crash
+   * @param removeBadlyDamaged   whether or not to remove a volume if it's 
+   *                             badly damaged
+   * @param writeInodes   whether or not to record a list of inodes modified
+   * @param writeRootInodes   whether or not to record a list of AFS 
+   *                          inodes owned by root
+   * @param forceDirectory   whether or not to salvage an entire directory 
+   *                         structure
+   * @param forceBlockReads   whether or not to force the salvager to read 
+   *                          the partition
+   *                          one block at a time and skip badly damaged 
+   *                          blocks.  Use if partition has disk errors
+   */
+  protected static native void salvage( int cellHandle, int serverHandle, 
+                                       String partitionName, String volName,
+                                       int numSalvagers, String tempDir, 
+                                       String logFile, 
+                                       boolean inspectAllVolumes,
+                                       boolean removeBadlyDamaged, 
+                                       boolean writeInodes, 
+                                       boolean writeRootInodes, 
+                                       boolean forceDirectory, 
+                                       boolean forceBlockReads)
+       throws AFSException;
+
+  /**
+   *  Synchronizes a particular server with the volume location database.
+   *
+   * @param cellHandle    the handle of the cell to which the server belongs
+   * @see #getCellHandle
+   * @param serverHandle  the vos handle of the server     
+   * @see #getVosServerHandle
+   * @param partition   the id of the partition to sync, can be -1 to ignore
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void syncServerWithVLDB( int cellHandle, 
+                                                  int serverHandle, 
+                                                  int partition )
+    throws AFSException;
+
+  /**
+   *  Synchronizes the volume location database with a particular server.
+   *
+   * @param cellHandle    the handle of the cell to which the server belongs
+   * @see #getCellHandle
+   * @param serverHandle  the vos handle of the server     
+   * @see #getVosServerHandle
+   * @param partition   the id of the partition to sync, can be -1 to ignore
+   * @param forceDeletion   whether or not to force the deletion of bad volumes
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void syncVLDBWithServer( int cellHandle, 
+                                                  int serverHandle, 
+                                                  int partition, 
+                                                  boolean forceDeletion )
+    throws AFSException;
+
+  /**
+   * Retrieves a specified bos log from a server.  Right now this 
+   * method will simply return a huge String containing the log, but 
+   * hopefully we can devise a better way to make this work more efficiently.
+   *
+   * @param serverHandle  the bos handle of the server to which the key belongs
+   * @see #getBosServerHandle
+   * @param logLocation   the full path and name of the desired bos log
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native String getLog( int serverHandle, String logLocation )
+    throws AFSException;
+
+  /**
+   *  Fills in the restart time fields of the given {@link Server Server} 
+   *  object. 
+   *
+   * @param serverHandle  the bos handle of the server to which the key belongs
+   * @see #getBosServerHandle
+   * @param restartType  whether to get the general or binary restart. 
+   *                     Acceptable values are:<ul>
+   *                     <li>{@link RESTART_BINARY}</li>
+   *                     <li>{@link RESTART_GENERAL}</li></ul>     
+   * @param theServer   the <code>Server</code> object, in which to fill 
+   *                    the restart time fields
+   * @exception AFSException  If an error occurs in the native code
+   */
+  private static native void getRestartTime( int serverHandle, 
+                                              int restartType, 
+                                              ExecutableTime executableTime )
+    throws AFSException;
+
+  /**
+   *  Sets the restart time of the bos server.
+   *
+   * @param serverHandle  the bos handle of the server to which the key belongs
+   * @see #getBosServerHandle
+   * @param restartType  whether this is to be a general or binary restart. 
+   *                     Acceptable values are:<ul>
+   *                     <li>{@link RESTART_BINARY}</li>
+   *                     <li>{@link RESTART_GENERAL}</li></ul>
+   * @param theServer   the server object containing the desired information
+   * @exception AFSException  If an error occurs in the native code
+   */
+  private static native void setRestartTime( int serverHandle, 
+                                              int restartType, 
+                                              ExecutableTime executableTime )
+    throws AFSException;
+
+  /**
+   * Start all server processes.
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      processes belong
+   * @see #getBosServerHandle
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void startAllProcesses( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Restart all server processes.
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      processes belong
+   * @see #getBosServerHandle
+   * @param restartBosServer   whether or not to restart the bos server as well
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void restartAllProcesses( int serverHandle, 
+                                                   boolean restartBosServer )
+    throws AFSException;
+
+  /**
+   * Stop all server processes.
+   *
+   * @param serverHandle  the bos handle of the server to which the 
+   *                      processes belong
+   * @see #getBosServerHandle
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void stopAllProcesses( int serverHandle )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the server portion of the native 
+   * library. This method should be called when no more <code>Server</code> 
+   * objects are expected to be used.
+   */
+  protected static native void reclaimServerMemory();
+
+  /*====================================================================*/
+  /* INNER CLASSES  */
+  /*====================================================================*/
+  public static final class ExecutableTime implements Serializable
+  {
+    public static final short NEVER = 0;
+    public static final short NOW = 1;
+
+    public static final short EVERYDAY  = -1;
+    public static final short SUNDAY    = 0;
+    public static final short MONDAY    = 1;
+    public static final short TUESDAY   = 2;
+    public static final short WEDNESDAY = 3;
+    public static final short THURSDAY  = 4;
+    public static final short FRIDAY    = 5;
+    public static final short SATURDAY  = 6;
+
+    static final DecimalFormat formatter = 
+       (DecimalFormat)DecimalFormat.getInstance();
+
+    private short second;
+    private short minute;
+    private short hour;
+    private short day;
+    private boolean now;
+    private boolean never;
+
+    static
+    {
+      formatter.applyPattern("00");
+    }
+
+    /**
+     * Internal constructor used to construct an empty object that will 
+     * be passed to JNI for member synchronization of the BOS Server 
+     * executable time this object represents.
+     */
+    ExecutableTime()
+    {
+      this.second = (short) 0;
+      this.minute = (short) 0;
+      this.hour = (short) 0;
+      this.day = (short) -1;
+      this.now = false;
+      this.never = false;
+    }
+
+    /**
+     * Constructs an <code>ExecutableTime</code> object that represents either 
+     * a "<CODE>now</CODE>" <B>or</B> "<CODE>never</CODE>" BOS Executable 
+     * Restart Time. 
+     *
+     * <P>Valid values for the <CODE>type</CODE> parameter are ExecutableTime.NOW
+     * or ExecutableTime.NEVER.  If a value other than these two is used an
+     * IllegalArgumentException will be thrown.
+     *
+     * @param type   either ExecutableTime.NOW or ExecutableTime.NEVER
+     * @exception    IllegalArgumentException
+     *               If a value other than ExecutableTime.NOW or 
+     *               ExecutableTime.NEVER is used for the <CODE>type</CODE> 
+     *               parameter.
+     * @see #isNow()
+     * @see #isNever()
+     * @see #Server.ExecutableTime(short, short, short)
+     */
+    public ExecutableTime(short type) throws IllegalArgumentException
+    {
+      if (type == NOW) {
+        this.now = true;
+        this.never = false;
+      } else if (type == NEVER) {
+        this.now = false;
+        this.never = true;
+      } else {
+        throw new IllegalArgumentException("You must specify either " + 
+                                           "ExecutableTime.NOW or " + 
+                                           "ExecutableTime.NEVER when " +
+                                           "using this constructor.");
+      }
+      this.second = (short) 0;
+      this.minute = (short) 0;
+      this.hour = (short) 0;
+      this.day = (short) -1;
+    }
+
+    /**
+     * Constructs an <code>ExecutableTime</code> object that may be used to 
+     * represent a <U>daily</U> BOS Executable Restart Time of a process. 
+     *
+     * @param second   the second field for this representation of a 
+     *                 BOS Server restart time value (range: 0-59)
+     * @param minute   the minute field for this representation of a 
+     *                 BOS Server restart time value (range: 0-59)
+     * @param hour     the hour field for this representation of a BOS 
+     *                 Server restart time value (range: 0-23)
+     * @exception      IllegalArgumentException
+     *                 If any of the parameters values are out of range 
+     *                 of their respective fields.
+     * @see #Server.ExecutableTime(short, short, short, short)
+     * @see #getSecond()
+     * @see #getMinute()
+     * @see #getHour()
+     */
+    public ExecutableTime(short second, short minute, short hour)
+      throws IllegalArgumentException
+    {
+      this(second, minute, hour, ExecutableTime.EVERYDAY);
+    }
+
+    /**
+     * Constructs an <code>ExecutableTime</code> object that may be used to 
+     * represent the BOS Executable Restart Time of a process. 
+     *
+     * @param second   the second field for this representation of a 
+     *                 BOS Server restart time value (range: 0-59)
+     * @param minute   the minute field for this representation of a 
+     *                 BOS Server restart time value (range: 0-59)
+     * @param hour     the hour field for this representation of a BOS 
+     *                 Server restart time value (range: 0-23)
+     * @param day      the day field for this representation of a BOS 
+     *                 Server restart time value.<BR><UL>Valid values include:
+     *                 <CODE>ExecutableTime.EVERYDAY</CODE> (see also {@link 
+     *                 #Server.ExecutableTime(short, short, short)})<BR>
+     *                 <CODE>
+     *                 ExecutableTime.SUNDAY<BR>
+     *                 ExecutableTime.MONDAY<BR>
+     *                 ExecutableTime.TUESDAY<BR>
+     *                 ExecutableTime.WEDNESDAY<BR>
+     *                 ExecutableTime.THURSDAY<BR>
+     *                 ExecutableTime.FRIDAY<BR>
+     *                 ExecutableTime.SATURDAY<BR>
+     *                 </CODE></UL>
+     * @exception      IllegalArgumentException
+     *                 If any of the parameters values are out of range 
+     *                 of their respective fields.
+     * @see #Server.ExecutableTime(short, short, short)
+     * @see #getSecond()
+     * @see #getMinute()
+     * @see #getHour()
+     * @see #getDay()
+     */
+    public ExecutableTime(short second, short minute, short hour, short day) 
+    {
+      if ( (0 > second || second > 59) ||
+           (0 > minute || minute > 59) ||
+           (0 > hour || hour > 24) ||
+           (-1 > day || day > 6) ) {
+        throw new IllegalArgumentException("One of the specified values " + 
+                                        "are invalid.");
+      }
+      this.second = second;
+      this.minute = minute;
+      this.hour = hour;
+      this.day = day;
+      this.now = false;
+      this.never = false;
+    }
+
+    /**
+     * Returns the second of this ExecutableTime object.
+     *
+     * @return the second of this ExecutableTime object.
+     * @exception IllegalStateException 
+     *            If the executable time this object represents has a value of
+     *            "<CODE>now</CODE>" or "<CODE>never</CODE>".
+     */
+    public short getSecond() throws IllegalStateException 
+    {
+      if (now || never) {
+        throw new IllegalStateException("Executable time is set to 'now' or" +
+                                                       " 'never'.");
+      }
+      return second;
+    }
+
+    /**
+     * Returns the minute of this ExecutableTime object.
+     *
+     * @return the minute of this ExecutableTime object.
+     * @exception IllegalStateException 
+     *            If the executable time this object represents has a value of
+     *            "<CODE>now</CODE>" or "<CODE>never</CODE>".
+     */
+    public short getMinute() throws IllegalStateException 
+    {
+      if (now || never) {
+        throw new IllegalStateException("Executable time is set to 'now' or" + 
+                                        " 'never'.");
+      }
+      return minute;
+    }
+
+    /**
+     * Returns the hour of this ExecutableTime object, in 24 hour time.
+     *
+     * @return the hour of this ExecutableTime object.
+     * @exception IllegalStateException
+     *            If the executable time this object represents has a value of
+     *            "<CODE>now</CODE>" or "<CODE>never</CODE>".
+     */
+    public short getHour() throws IllegalStateException
+    {
+      if (now || never) {
+        throw new IllegalStateException("Executable time is set to 'now' or" + 
+                                                       " 'never'.");
+      }
+      return hour;
+    }
+
+    /**
+     * Returns a numeric representation of the day of this ExecutableTime 
+     * object. If it is daily, the value of ExecutableTime.EVERYDAY is returned.
+     * 
+     * <P>Possible return values are:<BR>
+     * <CODE>
+     * ExecutableTime.EVERYDAY<BR>
+     * <BR>
+     * ExecutableTime.SUNDAY<BR>
+     * ExecutableTime.MONDAY<BR>
+     * ExecutableTime.TUESDAY<BR>
+     * ExecutableTime.WEDNESDAY<BR>
+     * ExecutableTime.THURSDAY<BR>
+     * ExecutableTime.FRIDAY<BR>
+     * ExecutableTime.SATURDAY<BR>
+     * </CODE>
+     *
+     * @return    a numeric representation of the day of this ExecutableTime 
+     *            object.
+     * @exception IllegalStateException
+     *            If the executable time this object represents has a value of
+     *            "<CODE>now</CODE>" or "<CODE>never</CODE>".
+     */
+    public short getDay() throws IllegalStateException
+    {
+      if (now || never) {
+        throw new IllegalStateException("Executable time is set to 'now' or" + 
+                                                       " 'never'.");
+      }
+      return day;
+    }
+
+    /**
+     * Returns a String representation, name for the day of the week or 
+     * "Everyday", of this object's day property.
+     * 
+     * <P>Possible return values are:
+     * <PRE>
+     * Sunday
+     * Monday
+     * Tuesday
+     * Wednesday
+     * Thursday
+     * Friday
+     * Saturday
+     * 
+     * Everyday
+     * </PRE>
+     *
+     * @return the day of this ExecutableTime object.
+     * @exception IllegalStateException
+     *            If the executable time this object represents has a value of
+     *            "<CODE>now</CODE>" or "<CODE>never</CODE>".
+     * @see #getDay()
+     */
+    public String getDayString() throws IllegalStateException
+    {
+      switch (getDay())
+      {
+        case 0:
+            return "Sunday";
+        case 1:
+            return "Monday";
+        case 2:
+            return "Tuesday";
+        case 3:
+            return "Wednesday";
+        case 4:
+            return "Thursday";
+        case 5:
+            return "Friday";
+        case 6:
+            return "Saturday";
+        default:
+            return "Everyday";
+      }
+    }
+
+    /**
+     * Returns whether or not the BOS restart time, represented by this 
+     * ExecutableTime object, is set to "<CODE>now</CODE>" or not.  
+     * This means that at some point in the past, when someone set it to 
+     * "<CODE>now</CODE>", the bosserver restarted all its processes,
+     * and never again.
+     *
+     * @return whether or not the restart time is "<CODE>now</CODE>"
+     */
+    public boolean isNow()
+    {
+      return now;
+    }
+
+    /**
+     * Returns the second of this ExecutableTime object.
+     *
+     * @return the second of this ExecutableTime object.
+     */
+    /**
+     * Returns whether or not the BOS restart time, represented by this 
+     * ExecutableTime object, is set to "<CODE>never</CODE>" or not.  
+     * This means that the bosserver will never restart its processes.
+     *
+     * @return whether or not the restart time is "<CODE>never</CODE>"
+     */
+    public boolean isNever()
+    {
+      return never;
+    }
+
+    /**
+     * Tests whether two <code>ExecutableTime</code> objects are equal, 
+     * based on a
+     * comparison of each of their respective properties. If 
+     * "<CODE>now</CODE>" or "<CODE>never</CODE>" is set in either object, 
+     * only those properties are analyzed.
+     *
+     * @param time   the ExecutableTime to test against
+     * @return whether the specifed ExecutableTime is the same as this 
+     * ExecutableTime as defined above
+     */
+    public boolean equals( ExecutableTime time )
+    {
+      boolean same = false;
+      try {
+        same = ( (second == time.getSecond()) &&
+                 (minute == time.getMinute()) &&
+                 (hour   == time.getHour()  ) &&
+                 (day    == time.getDay()   ) );
+      } catch (Exception e) {
+        same = ( (now    == time.isNow()    ) &&
+                 (never  == time.isNever()  ) );
+
+      }
+      return same;
+    }
+
+    /**
+     * Returns the String representation of time value of this 
+     * <CODE>ExecutableTime</CODE> object.
+     *
+     * <P> Possible return values:<BR>
+     * <LI> "Now"<BR>
+     * <LI> "Never"<BR>
+     * <LI> Day and time string in the form:<BR>&nbsp;&nbsp;&nbsp;&nbsp; 
+     * <CODE>&#60;day&#62; at &#60;hh&#62;:&#60;MM&#62;[:&#60;ss&#62;]</CODE>
+     * <BR><BR>
+     *
+     * <B>Example Return Values:</B><PRE>
+     * Sunday at 04:00
+     * Sunday at 05:10:30
+     * Everyday at 20:00</PRE>
+     *
+     * @return the String representation of this <CODE>ExecutableTime</CODE> 
+     * object
+     */
+    public String toString()
+    {
+      if (now) {
+        return "Now";
+      } else if (never) {
+        return "Never";
+      } else {
+        try {
+          if (second != 0) {
+            return getDayString() + " at " + 
+               ExecutableTime.formatter.format(hour) + ":" + 
+               ExecutableTime.formatter.format(minute) + ":" + 
+               ExecutableTime.formatter.format(second);
+          } else {
+            return getDayString() + " at " + 
+               ExecutableTime.formatter.format(hour) + ":" + 
+               ExecutableTime.formatter.format(minute);
+          }
+        } catch (Exception e) {
+          return "(unknown)";
+        }
+      }
+    }
+
+  }
+  /*====================================================================*/
+
+}
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Token.java b/src/JAVA/classes/org/openafs/jafs/Token.java
new file mode 100644 (file)
index 0000000..5fe2ef7
--- /dev/null
@@ -0,0 +1,493 @@
+/*
+ * @(#)Token.java      1.2 05/06/2002
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS authentication token.  It conveniently
+ * maintains the handle associated with token and the cell to which the token
+ * is authenticated.
+ * <BR><BR>
+ *
+ * Constructing a <code>Token</code> object results in an immediate attempt to
+ * authenticate the user within the specified cell.  If this attempt fails, an
+ * <code>{@link AFSException}</code> will be thrown.  Therefore, if the
+ * construction of the object succeeds without an exception, then the 
+ * <code>Token</code> is considered authenticated.
+ *
+ * The construction of a <code>Token</code> object acts as an entry point
+ * for authentication into the AFS system.  Thus, when you construct a 
+ * <code>{@link Cell}</code> object, you must pass in an instance of a
+ * <code>Token</code> that has been authenticated within the AFS cell that
+ * <code><I>Cell</I></code> is intended to represent.  You will only be 
+ * allowed to perform actions that the user, used to authenticate 
+ * <code>Token</code>, is authorized to perform.  You must construct a 
+ * <code>Token</code> object before constructing a <code>Cell</code> object,
+ * which is required by all other objects within this package either directly 
+ * or indirectly.<BR><BR>
+ *
+ * If an error occurs during a method call, an 
+ * <code>AFSException</code> will be thrown.  This class is the Java
+ * equivalent of errors thrown by AFS; see {@link AFSException}
+ * for a complete description.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Token</code> object. It shows how to construct a <code>Cell</code> 
+ * using a <code>Token</code>.  See {@link Cell} for a more detailed example 
+ * of constructing and using a <code>Cell</code> object.<BR><BR>
+ *
+ * <PRE>
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.Token;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Token token;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ *     String serverName = arg[3];
+ * 
+ *     token = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     ...
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+
+public class Token implements Serializable, Comparable
+{
+  public static int ANYUSER_PAG_ID;
+
+  protected int tokenHandle;
+  protected int pagID = -1;
+  protected int errno;
+
+  protected String cellName;
+  protected String username;
+  private   String password;
+
+  private boolean hasInitialized = false;
+
+  /**
+   * Load the native libraries <code>libjafs</code> and 
+   * <code>libjafs</code>.
+   */
+  static
+  {
+    try {
+      Class.forName("org.openafs.jafs.AFSLibraryLoader");
+      try {
+        initializeAdminClient();
+      } catch (Exception e) {
+        System.err.println(e);
+      }
+    } catch (ClassNotFoundException e) {
+      /* Most likely running on a client, do nothing */
+    }
+  }
+
+  /**
+   * Constructs a new <CODE>Token</CODE> object instance given 
+   * the name of the AFS cell it represents and the username and password 
+   * of the user to be Tokend for 
+   * administrative access.
+   *
+   * @param username    the name of the user to Token with
+   * @param password    the password of that user
+   * @param cellName    the name of the cell to Token into
+   * @param login       if true, automatically login upon construction
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected Token( String username, String password, String cellName, 
+                   boolean automaticallyLogin ) 
+      throws AFSException
+  {
+    this.username = username;
+    this.password = password;
+    this.cellName = cellName;
+
+    /* By default lets authenticate the user using libafsauthent.a */
+    if (automaticallyLogin) login();
+  }
+
+  /**
+   * Constructs a new <CODE>Token</CODE> object instance given 
+   * the name of the AFS cell it represents and the username and password 
+   * of the user to be Tokend for 
+   * administrative access.
+   *
+   * @param username    the name of the user to Token with
+   * @param password    the password of that user
+   * @param cellName    the name of the cell to Token into
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public Token( String username, String password, String cellName ) 
+      throws AFSException
+  {
+    this.username = username;
+    this.password = password;
+    this.cellName = cellName;
+
+System.out.println(username + ", " + cellName);
+    /* By default lets authenticate the user using libafsauthent.a */
+    login();
+  }
+
+  /**
+   * Returns the name of the AFS cell that this <code>Token</code> was
+   * authenticated against.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the name of the AFS cell associated with this <code>Token</code>.
+   */
+  public String getCellName()
+  {
+    return cellName;
+  }
+
+  /**
+   * Returns the username of user to whom this token belongs.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the username of the user represented by this Token
+   */
+  public String getUsername()
+  {
+    return username;
+  }
+
+  /**
+   * Returns a token handle that can be used to prove this authentication 
+   * later.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return a token representing the authentication
+   */
+  protected int getHandle()
+  {
+    return tokenHandle;
+  }
+
+  /**
+   * Closes the given currently open token.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void close() throws AFSException
+  {
+    close(tokenHandle);
+  }
+
+  /**
+   *  Gets the expiration time for a given token.
+   *
+   * @return a long representing the UTC time for the token expiration
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public long getExpiration() throws AFSException
+  {
+    return getExpiration(tokenHandle);
+  }
+
+  /**
+   * Authenticates a user in kas, and binds that authentication
+   * to the current process.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void klog() throws AFSException
+  {
+    if (!hasInitialized) {
+      initializeUserSpace();
+      hasInitialized = true;
+    }
+    if (pagID > -1) {
+      relog(pagID);
+    } else {
+      pagID = klog(username, password, cellName, pagID);
+    }
+  }
+
+  /**
+   * Authenticates a user in KAS, and binds that authentication
+   * to the current process.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void login() throws AFSException
+  {
+    this.tokenHandle = this.getToken(cellName, username, password);
+System.out.println("Token handle -> " + tokenHandle);
+  }
+
+  /**
+   * Initialize the user space AFS client (libjafs).
+   *
+   * <P> The user space client must be initialized prior to any
+   * user space related methods, including: klog, unlog, relog,
+   * and shutdown.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static void initializeUserSpace() throws AFSException
+  {
+    try {
+      Token.initUserSpace();
+    } catch (AFSException e) {
+      System.err.println(e.getMessage());
+    }
+    try {
+       Runtime.getRuntime().addShutdownHook(new AFSShutdownHandler());
+    } catch (Exception e) {
+       System.err.println("Could not register shutdown hook: " + e.toString());
+    }
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two ACL objects respective to their paths and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     acl    The ACL object to be compared to this ACL
+   *                   instance
+   * 
+   * @return    Zero if the argument is equal to this ACL's path, a
+   *           value less than zero if this ACL's path is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this ACL's path is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Token token)
+  {
+    return this.toString().compareTo(token.toString());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Token)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Token)obj);
+  }
+
+  /**
+   * Tests whether two <code>Cell</code> objects are equal, based on their 
+   * names.  Does not test whether the objects are actually the same
+   * representational instance of the AFS cell.
+   *
+   * @param otherCell   the <code>Cell</code> to test
+   * @return whether the specifed user is the same as this user
+   */
+  public boolean equals( Token token )
+  {
+    return this.toString().equals( token.toString() );
+  }
+
+  /**
+   * Returns the name of this <CODE>Cell</CODE>
+   *
+   * @return the name of this <CODE>Cell</CODE>
+   */
+  public String toString()
+  {
+    return username + "@" + cellName + ":" + tokenHandle;
+  }
+
+  /////////////// native methods found in *Token.c ////////////////////
+
+  /**
+   * Initialize the user space library.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  private static native void initUserSpace() throws AFSException;
+
+  /**
+   * Initialize the administrative library.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void initializeAdminClient() throws AFSException;
+
+  /**
+   * Returns a token handle that can be used to prove this authentication 
+   * later.
+   *
+   * @param cellName    the name of the cell in which to Token this user
+   * @param userName    the name of the user to Token
+   * @param password    the password of the user
+   * @exception AFSException  If an error occurs in the native code
+   * @return a token representing the authentication
+   */
+  protected native int getToken( String cellName, String username, 
+                                 String password ) 
+       throws AFSException;
+
+  /**
+   * Closes the given currently open token.
+   *
+   * @param tokenHandle   the token to close
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected native void close( int tokenHandle ) throws AFSException;
+
+  /**
+   *  Gets the expiration time for a given token.
+   *
+   * @param tokenHandle    a token handle previously returned by a call 
+   *                       to {@link #getToken}
+   * @see #getToken
+   * @return a long representing the UTC time for the token expiration
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected native long getExpiration( int tokenHandle )
+      throws AFSException;
+
+  /**
+   * Authenticates a user in KAS, and binds that authentication
+   * to the current thread or native process.
+   *
+   * @param username         the login to authenticate 
+   *                         (expected as username@cellname)
+   * @param password         the password of the login
+   * @param cellName         the name of the cell to authenticate into
+   * @param id               the existing pag (or 0)
+   *
+   * @return the assigned pag
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected native int klog(String username, String password, 
+                            String cellName, int id) 
+       throws AFSException;
+
+  /**
+   * Authenticates a user in KAS by a previously acquired PAG ID, and binds 
+   * that authentication to the current thread or native process.
+   *
+   * <P> This method does not require the user's username and password to
+   * fully authenticate their request.  Rather it utilizes the user's PAG ID
+   * to recapture the user's existing credentials.
+   *
+   * <P> This method is called by the public <code>klog</code> method, which
+   * internally manages the PAG ID. Additionally, an application needs only
+   * call <code>klog</code>, this reduces the amount of complexity and ensures
+   * that <code>relog</code> is never called before a <code>klog</code>.
+   *
+   * @param int User's current PAG (process authentication group) ID
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected native void relog(int id) throws AFSException;
+
+  /**
+   * Manually discards all AFS credentials associated with the bound user.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public native void unlog() throws AFSException;
+
+  /**
+   * Inform the native library that the application is 
+   * shutting down and will be unloading.
+   *
+   * <p> The library will make a call informing the file server that it will 
+   * no longer be available for callbacks.
+   */
+  protected static native void shutdown();
+
+  /**
+   * Reclaims all memory being saved by the authentication portion of 
+   * the native library.
+   * This method should be called when no more authentications are expected.
+   */
+  protected static native void reclaimAuthMemory();
+}
+
+/*=======================================================================*/
+/**
+ * Class that loads the native libraries required for direct communication with
+ * AFS. Since the Token class is serializable the function of loading the 
+ * native libraries must be performed in a non-serialized class, one that will
+ * not be included in any client side application packages.
+ *                                                                             
+ * @version 1.0, 06/13/2001
+ */
+class AFSLibraryLoader
+{
+  static
+  {
+    System.loadLibrary("jafs");
+    System.loadLibrary("jafsadm");
+  }
+}
+/*=======================================================================*/
+/**
+ * Class that handles graceful AFS application shutdown procedures by
+ * instructing the native library to inform the file system server that
+ * it is shutting down.
+ *                                                                             
+ * @version 1.0, 06/13/2001
+ */
+class AFSShutdownHandler extends Thread
+{
+  public AFSShutdownHandler() {}
+
+  /**
+   * This is the execution method satisfying the interface requirement as a 
+   * stand alone runnable thread.
+   *
+   * <p> This method will automatically be invoked by the Thread instantiator.
+   *
+   * @see Token#shutdown()
+   */
+  public void run() 
+  {
+    System.out.println("Shutting down Java AFS library...");
+    org.openafs.jafs.Token.shutdown();
+  }
+}
+/*=======================================================================*/
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/User.java b/src/JAVA/classes/org/openafs/jafs/User.java
new file mode 100644 (file)
index 0000000..b34293f
--- /dev/null
@@ -0,0 +1,1948 @@
+/*
+ * @(#)User.java       1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.util.ArrayList;
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS user.  It holds information about 
+ * the user, such as what groups it belongs to.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>User</code> does not mean an actual 
+ * AFS user is created in a cell -- usually a <code>User</code>
+ * object is a representation of an already existing AFS user.  If, 
+ * however, the <code>User</code> is constructed with the name of a 
+ * user that does not exist in the cell represented by the provided 
+ * <code>Cell</code>, a new user with that name can be
+ * created in that server by calling the {@link #create(String, int)} or
+ * {@link #create(String)} method. If such a user does already exist when 
+ * one of these methods is called, an exception will be thrown.<BR><BR>
+ *
+ * Each <code>User</code> object has its own individual set of
+ * <code>Group</code>s that it owns and <code>Group</code>s for which
+ * it is a member.  These represents the properties and attributes 
+ * of an actual AFS user.
+ * <BR><BR>
+ *
+ * Since this <code>User</code> object is a union of both the PTS and KAS
+ * properties of AFS users, some methods meant for users with a PTS entry
+ * will throw exceptions if used on a user with only a KAS entry, and vice
+ * versa.<BR><BR>  
+ *
+ * <!--Information on how member values are set-->
+ *
+ * Associated with an AFS user are many attributes, such as whether or not
+ * it can change its own password, or who is allowed to find out the groups
+ * to which this user belongs.  The <code>User</code> class has many
+ * "set" methods to indicate values for these attributes (i.e. 
+ * {@link #setChangePassword(boolean)} and {@link #setListMembership(int)}).  
+ * However, in order for these values to be written to the actual AFS user, 
+ * the {@link #flushInfo()} method needs to be called.  This writes all user
+ * attributes set through this API to AFS.  This is done to minimize calls 
+ * through JNI.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>User</code> object.  It iterates through the list of users 
+ * (a union of pts and kas users) for a cell, and prints out the name and 
+ * id of each.
+ *
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.User;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username   = arg[0];
+ *     String password   = arg[1];
+ *     String cellName   = arg[2];
+ * 
+ *     token  = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = cell.getServer(serverName);
+ * 
+ *     System.out.println("Users in Cell " + cell.getName() + ":");
+ *     User[] users = cell.getUsers();
+ *     for (int i = 0; i < users.length; i++) {
+ *       System.out.println(" -> " + users[i] + ": " users[i].getID());
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class User implements PTSEntry, Serializable, Comparable
+{
+  /**
+   * Only the owner of the user has access
+   */
+  public static final int USER_OWNER_ACCESS = 0;
+  /**
+   * Any user has access
+   */
+  public static final int USER_ANYUSER_ACCESS = 1;
+
+  /**
+   * User has administrative kas privileges
+   */
+  public static final int ADMIN = 0;
+  /**
+   * User has no administrative kas privileges
+   */
+  public static final int NO_ADMIN = 1;
+
+  /**
+   * TGS will grant tickets for user
+   */
+  public static final int GRANT_TICKETS = 0;
+  /**
+   * TGS will not grant tickets for user
+   */
+  public static final int NO_GRANT_TICKETS = 1;
+
+  /**
+   * TGS can use user's key for an encryption key
+   */
+  public static final int ENCRYPT = 0;
+  /**
+   * TGS cannot use user's key for an encryption key
+   */
+  public static final int NO_ENCRYPT = 1;
+
+  /**
+   * User can change their password
+   */
+  public static final int CHANGE_PASSWORD = 0;
+  /**
+   * User cannot change their password
+   */
+  public static final int NO_CHANGE_PASSWORD = 1;
+
+  /**
+   * User can reuse their password
+   */
+  public static final int REUSE_PASSWORD = 0;
+  /**
+   * User cannot reuse their password
+   */
+  public static final int NO_REUSE_PASSWORD = 1;
+
+  protected Cell cell;
+  protected int cellHandle;
+  protected String name;
+
+  /**
+   * Does this user have a kas entry?
+   */
+  protected boolean kas;
+  /**
+   * Does this user have a pts entry?
+   */
+  protected boolean pts;
+
+  // pts fields
+  protected int groupCreationQuota;
+  protected int groupMembershipCount;
+  protected int nameUID;
+  protected int ownerUID;
+  protected int creatorUID;
+
+  /**
+   * who is allowed to execute pts examine for this user.  Valid values are:
+   *   <ul>
+   *   <li>{@link #USER_OWNER_ACCESS}  
+   *       -- only the owner has permission</li>
+   *   <li>{@link #USER_ANYUSER_ACCESS} 
+   *       -- any user has permission</li></ul>
+   */
+  protected int listStatus;
+  /**
+   * who is allowed to execute pts listowned for this user.  Valid values are:
+   *   <ul>
+   *   <li>{@link #USER_OWNER_ACCESS}  
+   *       -- only the owner has permission</li>
+   *   <li>{@link #USER_ANYUSER_ACCESS} 
+   *       -- any user has permission</li></ul>
+   */
+  protected int listGroupsOwned;
+  /**
+   * who is allowed to execute pts membership for this user.  Valid values are:
+   *   <ul>
+   *   <li>{@link #USER_OWNER_ACCESS} 
+   *       -- only the owner has permission</li>
+   *   <li>{@link #USER_ANYUSER_ACCESS} 
+   *       -- any user has permission</li></ul>
+   */
+  protected int listMembership;
+  protected String owner;
+  protected String creator;
+  
+  // lists
+  protected ArrayList groups;
+  protected ArrayList groupNames;
+  protected ArrayList groupsOwned;
+  protected ArrayList groupsOwnedNames;
+
+  // kas fields
+  /**
+   * whether or not this user has kas administrative privileges. 
+   *   Valid values are:
+   *   <ul>
+   *   <li>{@link #ADMIN}</li>
+   *   <li>{@link #NO_ADMIN}</li></ul>
+   */
+  protected int adminSetting;
+  /**
+   * whether the TGS will grant tickets for this user. Valid values are:
+   *   <ul>
+   *   <li>{@link #GRANT_TICKETS}</li>
+   *   <li>{@link #NO_GRANT_TICKETS}</li></ul>
+   */
+  protected int tgsSetting;
+  /**
+   * whether the TGS can use this user's key as an encryption key. Valid values are:
+   *   <ul>
+   *   <li>{@link #ENCRYPT}</li>
+   *   <li>{@link #NO_ENCRYPT}</li></ul>
+   */
+  protected int encSetting;
+  /**
+   * whether this user is allowed to change its password. Valid values are:
+   *   <ul>
+   *   <li>{@link #CHANGE_PASSWORD}</li>
+   *   <li>{@link #NO_CHANGE_PASSWORD}</li></ul>
+   */
+  protected int cpwSetting;
+  /**
+   * whether this user is allowed to reuse its password. Valid values are:
+   *   <ul>
+   *   <li>{@link #REUSE_PASSWORD}</li>
+   *   <li>{@link #NO_REUSE_PASSWORD}</li></ul>
+   */
+  protected int rpwSetting;
+  protected int userExpiration;
+  protected int lastModTime;
+  protected String lastModName;
+  protected int lastChangePasswordTime;
+  protected int maxTicketLifetime;
+  protected int keyVersion;
+  protected String encryptionKey;
+  protected long keyCheckSum;
+  protected int daysToPasswordExpire;
+  protected int failLoginCount;
+  protected int lockTime;
+  protected int lockedUntil;
+
+  // Dates and times
+  protected GregorianCalendar lockedUntilDate;
+  protected GregorianCalendar userExpirationDate;
+  protected GregorianCalendar lastModTimeDate;
+  protected GregorianCalendar lastChangePasswordTimeDate;
+
+  /**
+   * Whether or not the information fields of this user have been filled.
+   */
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <code>User</code> object instance given the name 
+   * of the AFS user and the AFS cell, represented by 
+   * <CODE>cell</CODE>, to which it belongs.   This does not actually
+   * create a new AFS user, it just represents one.
+   * If <code>name</code> is not an actual AFS user, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String, int)} or {@link #create(String)}
+   * method is explicitly called to create it.
+   *
+   * @param name  the name of the user to represent
+   * @param cell  the cell to which the user belongs.
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public User( String name, Cell cell ) throws AFSException
+  {
+    this.name = name;
+    this.cell = cell;
+    cellHandle = cell.getCellHandle();
+    
+    groups = null;
+    groupNames = null;
+    groupsOwned = null;
+    groupsOwnedNames = null;
+    cachedInfo = false;
+    kas = false;
+    pts = false;
+  }
+
+  /**
+   * Constructs a new <code>User</code> object instance given the name 
+   * of the AFS user and the AFS cell, represented by 
+   * <CODE>cell</CODE>, to which it belongs.   This does not actually
+   * create a new AFS user, it just represents one.
+   * If <code>name</code> is not an actual AFS user, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(String, int)} or {@link #create(String)}
+   * method is explicitly called to create it.   Note that if the process
+   * doesn't exist and <code>preloadAllMembers</code> is true, an exception
+   * will be thrown.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications. It ensures all data member values are set and 
+   * available without calling back to the filesystem at the first request 
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param name               the name of the user to represent 
+   * @param cell               the cell to which the user belongs.
+   * @param preloadAllMembers  true will ensure all object members are 
+   *                           set upon construction;
+   *                           otherwise members will be set upon access, 
+   *                           which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public User( String name, Cell cell, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, cell);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Constructs a blank <code>User</code> object given the cell to which 
+   * the user belongs.  This blank object can then be passed into other 
+   * methods to fill out its properties.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param cell       the cell to which the user belongs.
+   */
+  User( Cell cell ) throws AFSException
+  {
+    this( null, cell );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Creates the kas and pts entries for a new user in this cell.  
+   * Automatically assigns a user id.
+   *   *
+   * @param password      the password for the new user
+   * @exception AFSException  If an error occurs in the native code
+   */    
+  public void create( String password ) throws AFSException
+  {
+    create( password, 0 );
+  }
+
+  /**
+   * Creates the kas and pts entries for a new user in this cell.
+   *
+   * @param password      the password for the new user
+   * @param uid       the user id to assign to the new user
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void create( String password, int uid ) throws AFSException
+  {
+    create( cell.getCellHandle(), name, password, uid );
+  }
+
+  /**
+   * Deletes the pts and kas entries for a user in this cell. Deletes this user
+   * from the membership list of the groups to which it belonged, but does not 
+   * delete the groups owned by this user.  Also nullifies this corresponding 
+   * Java object.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void delete() throws AFSException
+  {
+       delete( cell.getCellHandle(), name );
+
+       cell = null;
+       name = null;
+       kas = false;
+       pts = false;
+       owner = null;
+       creator = null;
+       groups = null;
+       groupsOwned = null;
+       groupNames = null;
+       groupsOwnedNames = null;
+       lastModName = null;
+       encryptionKey = null;
+       lockedUntilDate = null;
+       userExpirationDate = null;
+       lastModTimeDate = null;
+       lastChangePasswordTimeDate = null;
+       try {
+           finalize();
+       } catch( java.lang.Throwable t ) {
+           throw new AFSException( t.getMessage() );
+       }
+  }
+
+  /**
+   * Unlocks the given user if they were locked out of the cell.
+   *
+   * @param userName      the name of the user to unlock
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void unlock() throws AFSException
+  {
+    unlock( cell.getCellHandle(), name );
+    lockedUntil = 0;
+    lockedUntilDate = null;
+  }
+
+  /**
+   * Flushes the current information of this <code>User</code> object to disk.
+   * This will update the information of the actual AFS user to match the 
+   * settings that have been modified within this <code>User</code> object.  
+   * This function must be called before any changes made to the information 
+   * fields of this user will be seen by AFS.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public void flushInfo() throws AFSException
+  {
+    setUserInfo( cell.getCellHandle(), name, this );
+  }
+
+  /**
+   * Change the name of this user.  Automatically flushes the info of this 
+   * user in order to update kas entry of the new name.  NOTE:  renaming a 
+   * locked user will unlock that user.
+   *
+   * @param newName    the new name for this user
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void rename( String newName ) throws AFSException
+  {
+    rename( cell.getCellHandle(), name, newName );
+    name = newName;
+    flushInfo();
+  }
+
+  /**
+   * Refreshes the properties of this User object instance with values from 
+   * the AFS user it represents.  All properties that have been initialized 
+   * and/or accessed will be renewed according to the values of the AFS user 
+   * this User object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this User object instance with values from 
+   * the AFS user it represents.  If <CODE>all</CODE> is <CODE>true</CODE> 
+   * then <U>all</U> of the properties of this User object instance will be 
+   * set, or renewed, according to the values of the AFS user it represents, 
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise renew 
+   *              all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if ( all || cachedInfo ) {
+      refreshInfo();
+    }
+    if ( all || groupsOwned != null ) {
+      refreshGroupsOwned();
+    }
+    if ( all || groupsOwnedNames != null ) {
+      refreshGroupsOwnedNames();
+    }
+    if ( all || groups != null ) {
+      refreshGroups();
+    }
+    if ( all || groupNames != null ) {
+      refreshGroupNames();
+    }
+  }
+
+  /**
+   * Refreshes the information fields of this <code>User</code> to reflect 
+   * the current state of the AFS user.  Does not refresh the groups to which 
+   * the user belongs or groups owned by the user.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getUserInfo( cell.getCellHandle(), name, this );
+    cachedInfo = true;
+    lastModTimeDate = null;
+    lastChangePasswordTimeDate = null;
+    lockedUntilDate = null;
+    userExpirationDate = null;
+  }
+
+  /**
+   * Refreshes the current information about the group names to which the 
+   * user belongs.  Does not refresh the information fields of the user or 
+   * the groups owned.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupNames() throws AFSException
+  {
+    String currName;
+    int iterationID = getUserGroupsBegin( cell.getCellHandle(), name );
+    groupNames = new ArrayList();
+    while( ( currName = getUserGroupsNextString( iterationID ) ) != null ) {
+      groupNames.add( currName );
+    } 
+    getUserGroupsDone( iterationID );
+  }
+  
+  /**
+   * Refreshes the current information about the group objects to which the 
+   * user belongs.  Does not refresh the information fields of the user or 
+   * the groups owned.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroups() throws AFSException
+  {
+    Group currGroup;
+    int iterationID = getUserGroupsBegin( cell.getCellHandle(), name );
+
+    groups = new ArrayList();
+
+    currGroup = new Group( cell );
+    while( getUserGroupsNext( cellHandle, iterationID, currGroup ) != 0 ) {
+      groups.add( currGroup );
+      currGroup = new Group( cell );
+    } 
+    getUserGroupsDone( iterationID );
+  }
+
+  /**
+   * Refreshes the current information about the group names that the user 
+   * owns.  Does not refresh the information fields of the user or the groups 
+   * belonged to.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupsOwnedNames() throws AFSException
+  {
+    String currName;
+    int iterationID = this.getGroupsOwnedBegin( cell.getCellHandle(), name );
+    groupsOwnedNames = new ArrayList();
+    while( ( currName = this.getGroupsOwnedNextString( iterationID ) ) 
+          != null ) {
+      groupsOwnedNames.add( currName );
+    } 
+    this.getGroupsOwnedDone( iterationID );
+  }
+  
+  /**
+   * Refreshes the current information about the group objects that the user \
+   * owns.  Does not refresh the information fields of the user or the groups 
+   * belonged to.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected void refreshGroupsOwned() throws AFSException
+  {
+    Group currGroup;
+    int iterationID = getGroupsOwnedBegin( cell.getCellHandle(), name );
+    groupsOwned = new ArrayList();
+    currGroup = new Group( cell );
+    while( getGroupsOwnedNext( cellHandle, iterationID, currGroup ) != 0 ) {
+      groupsOwned.add( currGroup );
+      currGroup = new Group( cell );
+    } 
+    getGroupsOwnedDone( iterationID );
+  }
+
+  /**
+   * Adds an access control list entry for some AFS directory for this user.
+   *
+   * @param directory    the full path of the place in the AFS file system 
+   *                     for which to add an entry
+   * @param read    whether or not to allow read access to this user
+   * @param write    whether or not to allow write access to this user
+   * @param lookup    whether or not to allow lookup access to this user
+   * @param delete    whether or not to allow deletion access to this user
+   * @param insert    whether or not to allow insertion access to this user
+   * @param lock    whether or not to allow lock access to this user
+   * @param admin    whether or not to allow admin access to this user
+   * @exception AFSException  If an error occurs in the native code
+  public void setACL( String directory, boolean read, boolean write, boolean lookup, boolean delete, boolean insert, boolean lock, boolean admin ) throws AFSException
+  {
+    cell.setACL( directory, name, read, write, lookup, delete, insert, lock, admin );
+  }
+   */
+
+  //////////////// ACCESSORS ////////////////////////
+
+  /**
+   * Returns the name of this user.
+   *
+   * @return the name of this user
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns the Cell this user belongs to.
+   *
+   * @return the Cell this user belongs to
+   */
+  public Cell getCell()
+  {
+    return cell;
+  }
+
+  /**
+   * Returns whether or not this user has a kas entry.
+   *
+   * @return whether or not this user has a kas entry
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isKAS() throws AFSException
+  {
+    if ( !cachedInfo ) refreshInfo();
+    return kas;
+  }
+
+  /**
+   * Returns whether or not this user has a pts entry.
+   *
+   * @return whether or not this user has a pts entry
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isPTS() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return pts;
+  }
+
+  /**
+   * PTS: Returns an array of the <code>Group</code> objects 
+   * to which this user belongs.
+   *
+   * @return      an array of the groups to which this user belongs
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public Group[] getGroups() throws AFSException
+  {
+    if( groups == null ) refreshGroups();
+    return (Group[]) groups.toArray( new Group[groups.size()] );
+  }
+
+  /**
+   * PTS: Returns the total count of groups this user owns.
+   *
+   * <P>If the total list of groups or group names have already been 
+   * collected (see {@link #getGroupsOwned()}), then the returning value 
+   * will be calculated based upon the current list.  Otherwise, PTS will 
+   * be explicitly queried for the information.
+   *
+   * @return      total count of groups this user owns
+   * @exception AFSException  If an error occurs in the native code
+   * @see #getGroupsOwned()
+   * @see #getGroupsOwnedNames()
+   */
+  public int getGroupsOwnedCount() throws AFSException
+  {
+    if( groupsOwned != null ) {
+      return groupsOwned.size();
+    } else if ( groupsOwnedNames != null ) {
+      return groupsOwnedNames.size();
+    } else {
+      return getGroupsOwnedCount( cell.getCellHandle(), name );
+    }
+  }
+
+  /**
+   * PTS: Returns an array of the <code>Group</code> objects 
+   * this user owns.
+   *
+   * @return     an array of the <code>Groups</code> this user owns
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public Group[] getGroupsOwned() throws AFSException
+  {
+    if( groupsOwned == null ) refreshGroupsOwned();
+    return (Group[]) groupsOwned.toArray( new Group[groupsOwned.size()] );
+  }
+
+  /**
+   * PTS: Returns a <code>String</code> array of the group names 
+   * to which this user belongs.
+   *
+   * @return      a <code>String</code> array of the groups to which this 
+   *              user belongs
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getGroupNames() throws AFSException
+  {
+    if( groupNames == null ) refreshGroupNames();
+    return (String []) groupNames.toArray( new String[groupNames.size() ] );
+  }
+
+  /**
+   * PTS: Returns a <code>String</code> array of the group names 
+   * this user owns.
+   *
+   * @return     a <code>String</code> array of the groups this user owns
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String[] getGroupsOwnedNames() throws AFSException
+  {
+    if( groupsOwnedNames == null ) refreshGroupsOwnedNames();
+    return (String []) groupsOwnedNames.toArray( new String[groupsOwnedNames.size() ] );
+  }
+
+  /**
+   * PTS: Returns the numeric AFS id of this user.
+   *
+   * @return the AFS id of this user
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getUID() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return nameUID;
+  }
+
+  /**
+   * PTS: Returns how many more groups this user is allowed to create.  
+   * -1 indicates unlimited.
+   *
+   * @return the group creation quota
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getGroupCreationQuota() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return groupCreationQuota;
+  }
+
+  /**
+   * PTS: Returns the number of groups to which this user belongs.
+   *
+   * @return the group membership count
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getGroupMembershipCount() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return groupMembershipCount;
+  }
+
+  /**
+   * PTS: Returns the owner of this user in the form of a {@link PTSEntry}.
+   *
+   * <P>The returning object could be either a {@link User} or {@link Group};
+   * to determine what type of object the {@link PTSEntry} represents,
+   * call the {@link PTSEntry#getType()} method.
+   *
+   * @return the owner of this user
+   * @exception AFSException  If an error occurs in the native code
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   * @see #refresh()
+   */
+  public PTSEntry getOwner() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if (owner == null) return null;
+    if (ownerUID > 0) {
+      return new User(owner, cell);
+    } else {
+      return new Group(owner, cell);
+    }
+  }
+
+  /**
+   * PTS: Returns the creator of this user in the form of a {@link PTSEntry}.
+   *
+   * <P>The returning object could be either a {@link User} or {@link Group};
+   * to determine what type of object the {@link PTSEntry} represents,
+   * call the {@link PTSEntry#getType()} method.
+   *
+   * @return the creator of this user
+   * @exception AFSException  If an error occurs in the native code
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   * @see #refresh()
+   */
+  public PTSEntry getCreator() throws AFSException
+  {
+    if (!cachedInfo) refreshInfo();
+    if (creator == null) return null;
+    if (creatorUID > 0) {
+      return new User(creator, cell);
+    } else {
+      return new Group(creator, cell);
+    }
+  }
+
+  /**
+   * Returns the type of {@link PTSEntry} this object represents.
+   *
+   * <P>This method will always return {@link PTSEntry#PTS_USER}.
+   *
+   * @return  the type of PTSEntry this object represents 
+              (will always return {@link PTSEntry#PTS_USER})
+   * @see PTSEntry
+   * @see PTSEntry#getType()
+   */
+  public short getType()
+  {
+    return PTSEntry.PTS_USER;
+  }
+
+  /**
+   * PTS: Returns who can list the status (pts examine) of this user.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the status listing permission
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getListStatus() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listStatus;
+  }
+
+  /**
+   * PTS: Returns who can list the groups owned (pts listowned) by this user.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the groups owned listing permission
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getListGroupsOwned() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listGroupsOwned;
+  }
+
+  /**
+   * PTS: Returns who can list the groups (pts membership) to which this 
+   * user belongs.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @return the membership listing permission
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getListMembership() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return listMembership;
+  }
+
+  /**
+   * KAS: Returns whether or not this user has kas administrative privileges
+   *
+   * @return whether or not this user has kas administrative priveleges
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean isAdmin() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return (adminSetting == this.ADMIN);
+  }
+
+  /**
+   * KAS: Returns whether or not TGS will issue tickets for this user
+   *
+   * @return whether or not TGS will issue tickets for this user
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean willGrantTickets() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return (tgsSetting == this.GRANT_TICKETS);
+  }
+
+  /**
+   * KAS: Returns whether or not TGS can use this users ticket for an encryption key
+   *
+   * @return whether or not TGS can use this users ticket for an encryption key
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean canEncrypt() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return (encSetting == this.ENCRYPT);
+  }
+
+  /**
+   * KAS: Returns whether or not the user can change their password
+   *
+   * @return whether or not the user can change their password
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean canChangePassword() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return (cpwSetting == this.CHANGE_PASSWORD);
+  }
+
+  /**
+   * KAS: Returns whether or not the user can reuse their password
+   *
+   * @return whether or not the user can reuse their password
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public boolean canReusePassword() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return (rpwSetting == this.REUSE_PASSWORD);
+  }
+
+  /**
+   * KAS: Returns the date and time the user expires.  
+   * A <code>null</code> value indicates the user never exipres (or that
+   * there is no kas entry for this user).
+   *
+   * @return the date and time the user expires
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getUserExpiration() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return userExpiration;
+  }
+  /**
+   * KAS: Returns the date and time the user expires.  
+   * A <code>null</code> value indicates the user never expires (or that
+   * there is no kas entry for this user).
+   *
+   * @return the date and time the user expires
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public GregorianCalendar getUserExpirationDate() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    if( userExpirationDate == null && userExpiration != 0 ) {
+      // make it into a date . . .
+      if( userExpiration == 0 ) {
+        userExpirationDate = null;
+      } else {
+        userExpirationDate = new GregorianCalendar();
+        long longTime = ((long) userExpiration)*1000;
+        Date d = new Date( longTime );
+        userExpirationDate.setTime( d );
+      }
+    }
+    return userExpirationDate;
+  }
+
+  /**
+   * KAS: Returns the date and time (in UTC) the user's KAS entry was 
+   * last modified.
+   *
+   * @return the date and time (in UTC) the user was last modified
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getLastModTime() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return lastModTime;
+  }
+  /**
+   * KAS: Returns the date and time the user was last modified.
+   *
+   * @return the date and time the user was last modified
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public GregorianCalendar getLastModTimeDate() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    if( lastModTimeDate == null ) {
+      // make it into a date . . .
+      lastModTimeDate = new GregorianCalendar();
+      long longTime = ((long) lastModTime)*1000;
+      Date d = new Date( longTime );
+      lastModTimeDate.setTime( d );
+    }
+    return lastModTimeDate;
+  }
+
+  /**
+   * KAS: Returns the name of the user that last modified this user.
+   *
+   * @return the name of this user that last modified this user.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String getLastModName() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return lastModName;
+  }
+
+  /**
+   * KAS: Returns the last date and time the user changed its password.
+   *
+   * @return the last date and time the user changed its password.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public GregorianCalendar getLastChangePasswordTimeDate() 
+      throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    if( lastChangePasswordTimeDate == null ) {
+      // make it into a date . . .
+      lastChangePasswordTimeDate = new GregorianCalendar();
+      long longTime = ((long) lastChangePasswordTime)*1000;
+      Date d = new Date( longTime );
+      lastChangePasswordTimeDate.setTime( d );
+    }
+    return lastChangePasswordTimeDate;
+  }
+
+  /**
+   * KAS: Returns the last date and time (in UTC) the user changed 
+   * its password.
+   *
+   * @return the last date and time (in UTC) the user changed its password.
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getLastChangePasswordTime() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return lastChangePasswordTime;
+  }
+
+  /**
+   * KAS: Returns the maximum lifetime of a ticket issued to this user 
+   * (in seconds).
+   *
+   * @return the maximum lifetime of a ticket issued to this user (in seconds).
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getMaxTicketLifetime() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return maxTicketLifetime;
+  }
+
+  /**
+   * KAS: Returns the number of days a password is valid before it expires.  
+   * A value of 0 indicates passwords never expire.
+   *
+   * @return the number of days for which a password is valid
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getDaysToPasswordExpire() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return daysToPasswordExpire;
+  }
+
+  /**
+   * KAS: Returns the number of failed login attempts this user is allowed 
+   * before being locked out.  A value of 0 indicates there is no limit.
+   *
+   * @return the number of failed login attempts a user is allowed
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getFailLoginCount() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return failLoginCount;
+  }
+
+  /**
+   * KAS: Returns the amount of time (in seconds) a user is locked out when 
+   * it exceeds the maximum number of allowable failed login attempts.  
+   * A value of 0 indicates an infinite lockout time.
+   *
+   * @return the number of failed login attempts a user is allowed
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getLockTime() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return lockTime;
+  }
+
+  /**
+   * KAS: Returns the encryption key, in octal form, of this user.  An
+   * example of a key in octal form is:    
+   * '\040\205\211\241\345\002\023\211'.
+   *
+   * @return the encryption key
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public String getEncryptionKey() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return encryptionKey;
+  }
+
+  /**
+   * KAS: Returns the check sum of this user's key.
+   *
+   * @return the check sum
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public long getKeyCheckSum() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return keyCheckSum;
+  }
+
+  /**
+   * KAS: Returns the version number of the user's key.
+   *
+   * @return the key version
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getKeyVersion() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return keyVersion;
+  }
+
+  /**
+   * KAS: Returns the date and time (in UTC) at which the user stops 
+   * being locked out. A value of 0 indicates the user is not currently 
+   * locked out. If the user is locked out forever, the value 
+   * will be equal to -1.
+   *
+   * @return the date and time (in UTC) at which the user stops being 
+   *         locked out
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public int getLockedUntil() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return lockedUntil;
+  }
+
+  /**
+   * KAS: Returns the date and time at which the user stops being locked out.
+   * A value of <code>null</code> indicates the user is not currently locked 
+   * out. If the user is locked out forever, the value 
+   * <code>getLockedUntil().getTime().getTime()</code> will be equal to -1.
+   *
+   * @return the date and time at which the user stops being locked out
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  public GregorianCalendar getLockedUntilDate() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    if( lockedUntilDate == null ) {
+      // make it into a date . . .
+      Date d;
+      if( lockedUntil == 0 ) {
+        lockedUntilDate = null;
+      } else if( lockedUntil == -1 ) {
+        d = new Date( lockedUntil );
+        lockedUntilDate = new GregorianCalendar();
+        lockedUntilDate.setTime( d );
+      } else {
+        d = new Date( ((long) lockedUntil)*1000 );
+        lockedUntilDate = new GregorianCalendar();
+        lockedUntilDate.setTime( d );
+      }
+    }
+    return lockedUntilDate;
+  }
+
+  /////////////// mutators ////////////////////
+
+  /**
+   * PTS: Sets how many more groups this user is allowed to create.  
+   * -1 indicates unlimited.
+   *
+   * @param quota    the new group creation quota
+   */
+  public void setGroupCreationQuota( int quota )
+  {
+    groupCreationQuota = quota;
+  }
+
+
+  /**
+   * PTS: Sets who can list the status (pts examine) of this user.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list status permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListStatus( int value ) throws AFSException
+  {
+    if( (value != this.USER_OWNER_ACCESS) && 
+       (value != this.USER_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listStatus to " 
+                                           + value );
+    } else {
+       listStatus = value;
+    }
+  }
+
+  /**
+   * PTS: Sets who can list the groups owned (pts listowned) by this user.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list groups owned permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListGroupsOwned( int value ) throws AFSException
+  {
+    if( (value != this.USER_OWNER_ACCESS) && 
+       (value != this.USER_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listGroupsOwned to " 
+                                           + value );
+    } else {
+       listGroupsOwned = value;
+    }
+  }
+
+  /**
+   * PTS: Sets who can list the groups (pts membership) to which this 
+   *      user belongs.  
+   * Valid values are:
+   * <ul>
+   * <li><code>{@link #USER_OWNER_ACCESS}</code> 
+   *           -- only the owner has permission</li>
+   * <li><code>{@link #USER_ANYUSER_ACCESS}</code> 
+   *           -- any user has permission</li>
+   * </ul>
+   *
+   * @param value    the value of the new list membership permission
+   * @exception AFSException      if an error occurs in the native code
+   * @exception IllegalArgumentException   if an invalud argument is provided
+   */
+  public void setListMembership( int value ) throws AFSException
+  {
+    if( (value != this.USER_OWNER_ACCESS) && 
+       (value != this.USER_ANYUSER_ACCESS) ) {
+       throw new IllegalArgumentException( "Cannot set listMembership to " 
+                                           + value );
+    } else {
+       listMembership = value;
+    }
+  }
+
+  /**
+   * KAS: Sets whether or not this user has kas administrative privileges
+   *
+   * @param setting     whether or not this user has kas 
+   *                    administrative privileges
+   */
+  public void setAdmin( boolean setting )
+  {
+    if ( setting ) {
+       adminSetting = this.ADMIN;
+    } else {
+       adminSetting = this.NO_ADMIN;
+    }
+  }
+
+  /**
+   * KAS: Sets whether or not TGS will issue tickets for this user
+   *
+   * @param setting    whether or not TGS will issue tickets for this user
+   */
+  public void setGrantTickets( boolean setting )
+  {
+    if ( setting ) {
+       tgsSetting = this.GRANT_TICKETS;
+    } else {
+       tgsSetting = this.NO_GRANT_TICKETS;
+    }
+  }
+
+  /**
+   * KAS: Sets whether or not TGS can use this users ticket for an 
+   *      encryption key
+   *
+   * @param setting     whether or not TGS can use this users ticket for an 
+   *                    encryption key
+   */
+  public void setEncrypt( boolean setting )
+  {
+    if ( setting ) {
+       encSetting = this.ENCRYPT;
+    } else {
+       encSetting = this.NO_ENCRYPT;
+    }
+  }
+
+  /**
+   * KAS: Sets whether or not the user can change their password
+   *
+   * @param setting     whether or not the user can change their password
+   */
+  public void setChangePassword( boolean setting )
+  {
+    if ( setting ) {
+       cpwSetting = this.CHANGE_PASSWORD;
+    } else {
+       cpwSetting = this.NO_CHANGE_PASSWORD;
+    }
+  }
+
+  /**
+   * KAS: Sets whether or not the user can reuse their password
+   *
+   * @param setting      whether or not the user can reuse their password
+   */
+  public void setReusePassword( boolean setting )
+  {
+    if ( setting ) {
+       rpwSetting = this.REUSE_PASSWORD;
+    } else {
+       rpwSetting = this.NO_REUSE_PASSWORD;
+    }
+  }
+
+  /**
+   * KAS: Sets the date and time the user expires.  
+   * A <code>null</code> value indicates the user never exipres.
+   *
+   * @param expirationDate     the date and time the user expires
+   */
+  public void setUserExpiration( GregorianCalendar expirationDate )
+  {
+    userExpirationDate = expirationDate;
+    if( expirationDate == null ) {
+      userExpiration = -1;
+    } else {
+      Date d = expirationDate.getTime();
+      long millis = d.getTime();
+      userExpiration = (int) (millis/((long)1000));
+    }
+  }
+
+  /**
+   * KAS: Sets the maximum lifetime of a ticket issued to this user 
+   *      (in seconds).
+   *
+   * @param seconds    the maximum lifetime of a ticket issued to this user (in seconds).
+   */
+  public void setMaxTicketLifetime( int seconds )
+  {
+    maxTicketLifetime = seconds;
+  }
+
+  /**
+   * KAS: Sets the number of days a password is valid before it expires.  
+   * A value of 0 indicates passwords never expire.
+   *
+   * @param days     the number of days for which a password is valid
+   */
+  public void setDaysToPasswordExpire( int days )
+  {
+    daysToPasswordExpire = days;
+  }
+
+  /**
+   * KAS: Sets the number of failed login attempts this user is allowed before 
+   * being locked out.  A value of 0 indicates there is no limit.
+   *
+   * @param logins      the number of failed login attempts a user is allowed
+   */
+  public void setFailLoginCount( int logins )
+  {
+    failLoginCount = logins;
+  }
+
+  /**
+   * KAS: Sets the amount of time (in seconds) a user is locked out when it 
+   * exceeds the maximum number of allowable failed login attempts. 
+   * A value of 0 indicates an infinite lockout time.  Any nonzero value gets
+   * rounded up to the next highest multiple of 8.5 minutes, and any value over
+   * 36 hours gets rounded down to 36 hours.
+   *
+   * @param seconds      the number of failed login attempts a user is allowed
+   */
+  public void setLockTime( int seconds )
+  {
+    lockTime = seconds;
+  }
+
+  /**
+   * Sets the password of this user to something new.  Sets the key version 
+   * to 0 automatically.
+   *
+   * @param newPassword     the new password for this user
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void setPassword( String newPassword ) throws AFSException
+  {
+    this.setPassword( cell.getCellHandle(), name, newPassword );
+  }
+  
+  /////////////// custom information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>User</code>.  
+   * Contains the information fields and groups.
+   *
+   * @return a <code>String</code> representation of the <code>User</code>
+   */
+  protected String getInfo()
+  {
+    String r;
+    try {
+    
+       r = "User: " + name;
+
+       if( pts ) {
+           r += ", uid: " + getUID();
+       }
+       r += "\n";
+       r += "\tKAS: " + isKAS() + "\tPTS: " + isPTS() + "\n";
+
+       if( pts ) { 
+           r += "\towner: " + getOwner().getName() + ", uid: " 
+               + getOwner().getUID() + "\n";
+           r += "\tcreator: " + getCreator().getName() + ", uid: " 
+               + getCreator().getUID() + "\n";
+           r += "\tGroup creation quota: " + getGroupCreationQuota() + "\n";
+           r += "\tGroup membership count: " + getGroupMembershipCount() 
+               + "\n";
+           r += "\tList status: " + getListStatus() + "\n";
+           r += "\tList groups owned: " + getListGroupsOwned() + "\n";
+           r += "\tList membership: " + getListMembership() + "\n";
+       }
+       
+       if( kas ) {
+           r += "\tKAS admin status: ";
+
+           if( isAdmin() ) {
+               r += "Yes\n";
+           } else {
+               r += "No\n";
+           }
+
+           r += "\tTGS grant tickets: ";
+           if( willGrantTickets() ) {
+           r += "Yes\n";
+           } else {
+               r += "No\n";
+           }
+           
+           r += "\tUse key as encryption key: ";
+           if( canEncrypt() ) {
+               r += "Yes\n";
+           } else {
+               r += "No\n";
+           }
+           
+       
+           r += "\tCan change password: ";
+           
+           if( canChangePassword() ) {
+               r += "Yes\n";
+           } else {
+               r += "No\n";
+           }
+           
+           r += "\tCan reuse password: ";
+           if( canReusePassword() ) {
+               r += "Yes\n";
+           } else {
+               r += "No\n";
+           }
+
+           if( userExpiration != 0 ) {
+               r += "\tExpiration date: " 
+                   + getUserExpirationDate().getTime() + "\n";
+           } else {
+               r += "\tUser never expires\n";
+           }
+           r += "\tLast modified " + getLastModTimeDate().getTime() 
+               + " by " + getLastModName() + "\n";
+           r += "\tLast changed password " 
+               + getLastChangePasswordTimeDate().getTime() + "\n";
+           r += "\tMax ticket lifetime: " + getMaxTicketLifetime() + "\n";
+           r += "\tKey: " + getEncryptionKey() + ", version: " 
+               + getKeyVersion() + ", checksum: " + getKeyCheckSum() + "\n";
+           r += "\tDays till password expires: " + getDaysToPasswordExpire() 
+               + "\n";
+           r += "\tAllowed failed logins: " + getFailLoginCount() + "\n";
+           r += "\tLock time after failed logins: " + getLockTime() + "\n";
+           if( lockedUntil == 0 ) { 
+               r += "\tNot locked\n";
+           } else if( getLockedUntilDate().getTime().getTime() == -1 ) {
+               r += "\tLocked forever\n";
+           } else {
+               r += "\tLocked until: " + getLockedUntilDate().getTime() 
+                   + "\n";
+           }      
+           
+       }
+       if( pts ) {
+  
+           r += "\tBelongs to groups: \n";
+           
+           String grps[] = getGroupNames();
+           for( int i = 0; i < grps.length; i++ ) {
+               r += "\t\t" + grps[i] + "\n";
+           }
+           
+           r += "\tOwns groups: \n";
+           grps = getGroupsOwnedNames();
+           for( int i = 0; i < grps.length; i++ ) {
+               r += "\t\t" + grps[i] + "\n";
+           }
+           
+       }
+    } catch( AFSException e ) {
+       return e.toString();
+    }
+    return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the groups to which this user belongs.
+   *
+   * @return    a <code>String</code> representation of the groups belonged to
+   * @see       Group#toString
+   */
+  protected String getInfoGroups() throws AFSException
+  {
+       String r;
+       r = "User: " + name + "\n\n";
+       r += "--Member of Groups:--\n";
+
+       Group grps[] = getGroups();
+       for( int i = 0; i < grps.length; i++ ) {
+           r += grps[i].getInfo() + "\n";
+       }
+       return r;
+  }
+
+  /**
+   * Returns a <code>String</code> containing the <code>String</code> 
+   * representations of all the groups that this user owns.
+   *
+   * @return    a <code>String</code> representation of the groups owned
+   * @see       Group#toString
+   */
+  protected String getInfoGroupsOwned() throws AFSException
+  {
+       String r;
+       r = "User: " + name + "\n\n";
+       r += "--Owns Groups:--\n";
+       Group grps[] = getGroupsOwned();
+       for( int i = 0; i < grps.length; i++ ) {
+           r += grps[i].getInfo() + "\n";
+       }
+       return r;
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two User objects respective to their names and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     user    The User object to be compared to this User instance
+   * 
+   * @return    Zero if the argument is equal to this User's name, a
+   *           value less than zero if this User's name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this User's name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(User user)
+  {
+    return this.getName().compareTo(user.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(User)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((User)obj);
+  }
+
+  /**
+   * Tests whether two <code>User</code> objects are equal, based on their 
+   * names.
+   *
+   * @param otherUser   the user to test
+   * @return whether the specifed user is the same as this user
+   */
+  public boolean equals( User otherUser )
+  {
+    return name.equals(otherUser.getName());
+  }
+
+  /**
+   * Returns the name of this <CODE>User</CODE>
+   *
+   * @return the name of this <CODE>User</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Creates the kas and pts entries for a new user.  Pass in 0 for the uid 
+   * if pts is to automatically assign the user id.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param userName      the name of the user to create
+   * @param password      the password for the new user
+   * @param uid     the user id to assign to the user (0 to have one 
+   *                automatically assigned)
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void create( int cellHandle, String userName, 
+                                      String password, int uid )
+       throws AFSException;
+
+  /**
+   * Deletes the pts and kas entry for a user.  Deletes this user from the 
+   * membership list of the groups to which it belonged, but does not delete 
+   * the groups owned by this user.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param groupName      the name of the user to delete
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void delete( int cellHandle, String userName )
+       throws AFSException;
+
+  /**
+   * Unlocks a user.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param groupName      the name of the user to unlock
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native void unlock( int cellHandle, String userName )
+       throws AFSException;
+
+  /**
+   * Fills in the information fields of the provided <code>User</code>.  
+   * Fills in values based on the current pts and kas information of the user.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param name     the name of the user for which to get the information
+   * @param user     the <code>User</code> object in which to fill in the 
+   *                 information
+   * @see User
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getUserInfo( int cellHandle, String name, 
+                                           User user ) 
+       throws AFSException;
+
+  /**
+   * Sets the information values of this AFS user to be the parameter values.  
+   * Sets both kas and pts fields.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param name     the name of the user for which to set the information
+   * @param theUser  the <code>User</code> object containing the desired 
+   *                 information
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void setUserInfo( int cellHandle, String name, 
+                                           User theUser ) 
+       throws AFSException;
+
+  /**
+   * Renames the given user.  Does not update the info fields of the kas entry
+   *  -- the calling code is responsible for that.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param oldName     the name of the user to rename
+   * @param newName     the new name for the user
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void rename( int cellHandle, String oldName, 
+                                      String newName )
+       throws AFSException;
+
+  /**
+   * Sets the password of the given user.  Sets the key version to 0.
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param userName     the name of the user for which to set the password
+   * @param newPassword     the new password for the user
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void setPassword( int cellHandle, String userName, 
+                                           String newPassword )
+       throws AFSException;
+
+  /**
+   * Begin the process of getting the groups to which the user belongs.  
+   * Returns an iteration ID to be used by subsequent calls to 
+   * <code>getUserGroupsNext</code> and <code>getUserGroupsDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param name          the name of the user for which to get the groups
+   * @return an iteration ID
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native int getUserGroupsBegin( int cellHandle, String name )
+       throws AFSException;
+
+  /**
+   * Returns the next group to which the user belongs.  Returns 
+   * <code>null</code> if there are no more groups.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getUserGroupsBegin
+   * @return the name of the next group
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native String getUserGroupsNextString( int iterationId )
+       throws AFSException;
+
+  /**
+   * Fills the next group object of which the user belongs.  Returns 0 if there
+   * are no more groups, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see getUserGroupsBegin
+   * @param theGroup   a Group object to be populated with the values of the 
+   *                   next group
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getUserGroupsNext( int cellHandle, 
+                                                int iterationId, 
+                                                Group theGroup )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getUserGroupsBegin
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getUserGroupsDone( int iterationId )
+       throws AFSException;
+
+  /**
+   * Returns the total number of groups owned by the user.  
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @param name          the name of the user for which to get the groups
+   * @return total number of groups owned by the user
+   * @exception AFSException   If an error occurs in the native code
+   * @see Cell#getCellHandle
+   */
+  protected static native int getGroupsOwnedCount( int cellHandle, String name )
+       throws AFSException;
+
+  /**
+   * Begin the process of getting the groups that a user or group owns.  
+   * Returns an iteration ID to be used by subsequent calls to 
+   * <code>getGroupsOwnedNext</code> and <code>getGroupsOwnedDone</code>.  
+   *
+   * @param cellHandle    the handle of the cell to which the user belongs
+   * @see Cell#getCellHandle
+   * @param name  the name of the user or group for which to get the groups
+   * @return an iteration ID
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native int getGroupsOwnedBegin( int cellHandle, 
+                                                  String name )
+       throws AFSException;
+
+  /**
+   * Returns the next group the user or group owns.  Returns <code>null</code> 
+   * if there are no more groups.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupsOwnedBegin
+   * @return the name of the next group
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native String getGroupsOwnedNextString( int iterationId )
+       throws AFSException;
+
+  /**
+   * Fills the next group object that the user or group owns.  Returns 0 if 
+   * there are no more groups, != 0 otherwise.
+   *
+   * @param cellHandle    the handle of the cell to which the users belong
+   * @see Cell#getCellHandle
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupsOwnedBegin
+   * @param theGroup   a Group object to be populated with the values of the 
+   *                   next group
+   * @return 0 if there are no more users, != 0 otherwise
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int getGroupsOwnedNext( int cellHandle, 
+                                                 int iterationId, 
+                                                 Group theGroup )
+    throws AFSException;
+
+  /**
+   * Signals that the iteration is complete and will not be accessed anymore.
+   *
+   * @param iterationId   the iteration ID of this iteration
+   * @see getGroupsOwnedBegin
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getGroupsOwnedDone( int iterationId )
+       throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the user portion of the native library.
+   * This method should be called when no more <code>Users</code> are expected
+   * to be used.
+   */
+  protected static native void reclaimUserMemory();
+}
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/classes/org/openafs/jafs/Volume.java b/src/JAVA/classes/org/openafs/jafs/Volume.java
new file mode 100644 (file)
index 0000000..88cf7de
--- /dev/null
@@ -0,0 +1,1394 @@
+/*
+ * @(#)Volume.java     1.0 6/29/2001
+ *
+ * Copyright (c) 2001 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+package org.openafs.jafs;
+
+import java.util.GregorianCalendar;
+import java.util.Date;
+import java.io.Serializable;
+
+/**
+ * An abstract representation of an AFS volume.  It holds information about 
+ * the server, such as what its quota is.
+ * <BR><BR>
+ *
+ * Constructing an instance of a <code>Volume</code> does not mean an actual 
+ * AFS partition is created on a partition -- usually a <code>Volume</code>
+ * object is a representation of an already existing AFS volume.  If, 
+ * however, the <code>Volume</code> is constructed with the name of a 
+ * volume that does not exist in the cell to which the provided 
+ * <code>Partition</code> belongs, a new AFS volume with that name can be
+ * created on that partition by calling the {@link #create(int)} method.  If
+ * such a volume does already exist when this method is called, an exception 
+ * will be thrown.<BR><BR>
+ *
+ * <!--Example of how to use class-->
+ * The following is a simple example of how to construct and use a 
+ * <code>Volume</code> object.  This example obtains the list of 
+ * <code>Volume</code> objects residing on a particular partition, and prints
+ * out the name and id of each one.<BR><BR>
+ *
+ * <PRE>
+ * import org.openafs.jafs.Cell;
+ * import org.openafs.jafs.AFSException;
+ * import org.openafs.jafs.Partition;
+ * import org.openafs.jafs.Server;
+ * import org.openafs.jafs.Volume;
+ * ...
+ * public class ...
+ * {
+ *   ...
+ *   private Cell cell;
+ *   private Server server;
+ *   private Partition partition;
+ *   ...
+ *   public static void main(String[] args) throws Exception
+ *   {
+ *     String username      = arg[0];
+ *     String password      = arg[1];
+ *     String cellName      = arg[2];
+ *     String serverName    = arg[3];
+ *     String partitionName = arg[4];
+ * 
+ *     token  = new Token(username, password, cellName);
+ *     cell   = new Cell(token);
+ *     server = cell.getServer(serverName);
+ *     partition = cell.getPartition(partitionName);
+ * 
+ *     System.out.println("Volumes in Partition " + partition.getName() + ":");
+ *     Volume[] volumes = partition.getVolumes();
+ *     for (int i = 0; i < volumes.length; i++) {
+ *       System.out.println(" -> " + volumes[i] + ": " + volumes[i].getID());
+ *     }
+ *   }
+ *   ...
+ * }
+ * </PRE>
+ *
+ */
+public class Volume implements Serializable, Comparable
+{
+  /**
+   * Read-write volume type
+   */
+  public static final int VOLUME_TYPE_READ_WRITE = 0;
+  /**
+   * Read-only volume type
+   */
+  public static final int VOLUME_TYPE_READ_ONLY = 1;
+  /**
+   * Backup volume type
+   */
+  public static final int VOLUME_TYPE_BACKUP = 2;
+
+  /**
+   * Status/disposition ok
+   */
+  public static final int VOLUME_OK = 0;
+  /**
+   * Status/disposition salvage
+   */
+  public static final int VOLUME_SALVAGE = 1;
+  /**
+   * Status/disposition no vnode
+   */
+  public static final int VOLUME_NO_VNODE = 2;
+  /**
+   * Status/disposition no volume
+   */
+  public static final int VOLUME_NO_VOL = 3;
+  /**
+   * Status/disposition volume exists
+   */
+  public static final int VOLUME_VOL_EXISTS = 4;
+  /**
+   * Status/disposition no service 
+   */
+  public static final int VOLUME_NO_SERVICE = 5;
+  /**
+   * Status/disposition offline
+   */
+  public static final int VOLUME_OFFLINE = 6;
+  /**
+   * Status/disposition online
+   */
+  public static final int VOLUME_ONLINE = 7;
+  /**
+   * Status/disposition disk full
+   */
+  public static final int VOLUME_DISK_FULL = 8;
+  /**
+   * Status/disposition over quota
+   */
+  public static final int VOLUME_OVER_QUOTA = 9;
+  /**
+   * Status/disposition busy
+   */
+  public static final int VOLUME_BUSY = 10;
+  /**
+   * Status/disposition moved
+   */
+  public static final int VOLUME_MOVED = 11;
+
+  protected Cell cell;
+  protected Server server;
+  protected Partition partition;
+
+  protected String name;
+
+  protected int id;
+  protected int readWriteID;
+  protected int readOnlyID;
+  protected int backupID;
+
+  protected long creationDate;
+  protected long lastAccessDate;
+  protected long lastUpdateDate;
+  protected long lastBackupDate;
+  protected long copyCreationDate;
+
+  protected int accessesSinceMidnight;
+  protected int fileCount;
+  protected int maxQuota;
+  protected int currentSize;
+  protected int status;
+  protected int disposition;
+  protected int type;
+
+  protected GregorianCalendar creationDateCal;
+  protected GregorianCalendar lastUpdateDateCal;
+  protected GregorianCalendar copyCreationDateCal;
+
+  protected boolean cachedInfo;
+
+  /**
+   * Constructs a new <CODE>Volume</CODE> object instance given the name of 
+   * the AFS volume and the AFS cell, represented by <CODE>partition</CODE>, 
+   * to which it belongs.   This does not actually
+   * create a new AFS volume, it just represents one.
+   * If <code>name</code> is not an actual AFS volume, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(int)} method is explicitly called
+   * to create it.
+   *
+   * @param name       the name of the volume to represent 
+   * @param partition  the partition on which the volume resides
+   * @exception AFSException      If an error occurs in the native code
+   */
+  public Volume( String name, Partition partition ) throws AFSException
+  {
+    this.partition = partition;
+    this.server = partition.getServer();
+    this.cell = server.getCell();
+    this.name = name;
+    
+    creationDateCal = null;
+    lastUpdateDateCal = null;
+    copyCreationDateCal = null;
+    
+    id = -1;
+    cachedInfo = false;
+  }
+
+  /**
+   * Constructs a new <CODE>Volume</CODE> object instance given the name of 
+   * the AFS volume and the AFS partition, represented by 
+   * <CODE>partition</CODE>, to which it belongs.    This does not actually
+   * create a new AFS volume, it just represents one.
+   * If <code>name</code> is not an actual AFS volume, exceptions
+   * will be thrown during subsequent method invocations on this 
+   * object, unless the {@link #create(int)} method is explicitly called
+   * to create it.  Note that if the volume doesn't exist and 
+   * <code>preloadAllMembers</code> is true, an exception will be thrown.
+   *
+   * <P> This constructor is ideal for point-in-time representation and 
+   * transient applications. It ensures all data member values are set 
+   * and available without calling back to the filesystem at the first request
+   * for them.  Use the {@link #refresh()} method to address any coherency 
+   * concerns.
+   *
+   * @param name               the name of the volume to represent 
+   * @param partition          the partition on which the volume resides.
+   * @param preloadAllMembers  true will ensure all object members are set 
+   *                           upon construction; otherwise members will be 
+   *                           set upon access, which is the default behavior.
+   * @exception AFSException      If an error occurs in the native code
+   * @see #refresh
+   */
+  public Volume( String name, Partition partition, boolean preloadAllMembers ) 
+      throws AFSException
+  {
+    this(name, partition);
+    if (preloadAllMembers) refresh(true);
+  }
+  
+  /**
+   * Creates a blank <code>Volume</code> given the cell to which the volume
+   * belongs, the server on which the partition resides, and 
+   * the partition on which the volume resides. This blank 
+   * object can then be passed into other methods to fill out its properties. 
+   *
+   * @exception AFSException      If an error occurs in the native code
+   * @param cell       the cell to which the server belongs.
+   * @param server     the server on which the partition resides
+   * @param partition  the partition on which the volume resides
+   */
+  Volume( Partition partition ) throws AFSException
+  {
+    this( null, partition );
+  }
+
+  /*-------------------------------------------------------------------------*/
+
+  /**
+   * Refreshes the properties of this Volume object instance with values from 
+   * the AFS volume it represents.  All properties that have been initialized 
+   * and/or accessed will be renewed according to the values of the AFS volume
+   * this Volume object instance represents.
+   *
+   * <P>Since in most environments administrative changes can be administered
+   * from an AFS command-line program or an alternate GUI application, this
+   * method provides a means to refresh the Java object representation and
+   * thereby ascertain any possible modifications that may have been made
+   * from such alternate administrative programs.  Using this method before
+   * an associated instance accessor will ensure the highest level of 
+   * representative accuracy, accommodating changes made external to the
+   * Java application space.  If administrative changes to the underlying AFS 
+   * system are only allowed via this API, then the use of this method is 
+   * unnecessary.
+   * 
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void refresh() throws AFSException
+  {
+    refresh(false);
+  }
+
+  /**
+   * Refreshes the properties of this Volume object instance with values from 
+   * the AFS volume it represents.  If <CODE>all</CODE> is <CODE>true</CODE> 
+   * then <U>all</U> of the properties of this Volume object instance will be 
+   * set, or renewed, according to the values of the AFS volume it represents,
+   * disregarding any previously set properties.
+   *
+   * <P> Thus, if <CODE>all</CODE> is <CODE>false</CODE> then properties that 
+   * are currently set will be refreshed and properties that are not set will 
+   * remain uninitialized. See {@link #refresh()} for more information.
+   *
+   * @param all   if true set or renew all object properties; otherwise renew 
+   *              all set properties
+   * @exception AFSException  If an error occurs in the native code
+   * @see #refresh()
+   */
+  protected void refresh(boolean all) throws AFSException
+  {
+    if (all || cachedInfo) refreshInfo();
+  }
+
+  /**
+   * Refreshes the information fields of this <code>Volume</code> to reflect 
+   * the current state of the AFS volume. These include the last update time,
+   * file count, etc.
+   *
+   * @exception AFSException      If an error occurs in the native code
+   */
+  protected void refreshInfo() throws AFSException
+  {
+    getVolumeInfo( cell.getCellHandle(), server.getVosHandle(), 
+                  partition.getID(), getID(), this );
+    cachedInfo = true;
+    creationDateCal = null;
+    lastUpdateDateCal = null;
+    copyCreationDateCal = null;
+  }
+
+  /**
+   * Creates a new volume on the server and partition given upon construction.
+   *
+   * @param quota    the quota for the volume in K, 0 indicates an unlimited 
+   *                 quota
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void create( int quota ) throws AFSException
+  {
+    id = create( cell.getCellHandle(), server.getVosHandle(), 
+                partition.getID(), name, quota );
+    maxQuota = quota;
+  }
+
+  /**
+   * Creates a backup volume for this volume.
+   *
+   * @return  the <code>Volume</code> object representation for the
+   *          backup volume that was created
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public Volume createBackup( ) throws AFSException
+  {
+    createBackupVolume( cell.getCellHandle(), getID() );
+    return new Volume( name + ".backup", partition );
+  }
+
+  /**
+   * Creates a readonly site for this volume on the specified server and 
+   * partition.  Automatically releases the volume.
+   *
+   * @param sitePartition   the partition on which the readonly volume is 
+   *                        to reside
+   *
+   * @return the <code>Volume</code> representation for the
+   *         read-only volume that was created
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public Volume createReadOnly( Partition sitePartition ) 
+      throws AFSException
+  {
+    Server siteServer = sitePartition.getServer();
+    createReadOnlyVolume( cell.getCellHandle(), siteServer.getVosHandle(), 
+                         sitePartition.getID(), getID() );
+    release( false );
+    return new Volume( name + ".readonly", sitePartition );
+  }
+
+  /**
+   * Deletes the volume from the cell.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void delete() throws AFSException
+  {
+    delete( cell.getCellHandle(), server.getVosHandle(), partition.getID(), 
+           getID() );
+    name = null;
+    creationDateCal = null;
+    lastUpdateDateCal = null;
+    copyCreationDateCal = null;
+    cell = null;
+    server = null;
+    partition = null;
+  }
+
+  /**
+   * Releases this volume, which updates the read-only copies of it.
+   *
+   * <P> This method will force a complete release; a complete release updates
+   * all read-only sites even if the VLDB entry has a flag.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void release() throws AFSException
+  {
+    this.release( true );
+  }
+
+  /**
+   * Releases this volume, which updates the read-only copies of it.
+   *
+   * @param forceComplete   whether or not to force a complete release; 
+   *                        a complete release updates all read-only sites 
+   *                        even if the VLDB entry has a flag
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void release( boolean forceComplete ) throws AFSException
+  {
+    release( cell.getCellHandle(), getID(), forceComplete );
+  }
+
+  /**
+   * Dumps this volume to a file. If you use the dumpSince argument you will 
+   * create an incremental dump, but you can leave it <code>null</code> 
+   * for a full dump.
+   *
+   * @param fileName    the path name of the file on the client machine to 
+   *                    which to dump this volume
+   * @param dumpSince   dump only files that have been modified more recently 
+   *                    than this date
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void dump( String fileName, GregorianCalendar dumpSince ) 
+      throws AFSException
+  {
+    int startTime = 0;
+    if ( dumpSince != null ) {
+      startTime = (int) ((dumpSince.getTime().getTime())/((long) 1000));
+    }
+    dump( cell.getCellHandle(), server.getVosHandle(), partition.getID(), 
+         getID(), startTime, fileName );
+  }
+
+  /**
+   * Dumps this volume to a file. Creates a full dump.
+   *
+   * @param fileName    the path name of the file to which to dump this volume
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void dump( String fileName ) throws AFSException
+  {
+    this.dump( fileName, null );
+  }
+
+  /**
+   * Restores a file to this volume.  Note that this does not have to be an 
+   * existing volume in order to be restored - you may create a 
+   * <code>Volume</code> as a volume that doesn't yet exist and then restore 
+   * a file to it.  Or you can restore over an existing volume.  If a new 
+   * volume is being created with this method, the id will be automatically 
+   * assigned.
+   *
+   * @param fileName    the path name of the file on the client machine from 
+   * which to restore this volume
+   * @param incremental   if true, restores an incremental dump over an 
+   *                      existing volume
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void restore( String fileName, boolean incremental ) 
+      throws AFSException
+  {
+      restore( fileName, incremental, 0 );
+  }
+
+  /**
+   * Restores a file to this volume.  Note that this does not have to be an 
+   * existing volume in order to be restored - you may create a 
+   * <code>Volume</code> as a volume that doesn't yet exist and then restore 
+   * a file to it.  Or you can restore over an existing volume.
+   *
+   * @param fileName    the path name of the file on the client machine from 
+   * which to restore this volume
+   * @param incremental   if true, restores an incremental dump over an 
+   *                      existing volume
+   * @param id     the id to assign this volume
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void restore( String fileName, boolean incremental, int id ) 
+      throws AFSException
+  {
+    restore( cell.getCellHandle(), server.getVosHandle(), partition.getID(), 
+            id, name, fileName, incremental );
+  }
+
+  /**
+   * Mounts this volume, bringing it online and making it accessible.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void mount( ) throws AFSException
+  {
+    mount( server.getVosHandle(), partition.getID(), getID(), 0, true );
+  }
+
+  /**
+   * Unmounts this volume, bringing it offline and making it inaccessible.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void unmount( ) throws AFSException
+  {
+    unmount( server.getVosHandle(), partition.getID(), getID() );
+  }
+
+  /**
+   * Locks the VLDB enrty for this volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void lock( ) throws AFSException
+  {
+    lock( cell.getCellHandle(), getID() );
+  }
+
+  /**
+   * Unlocks the VLDB entry for this volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void unlock( ) throws AFSException
+  {
+    unlock( cell.getCellHandle(), getID() );
+  }
+
+  /**
+   * Moves this volume to the specified partition (which indirectly 
+   * specifies a new server, as well).  Caution: This will remove any backup 
+   * volumes at the original site.
+   *
+   * @param newPartition   the partition to which to move the volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void moveTo( Partition newPartition ) throws AFSException
+  {
+    Server newServer = newPartition.getServer();
+    move( cell.getCellHandle(), server.getVosHandle(), partition.getID(), 
+         newServer.getVosHandle(), newPartition.getID(), getID() );
+
+    server = newServer;
+    partition = newPartition;
+  }
+
+  /**
+   * Renames this volume.
+   *
+   * @param newName   the new name for this volume
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void rename( String newName ) throws AFSException
+  {
+    rename( cell.getCellHandle(), getID(), newName );
+    name = newName;
+  }
+
+  /**
+   * Salvages (restores consistency to) this volume.  Uses default values for
+   * most salvager options in order to simplify the API.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   */ 
+  public void salvage() throws AFSException
+  {
+    Server.salvage( cell.getCellHandle(), server.getBosHandle(), 
+                   partition.getName(), name, 4, null, null, false, false, 
+                   false, false, false, false );
+  }
+
+  /**
+   * Creates a read-write mount point for this volume.  Does not ensure the 
+   * volume already exists.
+   *
+   * @param directory   the name of the directory where this volume 
+   * should be mounted
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void createMountPoint( String directory ) throws AFSException
+  {
+    createMountPoint(directory, true); 
+  }
+
+  /**
+   * Creates a mount point for this volume.  Does not ensure the volume 
+   * already exists.
+   *
+   * @param directory   the name of the directory where this volume should be 
+   *                    mounted
+   * @param readWrite   whether or not this mount point should be read-write
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public void createMountPoint( String directory, boolean readWrite ) 
+    throws AFSException
+  {
+    Cell.createMountPoint( cell.getCellHandle(), directory, getName(), 
+                          readWrite, false ); 
+  }
+
+  //////////////// accessors:  ////////////////////////
+
+  /**
+   * Returns the name of this volume.
+   *
+   * @return the name of this volume
+   */
+  public String getName()
+  {
+    return name;
+  }
+
+  /**
+   * Returns this volume's hosting partition.
+   *
+   * @return this volume's partition
+   */
+  public Partition getPartition()
+  {
+    return partition;
+  }
+
+  /**
+   * Returns the id of this volume.   
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the id of this volume
+   */
+  public int getID() throws AFSException
+  {
+    if( id == -1 && name != null ) {
+       String nameNoSuffix;
+       if( name.endsWith( "backup" ) ) {
+           type = VOLUME_TYPE_BACKUP;
+           nameNoSuffix = name.substring( 0, name.lastIndexOf( '.' ) );
+       } else if( name.endsWith( "readonly" ) ) {
+           type = VOLUME_TYPE_READ_ONLY;
+           nameNoSuffix = name.substring( 0, name.lastIndexOf( '.' ) );
+       } else {
+           type = VOLUME_TYPE_READ_WRITE;
+           nameNoSuffix = name;
+       }
+       id = translateNameToID( cell.getCellHandle(), 
+                                    nameNoSuffix, type );
+    }
+    return id;
+  }
+
+  /**
+   * Returns the read-write ID of this volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the read-write id
+   */
+  public int getReadWriteID() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    return readWriteID;
+  }
+
+  /**
+   * Returns the read-only ID of this volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the read-only id
+   */
+  public int getReadOnlyID() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    return readOnlyID;
+  }
+
+  /**
+   * Returns the backup ID of this volume
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the backup id
+   */
+  public int getBackupID() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    return backupID;
+  }
+
+  /**
+   * Returns the date the volume was created
+   *
+   * @return the date the volume was created
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public GregorianCalendar getCreationDate() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    if( creationDateCal == null ) {
+      // make it into a date . . .
+      creationDateCal = new GregorianCalendar();
+      Date d = new Date( creationDate*1000 );
+      creationDateCal.setTime( d );
+    }
+    return creationDateCal;
+  }
+
+  /**
+   * Returns the date the volume was last updated.
+   * After this method is called once, it saves the date 
+   * and returns that date on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the date the volume was last updated
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public GregorianCalendar getLastUpdateDate() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    if( lastUpdateDateCal == null ) {
+      // make it into a date . . .
+      lastUpdateDateCal = new GregorianCalendar();
+      Date d = new Date( lastUpdateDate*1000 );
+      lastUpdateDateCal.setTime( d );
+    }
+    return lastUpdateDateCal;
+  }
+
+  /**
+   * Returns the date the volume was copied.
+   * After this method is called once, it saves the date 
+   * and returns that date on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @return the date the volume was copied
+   * @exception AFSException  If an error occurs in the native code
+   */
+  public GregorianCalendar getCopyCreationDate() throws AFSException
+  {
+    if( !cachedInfo ) {
+      refreshInfo();
+    }
+    if( copyCreationDateCal == null ) {
+      // make it into a date . . .
+      copyCreationDateCal = new GregorianCalendar();
+      Date d = new Date( copyCreationDate*1000 );
+      copyCreationDateCal.setTime( d );
+    }
+    return copyCreationDateCal;
+  }
+
+  /**
+   * Returns the number of accesses since midnight.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the number of accesses since midnight
+   */
+  public int getAccessesSinceMidnight() throws AFSException
+  {
+    if( !cachedInfo ) {
+         refreshInfo();
+    }
+    return accessesSinceMidnight;
+  }
+
+  /**
+   * Returns file count.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the file count
+   */
+  public int getFileCount() throws AFSException
+  {
+    if( !cachedInfo ) {
+         refreshInfo();
+    }
+    return fileCount;
+  }
+
+  /**
+   * Returns current volume size in K.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the current volume size in K
+   */
+  public int getCurrentSize() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return currentSize;
+  }
+
+  /**
+   * Returns the difference between quota and current volume size (in K).
+   *
+   * <P> Please note: the product of this method is <U>not</U> saved.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return the current free space in K
+   */
+  public int getTotalFreeSpace() throws AFSException
+  {
+    if ( !cachedInfo ) refreshInfo();
+    return (maxQuota - currentSize);
+  }
+
+  /**
+   * Returns this volume's quota, expressed in kilobyte blocks (1024 
+   * kilobyte blocks equal one megabyte). After this method is called once, 
+   * it saves the value and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * <P><B>Note:</B> A quota value of zero, "0", grants an unlimited quota
+   * in AFS.  Consequently, to avoid delusion this method will throw an 
+   * {@link AFSException} if the returning value is zero.
+   *
+   * @exception AFSException  If an error occurs in the native code or
+   *                               this volume's quota is configured as
+   *                               unlimited.
+   * @return the volume quota in K
+   * @see #isQuotaUnlimited()
+   */
+  public int getQuota() throws AFSException
+  {
+    if ( !cachedInfo ) refreshInfo();
+    if (maxQuota == 0) {
+      throw new AFSException("Volume with id " + id + 
+                                  " has an unlimited quota configured.", 0);
+    }
+    return maxQuota;
+  }
+
+  /**
+   * Tests whether this volume's quota is configured as unlimited.
+   *
+   * <P>After this method is called once, it saves the value and returns 
+   * that value on subsequent calls, until the {@link #refresh()} 
+   * method is called and a more current value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return <CODE>true</CODE> if this volume's quota is configured as
+   *         unlimited; otherwise <CODE>false</CODE>.
+   * @see #getQuota()
+   */
+  public boolean isQuotaUnlimited() throws AFSException
+  {
+    if ( !cachedInfo ) refreshInfo();
+    return (maxQuota == 0);
+  }
+
+  /**
+   * Returns volume status. Possible values are:<ul>
+   *       <li>{@link #VOLUME_OK}</li>
+   *       <li>{@link #VOLUME_SALVAGE}</li>
+   *       <li>{@link #VOLUME_NO_VNODE}</li>
+   *       <li>{@link #VOLUME_NO_VOL}</li>
+   *       <li>{@link #VOLUME_VOL_EXISTS}</li>
+   *       <li>{@link #VOLUME_NO_SERVICE}</li>
+   *       <li>{@link #VOLUME_OFFLINE}</li>
+   *       <li>{@link #VOLUME_ONLINE}</li>
+   *       <li>{@link #VOLUME_DISK_FULL}</li>
+   *       <li>{@link #VOLUME_OVER_QUOTA}</li>
+   *       <li>{@link #VOLUME_BUSY}</li>
+   *       <li>{@link #VOLUME_MOVED}</li></ul>
+   * Typical value is VOLUME_OK.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return volume status
+   */
+  public int getStatus() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return status;
+  }
+
+  /**
+   * Returns volume disposition. Possible values are:<ul>
+   *       <li>{@link #VOLUME_OK}</li>
+   *       <li>{@link #VOLUME_SALVAGE}</li>
+   *       <li>{@link #VOLUME_NO_VNODE}</li>
+   *       <li>{@link #VOLUME_NO_VOL}</li>
+   *       <li>{@link #VOLUME_VOL_EXISTS}</li>
+   *       <li>{@link #VOLUME_NO_SERVICE}</li>
+   *       <li>{@link #VOLUME_OFFLINE}</li>
+   *       <li>{@link #VOLUME_ONLINE}</li>
+   *       <li>{@link #VOLUME_DISK_FULL}</li>
+   *       <li>{@link #VOLUME_OVER_QUOTA}</li>
+   *       <li>{@link #VOLUME_BUSY}</li>
+   *       <li>{@link #VOLUME_MOVED}</li></ul>
+   * Typical value is VOLUME_ONLINE.
+   * After this method is called once, it saves the value 
+   * and returns that value on subsequent calls, 
+   * until the {@link #refresh()} method is called and a more current 
+   * value is obtained.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return volume disposition
+   */
+  public int getDisposition() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return disposition;
+  }
+
+  /**
+   * Returns volume type. Possible values are:<ul>
+   *       <li>{@link #VOLUME_TYPE_READ_WRITE}</li>
+   *       <li>{@link #VOLUME_TYPE_READ_ONLY}</li>
+   *       <li>{@link #VOLUME_TYPE_BACKUP}</li></ul>
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @return volume type
+   */
+  public int getType() throws AFSException
+  {
+    if( !cachedInfo ) refreshInfo();
+    return type;
+  }
+
+  //////////////// mutators:  ////////////////////////
+
+  /**
+   * Sets quota of volume, 0 denotes an unlimited quota.
+   *
+   * @exception AFSException  If an error occurs in the native code
+   * @param quota  the new volume quota in K (0 for unlimited)
+   */
+  public void setQuota( int quota ) throws AFSException
+  {
+    this.changeQuota( cell.getCellHandle(), server.getVosHandle(), 
+                     partition.getID(), getID(), quota );
+    maxQuota = quota;
+  }
+
+  /////////////// custom information methods ////////////////////
+
+  /**
+   * Returns a <code>String</code> representation of this <code>Volume</code>.
+   * Contains the information fields.
+   *
+   * @return a <code>String</code> representation of the <code>Volume</code>
+   */
+  protected String getInfo()
+  {
+      String r;
+      try {
+       r = "Volume: " + name + "\tid: " + getID() + "\n";
+       
+       r += "\tread-write id: " + getReadWriteID() + "\tread-only id: " 
+         + getReadOnlyID() + "\n";
+       r += "\tbackup id: " + getBackupID() + "\n";
+
+       r += "\tcreation date: " + getCreationDate().getTime() + "\n";
+       r += "\tlast update date: " + getLastUpdateDate().getTime() + "\n";
+       r += "\tcopy creation date: " + getCopyCreationDate().getTime() + "\n";
+       r += "\taccesses since midnight: " + getAccessesSinceMidnight() + "\n";
+       r += "\tfile count: " + getFileCount() + "\n";
+       r += "\tcurrent size: " + getCurrentSize() + " K\tquota: " + 
+         getQuota() + " K\n";
+       r += "\tstatus: ";
+       switch( getStatus() ) {
+       case VOLUME_OK: 
+           r += "OK";
+           break;
+       default:
+           r += "OTHER";
+       }
+
+       r += "\tdisposition: ";
+       switch( getDisposition() ) {
+       case VOLUME_ONLINE: 
+           r += "ONLINE";
+           break;
+       default:
+           r += "OTHER - " + getDisposition();
+       }
+       r += "\n";
+
+       r += "\ttype: ";
+       switch( getType() ) {
+       case VOLUME_TYPE_READ_WRITE: 
+           r += "read-write";
+           break;
+       case VOLUME_TYPE_READ_ONLY: 
+           r += "read-only";
+           break;
+       case VOLUME_TYPE_BACKUP: 
+           r += "backup";
+           break;
+       default:
+           r += "OTHER";
+       }
+       r += "\n";
+
+      } catch( Exception e ) {
+       return e.toString();
+      }
+      return r;
+  }
+
+  /////////////// custom override methods ////////////////////
+
+  /**
+   * Compares two Volume objects respective to their names and does not
+   * factor any other attribute.    Alphabetic case is significant in 
+   * comparing names.
+   *
+   * @param     volume    The Volume object to be compared to this Volume 
+   *                      instance
+   * 
+   * @return    Zero if the argument is equal to this Volume's name, a
+   *           value less than zero if this Volume's name is
+   *           lexicographically less than the argument, or a value greater
+   *           than zero if this Volume's name is lexicographically
+   *           greater than the argument
+   */
+  public int compareTo(Volume volume)
+  {
+    return this.getName().compareTo(volume.getName());
+  }
+
+  /**
+   * Comparable interface method.
+   *
+   * @see #compareTo(Volume)
+   */
+  public int compareTo(Object obj)
+  {
+    return compareTo((Volume)obj);
+  }
+
+  /**
+   * Tests whether two <code>Volume</code> objects are equal, based on their 
+   * names and hosting partition.
+   *
+   * @param otherVolume   the Volume to test
+   * @return whether the specifed Volume is the same as this Volume
+   */
+  public boolean equals( Volume otherVolume )
+  {
+    return ( name.equals(otherVolume.getName()) ) &&
+           ( this.getPartition().equals(otherVolume.getPartition()) );
+  }
+
+  /**
+   * Returns the name of this <CODE>Volume</CODE>
+   *
+   * @return the name of this <CODE>Volume</CODE>
+   */
+  public String toString()
+  {
+    return getName();
+  }
+
+
+  /////////////// native methods ////////////////////
+
+  /**
+   * Fills in the information fields of the provided <code>Volume</code>.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which the volume 
+   *                      resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volume 
+   *                    resides
+   * @param volId  the numeric id of the volume for which to get the info
+   * @param theVolume   the {@link Volume Volume} object in which to fill in 
+   *                    the information
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void getVolumeInfo( int cellHandle, int serverHandle,
+                                             int partition, int volId, 
+                                             Volume theVolume ) 
+       throws AFSException;
+
+  /**
+   * Creates a volume on a particular partition.
+   *
+   * @param cellHandle    the handle of the cell in which to create the volume
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which to create 
+   *                      the volume
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which to create 
+   *                    the volume
+   * @param volumeName   the name of the volume to create
+   * @param quota    the amount of space (in KB) to set as this volume's quota
+   * @return the numeric ID assigned to the volume
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native int create( int cellHandle, int serverHandle, 
+                                     int partition, String volumeName, 
+                                     int quota ) 
+       throws AFSException;
+
+  /**
+   * Deletes a volume from a particular partition.
+   *
+   * @param cellHandle    the handle of the cell in which to delete the volume
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server from which to delete 
+   *                      the volume
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition from which to delete 
+   *                    the volume
+   * @param volId   the numeric id of the volume to delete
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void delete( int cellHandle, int serverHandle, 
+                                      int partition, int volId ) 
+       throws AFSException;
+
+  /**
+   * Creates a backup volume for the specified regular volume.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @param volId  the numeric id of the volume for which to create a backup 
+   *               volume
+   * @see Cell#getCellHandle
+   */
+  protected static native void createBackupVolume( int cellHandle, int volId )
+       throws AFSException;
+
+  /**
+   * Creates a read-only volume for the specified regular volume.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @param serverHandle  the vos handle of the server on which the read-only 
+   *                      volume is to reside 
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the read-only 
+                        volume is to reside 
+   * @param volId  the numeric id of the volume for which to create a read-only volume
+   * @see Cell#getCellHandle
+   */
+  protected static native void createReadOnlyVolume( int cellHandle, 
+                                                    int serverHandle, 
+                                                    int partition, int volId )
+       throws AFSException;
+
+  /**
+   * Deletes a read-only volume for the specified regular volume.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @param serverHandle  the vos handle of the server on which the read-only 
+   *                      volume residea 
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the read-only
+   *                     volume resides 
+   * @param volId  the numeric read-write id of the volume for which to 
+   *               delete the read-only volume
+   * @see Cell#getCellHandle
+   */
+  protected static native void deleteReadOnlyVolume( int cellHandle, 
+                                                    int serverHandle, 
+                                                    int partition, int volId )
+       throws AFSException;
+
+  /**
+   * Changes the quota of the specified volume.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which the volume 
+   *                      resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volume 
+   *                    resides
+   * @param volId  the numeric id of the volume for which to change the quota
+   * @param newQuota    the new quota (in KB) to assign the volume
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void changeQuota( int cellHandle, int serverHandle, 
+                                           int partition, int volId, 
+                                           int newQuota ) 
+       throws AFSException;
+
+  /**
+   * Move the specified volume to a different site.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param fromServerHandle  the vos handle of the server on which the volume
+   *                          currently resides
+   * @see Server#getVosServerHandle
+   * @param fromPartition   the numeric id of the partition on which the volume
+   *                        currently resides
+   * @param toServerHandle  the vos handle of the server to which the volume 
+   *                        should be moved
+   * @param toPartition   the numeric id of the partition to which the volume 
+   *                      should be moved
+   * @param volId  the numeric id of the volume to move
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void move( int cellHandle, int fromServerHandle, 
+                                    int fromPartition, int toServerHandle, 
+                                    int toPartition, int volId ) 
+       throws AFSException;
+
+  /**
+   * Releases the specified volume that has readonly volume sites.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @param volId  the numeric id of the volume to release
+   * @param forceComplete  whether or not to force a complete release
+   * @see Cell#getCellHandle
+   */
+  protected static native void release( int cellHandle, int volId, 
+                                       boolean forceComplete )
+       throws AFSException;
+
+  /**
+   * Dumps the specified volume to a file.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which the volume 
+   *                      resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the 
+   *                    volume resides
+   * @param volId  the numeric id of the volume to dump
+   * @param startTime   files with a modification time >= to this time will 
+   *                    be dumped
+   * @param dumpFile   the full path of the file to which to dump
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void dump( int cellHandle, int serverHandle, 
+                                    int partition, int volId, int startTime, 
+                                    String dumpFile ) 
+       throws AFSException;
+
+  /**
+   * Restores the specified volume from a dump file.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param serverHandle  the vos handle of the server on which the volume is 
+   *                      to reside
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volume is
+   *                    to reside
+   * @param volId  the numeric id to assign the restored volume (can be 0)
+   * @param volumeName  the name of the volume to restore as
+   * @param dumpFile   the full path of the dump file from which to restore
+   * @param incremental  if true, restores an incremental dump over an existing
+   *                     volume (server and partition values must correctly 
+   *                     indicate the current position of the existing volume),
+   *                     otherwise restores a full dump
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void restore( int cellHandle, int serverHandle, 
+                                       int partition, int volId, 
+                                       String volumeName, String dumpFile, 
+                                       boolean incremental ) 
+       throws AFSException;
+
+  /**
+   * Renames the specified read-write volume.
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param volId  the numeric id of the read-write volume to rename
+   * @param newVolumeName  the new name for the volume
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void rename( int cellHandle, int volId, 
+                                      String newVolumeName ) 
+       throws AFSException;
+
+  /**
+   * "Mounts" the specified volume, bringing it online.
+   *
+   * @param serverHandle  the vos handle of the server on which the volume 
+   *                      resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volume 
+   *                    resides
+   * @param volId  the numeric id of the volume to bring online
+   * @param sleepTime  ?  (not sure what this is yet, possibly a time to wait 
+   *                      before brining it online)
+   * @param offline   ?  (not sure what this is either, probably the current 
+   *                     status of the volume -- busy or offline)
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void mount( int serverHandle, int partition, 
+                                     int volId, int sleepTime, 
+                                     boolean offline ) 
+       throws AFSException;
+
+  /**
+   * "Unmounts" the specified volume, bringing it offline.
+   *
+   * @param serverHandle  the vos handle of the server on which the volume 
+   *                      resides
+   * @see Server#getVosServerHandle
+   * @param partition   the numeric id of the partition on which the volume 
+   *                    resides
+   * @param volId  the numeric id of the volume to bring offline
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void unmount( int serverHandle, int partition, 
+                                       int volId ) 
+       throws AFSException;
+
+  /**
+   * Locks the VLDB entry specified volume
+   *
+   * @param cellHandle  the handle of the cell on which the volume resides
+   * @see Cell#getCellHandle
+   * @param volId  the numeric id of the volume to lock
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void lock( int cellHandle, int volId ) 
+       throws AFSException;
+
+  /**
+   * Unlocks the VLDB entry of the specified volume
+   *
+   * @param cellHandle  the handle of the cell on which the volume resides
+   * @see Cell#getCellHandle
+   * @param volId  the numeric id of the volume to unlock
+   * @exception AFSException   If an error occurs in the native code
+   */
+  protected static native void unlock( int cellHandle, int volId ) 
+       throws AFSException;
+
+  /**
+   * Translates a volume name into a volume id
+   *
+   * @param cellHandle    the handle of the cell to which the volume belongs
+   * @see Cell#getCellHandle
+   * @param name  the name of the volume in question, cannot end in backup or
+   *              readonly
+   * @param type  the type of volume: read-write, read-only, or backup.  
+   *              Acceptable values are:<ul>
+   *              <li>{@link #VOLUME_TYPE_READ_WRITE}</li>
+   *              <li>{@link #VOLUME_TYPE_READ_ONLY}</li>
+   *              <li>{@link #VOLUME_TYPE_BACKUP}</li></ul> 
+   * @return   the id of the volume in question
+   * @exception AFSException  If an error occurs in the native code
+   */
+  protected static native int translateNameToID( int cellHandle, String name, 
+                                                int volumeType )
+    throws AFSException;
+
+  /**
+   * Reclaims all memory being saved by the volume portion of the native 
+   * library. This method should be called when no more <code>Volume</code> 
+   * objects are expected to be used.
+   */
+  protected static native void reclaimVolumeMemory();
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/ACL.c b/src/JAVA/libjafs/ACL.c
new file mode 100644 (file)
index 0000000..7770bb6
--- /dev/null
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_ACL.h"
+
+#include <errno.h>
+#include <stdio.h>
+#include <sys/ioctl.h>
+#include <afs/vice.h>
+#include <afs/venus.h>
+#include <afs/afs_args.h>
+#include <afs/afs_osi.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+#define ACL_LEN      1024
+
+extern int errno;
+
+/**
+ * Returns a formatted string representing the ACL for the specified path.
+ *
+ * path     the directory path
+ * returns NULL if an exception is encountered.
+ */
+char* getACL(char *path)
+{
+    struct ViceIoctl params;
+    char *buffer;
+
+    params.in = NULL;
+    params.in_size = 0;
+    buffer = (char*) malloc(ACL_LEN);
+
+    if (!buffer) {
+      fprintf(stderr, "ERROR: ACL::getACL -> could not allocate buffer\n");
+      return NULL;
+    }
+
+    params.out = buffer;
+    params.out_size = ACL_LEN; 
+
+    if(call_syscall(AFSCALL_PIOCTL, path, VIOCGETAL, &params, 1)) {
+      fprintf(stderr, "ERROR: ACL::getACL -> VIOCGETAL failed: %d\n", errno);
+      free(buffer);
+      return NULL;
+    }
+
+    return params.out;
+}
+
+/**
+ * Sets the ACL for the specified path using the provided string
+ * representation.
+ *
+ * path       the directory path
+ * aclString  string representation of ACL to be set
+ * returns TRUE if the operation succeeds; otherwise FALSE;
+ */
+jboolean setACL(char *path, char *aclString)
+{
+    struct ViceIoctl params;
+    char *redirect, *parentURI, *cptr;
+
+    params.in = aclString;
+    params.in_size = strlen(aclString) + 1;
+    params.out = NULL;
+    params.out_size = 0;
+
+    if(call_syscall(AFSCALL_PIOCTL, path, VIOCSETAL, &params, 1)) {
+      fprintf(stderr, "ERROR: ACL::setACL -> VIOCSETAL failed: %d\n", errno);
+      return JNI_FALSE;
+    }
+
+    return JNI_TRUE;
+}
+
+/**
+ * Returns a formatted string representing the ACL for the specified path.
+ *
+ * The string format is in the form of a ViceIoctl and is as follows:
+ * printf("%d\n%d\n", positiveEntriesCount, negativeEntriesCount);
+ * printf("%s\t%d\n", userOrGroupName, rightsMask);
+ *
+ * path     the directory path
+ * returns NULL if an exception is encountered.
+ */
+JNIEXPORT jstring JNICALL Java_org_openafs_jafs_ACL_getACLString
+  (JNIEnv *env, jobject obj, jstring pathUTF)
+{
+    char *path, *acl;
+    jstring answer = NULL;
+
+    path = (char*) (*env)->GetStringUTFChars(env, pathUTF, 0);
+
+    if(path == NULL) {
+      fprintf(stderr, "ERROR: ACL::getACLString ->");
+      fprintf(stderr, "GetStringUTFChars() returned path = NULL\n");
+      throwMessageException( env, "Path is NULL" ); 
+      return NULL;
+    }
+
+    acl = getACL(path);
+    (*env)->ReleaseStringUTFChars(env, pathUTF, path);
+
+    if(acl) {
+      answer =  (*env) -> NewStringUTF(env, acl);
+      free(acl);
+    } else {
+      throwAFSException( env, errno );
+    }
+
+    return answer;
+}
+
+/**
+ * Sets the ACL for the specified path using the provided string
+ * representation.
+ *
+ * The string format is in the form of a ViceIoctl and is as follows:
+ * printf("%d\n%d\n", positiveEntriesCount, negativeEntriesCount);
+ * printf("%s\t%d\n", userOrGroupName, rightsMask);
+ *
+ * path       the directory path
+ * aclString  string representation of ACL to be set
+ * throws an afsAdminExceptionName if an internal exception is encountered.
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_ACL_setACLString
+  (JNIEnv *env, jobject obj, jstring pathUTF, jstring aclStringUTF)
+{
+    char *path, *aclString;
+
+    if(!pathUTF) {
+      fprintf(stderr, "ERROR: ACL::setACLString -> pathUTF == NULL\n");
+      throwMessageException( env, "pathUTF == NULL" );
+      return;
+    }
+
+    path = (char*) (*env)->GetStringUTFChars(env, pathUTF, 0);
+
+    if(path == NULL) {
+      fprintf(stderr, "ERROR: ACL::setACLString -> failed to get path\n");
+      throwMessageException( env, "Failed to get path" );
+      return;
+    }
+
+    if(!aclStringUTF) {
+      fprintf(stderr, "ERROR: ACL::setACLString -> aclStringUTF == NULL\n");
+      throwMessageException( env, "aclStringUTF == NULL" ); 
+      return;
+    }
+
+    aclString = (char*) (*env)->GetStringUTFChars(env, aclStringUTF, 0);
+
+    if(aclString == NULL) {
+      fprintf(stderr, "ERROR: ACL::setACLString -> failed to get aclString\n");
+      (*env)->ReleaseStringUTFChars(env, pathUTF, path);
+      throwMessageException( env, "aclString == NULL" ); 
+      return;
+    }
+
+    if (!setACL(path, aclString)) {
+      throwAFSException( env, errno );
+    }
+
+    (*env)->ReleaseStringUTFChars(env, pathUTF, path);
+    (*env)->ReleaseStringUTFChars(env, aclStringUTF, aclString);
+}
+
+
+
diff --git a/src/JAVA/libjafs/AdminToken.c b/src/JAVA/libjafs/AdminToken.c
new file mode 100644 (file)
index 0000000..b1d3358
--- /dev/null
@@ -0,0 +1,415 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Cell.h"
+#include "org_openafs_jafs_Token.h"
+
+#include <stdio.h>
+#include <afs_kasAdmin.h>
+#include <afs_ptsAdmin.h>
+#include <afs_clientAdmin.h>
+#include <kautils.h>
+#include <cellconfig.h>
+#include <afs_AdminClientErrors.h>
+
+/**
+ * Static function used to initialize the client library and the 
+ * jafs library.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Token_initializeAdminClient(JNIEnv *env, jclass cls)
+{
+  afs_status_t ast;
+  if( !afsclient_Init( &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+}
+
+
+/**
+ * Authenticates the given user and password in the cell.  Returns
+ * a token that can be used to prove this authentication later.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * jcellName    the name of the cell in which to authenticate this user
+ * juserName    the name of the user to authenticate
+ * jpassword    the password of the user
+ * returns a token representing the authentication
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Token_getToken
+  (JNIEnv *env, jobject obj, jstring jcellName, jstring juserName,
+   jstring jpassword)
+{
+  afs_status_t ast;
+  const char *cellName;
+  const char *userName;
+  const char *password;
+  void *tokenHandle;
+  int rc;
+
+  // convert java strings
+  if( jcellName != NULL ) { 
+      cellName = (*env)->GetStringUTFChars(env, jcellName, 0);
+      if( !cellName ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return 0;    
+      }
+  } else {
+      cellName = NULL;
+  }
+  if( juserName != NULL ) {
+      userName = (*env)->GetStringUTFChars(env, juserName, 0);
+      if( !userName ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return 0;    
+      }
+  } else {
+      userName = NULL;
+  }
+  if( jpassword != NULL ) {
+      password = (*env)->GetStringUTFChars(env, jpassword, 0);
+      if( !password ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return 0;    
+      }
+  } else {
+      password = NULL;
+  }
+
+  if ( !(afsclient_TokenGetNew( cellName, userName, password, &tokenHandle, 
+                               &ast) ) ) {
+    // release converted strings
+      if( cellName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jcellName, cellName);
+      }
+      if( userName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( password != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jpassword, password);
+      }
+      throwAFSException( env, ast );
+      return 0;
+  }
+
+  // release converted strings
+  if( cellName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jcellName, cellName);
+  }
+  if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+  if( password != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpassword, password);
+  }
+
+  return (jint) tokenHandle;
+}
+
+/**
+ * Closes the given currently open token.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * tokenHandle   the token to close
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Token_close
+  (JNIEnv *env, jobject obj, jint tokenHandle)
+{
+    afs_status_t ast;
+
+    if( !afsclient_TokenClose( (void *) tokenHandle, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+    }
+}
+
+/**
+ * Opens a cell for administrative use, based on the token provided.  
+ * Returns a cell handle to be used by other methods as a means of 
+ * authentication.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * jcellName    the name of the cell for which to get the handle
+ * tokenHandle    a token handle previously returned by a call to getToken
+ * returns a handle to the open cell
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getCellHandle
+  (JNIEnv *env, jobject obj, jstring jcellName, jint tokenHandle)
+{
+  afs_status_t ast;
+  const char *cellName;
+  void *cellHandle;
+
+  if( jcellName != NULL ) {
+    cellName = (*env)->GetStringUTFChars(env, jcellName, 0);
+    if( !cellName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    cellName = NULL;
+  }
+
+  if( !afsclient_CellOpen( cellName, (void *) tokenHandle, 
+                          &cellHandle, &ast ) ) {
+    if( cellName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jcellName, cellName);
+    }  
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( cellName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jcellName, cellName);
+  }  
+  return (jint) cellHandle;
+}
+
+/**
+ * Closes the given currently open cell handle.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * cellHandle   the cell handle to close
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_closeCell (JNIEnv *env, jobject obj, 
+                                         jint cellHandle)
+{
+
+   afs_status_t ast;
+
+    if( !afsclient_CellClose( (void *) cellHandle, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+    }
+
+}
+
+/**
+ * Opens a server for administrative vos use, based on the cell handle 
+ * provided.  Returns a vos server handle to be used by other 
+ * methods as a means of identification.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * cellHandle    a cell handle previously returned by 
+ *                      a call to getCellHandle
+ * jserverName    the name of the server for which to retrieve 
+ *                      a vos handle
+ * returns a vos handle to the server
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getVosServerHandle
+  (JNIEnv *env, jobject obj, jint cellHandle, jstring jserverName)
+{
+  afs_status_t ast;
+  void *serverHandle;
+  // convert java string
+  const char *serverName;
+
+  if( jserverName != NULL ) {
+      serverName = (*env)->GetStringUTFChars(env, jserverName, 0);
+      if( !serverName ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      serverName = NULL;
+  }
+
+  if( !vos_ServerOpen( (void *) cellHandle, serverName, 
+                      (void **) &serverHandle, &ast ) ) {
+      if( serverName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jserverName, serverName);
+      }
+      throwAFSException( env, ast );
+      return 0;
+  }
+
+  // release converted string
+  if( serverName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jserverName, serverName);
+  }
+
+  return (jint) serverHandle;
+}
+
+/**
+ * Closes the given currently open vos server handle.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * vosServerHandle   the vos server handle to close
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_closeVosServerHandle
+  (JNIEnv *env, jobject obj, jint vosServerHandle)
+{
+    afs_status_t ast;
+
+    if( !vos_ServerClose( (void *) vosServerHandle, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+    }
+}
+
+/**
+ * Opens a server for administrative bos use, based on the cell handle 
+ * provided.  Returns a bos server handle to be used by other methods 
+ * as a means of identification.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * cellHandle    a cell handle previously returned by a call 
+ *                      to getCellHandle
+ * jserverName    the name of the server for which to retrieve 
+ *                      a bos handle
+ * returns a bos handle to the server
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getBosServerHandle
+  (JNIEnv *env, jobject obj, jint cellHandle, jstring jserverName)
+{
+  afs_status_t ast;
+  void *serverHandle;
+  // convert java string
+  const char *serverName;
+
+  if( jserverName != NULL ) {
+      serverName = (*env)->GetStringUTFChars(env, jserverName, 0);
+      if( !serverName ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      serverName = NULL;
+  }
+
+  if( !bos_ServerOpen( (void *) cellHandle, serverName, 
+                      (void **) &serverHandle, &ast ) ) {
+      if( serverName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jserverName, serverName);
+      }
+      throwAFSException( env, ast );
+      return 0;
+  }
+
+  // release converted string
+  if( serverName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jserverName, serverName);
+  }
+
+  return (jint) serverHandle;
+}
+
+/**
+ * Closes the given currently open bos server handle.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * bosServerHandle   the bos server handle to close
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_closeBosServerHandle
+  (JNIEnv *env, jobject obj, jint bosServerHandle)
+{
+    afs_status_t ast;
+
+    if( !bos_ServerClose( (void *) bosServerHandle, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+    }
+}
+
+/**
+ * Gets the expiration time for a given token.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * tokenHandle    a token handle previously returned by a call 
+ *                       to getToken
+ * returns a long representing the UTC time for the token expiration
+ */
+JNIEXPORT jlong JNICALL 
+Java_org_openafs_jafs_Token_getExpiration
+  (JNIEnv *env, jobject obj, jint tokenHandle)
+{
+    afs_status_t ast;
+    unsigned long expTime;
+    char *prince = malloc( sizeof(char)*KAS_MAX_NAME_LEN ); 
+    char *inst = malloc( sizeof(char)*KAS_MAX_NAME_LEN );    
+    char *cell = malloc( sizeof(char)*AFS_MAX_SERVER_NAME_LEN );    
+    int hkt;
+
+    if( !prince || !inst || !cell ) {
+      if( prince ) {
+        free( prince );
+      }
+      if( inst ) {
+        free( inst );
+      }
+      if( cell ) {
+        free( cell );
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    if( !afsclient_TokenQuery( (void *) tokenHandle, &expTime, prince, inst, 
+                              cell, &hkt, &ast ) ) {
+       free( prince );
+       free( inst );
+       free( cell );
+       throwAFSException( env, ast );
+       return 0;
+    }
+
+    free( prince );
+    free( inst );
+    free( cell );
+
+    return (jlong) expTime;
+}
+
+// reclaim global memory used by this portion
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Token_reclaimAuthMemory (JNIEnv *env, jclass cls)
+{
+}
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/Cell.c b/src/JAVA/libjafs/Cell.c
new file mode 100644 (file)
index 0000000..47bb0df
--- /dev/null
@@ -0,0 +1,1485 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Cell.h"
+
+#include <stdio.h>
+#include <afs_kasAdmin.h>
+#include <afs_ptsAdmin.h>
+#include <afs_clientAdmin.h>
+#include <pterror.h>
+#include <kautils.h>
+#include <ptclient.h>
+#include <afs_AdminPtsErrors.h>
+#include <afs_AdminClientErrors.h>
+#include <afs_AdminCommonErrors.h>
+
+//// definitions in Internal.c  //////////////////
+
+extern jclass userCls;
+//extern jfieldID user_cellHandleField;
+extern jfieldID user_nameField;
+extern jfieldID user_cachedInfoField;
+
+extern jclass groupCls;
+//extern jfieldID group_cellHandleField;
+extern jfieldID group_nameField;
+extern jfieldID group_cachedInfoField;
+
+extern jclass serverCls;
+//extern jfieldID server_cellHandleField;
+extern jfieldID server_cachedInfoField;
+
+//////////////////////////////////////////////////////////
+
+///// definition in jafs_User.c /////////////////
+
+extern void getUserInfoChar (JNIEnv *env, int cellHandle, const char *name, 
+                            jobject user);
+
+///////////////////////////////////////////////////
+
+///// definition in jafs_Group.c /////////////////
+
+extern void getGroupInfoChar (JNIEnv *env, int cellHandle, const char *name, 
+                             jobject group);
+
+///////////////////////////////////////////////////
+
+///// definition in jafs_Server.c /////////////////
+
+extern void fillServerInfo (JNIEnv *env, jint cellHandle, jobject server, 
+                           afs_serverEntry_t servEntry);
+
+///////////////////////////////////////////////////
+
+/**
+ * Returns the total number of KAS users belonging to the cell denoted
+ * by cellHandle.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  returns total count of KAS users
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getKasUserCount (JNIEnv *env, jclass cls, 
+                                              jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  kas_identity_t who;
+  int i = 0;
+
+  if( !kas_PrincipalGetBegin( (void *) cellHandle, NULL, 
+                             &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  while ( kas_PrincipalGetNext( (void *) iterationId, &who, &ast ) ) i++;
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+
+/**
+ * Begin the process of getting the kas users that belong to the cell.  
+ * Returns an iteration ID to be used by subsequent calls to 
+ * getKasUsersNextString (or getKasUsersNext) 
+ * and getKasUsersDone.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getKasUsersBegin (JNIEnv *env, jclass cls, 
+                                               jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !kas_PrincipalGetBegin( (void *) cellHandle, NULL, &iterationId, 
+                             &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Begin the process of getting the KAS users, starting at
+ * startIndex, that belong to the cell.  
+ * Returns an iteration ID to be used by subsequent calls to 
+ * getKasUsersNextString (or getKasUsersNext) 
+ * and getKasUsersDone.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  startIndex    the starting base-zero index
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getKasUsersBeginAt (JNIEnv *env, jclass cls, 
+                                                 jint cellHandle, 
+                                                 jint startIndex) {
+
+  afs_status_t ast;
+  void *iterationId;
+  kas_identity_t who;
+  int i;
+
+  if( !kas_PrincipalGetBegin( (void *) cellHandle, NULL, 
+                             &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  for ( i = 1; i < startIndex; i++) {
+    if( !kas_PrincipalGetNext( (void *) iterationId, &who, &ast ) ) {
+      if( ast == ADMITERATORDONE ) {
+        return 0;
+      } else {
+        throwAFSException( env, ast );
+        return 0;
+      }
+    }
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next kas user of the cell.  Returns null if there
+ * are no more users.  Appends instance names to principal names as follows:
+ * principal.instance
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ *  returns the name of the next user of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getKasUsersNextString (JNIEnv *env, jclass cls, 
+                                                    jint iterationId) {
+
+  afs_status_t ast;
+  kas_identity_t who;
+  jstring juser;
+
+  if( !kas_PrincipalGetNext( (void *) iterationId, &who, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+       return NULL;
+    // other
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }    
+  }
+
+  if( strcmp( who.instance, "" ) ) {
+    char *fullName = (char *) malloc( sizeof(char)*( strlen( who.principal ) 
+                                                    + strlen( who.instance ) 
+                                                    + 2 ) );
+    if( !fullName ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+    *fullName = '\0';
+    strcat( fullName, who.principal );
+    strcat( fullName, "." );
+    strcat( fullName, who.instance );
+    juser = (*env)->NewStringUTF(env, fullName );
+    free( fullName );
+  } else {
+    juser = (*env)->NewStringUTF(env, who.principal);
+  }
+
+  return juser;
+
+}
+
+/**
+ * Fills the next kas user object of the cell.  Returns 0 if there
+ * are no more users, != 0 otherwise.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  iterationId   the iteration ID of this iteration
+ *  juserObject   a User object to be populated with the values of 
+ *                  the next kas user
+ *  returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getKasUsersNext (JNIEnv *env, jclass cls, 
+                                              jint cellHandle, 
+                                              jint iterationId, 
+                                              jobject juserObject) {
+
+  afs_status_t ast;
+  kas_identity_t who;
+  jstring juser;
+  char *fullName = NULL;
+
+
+  if( !kas_PrincipalGetNext( (void *) iterationId, &who, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+       return 0;
+    // other
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }    
+  }
+
+  // take care of the instance stuff(by concatenating with a period in between)
+  if( strcmp( who.instance, "" ) ) {
+    fullName = (char *) malloc( sizeof(char)*( strlen( who.principal ) + 
+                                              strlen( who.instance ) + 2 ) );
+    if( !fullName ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+    *fullName = '\0';
+    strcat( fullName, who.principal );
+    strcat( fullName, "." );
+    strcat( fullName, who.instance );
+    juser = (*env)->NewStringUTF(env, fullName );
+  } else {
+    juser = (*env)->NewStringUTF(env, who.principal);
+  }
+
+  if( userCls == 0 ) {
+    internal_getUserClass( env, juserObject );
+  }
+
+  (*env)->SetObjectField(env, juserObject, user_nameField, juser);
+
+  if( fullName != NULL ) { 
+      getUserInfoChar( env, (int) cellHandle, fullName, juserObject );
+      free( fullName );
+  } else {
+      getUserInfoChar( env, (int) cellHandle, who.principal, juserObject );
+  }
+  (*env)->SetBooleanField( env, juserObject, user_cachedInfoField, TRUE );
+
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_getKasUsersDone (JNIEnv *env, jclass cls, 
+                                              jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !kas_PrincipalGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Returns the name of the cell.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the user belongs
+ *  returns the name of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getCellName (JNIEnv *env, jclass cls, 
+                                          jint cellHandle) {
+
+  afs_status_t ast;
+  char *cellName;
+  jstring jcellName;
+
+  if( !afsclient_CellNameGet( (void *) cellHandle, 
+                             (const char **) &cellName, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+  
+  jcellName = (*env)->NewStringUTF(env, cellName);
+
+  return jcellName;
+
+}
+
+/**
+ * Returns the total number of PTS users belonging to the cell denoted
+ * by cellHandle.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  returns total number of PTS users
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getPtsUserCount (JNIEnv *env, jclass cls, 
+                                              jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  char *userName;
+  int i = 0;
+
+  if( !pts_UserListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !userName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;    
+  }
+
+  while ( pts_UserListNext( (void *) iterationId, userName, &ast ) ) i++;
+
+  free( userName );
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Returns the total number of PTS users, belonging to the cell denoted
+ * by cellHandle, that are not in KAS.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  returns total number of users that are in PTS and not KAS
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getPtsOnlyUserCount (JNIEnv *env, jclass cls, 
+                                                  jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+  kas_principalEntry_t kasEntry;
+  char *userName;
+  int i = 0;
+
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;
+  }
+
+  if( !pts_UserListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    free( who );
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+    
+  if( !userName ) {
+    free( who );
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;
+  }
+
+  while ( pts_UserListNext( (void *) iterationId, userName, &ast ) ) {
+    if( strcmp( userName, "anonymous" ) != 0 ) {
+      // make sure the name is within the allowed bounds
+      if( strlen( userName ) > KAS_MAX_NAME_LEN ) {
+           free( who );
+           free( userName );
+           throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+           return -1;
+         }
+  
+      // if there is a kas entry, recurse
+      internal_makeKasIdentity( userName, who );
+      if( !kas_PrincipalGet( (void *) cellHandle, NULL, who, 
+                            &kasEntry, &ast ) ) i++;
+       }
+  }
+
+  free( userName );
+  free( who );
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the pts users that belong to the cell.  
+ * Returns an iteration ID to be used by subsequent calls to 
+ * getPtsUsersNextString (or getPtsUsersNext) 
+ * and getPtsUsersDone.  
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getPtsUsersBegin (JNIEnv *env, jclass cls, 
+                                               jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !pts_UserListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next pts user of the cell.  Returns null if 
+ * there are no more users.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ *  returns the name of the next user of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getPtsUsersNextString (JNIEnv *env, jclass cls, 
+                                                    jint iterationId) {
+
+  afs_status_t ast;
+  char *userName;
+  jstring juser;
+
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !userName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_UserListNext( (void *) iterationId, userName, &ast ) ) {
+    free( userName );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  if( strcmp( userName, "anonymous" ) == 0 ) {
+    free( userName );
+    return Java_org_openafs_jafs_Cell_getPtsUsersNextString( env, cls, 
+                                                               iterationId );
+  }
+
+  juser = (*env)->NewStringUTF(env, userName);
+  free( userName );
+  return juser;
+
+}
+
+/**
+ * Returns the next pts user (who is not a kas user) of the cell.  
+ * Returns null if there are no more users.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ *  cellHandle   the cell handle to which these users will belong
+ *  returns the name of the next pts user (not kas user) of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getPtsOnlyUsersNextString (JNIEnv *env, 
+                                                        jclass cls, 
+                                                        jint iterationId, 
+                                                        jint cellHandle) {
+
+    kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+    kas_principalEntry_t kasEntry;
+    afs_status_t ast;
+    char *userName;
+    jstring juser;
+
+    if( !who ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+    
+    if( !userName ) {
+      free( who );
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    while( 1 ) {
+
+       if( !pts_UserListNext( (void *) iterationId, userName, &ast ) ) {
+           free( userName );
+           free( who );
+           if( ast == ADMITERATORDONE ) {
+               return NULL;
+           } else {
+               throwAFSException( env, ast );
+               return;
+           }
+       }
+
+       if( strcmp( userName, "anonymous" ) == 0 ) {
+           continue;
+       }
+       
+       // make sure the name is within the allowed bounds
+       if( strlen( userName ) > KAS_MAX_NAME_LEN ) {
+           free( who );
+           free( userName );
+           throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+           return;
+       }
+       
+       // if there is a kas entry, recurse
+       internal_makeKasIdentity( userName, who );
+       if( kas_PrincipalGet( (void *) cellHandle, NULL, who, 
+                             &kasEntry, &ast ) ) {
+           continue;
+       }
+       
+       juser = (*env)->NewStringUTF(env, userName);
+       free( userName );
+       free( who );
+       return juser;
+
+    }
+
+}
+
+/**
+ * Fills the next pts user object of the cell.  Returns 0 if there
+ * are no more users, != 0 otherwise.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  iterationId   the iteration ID of this iteration
+ *  juserObject   a User object to be populated with the values of 
+ *                  the next pts user
+ *  returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getPtsUsersNext (JNIEnv *env, jclass cls, 
+                                              jint cellHandle, 
+                                              jint iterationId, 
+                                              jobject juserObject ) {
+    
+  afs_status_t ast;
+  char *userName;
+  jstring juser;
+
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !userName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_UserListNext( (void *) iterationId, userName, &ast ) ) {
+    free( userName );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  if( strcmp( userName, "anonymous" ) == 0 ) {
+    free( userName );
+    return Java_org_openafs_jafs_Cell_getPtsUsersNext( env, cls, 
+                                                         cellHandle, 
+                                                         iterationId, 
+                                                         juserObject );
+  }
+  
+  juser = (*env)->NewStringUTF(env, userName);
+
+  if( userCls == 0 ) {
+    internal_getUserClass( env, juserObject );
+  }
+
+  (*env)->SetObjectField(env, juserObject, user_nameField, juser);
+
+  getUserInfoChar( env, (int) cellHandle, userName, juserObject );
+  (*env)->SetBooleanField( env, juserObject, user_cachedInfoField, TRUE );
+
+  free( userName );
+  return 1;
+
+}
+
+/**
+ * Fills the next pts user (who does not have a kas entry) object of 
+ * the cell.  Returns 0 if there are no more users, != 0 otherwise.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  iterationId   the iteration ID of this iteration
+ *  juserObject   a User object to be populated with the values of 
+ *                  the next pts (with no kas) user
+ *  returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getPtsOnlyUsersNext (JNIEnv *env, jclass cls, 
+                                                  jint cellHandle, 
+                                                  jint iterationId, 
+                                                  jobject juserObject ) {
+
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+  kas_principalEntry_t kasEntry;
+  afs_status_t ast;
+  char *userName;
+  jstring juser;
+
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+  
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !userName ) {
+    free( who );
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  while( 1 ) {
+
+      if( !pts_UserListNext( (void *) iterationId, userName, &ast ) ) {
+         free( userName );
+         free( who );
+         if( ast == ADMITERATORDONE ) {
+             return 0;
+         } else {
+             throwAFSException( env, ast );
+             return 0;
+         }
+      }
+      
+      if( strcmp( userName, "anonymous" ) == 0 ) {
+         continue;
+      }
+      
+      // make sure the name is within the allowed bounds
+      if( strlen( userName ) > KAS_MAX_NAME_LEN ) {
+         free( userName );
+         free( who );
+         throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+         return 0;
+      }
+      
+      if( userCls == 0 ) {
+         internal_getUserClass( env, juserObject );
+      }
+      
+      
+      // if there is a kas entry, recurse
+      internal_makeKasIdentity( userName, who );
+      if( kas_PrincipalGet( (void *) cellHandle, NULL, who, 
+                           &kasEntry, &ast ) ) {
+         continue;
+      } 
+      
+      juser = (*env)->NewStringUTF(env, userName);
+      
+      (*env)->SetObjectField(env, juserObject, user_nameField, juser);
+      getUserInfoChar( env, (int) cellHandle, userName, juserObject );
+      (*env)->SetBooleanField( env, juserObject, user_cachedInfoField, TRUE );
+      
+      free( who );
+      free( userName );
+      return 1;
+      
+  }
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_getPtsUsersDone (JNIEnv *env, jclass cls, 
+                                              jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !pts_UserListDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+  
+}
+
+/**
+ * Returns the total number of groups belonging to the cell denoted
+ * by cellHandle.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the groups belong
+ *  returns total number of groups
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getGroupCount (JNIEnv *env, jclass cls, 
+                                            jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  char *groupName;
+  int i = 0;
+
+  if( !pts_GroupListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;
+  }
+
+  while ( pts_GroupListNext( (void *) iterationId, groupName, &ast ) ) i++;
+
+  free( groupName );
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the groups that belong to the cell.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getGroupsNextString (or getGroupsNext) and 
+ * getGroupsDone.  
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the groups belong
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getGroupsBegin (JNIEnv *env, jclass cls, 
+                                             jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !pts_GroupListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Begin the process of getting the groups that belong to the cell, starting
+ * with element index startIndex.  Returns an iteration ID to 
+ * be used by subsequent calls to getGroupsNextString 
+ * (or getGroupsNext) and getGroupsDone.  
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the groups belong
+ *  startIndex    the starting base-zero index
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getGroupsBeginAt (JNIEnv *env, jclass cls, 
+                                               jint cellHandle, 
+                                               jint startIndex) {
+
+  afs_status_t ast;
+  void *iterationId;
+  char *groupName;
+  int i;
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !pts_GroupListBegin( (void *) cellHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return 0;
+  }
+
+  for ( i = 1; i < startIndex; i++) {
+    if( !pts_GroupListNext( (void *) iterationId, groupName, &ast ) ) {
+      free( groupName );
+      if( ast == ADMITERATORDONE ) {
+        return 0;
+      } else {
+        throwAFSException( env, ast );
+        return 0;
+      }
+    }
+  }
+
+  free( groupName );
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next group of the cell.  Returns null if there
+ * are no more groups.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ *  returns the name of the next user of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getGroupsNextString (JNIEnv *env, jclass cls, 
+                                                  jint iterationId) {
+
+  afs_status_t ast;
+  char *groupName;
+  jstring jgroup;
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_GroupListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  jgroup = (*env)->NewStringUTF(env, groupName);
+  free( groupName );
+  return jgroup;
+
+}
+
+/**
+ * Fills the next group object of the cell.  Returns 0 if there
+ * are no more groups, != 0 otherwise.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  iterationId   the iteration ID of this iteration
+ *  jgroupObject   a Group object to be populated with the values of 
+ *                   the next group
+ *  returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getGroupsNext (JNIEnv *env, jclass cls, 
+                                            jint cellHandle, 
+                                            jint iterationId, 
+                                            jobject jgroupObject) {
+
+  afs_status_t ast;
+  char *groupName;
+  jstring jgroup;
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN );
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+      return;    
+  }
+  
+  if( !pts_GroupListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  jgroup = (*env)->NewStringUTF(env, groupName);
+
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, jgroupObject );
+  }
+
+  (*env)->SetObjectField(env, jgroupObject, group_nameField, jgroup);
+
+  getGroupInfoChar( env, (int) cellHandle, groupName, jgroupObject );
+  (*env)->SetBooleanField( env, jgroupObject, group_cachedInfoField, TRUE );
+
+  free( groupName );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_getGroupsDone (JNIEnv *env, jclass cls, 
+                                            jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !pts_GroupListDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+  
+}
+
+/**
+ * Gets the maximum group pts ID that's been used within a cell.   
+ * The next auto-assigned group ID will be one less (more negative) 
+ * than this value.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the group belongs
+ *  returns an integer reresenting the max group id in a cell
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getMaxGroupID (JNIEnv *env, jclass cls, 
+                                            jint cellHandle) {
+
+  afs_status_t ast;
+  jint maxID;
+
+  if( !pts_GroupMaxGet( (void *) cellHandle, (int *) &maxID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return maxID;
+
+}
+
+/**
+ * Sets the maximum group pts ID that's been used within a cell.  The next 
+ * auto-assigned group ID will be one less (more negative) than this value.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the group belongs
+ *  maxID an integer reresenting the new max group id in a cell
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_setMaxGroupID (JNIEnv *env, jclass cls, 
+                                            jint cellHandle, jint maxID) {
+
+  afs_status_t ast;
+
+  if( !pts_GroupMaxSet( (void *) cellHandle, (int) maxID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Gets the maximum user pts ID that's been used within a cell.   
+ * The next auto-assigned user ID will be one greater (more positive) 
+ * than this value.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the user belongs
+ *  returns an integer reresenting the max user id in a cell
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getMaxUserID (JNIEnv *env, jclass cls, 
+                                           jint cellHandle) {
+
+  afs_status_t ast;
+  jint maxID;
+
+  if( !pts_UserMaxGet( (void *) cellHandle, (int *) &maxID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return maxID;
+
+}
+
+/**
+ * Sets the maximum user pts ID that's been used within a cell.  The next 
+ * auto-assigned user ID will be one greater (more positive) than this value.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the user belongs
+ *  maxID an integer reresenting the new max user id in a cell
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_setMaxUserID (JNIEnv *env, jclass cls, 
+                                           jint cellHandle, jint maxID) {
+
+  afs_status_t ast;
+
+  if( !pts_UserMaxSet( (void *) cellHandle, (int) maxID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Returns the total number of servers belonging to the cell denoted
+ * by cellHandle.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the servers belong
+ *  returns total number of servers
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getServerCount (JNIEnv *env, jclass cls, 
+                                             jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  afs_serverEntry_t servEntry;
+  int i = 0;
+
+  if( !afsclient_AFSServerGetBegin( (void *) cellHandle, 
+                                   &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  while ( afsclient_AFSServerGetNext( (void *) iterationId, 
+                                     &servEntry, &ast ) ) i++;
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the servers in the cell.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getServersNextString and getServersDone.  
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the servers belong
+ *  returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getServersBegin (JNIEnv *env, jclass cls, 
+                                              jint cellHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !afsclient_AFSServerGetBegin( (void *) cellHandle, 
+                                   &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next server of the cell.  Returns null if there
+ * are no more servers.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ *  returns the name of the next server of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Cell_getServersNextString (JNIEnv *env, jclass cls, 
+                                                   jint iterationId) {
+
+  afs_status_t ast;
+  jstring jserver;
+  afs_serverEntry_t servEntry;
+
+  if( !afsclient_AFSServerGetNext( (void *) iterationId, &servEntry, &ast ) ) {
+      if( ast == ADMITERATORDONE ) {
+         return NULL;
+      } else {
+         throwAFSException( env, ast );
+         return;
+      }
+  }
+
+  return jserver;
+
+}
+
+/**
+ * Fills the next server object of the cell.  Returns 0 if there are no 
+ * more servers, != 0 otherwise.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the users belong
+ *  iterationId   the iteration ID of this iteration
+ *  jserverObject   a Server object to be populated with the values 
+ *                    of the next server 
+ *  returns 0 if there are no more servers, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Cell_getServersNext
+  (JNIEnv *env, jclass cls, jint cellHandle, jint iterationId,
+   jobject jserverObject)
+{
+  afs_status_t ast;
+  jstring jserver;
+  afs_serverEntry_t servEntry;
+  jintArray jaddress;
+
+  if( !afsclient_AFSServerGetNext( (void *) iterationId, &servEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  // get the class fields if need be
+  if( serverCls == 0 ) {
+    internal_getServerClass( env, jserverObject );
+  }
+
+  fillServerInfo( env, (int) cellHandle, jserverObject, servEntry );
+
+  (*env)->SetBooleanField( env, jserverObject, server_cachedInfoField, TRUE );
+
+  return 1;
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_getServersDone
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+
+  if( !afsclient_AFSServerGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Creates a mount point for a volume within the file system.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  cellHandle    the handle of the cell to which the user belongs
+ *  jdirectory    the full path of the place in the AFS file system 
+ *                     at which to mount the volume
+ *  jvolumeName   the name of the volume to mount
+ *  readWrite   whether or not this is to be a readwrite mount point
+ *  forceCheck  whether or not to check if this volume name exists
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_createMountPoint (JNIEnv *env, jclass cls, 
+                                               jint cellHandle, 
+                                               jstring jdirectory, 
+                                               jstring jvolumeName, 
+                                               jboolean readWrite, 
+                                               jboolean forceCheck) {
+
+  afs_status_t ast;
+  const char *directory;
+  const char *volumeName;
+  vol_type_t type;
+  vol_check_t check;
+
+  if( jdirectory != NULL ) {
+    directory = (*env)->GetStringUTFChars(env, jdirectory, 0);
+    if( !directory ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    directory = NULL;
+  }
+  if( jvolumeName != NULL ) {
+    volumeName = (*env)->GetStringUTFChars(env, jvolumeName, 0);
+    if( !volumeName ) {
+      if( directory != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    volumeName = NULL;
+  }
+
+  if( readWrite ) {
+    type = READ_WRITE;
+  } else {
+    type = READ_ONLY;
+  }
+
+  if( forceCheck ) {
+    check = CHECK_VOLUME;
+  } else {
+    check = DONT_CHECK_VOLUME;
+  }
+
+  if( !afsclient_MountPointCreate( (void *) cellHandle, directory, 
+                                  volumeName, type, check, &ast ) ) {
+    if( volumeName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jvolumeName, volumeName);
+    }
+    if( directory != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( volumeName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jvolumeName, volumeName);
+  }
+  if( directory != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+  }
+
+}
+
+/*
+ * Sets an ACL for a given place in the AFS file system.
+ *
+ *  env      the Java environment
+ *  cls      the current Java class
+ *  jdirectory    the full path of the place in the AFS file system 
+ *                     for which to add an entry
+ *  jusername   the name of the user or group for which to add an entry
+ *  read    whether or not to allow read access to this user
+ *  write    whether or not to allow write access to this user
+ *  lookup    whether or not to allow lookup access to this user
+ *  delete    whether or not to allow deletion access to this user
+ *  insert    whether or not to allow insertion access to this user
+ *  lock    whether or not to allow lock access to this user
+ *  admin    whether or not to allow admin access to this user
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_setACL (JNIEnv *env, jclass cls, 
+                                     jstring jdirectory, jstring juserName, 
+                                     jboolean read, jboolean write, 
+                                     jboolean lookup, jboolean delete, 
+                                     jboolean insert, jboolean lock, 
+                                     jboolean admin) {
+
+  afs_status_t ast;
+  const char *directory;
+  const char *userName;
+  acl_t acl;
+
+  // Added by MP
+  if( !afsclient_Init( &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( jdirectory != NULL ) {
+    directory = (*env)->GetStringUTFChars(env, jdirectory, 0);
+    if( !directory ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    directory = NULL;
+  }
+  if( juserName != NULL ) {
+    userName = (*env)->GetStringUTFChars(env, juserName, 0);
+    if( !userName ) {
+      if( directory != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    userName = NULL;
+  }
+
+  if( read ) {
+    acl.read = READ;
+  } else {
+    acl.read = NO_READ;
+  }
+
+  if( write ) {
+    acl.write = WRITE;
+  } else {
+    acl.write = NO_WRITE;
+  }
+
+  if( lookup ) {
+    acl.lookup = LOOKUP;
+  } else {
+    acl.lookup = NO_LOOKUP;
+  }
+
+  if( delete ) {
+    acl.del = DELETE;
+  } else {
+    acl.del = NO_DELETE;
+  }
+
+  if( insert ) {
+    acl.insert = INSERT;
+  } else {
+    acl.insert = NO_INSERT;
+  }
+
+  if( lock ) {
+    acl.lock = LOCK;
+  } else {
+    acl.lock = NO_LOCK;
+  }
+
+  if( admin ) {
+    acl.admin = ADMIN;
+  } else {
+    acl.admin = NO_ADMIN;
+  }
+
+  if( !afsclient_ACLEntryAdd( directory, userName, &acl, &ast ) ) {
+      if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( directory != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+      }
+      throwAFSException( env, ast );
+      return;
+  }
+
+  if( userName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+  if( directory != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jdirectory, directory);
+  }
+
+}
+
+// reclaim global memory used by this portion
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Cell_reclaimCellMemory (JNIEnv *env, jclass cls) {
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/Exceptions.h b/src/JAVA/libjafs/Exceptions.h
new file mode 100644 (file)
index 0000000..1692cfd
--- /dev/null
@@ -0,0 +1,12 @@
+#ifndef _Jafsadm_Exceptions
+#define _Jafsadm_Exceptions
+
+static char *afsExceptionName         = "org/openafs/jafs/AFSException";
+static char *afsFileExceptionName     = "org/openafs/jafs/AFSFileException";
+static char *afsSecurityExceptionName = "org/openafs/jafs/AFSSecurityException";
+
+#endif
+
+
+
+
diff --git a/src/JAVA/libjafs/File.c b/src/JAVA/libjafs/File.c
new file mode 100644 (file)
index 0000000..4adcfd0
--- /dev/null
@@ -0,0 +1,674 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_File.h"
+
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <jni.h>
+#include <pthread.h>
+#include <afs/afs_usrops.h>
+#include <afs/prs_fs.h>
+#include <sys/time.h>
+#include <unistd.h>
+
+/* Access Rights */
+#define UAFS_READ 1
+#define UAFS_WRITE 2
+#define UAFS_INSERT 4
+#define UAFS_LOOKUP 8
+#define UAFS_DELETE 16
+#define UAFS_LOCK 32
+#define UAFS_ADMIN 64
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+void setFileNotExistsAttributes(JNIEnv *env, jobject *obj);
+
+typedef struct {
+    char *fileName;
+    void *next;
+} FileNameNode;
+
+/*static char compile_date[] = COMPILE_DATE;*/
+
+int sub_time(struct timeval *tv1, struct timeval *tv2)
+{
+   if (tv2->tv_usec > tv1->tv_usec) {
+     tv1->tv_usec += 1000000;
+     tv1->tv_usec -= 1;
+   }
+
+   tv1->tv_usec -= tv2->tv_usec;
+   tv1->tv_sec -= tv2->tv_sec;
+}
+
+/**
+ * Sets dirName to the absolute path according to the path field
+ * within the Java File object (obj).
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ */
+char* getAbsolutePath(JNIEnv *env, jobject *obj, char *dirName)
+{
+    jclass thisClass;
+    jstring jDirName;
+    jmethodID getAbsolutePathID;
+    const char *auxDirName;
+    jfieldID fid;
+
+    thisClass = (*env)->GetObjectClass(env, *obj);
+    if(thisClass == NULL) {
+      fprintf(stderr, "File::getAbsolutePath(): GetObjectClass failed\n");
+      return NULL;
+    }
+    fid = (*env)->GetFieldID(env, thisClass, "path", "Ljava/lang/String;");
+    if (fid == 0) {
+      fprintf(stderr, "File::getAbsolutePath(): GetFieldID (path) failed\n");
+      return NULL;
+    }
+    jDirName = (*env)->GetObjectField(env, *obj, fid);
+
+    if(jDirName == NULL) {
+      fprintf(stderr, "File::getAbsolutePath(): failed to get file name\n");
+      return NULL;
+    }
+    auxDirName = (*env) -> GetStringUTFChars(env, jDirName, 0);
+    strcpy(dirName, auxDirName);
+    (*env) -> ReleaseStringUTFChars(env, jDirName, auxDirName);
+}
+
+/**
+ * Performs a file stat on the actual AFS file and populates  
+ * the Java object's respective field members with the appropriate values.
+ * method will, if authorized, perform a stat on the
+ * actual AFS file and update its respective field members; defining
+ * this file object's attributes.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  true if and only if the current user is allowed to stat the file;
+ *         false otherwise.
+ *
+ * throws AFSException
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_setAttributes
+  (JNIEnv *env, jobject obj)
+{
+    char target[FILENAME_MAX+1];
+    char dirName[FILENAME_MAX];
+    jclass thisClass;
+    jfieldID fid;
+    struct stat st;
+    int rc, fd;
+    int mtpoint = 0;
+    jboolean islink = JNI_FALSE;
+    int i;
+    struct timeval tv0, tv1;
+    struct timezone tz;
+
+    /*memset(target, 0, FILENAME_MAX);*/
+
+    *dirName = '\0';
+    getAbsolutePath(env, &obj, dirName);
+
+    /*fprintf(stderr, "dirName=%s\n", dirName);*/
+
+    if(*dirName == '\0') {
+      fprintf(stderr, "File::setAttributes(): failed to get dirName\n");
+      return JNI_FALSE;
+    }
+
+    thisClass = (*env) -> GetObjectClass(env, obj);
+    if(thisClass == NULL) {
+      fprintf(stderr, "File::setAttributes(): GetObjectClass failed\n");
+      return JNI_FALSE;
+    }
+
+    gettimeofday(&tv0, &tz);
+    if ((strcmp(dirName, "/afs") == 0) || (strcmp(dirName, "/afs/") == 0)) {
+      rc = 1;   /* special case for /afs since statmountpoint fails on it */
+    } else {
+      rc = uafs_statmountpoint(dirName);
+      gettimeofday(&tv1, &tz);
+      sub_time(&tv1, &tv0);
+      /*printf("%s: statmountpoint %d.%06d\n", dirName, tv1.tv_sec, tv1.tv_usec);*/
+    }
+
+    if(rc < 0) {
+      setError(env, &obj, errno);
+      if(errno == ENOENT) {
+        setFileNotExistsAttributes(env, &obj);
+        return JNI_TRUE;   /* not really an error */
+      } else {
+        fprintf(stderr, "File::setAttributes(): uafs_statmountpoint failed "
+                        "for %s (%s)\n", dirName, error_message(errno));
+        return JNI_FALSE;
+      }
+    }
+
+    if (rc == 1) {
+      /* this is an AFS mount point; we don't want to stat it */
+      /* fake up a stat entry instead */
+      mtpoint = 1;
+      st.st_mtime = 0;
+      st.st_size = 2048;
+      st.st_mode = 0;  /* don't match anything */
+    } else {
+      mtpoint = 0;
+      fid = (*env)->GetFieldID(env, thisClass, "isLink", "Z");
+      if (fid == 0) {
+        fprintf(stderr, "File::setAttributes(): GetFieldID (isLink) failed\n");
+        setError(env, &obj, -1);
+        return JNI_FALSE;
+      }
+      islink = (*env)->GetBooleanField(env, obj, fid);
+      if (islink != JNI_TRUE) {
+        rc = uafs_lstat(dirName, &st);
+      } else {
+        /* Here we are being called on a link object for the second time,
+           so we want info on the object pointed to by the link. */
+        fprintf(stderr, "islink = TRUE on file %s\n", dirName);
+        rc = uafs_stat(dirName, &st);
+      }
+
+      if(rc < 0) {
+        setError(env, &obj, errno);
+        fprintf(stderr, "uafs_lstat failed for dir %s: error %d\n",
+                         dirName, errno);
+        if(errno == ENOENT) {
+          setFileNotExistsAttributes(env, &obj);
+          return JNI_TRUE;
+        }
+        fprintf(stderr, 
+            "File::setAttributes(): uafs_stat failed for %s (%s)\n", 
+             dirName, error_message(errno));
+        return JNI_FALSE;
+      }
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "exists", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (exists) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+    (*env)->SetBooleanField(env, obj, fid, JNI_TRUE);
+
+    fid = (*env)->GetFieldID(env, thisClass, "isDirectory", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (isDirectory) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+    if ((st.st_mode & S_IFMT) == S_IFDIR && !mtpoint) {
+      (*env)->SetBooleanField(env, obj, fid, JNI_TRUE);
+    } else {
+      (*env)->SetBooleanField(env, obj, fid, JNI_FALSE);
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "isFile", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (isFile) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+    if ((st.st_mode & S_IFMT) == S_IFREG) {
+      (*env)->SetBooleanField(env, obj, fid, JNI_TRUE);
+    } else {
+      (*env)->SetBooleanField(env, obj, fid, JNI_FALSE);
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "isLink", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (isLink) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+
+    if (islink != JNI_TRUE) {    /* don't re-process link */
+      if ((st.st_mode & S_IFMT) == S_IFLNK) {
+        (*env)->SetBooleanField(env, obj, fid, JNI_TRUE);
+
+        /* Find the target of the link */
+        rc = uafs_readlink(dirName, target, FILENAME_MAX);
+        if (rc < 0) {
+          fprintf(stderr, "File::setAttributes(): uafs_readlink failed\n");
+          setError(env, &obj, errno);
+          return JNI_FALSE;
+        } else {
+          target[rc] = 0;     /* weird that we have to do this */
+          /*fprintf(stderr, "readlink %s succeeded, target=%s, rc=%d\n", dirName,
+            target, rc);*/
+        }
+
+        rc = setString(env, &obj, "target", target);
+        if (rc < 0) {
+          fprintf(stderr, "setString dirName=%s target=%s failed\n",
+                           dirName, target);
+        }
+      } else {
+        (*env)->SetBooleanField(env, obj, fid, JNI_FALSE);
+      }
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "isMountPoint", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (isMountPoint) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+
+    if (mtpoint) {
+      (*env)->SetBooleanField(env, obj, fid, JNI_TRUE);
+    } else {
+      (*env)->SetBooleanField(env, obj, fid, JNI_FALSE);
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "lastModified", "J");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (lastModified) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+    (*env)->SetLongField(env, obj, fid, ((jlong) st.st_mtime)*1000);
+
+    fid = (*env)->GetFieldID(env, thisClass, "length", "J");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setAttributes(): GetFieldID (length) failed\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+    (*env)->SetLongField(env, obj, fid, st.st_size);
+
+    setError(env, &obj, 0);
+
+    return JNI_TRUE;
+}
+
+/**
+ * Returns the permission/ACL mask for the represented directory
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  permission/ACL mask
+ */
+JNIEXPORT jint JNICALL Java_org_openafs_jafs_File_getRights
+  (JNIEnv *env, jobject obj)
+{
+    char dirName[FILENAME_MAX];
+    jclass thisClass;
+    jfieldID fid;
+    int rc;
+    int afs_rights;
+    int rights;
+
+    *dirName = '\0';
+    getAbsolutePath(env, &obj, dirName);
+
+    if(*dirName == '\0') {
+      fprintf(stderr, "File::getRights(): failed to get dirName\n");
+      setError(env, &obj, -1);
+      return JNI_FALSE;
+    }
+
+    /*-Access Rights-
+     * UAFS_READ 1
+     * UAFS_WRITE 2
+     * UAFS_INSERT 4
+     * UAFS_LOOKUP 8
+     * UAFS_DELETE 16
+     * UAFS_LOCK 32
+     * UAFS_ADMIN 64
+     */
+
+    rights = 0;
+    afs_rights = uafs_getRights(dirName);
+    if (afs_rights < 0) {
+      setError(env, &obj, errno);
+      return -1;
+    }
+    
+    if (afs_rights & PRSFS_READ)
+      rights |= UAFS_READ;
+    if (afs_rights & PRSFS_WRITE)
+      rights |= UAFS_WRITE;
+    if (afs_rights & PRSFS_INSERT)
+      rights |= UAFS_INSERT;
+    if (afs_rights & PRSFS_LOOKUP)
+      rights |= UAFS_LOOKUP;
+    if (afs_rights & PRSFS_DELETE)
+      rights |= UAFS_DELETE;
+    if (afs_rights & PRSFS_LOCK)
+      rights |= UAFS_LOCK;
+    if (afs_rights & PRSFS_ADMINISTER)
+      rights |= UAFS_ADMIN;
+    
+    return rights;
+}
+
+
+/**
+ * List the contents of the represented directory.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return   the directory handle
+ */
+JNIEXPORT jlong JNICALL Java_org_openafs_jafs_File_listNative
+  (JNIEnv *env, jobject obj, jobject buffer)
+{
+    char dirName[FILENAME_MAX];
+    jclass arrayListClass;
+    jmethodID addID;
+    jstring entryJString;
+    usr_DIR *dirp;
+    struct usr_dirent *enp;
+    int i, dirSize;
+
+    *dirName='\0';
+    getAbsolutePath(env, &obj, dirName);
+    if(*dirName == '\0') {
+      fprintf(stderr, "File::listNative(): failed to get dirName\n");
+      setError(env, &obj, -1);
+      return 0;
+    }
+    arrayListClass = (*env)->GetObjectClass(env, buffer);
+    if(arrayListClass == NULL) {
+      fprintf(stderr, "File::listNative(): GetObjectClass failed\n");
+      setError(env, &obj, -1);
+      return 0;
+    }
+    addID = (*env) -> GetMethodID(env, arrayListClass, "add", 
+                                  "(Ljava/lang/Object;)Z");
+    if(addID == 0) {
+      fprintf(stderr, 
+            "File::listNative(): failed to get addID\n");
+      setError(env, &obj, -1);
+      return 0;
+    }
+    dirp = uafs_opendir(dirName);
+    if(dirp == NULL) {
+      fprintf(stderr, "File::listNative(): uafs_opendir(%s) failed(%s)\n",
+                       dirName, error_message(errno));
+      setError(env, &obj, errno);
+      //throwAFSSecurityException( env, errno );
+      return 0;
+    }
+    while((enp = uafs_readdir(dirp)) != NULL) {
+       if(strcmp(enp->d_name, ".") == 0 || strcmp(enp->d_name, "..") == 0) {
+        continue;
+       }
+      entryJString = (*env) -> NewStringUTF(env, enp->d_name);
+      if(entryJString == NULL) {
+        fprintf(stderr, "File::listNative(): NewStringUTF failed\n");
+        setError(env, &obj, -1);
+        return 0;
+      }
+      (*env) -> CallBooleanMethod(env, buffer, addID, entryJString);
+    }
+    /*uafs_closedir(dirp);*/
+
+    setError(env, &obj, 0);
+
+    return (jlong) dirp;
+}
+
+/**
+ * Close the currently open directory using a provided directory handle.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  true if the directory closes without error
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_closeDir
+  (JNIEnv *env, jobject obj, jlong dp)
+{
+  usr_DIR *dirp = (usr_DIR *) dp;
+  int rc;
+  
+  rc = uafs_closedir(dirp);
+  if (rc < 0) {
+    setError(env, &obj, errno);
+    return JNI_FALSE;
+  }
+  else return JNI_TRUE;
+}
+
+
+/**
+ * Removes/deletes the represented file.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  true if the file is removed without error
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_rmfile
+  (JNIEnv *env, jobject obj)
+{
+    char dirName[FILENAME_MAX];
+    int rc;
+
+    *dirName='\0';
+    getAbsolutePath(env, &obj, dirName);
+    if(*dirName == '\0') {
+      setError(env, &obj, EINVAL);
+      fprintf(stderr, "File::rmfile(): failed to get dirName\n");
+      return JNI_FALSE;
+    }
+    rc = uafs_unlink(dirName);
+    if(rc < 0) {
+      setError(env, &obj, errno);
+      fprintf(stderr, "File::rmfile(): uafs_unlink failed\n");
+      return JNI_FALSE;
+    }
+    setError(env, &obj, 0);
+    return JNI_TRUE;
+}
+
+/**
+ * Removes/deletes the represented directory.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  true if the directory is removed without error
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_rmdir
+  (JNIEnv *env, jobject obj)
+{
+    char dirName[FILENAME_MAX];
+    int rc;
+
+    *dirName='\0';
+    getAbsolutePath(env, &obj, dirName);
+    if(*dirName == '\0') {
+      setError(env, &obj, -1);
+      fprintf(stderr, "File::rmdir(): failed to get dirName\n");
+      return JNI_FALSE;
+    }
+    rc = uafs_rmdir(dirName);
+    if(rc < 0) {
+      setError(env, &obj, errno);
+      fprintf(stderr, "File::rmdir(): uafs_unlink failed\n");
+      return JNI_FALSE;
+    }
+    setError(env, &obj, 0);
+    return JNI_TRUE;
+}
+
+/**
+ * Creates the directory named by this abstract pathname.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ *
+ * return  true if and only if the directory was
+ *          created; false otherwise
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_mkdir
+  (JNIEnv *env, jobject obj)
+{
+    char dirName[FILENAME_MAX];
+    int rc;
+
+    *dirName='\0';
+    getAbsolutePath(env, &obj, dirName);
+    if(*dirName == '\0') {
+      setError(env, &obj, EINVAL);
+      fprintf(stderr, "File::mkdir(): failed to get dirName\n");
+      return JNI_FALSE;
+    }
+    rc = uafs_mkdir(dirName, 0755);
+    if(rc < 0) {
+      setError(env, &obj, errno);
+      fprintf(stderr, "File::mkdir(): uafs_mkdir failed\n");
+      return JNI_FALSE;
+    }
+    setError(env, &obj, 0);
+    return JNI_TRUE;
+}
+
+/**
+ * Renames the file denoted by this abstract pathname.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ * dest     the new abstract pathname for the named file
+ * 
+ * return   true if and only if the renaming succeeded;
+ *          false otherwise
+ *
+ * throws   NullPointerException  
+ *          If parameter dest is null
+ */
+JNIEXPORT jboolean JNICALL Java_org_openafs_jafs_File_renameTo
+  (JNIEnv *env, jobject obj, jobject newFile)
+{
+    char thisDirName[FILENAME_MAX], newDirName[FILENAME_MAX];
+    int rc;
+
+    *thisDirName='\0';
+    getAbsolutePath(env, &obj, thisDirName);
+    if(*thisDirName == '\0') {
+      setError(env, &obj, -1);
+      fprintf(stderr, "File::renameTo(): failed to get dirName for this \n");
+      return JNI_FALSE;
+    }
+    *newDirName='\0';
+    getAbsolutePath(env, &newFile, newDirName);
+    if(*newDirName == '\0') {
+      setError(env, &obj, -1);
+      fprintf(stderr, "File::renameTo(): failed to get dirName for new \n");
+      return JNI_FALSE;
+    }
+    rc = uafs_rename(thisDirName, newDirName);
+    if(rc < 0) {
+      setError(env, &obj, errno);
+      fprintf(stderr, "File::renameTo(): uafs_rename failed\n");
+      return JNI_FALSE;
+    }
+    return JNI_TRUE;
+}
+
+/**
+ * Set all the necessary fields within the Java File class to indicate the
+ * does not exist.
+ *
+ * env      the Java environment
+ * obj      the current Java object
+ */
+void setFileNotExistsAttributes
+    (JNIEnv *env, jobject *obj)
+{
+    jclass thisClass;
+    jfieldID fid;
+
+    thisClass = (*env) -> GetObjectClass(env, *obj);
+    if(thisClass == NULL) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetObjectClass failed\n");
+      return;
+    }
+
+    fid = (*env)->GetFieldID(env, thisClass, "exists", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetFieldID (exists) failed\n");
+      return;
+    }
+    (*env)->SetBooleanField(env, *obj, fid, JNI_FALSE);
+
+    fid = (*env)->GetFieldID(env, thisClass, "isDirectory", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetFieldID (isDirectory) 
+             failed\n");
+      return;
+    }
+    (*env)->SetBooleanField(env, *obj, fid, JNI_FALSE);
+
+    fid = (*env)->GetFieldID(env, thisClass, "isFile", "Z");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetFieldID (isDirectory) 
+             failed\n");
+      return;
+    }
+    (*env)->SetBooleanField(env, *obj, fid, JNI_FALSE);
+
+    fid = (*env)->GetFieldID(env, thisClass, "lastModified", "J");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetFieldID (lastModified) failed\n");
+      return;
+    }
+    (*env)->SetLongField(env, *obj, fid, 0);
+
+    
+    fid = (*env)->GetFieldID(env, thisClass, "length", "J");
+    if (fid == 0) {
+      fprintf(stderr, 
+            "File::setFileNotExistsAttributes(): GetFieldID (length) failed\n");
+      return;
+    }
+    (*env)->SetLongField(env, *obj, fid, 0);
+
+    return;
+}
+
+
diff --git a/src/JAVA/libjafs/FileInputStream.c b/src/JAVA/libjafs/FileInputStream.c
new file mode 100644 (file)
index 0000000..95b9327
--- /dev/null
@@ -0,0 +1,153 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_FileInputStream.h"
+
+#include <fcntl.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+extern int errno;
+
+/**
+ * Be carefull with the memory management:
+ *
+ * - For every GetStringUTFChars call the corresponding ReleaseStringUTFChars.
+ * - For every Get<type>ArrayElements call the corresponding
+ *   Release<type>ArrayElements
+ * - For every malloc call the corresponding free.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Opens an AFS file, with the specified name, for appending.
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *  fileNameUTF        name of file to be opened
+ *
+ * @return             file descriptor
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT jint JNICALL Java_org_openafs_jafs_FileInputStream_openReadOnly
+  (JNIEnv *env, jobject obj, jstring fileNameUTF)
+{
+  int err;
+  int fd = -1;         //file descriptor
+
+  fd = openAFSFile(env, fileNameUTF, O_RDONLY, 0, &err);
+  if (fd < 0) {
+    fprintf(stderr, "FileInputStream::openReadOnly(): err=%d\n", err);
+    throwAFSFileException( env, err, NULL );
+  }
+  return fd;
+}
+
+/**
+ * Reads up to 'length' bytes of data from this input stream
+ * into an array of bytes. This method blocks until some input is
+ * available.
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *  jbytes             the data to be written
+ *  offset             the start offset in the data
+ *  length             the number of bytes that are written
+ *
+ * @return             the total number of bytes read into the buffer, or
+ *                     <code>-1</code> if there is no more data because the end of
+ *                     the file has been reached.
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT jint JNICALL Java_org_openafs_jafs_FileInputStream_read
+  (JNIEnv *env, jobject obj, jbyteArray jbytes, jint offset, jint length)
+{
+  int fd, bytesLen, bytesRead;
+  jclass thisClass;
+  jmethodID getFileDescriptorID;
+  jbyte *bytes;
+  jfieldID fid;
+
+  /* If we have to read 0 bytes just return */
+  if(length == 0) return 0;
+
+  thisClass = (*env)->GetObjectClass(env, obj);
+  fid = (*env)->GetFieldID(env, thisClass, "fileDescriptor", "I");
+  fd = (*env)->GetIntField(env, obj, fid);
+
+  if(fd < 0) {
+    fprintf(stderr, "FileInputStream::read(): invalid file state\n");
+    throwAFSFileException(env, 0, "Invalid file state");
+    return -1;
+  }
+
+  bytes = (*env) -> GetByteArrayElements(env, jbytes, 0);
+  bytesLen = (*env) -> GetArrayLength(env, jbytes);
+  bytesRead = uafs_read(fd, bytes, bytesLen);
+
+  if (errno != 0) throwAFSFileException(env, errno, NULL);
+
+  (*env) -> ReleaseByteArrayElements(env, jbytes, bytes, 0);
+  return (bytesRead > 0) ? bytesRead : -1;
+}
+
+/**
+ * Closes this file input stream and releases any system resources 
+ * associated with this stream. This file input stream may no longer 
+ * be used for writing bytes. 
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_FileInputStream_close
+  (JNIEnv *env, jobject obj)
+{
+  int fd, rc;
+  jclass thisClass;
+  jmethodID getFileDescriptorID;
+  jfieldID fid;
+  char *bytes;
+
+  thisClass = (*env)->GetObjectClass(env, obj);
+  fid = (*env)->GetFieldID(env, thisClass, "fileDescriptor", "I");
+  fd = (*env)->GetIntField(env, obj, fid);
+
+  if(fd < 0) {
+    fprintf(stderr, "FileInputStream::close(): invalid file state\n");
+    throwAFSFileException(env, 0, "Invalid file state");
+    return;
+  }
+
+  rc = uafs_close(fd);
+
+  if (rc != 0) {
+    throwAFSFileException(env, errno, NULL);
+  }
+}
+
+
+
diff --git a/src/JAVA/libjafs/FileOutputStream.c b/src/JAVA/libjafs/FileOutputStream.c
new file mode 100644 (file)
index 0000000..ed3e617
--- /dev/null
@@ -0,0 +1,175 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_FileOutputStream.h"
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <afs/afs_usrops.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+/**
+ * Be carefull with the memory management:
+ *
+ * - For every GetStringUTFChars call the corresponding ReleaseStringUTFChars.
+ * - For every Get<type>ArrayElements call the corresponding
+ *   Release<type>ArrayElements
+ * - For every malloc call the corresponding free.
+ */
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Opens an AFS file, with the specified name, for appending.
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *  fileNameUTF        name of file to be opened
+ *
+ * @returns            file descriptor
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT jint JNICALL Java_org_openafs_jafs_FileOutputStream_openWrite
+  (JNIEnv *env, jobject obj, jstring fileNameUTF)
+{
+  int err;
+  jint fd = -1;                //file descriptor
+
+  fd = openAFSFile(env, fileNameUTF, O_CREAT | O_TRUNC, 0644, &err);
+  if (fd < 0) {
+    fprintf(stderr, "FileOutputStream::openWrite(): err=%d\n", err);
+    throwAFSFileException(env, err, NULL);
+  }
+  return fd;
+}
+
+/**
+ * Opens an AFS file, with the specified name, for writing.
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *  fileNameUTF        name of file to be opened
+ *
+ * @return             file descriptor
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT jint JNICALL Java_org_openafs_jafs_FileOutputStream_openAppend
+  (JNIEnv *env, jobject obj, jstring fileNameUTF)
+{
+  int err;
+  jint fd = -1;                //file descriptor
+
+  fd = openAFSFile(env, fileNameUTF, O_CREAT | O_APPEND, 0644, &err);
+  if (fd < 0) {
+    fprintf(stderr, "FileOutputStream::openAppend(): err=%d\n", err);
+    throwAFSFileException(env, err, NULL);
+  }
+  return fd;
+}
+
+/**
+ * Writes 'lenght' bytes from the specified byte array starting at offset 
+ * 'offset' to this file output stream. 
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *  jbytes             the data to be written
+ *  offset             the start offset in the data
+ *  length             the number of bytes that are written
+ *
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_FileOutputStream_write
+  (JNIEnv *env, jobject obj, jbyteArray jbytes, jint offset, jint length)
+{
+    int fd, written, toWrite;
+    jint twritten;
+    jclass thisClass;
+    jmethodID getFileDescriptorID;
+    char *bytes;
+    jfieldID fid;
+
+    thisClass = (*env)->GetObjectClass(env, obj);
+    fid = (*env)->GetFieldID(env, thisClass, "fileDescriptor", "I");
+    fd = (*env)->GetIntField(env, obj, fid);
+    if(fd < 0) {
+      fprintf(stderr, "FileOutputStream::write(): failed to get file 
+                       descriptor\n");
+      throwAFSFileException(env, 0, "Failed to get file descriptor!");
+    }
+    bytes = (char*) malloc(length);
+    if(bytes == NULL) {
+      fprintf(stderr, "FileOutputStream::write(): malloc failed of %d bytes\n",
+                       length);
+      throwAFSFileException(env, 0, "Failed to allocate memory!");
+    }
+    (*env) -> GetByteArrayRegion(env, jbytes, offset, length, bytes);
+    toWrite = length;
+    twritten = 0;
+    while(toWrite>0) {
+      written = uafs_write(fd, bytes, length);
+      twritten += written;
+      if(written<0) {
+        free(bytes);
+        throwAFSFileException(env, errno, NULL);
+      }
+      toWrite -= written;
+    }
+    free(bytes);
+}
+
+/**
+ * Closes this file output stream and releases any system resources 
+ * associated with this stream. This file output stream may no longer 
+ * be used for writing bytes. 
+ * 
+ *  env                the Java environment
+ *  obj                the current Java object
+ *
+ * @exception  AFSFileException  if an I/O or other file related error occurs.
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_FileOutputStream_close
+  (JNIEnv *env, jobject obj)
+{
+    int fd, rc;
+    jclass thisClass;
+    jmethodID getFileDescriptorID;
+    char *bytes;
+    jfieldID fid;
+
+    thisClass = (*env)->GetObjectClass(env, obj);
+    fid = (*env)->GetFieldID(env, thisClass, "fileDescriptor", "I");
+    fd = (*env)->GetIntField(env, obj, fid);
+    if(fd < 0) {
+      fprintf(stderr, "FileOutputStream::close(): failed to get file descriptor\n");
+      throwAFSFileException(env, 0, "Failed to get file descriptor!");
+    }
+    rc = uafs_close(fd);
+    if (rc != 0) {
+      throwAFSFileException(env, rc, NULL);
+    }
+}
+
+
diff --git a/src/JAVA/libjafs/Group.c b/src/JAVA/libjafs/Group.c
new file mode 100644 (file)
index 0000000..7b944ff
--- /dev/null
@@ -0,0 +1,827 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Group.h"
+
+#include <stdio.h>
+#include <afs_ptsAdmin.h>
+#include <afs_AdminPtsErrors.h>
+#include <afs_AdminClientErrors.h>
+#include <afs_AdminCommonErrors.h>
+#include <pterror.h>
+
+/////  definitions in Internal.c ///////////////
+
+extern jclass userCls;
+//extern jfieldID user_cellHandleField;
+extern jfieldID user_nameField;
+extern jfieldID user_cachedInfoField;
+
+extern jclass groupCls;
+extern jfieldID group_nameField;
+extern jfieldID group_nameUidField;
+extern jfieldID group_ownerUidField;
+extern jfieldID group_creatorUidField;
+extern jfieldID group_listStatusField;
+extern jfieldID group_listGroupsOwnedField;
+extern jfieldID group_listMembershipField;
+extern jfieldID group_listAddField;
+extern jfieldID group_listDeleteField;
+extern jfieldID group_membershipCountField;
+extern jfieldID group_ownerField;
+extern jfieldID group_creatorField;
+
+//////////////////////////////////////////////////////
+
+
+/**
+ * Creates the PTS entry for a new group.  Pass in 0 for the uid if PTS is to
+ * automatically assign the group id.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jgroupName      the name of the group to create
+ * jownerName      the owner of this group
+ * gid     the group id to assign to the group (0 to have one 
+ *                automatically assigned)
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_create
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupName, 
+   jstring jownerName, jint gid )
+{
+  afs_status_t ast;
+  // convert java strings
+  const char *groupName;
+  const char *ownerName;
+
+  if( jgroupName != NULL ) {
+    groupName = (*env)->GetStringUTFChars(env, jgroupName, 0);
+    if( !groupName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupName = NULL;
+  }
+
+  if( jownerName != NULL ) {
+    ownerName = (*env)->GetStringUTFChars(env, jownerName, 0);
+    if( !ownerName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    ownerName = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( groupName != NULL && strlen( groupName ) > PTS_MAX_NAME_LEN ) {
+    // release converted java strings
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    if( ownerName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jownerName, ownerName);
+    }
+    throwAFSException( env, ADMPTSGROUPNAMETOOLONG );
+    return;
+  }
+  
+  if( !pts_GroupCreate( (void *) cellHandle, groupName, ownerName, 
+                       (int *) &gid, &ast ) ) {
+    // release converted java strings
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    if( ownerName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jownerName, ownerName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  // release converted java strings
+  if( groupName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+  }
+  if( ownerName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jownerName, ownerName);
+  }
+
+}
+
+/**
+ * Deletes the PTS entry for a group.  Deletes this group from the 
+ * membership list of the users that belonged to it, but does not delete 
+ * the groups owned by this group.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jgroupName      the name of the group to delete
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_delete
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupName )
+{
+  afs_status_t ast;
+  // convert java strings
+  const char *groupName;
+
+  if( jgroupName != NULL ) {
+    groupName = (*env)->GetStringUTFChars(env, jgroupName, 0);
+    if( !groupName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupName = NULL;
+  }
+
+  if( !pts_GroupDelete( (void *) cellHandle, groupName, &ast ) ) {
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+  
+  // release converted java strings
+  if( groupName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+  }
+
+}
+
+/**
+ * Retrieve the information for the specified group and populate the
+ * given object
+ *
+ * env      the Java environment
+ * cellHandle    the handle of the cell to which the user belongs
+ * name      the name of the group for which to get the info
+ * group      the Group object to populate with the info
+ */
+void getGroupInfoChar
+  ( JNIEnv *env, jint cellHandle, const char *name, jobject group )
+{
+
+  jstring jowner;
+  jstring jcreator;
+  pts_GroupEntry_t entry;
+  afs_status_t ast;
+  // get the field ids if you haven't already
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, group );
+  }
+
+  if ( !pts_GroupGet( (void *) cellHandle, name, &entry, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  // set the fields
+  (*env)->SetIntField(env, group, group_nameUidField, entry.nameUid);
+  (*env)->SetIntField(env, group, group_ownerUidField, entry.ownerUid);
+  (*env)->SetIntField(env, group, group_creatorUidField, entry.creatorUid);
+  (*env)->SetIntField(env, group, group_membershipCountField, 
+                     entry.membershipCount);
+
+  if( entry.listStatus == PTS_GROUP_OWNER_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listStatusField, 
+                         org_openafs_jafs_Group_GROUP_OWNER_ACCESS);
+  } else if( entry.listStatus == PTS_GROUP_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listStatusField, 
+                         org_openafs_jafs_Group_GROUP_GROUP_ACCESS);
+  } else {
+      (*env)->SetIntField(env, group, group_listStatusField, 
+                         org_openafs_jafs_Group_GROUP_ANYUSER_ACCESS);
+  }
+
+  if( entry.listGroupsOwned == PTS_GROUP_OWNER_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listGroupsOwnedField, 
+                         org_openafs_jafs_Group_GROUP_OWNER_ACCESS);
+  } else if( entry.listGroupsOwned == PTS_GROUP_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listGroupsOwnedField, 
+                         org_openafs_jafs_Group_GROUP_GROUP_ACCESS);
+  } else {
+      (*env)->SetIntField(env, group, group_listGroupsOwnedField, 
+                         org_openafs_jafs_Group_GROUP_ANYUSER_ACCESS);
+  }
+
+  if( entry.listMembership == PTS_GROUP_OWNER_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listMembershipField, 
+                         org_openafs_jafs_Group_GROUP_OWNER_ACCESS);
+  } else if( entry.listMembership == PTS_GROUP_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listMembershipField, 
+                         org_openafs_jafs_Group_GROUP_GROUP_ACCESS);
+  } else {
+      (*env)->SetIntField(env, group, group_listMembershipField, 
+                         org_openafs_jafs_Group_GROUP_ANYUSER_ACCESS);
+  }
+
+  if( entry.listAdd == PTS_GROUP_OWNER_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listAddField, 
+                         org_openafs_jafs_Group_GROUP_OWNER_ACCESS);
+  } else if( entry.listAdd == PTS_GROUP_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listAddField, 
+                         org_openafs_jafs_Group_GROUP_GROUP_ACCESS);
+  } else {
+      (*env)->SetIntField(env, group, group_listAddField, 
+                         org_openafs_jafs_Group_GROUP_ANYUSER_ACCESS);
+  }
+
+  if( entry.listDelete == PTS_GROUP_OWNER_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listDeleteField, 
+                         org_openafs_jafs_Group_GROUP_OWNER_ACCESS);
+  } else if( entry.listDelete == PTS_GROUP_ACCESS ) {
+      (*env)->SetIntField(env, group, group_listDeleteField, 
+                         org_openafs_jafs_Group_GROUP_GROUP_ACCESS);
+  } else {
+      (*env)->SetIntField(env, group, group_listDeleteField, 
+                         org_openafs_jafs_Group_GROUP_ANYUSER_ACCESS);
+  }
+
+  jowner = (*env)->NewStringUTF(env, entry.owner);
+  jcreator =  (*env)->NewStringUTF(env, entry.creator);
+
+  (*env)->SetObjectField(env, group, group_ownerField, jowner);
+  (*env)->SetObjectField(env, group, group_creatorField, jcreator);
+
+}
+
+/**
+ * Fills in the information fields of the provided Group.  
+ * Fills in values based on the current PTS information of the group.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * name     the name of the group for which to get the information
+ * group     the Group object in which to fill in the 
+ *                  information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_getGroupInfo
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname, jobject group)
+{
+
+  const char *name;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+  getGroupInfoChar( env, cellHandle, name, group );
+
+  // get class fields if need be
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, group );
+  }
+
+  // set name in case blank object
+  (*env)->SetObjectField(env, group, group_nameField, jname);
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+}
+
+/**
+ * Sets the information values of this AFS group to be the parameter values.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * name     the name of the user for which to set the information
+ * theGroup   the group object containing the desired information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_setGroupInfo
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname, jobject group)
+{
+  const char *name;
+  pts_GroupUpdateEntry_t ptsEntry;
+  afs_status_t ast;
+
+  jint jlistStatus;
+  jint jlistGroupsOwned;
+  jint jlistMembership;
+  jint jlistAdd;
+  jint jlistDelete;
+
+  // get the field ids if you haven't already
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, group );
+  }
+
+  jlistStatus = (*env)->GetIntField(env, group, group_listStatusField);
+  jlistGroupsOwned = (*env)->GetIntField(env, group, 
+                                        group_listGroupsOwnedField);
+  jlistMembership = (*env)->GetIntField(env, group, group_listMembershipField);
+  jlistAdd = (*env)->GetIntField(env, group, group_listAddField);
+  jlistDelete = (*env)->GetIntField(env, group, group_listDeleteField);
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  if( jlistStatus == org_openafs_jafs_Group_GROUP_OWNER_ACCESS ) {
+    ptsEntry.listStatus = PTS_GROUP_OWNER_ACCESS;
+  } else if( jlistStatus == org_openafs_jafs_Group_GROUP_GROUP_ACCESS ) {
+    ptsEntry.listStatus = PTS_GROUP_ACCESS;
+  } else {
+    ptsEntry.listStatus = PTS_GROUP_ANYUSER_ACCESS;
+  }
+  if( jlistGroupsOwned == org_openafs_jafs_Group_GROUP_OWNER_ACCESS ) {
+    ptsEntry.listGroupsOwned = PTS_GROUP_OWNER_ACCESS;
+  } else if( jlistGroupsOwned == 
+            org_openafs_jafs_Group_GROUP_GROUP_ACCESS ) {
+    ptsEntry.listGroupsOwned = PTS_GROUP_ACCESS;
+  } else {
+    ptsEntry.listGroupsOwned = PTS_GROUP_ANYUSER_ACCESS;
+  }
+  if( jlistMembership == org_openafs_jafs_Group_GROUP_OWNER_ACCESS ) {
+    ptsEntry.listMembership = PTS_GROUP_OWNER_ACCESS;
+  } else if( jlistMembership == 
+            org_openafs_jafs_Group_GROUP_GROUP_ACCESS ) {
+    ptsEntry.listMembership = PTS_GROUP_ACCESS;
+  } else {
+    ptsEntry.listMembership = PTS_GROUP_ANYUSER_ACCESS;
+  }
+  if( jlistAdd == org_openafs_jafs_Group_GROUP_OWNER_ACCESS ) {
+    ptsEntry.listAdd = PTS_GROUP_OWNER_ACCESS;
+  } else if( jlistAdd == org_openafs_jafs_Group_GROUP_GROUP_ACCESS ) {
+    ptsEntry.listAdd = PTS_GROUP_ACCESS;
+  } else {
+    ptsEntry.listAdd = PTS_GROUP_ANYUSER_ACCESS;
+  }
+  if( jlistDelete == org_openafs_jafs_Group_GROUP_OWNER_ACCESS ) {
+    ptsEntry.listDelete = PTS_GROUP_OWNER_ACCESS;
+  } else if( jlistDelete == org_openafs_jafs_Group_GROUP_GROUP_ACCESS ) {
+    ptsEntry.listDelete = PTS_GROUP_ACCESS;
+  } else {
+    ptsEntry.listDelete = PTS_GROUP_ANYUSER_ACCESS;
+  }
+  if( !pts_GroupModify( (void *) cellHandle, name, &ptsEntry, &ast ) ) {
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    throwAFSException( env, ast );
+    return;    
+  }
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+}
+
+/**
+ * Begin the process of getting the users that belong to the group.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getGroupMembersNext and getGroupMembersDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jname          the name of the group for which to get the members
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Group_getGroupMembersBegin
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname)
+{
+  const char *name;
+  afs_status_t ast;
+  void *iterationId;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  if( !pts_GroupMemberListBegin( (void *) cellHandle, name, &iterationId, 
+                                &ast ) ) {
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next members that belongs to the group.  Returns 
+ * null if there are no more members.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next member
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Group_getGroupMembersNextString
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+  char *userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+  jstring juser;
+
+  if( !userName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_GroupMemberListNext( (void *) iterationId, userName, &ast ) ) {
+    free( userName );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+  
+  juser = (*env)->NewStringUTF(env, userName);
+  free( userName );
+  return juser;
+}
+
+/**
+ * Fills the next user object belonging to that group.  Returns 0 if there
+ * are no more users, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the users belong
+ * iterationId   the iteration ID of this iteration
+ * juserObject   a User object to be populated with the values of the 
+ *                  next user
+ * returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Group_getGroupMembersNext
+  (JNIEnv *env, jclass cls, jint cellHandle, jint iterationId,
+   jobject juserObject)
+{
+  afs_status_t ast;
+  char *userName;
+  jstring juser;
+
+  userName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !userName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_GroupMemberListNext( (void *) iterationId, userName, &ast ) ) {
+    free( userName );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  juser = (*env)->NewStringUTF(env, userName);
+
+  if( userCls == 0 ) {
+    internal_getUserClass( env, juserObject );
+  }
+
+  (*env)->SetObjectField(env, juserObject, user_nameField, juser);
+
+  getUserInfoChar( env, (void *) cellHandle, userName, juserObject );
+  (*env)->SetBooleanField( env, juserObject, user_cachedInfoField, TRUE );
+
+  free( userName );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_getGroupMembersDone
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+
+  if( !pts_GroupMemberListDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Adds a user to the specified group. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jgroupName          the name of the group to which to add a member
+ * juserName      the name of the user to add
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_addMember
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupName,
+   jstring juserName )
+{
+  afs_status_t ast;
+  const char *groupName;
+  const char *userName;
+
+  if( jgroupName != NULL ) {
+    groupName = (*env)->GetStringUTFChars(env, jgroupName, 0);
+    if( !groupName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupName = NULL;
+  }
+
+  if( juserName != NULL ) {
+    userName = (*env)->GetStringUTFChars(env, juserName, 0);
+    if( !userName ) {
+      if( groupName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    userName = NULL;
+  }
+
+  if( !pts_GroupMemberAdd( (void *) cellHandle, userName, groupName, &ast ) ) {
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( groupName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+  }
+  if( userName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+}
+
+/**
+ * Removes a user from the specified group. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jgroupName          the name of the group from which to remove a 
+ *                           member
+ * juserName      the name of the user to remove
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_removeMember
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupName,
+   jstring juserName)
+{
+  afs_status_t ast;
+  const char *groupName;
+  const char *userName;
+
+  if( jgroupName != NULL ) {
+    groupName = (*env)->GetStringUTFChars(env, jgroupName, 0);
+    if( !groupName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupName = NULL;
+  }
+
+  if( juserName != NULL ) {
+    userName = (*env)->GetStringUTFChars(env, juserName, 0);
+    if( !userName ) {
+      if( groupName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    userName = NULL;
+  }
+
+  if( !pts_GroupMemberRemove( (void *)cellHandle, userName, 
+                             groupName, &ast ) ) {
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+  
+  if( groupName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+  }
+  if( userName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+}
+
+/**
+ * Change the owner of the specified group. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * jgroupName          the name of the group of which to change the 
+ *                           owner
+ * jownerName      the name of the new owner
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_changeOwner
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupName,
+   jstring jownerName )
+{
+  afs_status_t ast;
+  const char *groupName;
+  const char *ownerName;
+
+  if( jgroupName != NULL ) {
+    groupName = (*env)->GetStringUTFChars(env, jgroupName, 0);
+    if( !groupName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupName = NULL;
+  }
+
+  if( jownerName != NULL ) {
+    ownerName = (*env)->GetStringUTFChars(env, jownerName, 0);
+    if( !ownerName ) {
+      if( groupName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    ownerName = NULL;
+  }
+
+  if( !pts_GroupOwnerChange( (void *)cellHandle, groupName, 
+                            ownerName, &ast ) ) {
+    if( groupName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+    }
+    if( ownerName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jownerName, ownerName);
+    }
+    throwAFSException( env, ast );
+    return; 
+  }
+
+  if( groupName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupName, groupName);
+  }
+  if( ownerName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jownerName, ownerName);
+  }
+  
+}
+
+/**
+ * Change the name of the specified group. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the group belongs
+ * joldGroupName          the old name of the group
+ * jnewGroupName      the new name for the group
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_rename
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jgroupOldName, 
+   jstring jgroupNewName )
+{
+  afs_status_t ast;
+  const char *groupOldName;
+  const char *groupNewName;
+
+  if( jgroupOldName != NULL ) {
+    groupOldName = (*env)->GetStringUTFChars(env, jgroupOldName, 0);
+    if( !groupOldName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    groupOldName = NULL;
+  }
+
+  if( jgroupNewName != NULL ) {
+    groupNewName = (*env)->GetStringUTFChars(env, jgroupNewName, 0);
+    if( !groupNewName ) {
+      if( groupOldName != NULL ) {
+        (*env)->ReleaseStringUTFChars(env, jgroupOldName, groupOldName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    groupNewName = NULL;
+  }
+
+  if( !pts_GroupRename( (void *)cellHandle, groupOldName, 
+                       groupNewName, &ast ) ) {
+    if( groupOldName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupOldName, groupOldName);
+    }
+    if( groupNewName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jgroupNewName, groupNewName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( groupOldName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupOldName, groupOldName);
+  }
+  if( groupNewName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jgroupNewName, groupNewName);
+  }
+}
+
+// reclaim global memory used by this portion
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Group_reclaimGroupMemory (JNIEnv *env, jclass cls)
+{
+  if( groupCls ) {
+      (*env)->DeleteGlobalRef(env, groupCls);
+      groupCls = 0;
+  }
+}
+
+
+
diff --git a/src/JAVA/libjafs/Internal.c b/src/JAVA/libjafs/Internal.c
new file mode 100644 (file)
index 0000000..c43bcb4
--- /dev/null
@@ -0,0 +1,909 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+extern int errno;
+
+#ifndef LIBJUAFS
+// user class and fields //
+jclass userCls = 0;
+jfieldID user_ptsField = 0;
+jfieldID user_kasField = 0;
+jfieldID user_nameField = 0;
+//jfieldID user_cellHandleField = 0;
+jfieldID user_cachedInfoField = 0;
+//pts fields
+jfieldID user_nameUidField = 0;
+jfieldID user_ownerUidField = 0;
+jfieldID user_creatorUidField = 0;
+jfieldID user_listStatusField = 0;
+jfieldID user_listGroupsOwnedField = 0;
+jfieldID user_listMembershipField = 0;
+jfieldID user_groupCreationQuotaField = 0;
+jfieldID user_groupMembershipCountField = 0;
+jfieldID user_ownerField = 0;
+jfieldID user_creatorField = 0;
+// kas fields
+jfieldID user_adminSettingField = 0;
+jfieldID user_tgsSettingField = 0;
+jfieldID user_encSettingField = 0;
+jfieldID user_cpwSettingField = 0;
+jfieldID user_rpwSettingField = 0;
+jfieldID user_userExpirationField = 0;
+jfieldID user_lastModTimeField = 0;
+jfieldID user_lastModNameField = 0;
+jfieldID user_lastChangePasswordTimeField = 0;
+jfieldID user_maxTicketLifetimeField = 0;
+jfieldID user_keyVersionField = 0;
+jfieldID user_encryptionKeyField = 0;
+jfieldID user_keyCheckSumField = 0;
+jfieldID user_daysToPasswordExpireField = 0;
+jfieldID user_failLoginCountField = 0;
+jfieldID user_lockTimeField = 0;
+jfieldID user_lockedUntilField = 0;
+
+// group class and fields //
+jclass groupCls = 0;
+jfieldID group_nameField = 0;
+//jfieldID group_cellHandleField = 0;
+jfieldID group_cachedInfoField = 0;
+jfieldID group_nameUidField = 0;
+jfieldID group_ownerUidField = 0;
+jfieldID group_creatorUidField = 0;
+jfieldID group_listStatusField = 0;
+jfieldID group_listGroupsOwnedField = 0;
+jfieldID group_listMembershipField = 0;
+jfieldID group_listAddField = 0;
+jfieldID group_listDeleteField = 0;
+jfieldID group_membershipCountField = 0;
+jfieldID group_ownerField = 0;
+jfieldID group_creatorField = 0;
+
+// server class and fields //
+jclass serverCls = 0;
+jfieldID server_nameField = 0;
+//jfieldID server_cellHandleField = 0;
+jfieldID server_cachedInfoField = 0;
+jfieldID server_databaseField = 0;
+jfieldID server_fileServerField = 0;
+jfieldID server_badDatabaseField = 0;
+jfieldID server_badFileServerField = 0;
+jfieldID server_IPAddressField = 0;
+
+// executable time class and fields //
+jclass exectimeCls = 0;
+jfieldID exectime_HourField = 0;
+jfieldID exectime_MinField = 0;
+jfieldID exectime_SecField = 0;
+jfieldID exectime_DayField = 0;
+jfieldID exectime_NowField = 0;
+jfieldID exectime_NeverField = 0;
+
+// partition class and fields //
+jclass partitionCls = 0;
+jfieldID partition_nameField = 0;
+jfieldID partition_cachedInfoField = 0;
+jfieldID partition_idField = 0;
+jfieldID partition_deviceNameField = 0;
+jfieldID partition_lockFileDescriptorField = 0;
+jfieldID partition_totalSpaceField = 0;
+jfieldID partition_totalFreeSpaceField = 0;
+
+// volume class and fields //
+jclass volumeCls = 0;
+jfieldID volume_nameField = 0;
+jfieldID volume_cachedInfoField = 0;
+jfieldID volume_idField = 0;
+jfieldID volume_readWriteIdField = 0;
+jfieldID volume_readOnlyIdField = 0;
+jfieldID volume_backupIdField = 0;
+jfieldID volume_creationDateField = 0;
+jfieldID volume_lastAccessDateField = 0;
+jfieldID volume_lastUpdateDateField = 0;
+jfieldID volume_lastBackupDateField = 0;
+jfieldID volume_copyCreationDateField = 0;
+jfieldID volume_accessesSinceMidnightField = 0;
+jfieldID volume_fileCountField = 0;
+jfieldID volume_maxQuotaField = 0;
+jfieldID volume_currentSizeField = 0;
+jfieldID volume_statusField = 0;
+jfieldID volume_dispositionField = 0;
+jfieldID volume_typeField = 0;
+
+// key class and fields //
+jclass keyCls = 0;
+jfieldID key_cachedInfoField = 0;
+jfieldID key_versionField = 0;
+jfieldID key_encryptionKeyField = 0;
+jfieldID key_lastModDateField = 0;
+jfieldID key_lastModMsField = 0;
+jfieldID key_checkSumField = 0;
+
+// process class and fields //
+jclass processCls = 0;
+jfieldID process_cachedInfoField = 0;
+jfieldID process_nameField = 0;
+//jfieldID process_serverHandleField = 0;
+jfieldID process_typeField = 0;
+jfieldID process_stateField = 0;
+jfieldID process_goalField = 0;
+jfieldID process_startTimeField = 0;
+jfieldID process_numberStartsField = 0;
+jfieldID process_exitTimeField = 0;
+jfieldID process_exitErrorTimeField = 0;
+jfieldID process_errorCodeField = 0;
+jfieldID process_errorSignalField = 0;
+jfieldID process_stateOkField = 0;
+jfieldID process_stateTooManyErrorsField = 0;
+jfieldID process_stateBadFileAccessField = 0;
+#endif /* !LIBJUAFS */
+
+/**
+ * Throws an exception up to the Java layer, using ast as the error code
+ * for the exception.  See Exceptions.h for the available
+ * exceptions.
+ */
+void throwException
+  (JNIEnv *env, jclass *excCls, char *excClsName, jmethodID *initID, int code)
+{
+  jobject exc;
+  if( *excCls == 0 ) {
+    *excCls = (*env)->NewGlobalRef(env, (*env)->FindClass(env, excClsName ));
+    if( !*excCls ) {
+      fprintf(stderr, "ERROR: Internal::throwException()\n
+                       Cannot find class: %s\n", excClsName);
+       return;
+    }
+    *initID = (*env)->GetMethodID( env, *excCls, "<init>", "(I)V" );
+    if( !*initID ) {
+      fprintf(stderr, "ERROR: Internal::throwException()\n
+                       Cannot find construction method: %s\n",
+                       excClsName);
+       return;
+    }
+  }
+  
+  exc = (*env)->NewObject( env, *excCls, *initID, code );
+  if( !exc ) {
+    fprintf(stderr, "ERROR: Internal::throwException()\n
+                     Cannot construct new exception object: %s\n",
+                     excClsName);
+    return;
+  }
+  (*env)->Throw(env, exc);
+}
+
+/**
+ * Throws an exception up to the Java layer, constructing it with msg.
+ * This function should only be used when a valid AFS error number/code
+ * is unavailable and it is necessary to interrupt the Java call with an
+ * exception. See Exceptions.h for the available exceptions.
+ */
+void throwMessageException( JNIEnv *env, char *msg )
+{
+  jclass excCls = (*env)->FindClass(env, afsExceptionName);
+  if(excCls == 0) {
+    fprintf(stderr, "ERROR: Internal::throwMessageException()\n
+                     Cannot find class: %s\n", afsExceptionName);
+    return;
+  }
+  (*env)->ThrowNew(env, excCls, msg);
+}
+
+/**
+ * Throws an exception up to the Java layer, using ast as the error code
+ * for the exception.  See Exceptions.h for the available
+ * exceptions.
+ */
+void throwAFSException( JNIEnv *env, int code )
+{
+  jclass afsExceptionCls;
+  jmethodID afsExceptionInit;
+  jthrowable exc;
+
+  afsExceptionCls = (*env)->FindClass(env, afsExceptionName);
+  if( !afsExceptionCls ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSException()\n
+                     Cannot find class: %s\n", afsExceptionName);
+    return;
+  }
+
+  afsExceptionInit = (*env)->GetMethodID( env, afsExceptionCls, 
+                             "<init>", "(I)V" );
+  if( !afsExceptionInit ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSException()\n
+                     Cannot find construction method: %s\n",
+                     afsExceptionName);
+    return;
+  }
+  
+  exc = (*env)->NewObject( env, afsExceptionCls, afsExceptionInit, code );
+
+  if( !exc ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSException()\n
+                     Cannot construct new exception object: %s\n",
+                     afsExceptionName);
+    return;
+  }
+  (*env)->Throw(env, exc);
+}
+
+/**
+ * Throws an exception up to the Java layer, using ast as the error code
+ * for the exception.  See Exceptions.h for the available
+ * exceptions.
+ */
+void throwAFSFileException( JNIEnv *env, int code, char *msg )
+{
+  jclass afsFileExceptionCls;
+  jmethodID afsFileExceptionInit;
+  jthrowable exc;
+
+  afsFileExceptionCls = (*env)->FindClass(env, afsFileExceptionName);
+  if( !afsFileExceptionCls ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSFileException()\n
+                     Cannot find class: %s\n", afsFileExceptionName);
+    return;
+  }
+
+  afsFileExceptionInit = (*env)->GetMethodID( env, afsFileExceptionCls, 
+                         "<init>", "(Ljava/lang/String;I)V" );
+  if( !afsFileExceptionInit ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSFileException()\n
+                     Cannot find construction method: %s\n",
+                     afsFileExceptionName);
+    return;
+  }
+  
+  exc = (*env)->NewObject( env, afsFileExceptionCls,
+                           afsFileExceptionInit, msg, code );
+  if( !exc ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSFileException()\n
+                     Cannot construct new exception object: %s\n",
+                     afsFileExceptionName);
+    return;
+  }
+  (*env)->Throw(env, exc);
+}
+
+/**
+ * Throws an exception up to the Java layer, using ast as the error code
+ * for the exception.  See Exceptions.h for the available
+ * exceptions.
+ */
+void throwAFSSecurityException( JNIEnv *env, int code )
+{
+  jclass afsSecurityExceptionCls;
+  jmethodID afsSecurityExceptionInit;
+  jthrowable exc;
+
+  afsSecurityExceptionCls = (*env)->FindClass(env, afsSecurityExceptionName);
+  if( !afsSecurityExceptionCls ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSSecurityException()\n
+                     Cannot find class: %s\n", afsSecurityExceptionName);
+    return;
+  }
+
+  afsSecurityExceptionInit = (*env)->GetMethodID( env, afsSecurityExceptionCls, 
+                             "<init>", "(I)V" );
+  if( !afsSecurityExceptionInit ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSSecurityException()\n
+                     Cannot find construction method: %s\n",
+                     afsSecurityExceptionName);
+    return;
+  }
+  
+  exc = (*env)->NewObject( env, afsSecurityExceptionCls,
+                           afsSecurityExceptionInit, code );
+  if( !exc ) {
+    fprintf(stderr, "ERROR: Internal::throwAFSSecurityException()\n
+                     Cannot construct new exception object: %s\n",
+                     afsSecurityExceptionName);
+    return;
+  }
+  (*env)->Throw(env, exc);
+}
+
+int setError(JNIEnv *env, jobject *obj, int code)
+{
+  jfieldID fid;
+  jclass cls = (*env)->GetObjectClass(env, *obj);
+  if (cls != NULL) {
+    fid = (*env)->GetFieldID(env, cls, "errno", "I");
+    if (fid)
+    {
+      (*env)->SetIntField(env, *obj, fid, code);
+      return 0;
+    }
+  }
+  return -1;
+}
+
+#ifdef LIBJUAFS
+
+/**
+ * Opens an AFS file, with the specified name, using the specified flags
+ * with in the specified mode (permission mode).
+ * 
+ *  env                the Java environment
+ *  fileNameUTF        name of file to be opened
+ *  flags              open mode: O_CREAT, O_APPEND
+ *  mode               UNIX permission mode mask
+ *  err                error variable
+ *
+ * @returns            file descriptor
+ */
+int openAFSFile
+  (JNIEnv *env, jstring fileNameUTF, int flags, int mode, int *err)
+{
+    char *fileName;
+    int fd = -1;
+
+    *err = 0;
+    errno = 0;
+    fileName=(char*) (*env)->GetStringUTFChars(env, fileNameUTF, 0);
+    if(fileName == NULL) {
+      fprintf(stderr, "Internal::openAFSFile(): failed to get fileName\n");
+      *err = -1;
+      return fd;
+    }
+    fd  = uafs_open(fileName, flags, mode);
+    *err = errno;
+    if (errno != 0) {
+      fprintf(stderr, "Internal::openAFSFile(): errno=%d\n", errno);
+      fprintf(stderr, "Internal::openAFSFile(): fd=%d\n", fd);
+    }
+    (*env)->ReleaseStringUTFChars(env, fileNameUTF, fileName);
+    if (fd < 0) {
+      fprintf(stderr, "Internal::openAFSFile(): failed to open fileName\n");
+      fprintf(stderr, "Internal::openAFSFile(): fd=%d\n", fd);
+      return -1;
+    }
+    return fd;
+}
+
+int readCacheParms(char *afsMountPoint, char *afsConfDir, char *afsCacheDir,
+                   int *cacheBlocks, int *cacheFiles, int *cacheStatEntries,
+                   int *dCacheSize, int *vCacheSize, int *chunkSize,
+                   int *closeSynch, int *debug, int *nDaemons, int *cacheFlags,
+                   char *logFile)
+{
+  FILE *f;
+  char line[100];
+  char *p;
+  int len1, len2, n;
+  char cacheConfigFile[100];
+  
+  p = (char *)getenv("LIBJAFS_CACHE_CONFIG");
+  if (p) {
+    strcpy(cacheConfigFile, p);
+  } else {
+    strcpy(cacheConfigFile, "/usr/afswsp/etc/CacheConfig");
+  }
+
+  f = fopen(cacheConfigFile, "r");
+  if (!f) {
+    fprintf(stderr, "Could not open cache config file: %s\n",
+            cacheConfigFile);
+    return -1;
+  }
+
+  while (1) {
+    fgets(line, 100, f);
+    if (feof(f)) break;
+    p = (char *)strchr(line, '\n');
+    if (p) *p = '\0';
+    if (strncmp(line, "#", 1) == 0) continue;  /* comment */
+
+    p = (char *)strchr(line, ' ');
+    if (!p) continue;
+    len1 = p - line;
+    p++; len2 = strlen(p);
+
+    if (strncmp(line, "MountPoint", len1) == 0)
+       strcpy(afsMountPoint, p);
+    else if (strncmp(line, "ConfDir", len1) == 0)
+       strcpy(afsConfDir, p);
+    else if (strncmp(line, "CacheDir", len1) == 0)
+       strcpy(afsCacheDir, p);
+    else if (strncmp(line, "CacheBlocks", len1) == 0)
+       *cacheBlocks = atoi(p);
+    else if (strncmp(line, "CacheFiles", len1) == 0)
+       *cacheFiles = atoi(p);
+    else if (strncmp(line, "CacheStatEntries", len1) == 0)
+       *cacheStatEntries = atoi(p);
+    else if (strncmp(line, "DCacheSize", len1) == 0)
+       *dCacheSize = atoi(p);
+    else if (strncmp(line, "VCacheSize", len1) == 0)
+       *vCacheSize = atoi(p);
+    else if (strncmp(line, "ChunkSize", len1) == 0)
+       *chunkSize = atoi(p);
+    else if (strncmp(line, "CloseSynch", len1) == 0)
+       *closeSynch = atoi(p);
+    else if (strncmp(line, "Debug", len1) == 0)
+       *debug = atoi(p);
+    else if (strncmp(line, "NDaemons", len1) == 0)
+       *nDaemons = atoi(p);
+    else if (strncmp(line, "CacheFlags", len1) == 0)
+       *cacheFlags = atoi(p);
+    else if (strncmp(line, "LogFile", len1) == 0)
+       strcpy(logFile, p);
+  }
+  return 0;
+}
+
+int setString(JNIEnv *env, jobject *obj, char *field, char *string)
+{
+  jclass cls;
+  jstring jstr;
+  jfieldID fid;
+
+  cls = (*env)->GetObjectClass(env, *obj);
+  /*fprintf(stderr, "setString: env=0x%x, obj=0x%x, cls=0x%x\n", env, obj, cls);*/
+  if (cls != NULL) {
+    fid = (*env)->GetFieldID(env, cls, field, "Ljava/lang/String;");
+    /*fprintf(stderr, "setString: field=%s, fid=0x%x\n", field, fid);*/
+    if (fid) {
+      jstr = (*env)->NewStringUTF(env, (string));
+      /*fprintf(stderr, "jstr = 0x%x\n", jstr);*/
+      (*env)->SetObjectField(env, *obj, fid, jstr);
+      return 0;
+    }
+  }
+  return -1;
+}
+
+#else
+
+/**
+ * Makes a kas identity given the full name of a kas user.  If the
+ * name contains a period, everything after the first period is
+ * considered to be the instance of that name, otherwise
+ * the instance is the empty string.  The memory for who 
+ * that's passed in should be fully allocated in advance.
+ */
+void internal_makeKasIdentity( const char *fullName, 
+                                      kas_identity_p who ) {
+
+  char *period;
+
+  if( (period = (char *) strchr( fullName, '.' )) != NULL ) {
+    strncpy( who->principal, fullName, period - fullName );
+    who->principal[period - fullName] = '\0';
+    strncpy( who->instance, period + 1, 
+            strlen(fullName) - (period - fullName) );
+  } else {
+    strcpy( who->principal, fullName);
+    strcpy( who->instance, "" );
+  }
+
+}
+
+/**
+ * Given a Java environment and an instance of a user, gets the object and
+ * field information for the user object from the Java environment.
+ */
+void internal_getUserClass( JNIEnv *env, jobject user ) {
+  if( userCls == 0 ) {
+    userCls = (*env)->NewGlobalRef( env, (*env)->GetObjectClass(env, user) );
+    if( !userCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    user_ptsField = (*env)->GetFieldID( env, userCls, "pts", "Z" );
+    user_kasField = (*env)->GetFieldID( env, userCls, "kas", "Z" );
+    user_nameField = (*env)->GetFieldID( env, userCls, "name", 
+                                        "Ljava/lang/String;" );
+    user_cachedInfoField = (*env)->GetFieldID( env, userCls, "cachedInfo", 
+                                              "Z" );
+    // pts fields
+    user_nameUidField = (*env)->GetFieldID( env, userCls, "nameUID", "I" );
+    user_ownerUidField = (*env)->GetFieldID( env, userCls, "ownerUID", "I" );
+    user_creatorUidField = (*env)->GetFieldID( env, userCls, "creatorUID", 
+                                              "I" );
+    user_listStatusField = (*env)->GetFieldID( env, userCls, "listStatus", 
+                                              "I" );
+    user_listGroupsOwnedField = (*env)->GetFieldID( env, userCls, 
+                                                   "listGroupsOwned", "I" );
+    user_listMembershipField = (*env)->GetFieldID( env, userCls, 
+                                                  "listMembership", "I" );
+    user_groupCreationQuotaField = (*env)->GetFieldID( env, userCls, 
+                                                      "groupCreationQuota", 
+                                                      "I" );
+    user_groupMembershipCountField = (*env)->GetFieldID( env, userCls, 
+                                                       "groupMembershipCount",
+                                                        "I" );
+    user_ownerField = (*env)->GetFieldID( env, userCls, "owner", 
+                                         "Ljava/lang/String;" );
+    user_creatorField = (*env)->GetFieldID( env, userCls, "creator", 
+                                           "Ljava/lang/String;" );
+    // kas fields
+    user_adminSettingField = (*env)->GetFieldID( env, userCls, "adminSetting",
+                                                "I" );
+    user_tgsSettingField = (*env)->GetFieldID( env, userCls, "tgsSetting", 
+                                              "I" );
+    user_encSettingField = (*env)->GetFieldID( env, userCls, "encSetting", 
+                                              "I" );
+    user_cpwSettingField = (*env)->GetFieldID( env, userCls, "cpwSetting", 
+                                              "I" );
+    user_rpwSettingField = (*env)->GetFieldID( env, userCls, "rpwSetting", 
+                                              "I" );
+    user_userExpirationField = (*env)->GetFieldID( env, userCls, 
+                                                  "userExpiration", "I" );
+    user_lastModTimeField = (*env)->GetFieldID( env, userCls, "lastModTime", 
+                                               "I" );
+    user_lastModNameField = (*env)->GetFieldID( env, userCls, "lastModName", 
+                                               "Ljava/lang/String;" );
+    user_lastChangePasswordTimeField = (*env)->GetFieldID( env, userCls, 
+                                                     "lastChangePasswordTime",
+                                                          "I" );
+    user_maxTicketLifetimeField = (*env)->GetFieldID( env, userCls, 
+                                                     "maxTicketLifetime", 
+                                                     "I" );
+    user_keyVersionField = (*env)->GetFieldID( env, userCls, "keyVersion", 
+                                              "I" );
+    user_encryptionKeyField = (*env)->GetFieldID( env, userCls, 
+                                                 "encryptionKey", 
+                                                 "Ljava/lang/String;" );
+    user_keyCheckSumField = (*env)->GetFieldID( env, userCls, "keyCheckSum", 
+                                               "J" );
+    user_daysToPasswordExpireField = (*env)->GetFieldID( env, userCls, 
+                                                       "daysToPasswordExpire",
+                                                        "I" );
+    user_failLoginCountField = (*env)->GetFieldID( env, userCls, 
+                                                  "failLoginCount", "I" );
+    user_lockTimeField = (*env)->GetFieldID( env, userCls, "lockTime", "I" );
+    user_lockedUntilField = (*env)->GetFieldID( env, userCls, "lockedUntil", 
+                                               "I" );
+    if( !user_ptsField || !user_kasField || !user_nameField || 
+       !user_cachedInfoField || !user_nameUidField || !user_ownerUidField || 
+       !user_creatorUidField || !user_listStatusField || 
+       !user_listGroupsOwnedField || !user_listMembershipField || 
+       !user_groupCreationQuotaField || !user_groupMembershipCountField || 
+       !user_ownerField || !user_creatorField || !user_adminSettingField || 
+       !user_tgsSettingField || !user_encSettingField || 
+       !user_cpwSettingField || !user_rpwSettingField || 
+       !user_userExpirationField || !user_lastModTimeField || 
+       !user_lastModNameField || !user_lastChangePasswordTimeField || 
+       !user_maxTicketLifetimeField || !user_keyVersionField || 
+       !user_encryptionKeyField || !user_keyCheckSumField || 
+       !user_daysToPasswordExpireField || !user_failLoginCountField || 
+       !user_lockTimeField || !user_lockedUntilField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  } 
+}
+
+/**
+ * Given a Java environment and an instance of a group, gets the object and
+ * field information for the group object from the Java environment.
+ */
+void internal_getGroupClass( JNIEnv *env, jobject group ) {
+  if( groupCls == 0 ) {
+    groupCls = (*env)->NewGlobalRef( env, (*env)->GetObjectClass(env, group) );
+    if( !groupCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    group_nameField = (*env)->GetFieldID( env, groupCls, "name", 
+                                         "Ljava/lang/String;" );
+    group_cachedInfoField = (*env)->GetFieldID( env, groupCls, "cachedInfo", 
+                                               "Z" );
+    group_nameUidField = (*env)->GetFieldID( env, groupCls, "nameUID", "I" );
+    group_ownerUidField = (*env)->GetFieldID( env, groupCls, "ownerUID", "I" );
+    group_creatorUidField = (*env)->GetFieldID( env, groupCls, "creatorUID", 
+                                               "I" );
+    group_listStatusField = (*env)->GetFieldID( env, groupCls, "listStatus", 
+                                               "I" );
+    group_listGroupsOwnedField = (*env)->GetFieldID( env, groupCls, 
+                                                    "listGroupsOwned", "I" );
+    group_listMembershipField = (*env)->GetFieldID( env, groupCls, 
+                                                   "listMembership", "I" );
+    group_listAddField = (*env)->GetFieldID( env, groupCls, "listAdd", "I" );
+    group_listDeleteField = (*env)->GetFieldID( env, groupCls, "listDelete", 
+                                               "I" );
+    group_membershipCountField = (*env)->GetFieldID( env, groupCls, 
+                                                    "membershipCount", "I" );
+    group_ownerField = (*env)->GetFieldID( env, groupCls, "owner", 
+                                          "Ljava/lang/String;" );
+    group_creatorField = (*env)->GetFieldID( env, groupCls, "creator", 
+                                            "Ljava/lang/String;" );
+    if( !group_nameField || !group_cachedInfoField || !group_nameUidField || 
+       !group_ownerUidField || !group_creatorUidField || 
+       !group_listStatusField || !group_listGroupsOwnedField || 
+       !group_listMembershipField || !group_listAddField || 
+       !group_listDeleteField || !group_membershipCountField || 
+       !group_ownerField || !group_creatorField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of a server, gets the object and
+ * field information for the server object from the Java environment.
+ */
+void internal_getServerClass( JNIEnv *env, jobject server ) {
+  if( serverCls == 0 ) {
+    serverCls = (*env)->NewGlobalRef( env,
+                                      (*env)->GetObjectClass(env, server) );
+    if( !serverCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    server_nameField = (*env)->GetFieldID( env, serverCls, "name", 
+                                          "Ljava/lang/String;" );
+    server_cachedInfoField = (*env)->GetFieldID( env, serverCls, "cachedInfo",
+                                                "Z" );
+    server_databaseField = (*env)->GetFieldID( env, serverCls, "database", 
+                                              "Z" );
+    server_fileServerField = (*env)->GetFieldID( env, serverCls, "fileServer", 
+                                                "Z" );
+    server_badDatabaseField = (*env)->GetFieldID( env, serverCls, 
+                                                 "badDatabase", "Z" );
+    server_badFileServerField = (*env)->GetFieldID( env, serverCls, 
+                                                   "badFileServer", "Z" );
+    server_IPAddressField = (*env)->GetFieldID( env, serverCls, "ipAddresses",
+                                               "[Ljava/lang/String;" );
+    if( !server_nameField || !server_cachedInfoField || !server_databaseField 
+       || !server_fileServerField || !server_badDatabaseField || 
+       !server_badFileServerField || !server_IPAddressField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of an executableTime, gets the 
+ * object and field information for the executableTime object from the 
+ * Java environment.
+ */
+void internal_getExecTimeClass( JNIEnv *env, jobject exectime ) {
+  if( exectimeCls == 0 ) {
+    exectimeCls = (*env)->NewGlobalRef( env, 
+                                      (*env)->GetObjectClass(env, exectime) );
+    if( !exectimeCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    exectime_HourField  = (*env)->GetFieldID( env, exectimeCls, "hour", "S" );
+    exectime_MinField   = (*env)->GetFieldID( env, exectimeCls, "minute", 
+                                             "S" );
+    exectime_SecField   = (*env)->GetFieldID( env, exectimeCls, "second", 
+                                             "S" );
+    exectime_DayField   = (*env)->GetFieldID( env, exectimeCls, "day", "S" );
+    exectime_NowField   = (*env)->GetFieldID( env, exectimeCls, "now", "Z" );
+    exectime_NeverField = (*env)->GetFieldID( env, exectimeCls, "never", "Z" );
+    if( !exectime_HourField || !exectime_MinField || !exectime_SecField || 
+       !exectime_DayField || !exectime_NowField || !exectime_NeverField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of a partition, gets the object and
+ * field information for the partition object from the Java environment.
+ */
+void internal_getPartitionClass( JNIEnv *env, jobject partition ) {
+  if( partitionCls == 0 ) {
+    partitionCls = (*env)->NewGlobalRef( env, 
+                                     (*env)->GetObjectClass(env, partition) );
+    if( !partitionCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    partition_nameField = (*env)->GetFieldID( env, partitionCls, "name", 
+                                             "Ljava/lang/String;" );
+    partition_deviceNameField = (*env)->GetFieldID( env, partitionCls, 
+                                                   "deviceName", 
+                                                   "Ljava/lang/String;" );
+    partition_idField = (*env)->GetFieldID( env, partitionCls, "id", "I" );
+    partition_cachedInfoField = (*env)->GetFieldID( env, partitionCls, 
+                                                   "cachedInfo", "Z" );
+    partition_lockFileDescriptorField = (*env)->GetFieldID( env, partitionCls,
+                                                         "lockFileDescriptor",
+                                                           "I" );
+    partition_totalSpaceField = (*env)->GetFieldID( env, partitionCls, 
+                                                   "totalSpace", "I" );
+    partition_totalFreeSpaceField = (*env)->GetFieldID( env, partitionCls, 
+                                                       "totalFreeSpace", "I");
+    if( !partition_nameField || !partition_cachedInfoField || 
+       !partition_idField || !partition_deviceNameField || 
+       !partition_lockFileDescriptorField || !partition_totalSpaceField || 
+       !partition_totalFreeSpaceField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of a volume, gets the object and
+ * field information for the volume object from the Java environment.
+ */
+void internal_getVolumeClass( JNIEnv *env, jobject volume ) {
+  if( volumeCls == 0 ) {
+    volumeCls = (*env)->NewGlobalRef( env, 
+                                     (*env)->GetObjectClass(env, volume) );
+    if( !volumeCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    volume_nameField = (*env)->GetFieldID( env, volumeCls, "name", 
+                                          "Ljava/lang/String;" );
+    volume_cachedInfoField = (*env)->GetFieldID( env, volumeCls, "cachedInfo", 
+                                                "Z" );
+    volume_idField = (*env)->GetFieldID( env, volumeCls, "id", "I" );
+    volume_readWriteIdField = (*env)->GetFieldID( env, volumeCls, 
+                                                 "readWriteID", "I" );
+    volume_readOnlyIdField = (*env)->GetFieldID( env, volumeCls, "readOnlyID", 
+                                                "I" );
+    volume_backupIdField = (*env)->GetFieldID( env, volumeCls, "backupID", 
+                                              "I" );
+    volume_creationDateField = (*env)->GetFieldID( env, volumeCls, 
+                                                  "creationDate", "J" );
+    volume_lastAccessDateField = (*env)->GetFieldID( env, volumeCls, 
+                                                    "lastAccessDate", "J" );
+    volume_lastUpdateDateField = (*env)->GetFieldID( env, volumeCls, 
+                                                    "lastUpdateDate", "J" );
+    volume_lastBackupDateField = (*env)->GetFieldID( env, volumeCls, 
+                                                    "lastBackupDate", "J" );
+    volume_copyCreationDateField = (*env)->GetFieldID( env, volumeCls, 
+                                                      "copyCreationDate", 
+                                                      "J" );
+    volume_accessesSinceMidnightField = (*env)->GetFieldID( env, volumeCls, 
+                                                      "accessesSinceMidnight",
+                                                           "I" );
+    volume_fileCountField = (*env)->GetFieldID( env, volumeCls, "fileCount", 
+                                               "I" );
+    volume_maxQuotaField = (*env)->GetFieldID( env, volumeCls, "maxQuota", 
+                                              "I" );
+    volume_currentSizeField = (*env)->GetFieldID( env, volumeCls, 
+                                                 "currentSize", "I" );
+    volume_statusField = (*env)->GetFieldID( env, volumeCls, "status", "I" );
+    volume_dispositionField = (*env)->GetFieldID( env, volumeCls, 
+                                                 "disposition", "I" );
+    volume_typeField = (*env)->GetFieldID( env, volumeCls, "type", "I" );
+    if( !volume_nameField || !volume_cachedInfoField || !volume_idField || 
+       !volume_readWriteIdField || !volume_readOnlyIdField || 
+       !volume_backupIdField || !volume_creationDateField || 
+       !volume_lastAccessDateField || !volume_lastUpdateDateField || 
+       !volume_lastBackupDateField || !volume_copyCreationDateField || 
+       !volume_accessesSinceMidnightField || !volume_fileCountField || 
+       !volume_maxQuotaField || !volume_currentSizeField || 
+       !volume_statusField || !volume_dispositionField || 
+       !volume_typeField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of a key, gets the object and
+ * field information for the key object from the Java environment.
+ */
+void internal_getKeyClass( JNIEnv *env, jobject key ) {
+  if( keyCls == 0 ) {
+    keyCls = (*env)->NewGlobalRef( env, (*env)->GetObjectClass(env, key) );
+    if( !keyCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    key_encryptionKeyField = (*env)->GetFieldID( env, keyCls, 
+                                                "encryptionKey", 
+                                                "Ljava/lang/String;" );
+    key_cachedInfoField = (*env)->GetFieldID( env, keyCls, "cachedInfo", "Z" );
+    key_versionField = (*env)->GetFieldID( env, keyCls, "version", "I" );
+    key_lastModDateField = (*env)->GetFieldID( env, keyCls, "lastModDate", 
+                                              "I" );
+    key_lastModMsField = (*env)->GetFieldID( env, keyCls, "lastModMs", "I" );
+    key_checkSumField = (*env)->GetFieldID( env, keyCls, "checkSum", "J" );
+    if( !key_cachedInfoField || !key_versionField || !key_encryptionKeyField 
+       || !key_lastModDateField || !key_lastModMsField || 
+       !key_checkSumField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+/**
+ * Given a Java environment and an instance of a process, gets the object and
+ * field information for the process object from the Java environment.
+ */
+void internal_getProcessClass( JNIEnv *env, jobject process ) {
+  if( processCls == 0 ) {
+    processCls = (*env)->NewGlobalRef( env, 
+                                      (*env)->GetObjectClass(env, process) );
+    if( !processCls ) {
+       throwAFSException( env, JAFSADMCLASSNOTFOUND );
+       return;
+    }
+    process_cachedInfoField = (*env)->GetFieldID( env, processCls, 
+                                                 "cachedInfo", "Z" );
+    process_nameField = (*env)->GetFieldID( env, processCls, "name", 
+                                           "Ljava/lang/String;" );
+    process_typeField = (*env)->GetFieldID( env, processCls, "type", "I" );
+    process_stateField = (*env)->GetFieldID( env, processCls, "state", "I" );
+    process_goalField = (*env)->GetFieldID( env, processCls, "goal", "I" );
+    process_startTimeField = (*env)->GetFieldID( env, processCls, "startTime", 
+                                                "J" ); 
+    process_numberStartsField = (*env)->GetFieldID( env, processCls, 
+                                                   "numberStarts", "J" ); 
+    process_exitTimeField = (*env)->GetFieldID( env, processCls, "exitTime", 
+                                               "J" ); 
+    process_exitErrorTimeField = (*env)->GetFieldID( env, processCls, 
+                                                    "exitErrorTime", "J" ); 
+    process_errorCodeField = (*env)->GetFieldID( env, processCls, "errorCode", 
+                                                "J" ); 
+    process_errorSignalField = (*env)->GetFieldID( env, processCls, 
+                                                  "errorSignal", "J" ); 
+    process_stateOkField = (*env)->GetFieldID( env, processCls, "stateOk", 
+                                              "Z" ); 
+    process_stateTooManyErrorsField = (*env)->GetFieldID( env, processCls, 
+                                                         "stateTooManyErrors",
+                                                         "Z" ); 
+    process_stateBadFileAccessField = (*env)->GetFieldID( env, processCls, 
+                                                         "stateBadFileAccess",
+                                                         "Z" ); 
+    if( !process_cachedInfoField || !process_nameField || !process_typeField 
+       || !process_stateField || !process_goalField || 
+       !process_startTimeField || !process_numberStartsField || 
+       !process_exitTimeField || !process_exitErrorTimeField || 
+       !process_errorCodeField || !process_errorSignalField || 
+       !process_stateOkField || !process_stateTooManyErrorsField || 
+       !process_stateBadFileAccessField ) {
+
+       throwAFSException( env, JAFSADMFIELDNOTFOUND );
+       return;
+
+    }
+  }
+}
+
+#endif /* LIBJUAFS */
+
+
diff --git a/src/JAVA/libjafs/Internal.h b/src/JAVA/libjafs/Internal.h
new file mode 100644 (file)
index 0000000..29d9129
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef _Jafsadm_Internal
+#define _Jafsadm_Internal
+
+#include <jni.h>
+#include "Exceptions.h"
+
+#ifndef LIBJUAFS
+#include <afs_Admin.h>
+#include <afs_kasAdmin.h>
+
+// error codes
+#define JAFSADMNOMEM 1050             // Memory problems
+#define JAFSADMCLASSNOTFOUND  1051    // Trouble finding a Java class
+#define JAFSADMMETHODNOTFOUND 1052    // Trouble finding a Java method
+#define JAFSADMFIELDNOTFOUND  1053    // Trouble finding a Java field
+
+// make an identity out of a full name (possibly including an instance ) 
+void internal_makeKasIdentity( const char *fullName, kas_identity_p who );
+
+void internal_getUserClass( JNIEnv *env, jobject user );
+void internal_getGroupClass( JNIEnv *env, jobject group );
+void internal_getServerClass( JNIEnv *env, jobject server );
+void internal_getPartitionClass( JNIEnv *env, jobject partition );
+void internal_getVolumeClass( JNIEnv *env, jobject volume );
+void internal_getKeyClass( JNIEnv *env, jobject key );
+void internal_getProcessClass( JNIEnv *env, jobject process );
+#else
+int openAFSFile(JNIEnv *env, jstring fileNameUTF, int flags, int mode, int *err);
+int readCacheParms(char *afsMountPoint, char *afsConfDir, char *afsCacheDir,
+                   int *cacheBlocks, int *cacheFiles, int *cacheStatEntries,
+                   int *dCacheSize, int *vCacheSize, int *chunkSize,
+                   int *closeSynch, int *debug, int *nDaemons, int *cacheFlags,
+                   char *logFile);
+#endif /* !LIBJUAFS */
+
+// throw a non-AFS exception with a message
+void throwMessageException( JNIEnv *env, char *msg );
+
+// throw an AFS exception with a message
+void throwAFSException( JNIEnv *env, int code );
+
+// throw an AFS Admin exception with a message
+void throwAFSException( JNIEnv *env, int code );
+
+// throw an AFS File or I/O related exception with a message
+void throwFileAdminException( JNIEnv *env, int code, char *msg );
+
+// throw an AFS Security exception with a message
+void throwAFSSecurityException( JNIEnv *env, int code );
+
+// throw an exception with an error code
+void throwException( JNIEnv *env, jclass *excCls, char *excClsName, jmethodID *initID, int code );
+
+// reclaim global memory used by exceptions 
+void reclaimExceptionMemory( JNIEnv *env, jclass cls );
+
+int setError(JNIEnv *env, jobject *obj, int code);
+int setString(JNIEnv *env, jobject *obj, char *field, char *string);
+
+#endif
+
+
+
diff --git a/src/JAVA/libjafs/JAFS_README b/src/JAVA/libjafs/JAFS_README
new file mode 100644 (file)
index 0000000..3939201
--- /dev/null
@@ -0,0 +1,123 @@
+Java API for OpenAFS (Jafs) README
+Current as of 6/5/02
+
+ ##########################################################################
+ # Copyright (c) 2001-2002 International Business Machines Corp.          #
+ # All rights reserved.                                                   #
+ #                                                                        #
+ # This software has been released under the terms of the IBM Public      #
+ # License.  For details, see the LICENSE file in the top-level source    #
+ # directory or online at http://www.openafs.org/dl/license10.html        #
+ #                                                                        #
+ # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS    #
+ # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT      #
+ # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR  #
+ # A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR #
+ # CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,  #
+ # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,    #
+ # PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR     #
+ # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF #
+ # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING   #
+ # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS     #
+ # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           #
+ ##########################################################################
+
+*** INTRODUCTION ***
+
+Jafs is an open source API designed to allow Java programmers the ability
+to create applications for the administration or use of OpenAFS file systems.
+It works by accessing libadmin and libuafs (administrative and user-level 
+libraries that come with OpenAFS) through JNI.  It consists of a Java package 
+called org.openafs.jafs, and a shared libraries libjafsadm.so and libjafs.so.
+
+*** USE ***
+
+There is a version of Jafs that has been compiled on Red Hat Linux 7.1, 
+and can be directly used without compilation.  It was compiled using
+OpenAFS 1.2.4 libraries (with a modified version of libjuafs.a).  It 
+consists of a JAR file (jafs.jar) and two shared libraries 
+(libjafsadm.so and libjafs.so).  It was compiled using the 
+--enable-transarc-paths on compilation (for use with the OpenAFS RPMs), 
+gcc 2.96, and Java Classic VM version 1.4.0.
+
+When you write Java code to use this API, import the
+org.openafs.jafs package. During compilation of your Java code, 
+ensure one of the following conditions are met:
+  - Use the "-classpath" option to javac to specify the jafs.jar file.
+  - Change your $CLASSPATH environment variable to include the
+    jafs.jar file (e.g. export CLASSPATH=$CLASSPATH:jafs.jar
+
+When running an application that uses Jafs, the shared libraries
+need to be found by Java's library loader.  The easiest way to
+accomplish this is to copy these files into the /usr/lib/ directory,
+or create symbolic links from that directory to the files.  Alternatively, 
+the directory containing the libraries can also be added to the
+LD_LIBRARY_PATH environment variable, instead.
+
+You also need to have an OpenAFS client set up on your machine
+(preferably version 1.2.4, but it should work for some past versions as well).
+You can obtain the OpenAFS client and view installation documentation at 
+http://www.openafs.org (the RPMs are easiest to use for Linux).  Also any 
+cells you plan to access through the API must have entries in your
+client's CellServDB file (located in the /usr/vice/etc/ directory in most
+setups).
+
+This API is most effective when used with a cell that uses the kaserver
+for authentication.  It does not currently support alternative methods of
+authentication such as Kerberos V.
+
+If you have successfully set up your Linux 7.1 environment as described
+above, you will be able to develop and execute applications that use
+the Jafs API.
+
+*** BUILD ***
+
+The first step in compiling your own versions of the library and jar file
+is to download the OpenAFS source code.  If the code doesn't contain the
+libjuafs version of the libuafs library -- noted by the README file located
+in the src/libuafs directory (if a README file doesn't exist in this directory
+then the libjuafs patch (libuafs.diff) has not been applied) -- you must 
+first apply the libjuafs patch. You can apply the libjuafs patch with the 
+following command, executed from the root directory of the download code 
+(i.e. openafs-1.2.4/):
+
+  patch -p1 < libuafs.diff
+
+Next, if the code doesn't contain the src/JAVA/libjafs directory, you can 
+apply the jafs patch to it with the following command, executed from the 
+root directory of the download code (i.e. openafs-1.2.4/):
+
+  patch -p1 < jafs.diff
+
+Note that the source code you download needs to be newer than 4/22/02,
+in order for the full functionality of the API to be effective.  Otherwise,
+you may experience link errors.
+
+From that same directory, run the configure script as you normally would 
+to compile OpenAFS, but run it with a java_home argument so the script can
+find your java distribution.  For example:
+
+  ./configure [other options] --java_home=/usr/local/jdk
+
+The configure script will ensure that this directory contains bin/ and lib/ 
+subdirectories, and that there are /bin/javac and/bin/javah executables and 
+an include/jni.h file.  If you don't supply a command line argument for the 
+java home, the script will look for it in environment variables: first in
+$JAVA_HOME and then in $JDK_HOME.  Also, note that if you have installed
+(or are planning to install) OpenAFS by using the RPMs for Linux, you
+should provide the --enable-transarc-paths configuration option.  If you
+get a "** Can't determine local cell name" error message, the most likely
+reason is that you didn't supply this option.
+
+Next, do a full build of OpenAFS by executing a make in the current
+directory.  After it finishes, you are ready to compile Jafs.  Execute
+'make jafs' from that same directory.  Afterward, there will be 
+libjafsadm.so and libjafs.so in the lib/ directory, and a jafs.jar in the 
+jlib/ directory. These can be used according to the instructions in the 
+'USE' section of this document. 
+
+If you'd like to edit the source code, you'll find the native C code in
+the src/libjafs directory, and the Java code in the 
+src/JAVA/org/openafs/jafs/ directory.  Please reference the 
+src/TechNotes-JavaAPI document for more information.
+
diff --git a/src/JAVA/libjafs/Key.c b/src/JAVA/libjafs/Key.c
new file mode 100644 (file)
index 0000000..a9ddac9
--- /dev/null
@@ -0,0 +1,249 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Key.h"
+
+#include <afs_bosAdmin.h>
+#include <afs_AdminCommonErrors.h>
+#include <kautils.h>
+
+//// definitions in Internal.c  //////////////////
+extern jclass keyCls;
+extern jfieldID key_versionField;
+extern jfieldID key_encryptionKeyField;
+extern jfieldID key_lastModDateField;
+extern jfieldID key_lastModMsField;
+extern jfieldID key_checkSumField;
+
+//////////////////////////////////////////////////////////////////
+
+/**
+ * Extract the information from the given key entry and populate the
+ * given object
+ *
+ * env      the Java environment
+ * key      the Key object to populate with the info
+ * keyEntry     the container of the key's information
+ */
+void fillKeyInfo( JNIEnv *env, jobject key, bos_KeyInfo_t keyEntry )
+{
+  jstring jencryptionKey;
+  char *convertedKey;
+  int i;
+
+  // get the class fields if need be
+  if( keyCls == 0 ) {
+    internal_getKeyClass( env, key );
+  }
+
+  // set all the fields
+  (*env)->SetIntField( env, key, key_versionField, keyEntry.keyVersionNumber );
+  
+  convertedKey = (char *) malloc( sizeof(char *)*
+                                 (sizeof(keyEntry.key.key)*4+1) );
+  if( !convertedKey ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+  for( i = 0; i < sizeof(keyEntry.key.key); i++ ) {
+    sprintf( &(convertedKey[i*4]), "\\%0.3o", keyEntry.key.key[i] );
+  }
+  jencryptionKey = (*env)->NewStringUTF(env, convertedKey);
+  (*env)->SetObjectField( env, key, key_encryptionKeyField, jencryptionKey );
+  
+  (*env)->SetIntField( env, key, key_lastModDateField, 
+                      keyEntry.keyStatus.lastModificationDate );
+  (*env)->SetIntField( env, key, key_lastModMsField, 
+                      keyEntry.keyStatus.lastModificationMicroSeconds );
+  (*env)->SetLongField( env, key, key_checkSumField, 
+                       (unsigned int) keyEntry.keyStatus.checkSum );
+
+  free( convertedKey );
+}
+
+/**
+ * Fills in the information fields of the provided Key. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle    the bos handle of the server to which the key 
+ *                        belongs
+ * version     the version of the key for which to get the information
+ * key     the Key object in which to fill in the 
+ *                information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Key_getKeyInfo
+  (JNIEnv *env, jclass cls, jint serverHandle, jint version, jobject key)
+{
+  afs_status_t ast;
+  bos_KeyInfo_t keyEntry;
+  void *iterationId;
+  int done;
+
+  if( !bos_KeyGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  done = FALSE;
+
+  // there's no KeyGet function, so we must iterate and find the 
+  // one with the matching version
+  while( !done ) {
+
+    if( !bos_KeyGetNext( iterationId, &keyEntry, &ast ) ) {
+      // no matching key
+      if( ast == ADMITERATORDONE ) {
+        afs_status_t astnew;
+        if( !bos_KeyGetDone( iterationId, &astnew ) ) {
+          throwAFSException( env, astnew );
+          return;
+        }
+        throwAFSException( env, KAUNKNOWNKEY );
+        return;
+        // other
+      } else {
+        throwAFSException( env, ast );
+        return;
+      }
+    }
+
+    if( keyEntry.keyVersionNumber == version ) {
+      done = TRUE;
+    }
+
+  }
+
+  fillKeyInfo( env, key, keyEntry );
+
+  if( !bos_KeyGetDone( iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Create a server key.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle   the handle of the cell to which the server belongs
+ * serverHandle  the bos handle of the server to which the key will 
+ *                      belong
+ * versionNumber   the version number of the key to create (0 to 255)
+ * jkeyString     the String version of the key that will
+ *                      be encrypted
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Key_create
+  (JNIEnv *env, jclass cls, jint cellHandle, jint serverHandle, jint version, 
+   jstring jkeyString)
+{
+    afs_status_t ast;
+    const char *keyString;
+    char *cellName;
+    kas_encryptionKey_p key = 
+      (kas_encryptionKey_p) malloc( sizeof(kas_encryptionKey_t) );
+    
+    if( !key ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    if( jkeyString != NULL ) {
+      keyString = (*env)->GetStringUTFChars(env, jkeyString, 0);
+      if( !keyString ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      keyString = NULL;
+    }
+
+    if( !afsclient_CellNameGet( (void *) cellHandle, &cellName, &ast ) ) {
+       free( key );
+       if( keyString != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jkeyString, keyString);
+       }
+       throwAFSException( env, ast );
+       return;
+    }   
+
+    if( !kas_StringToKey( cellName, keyString, key, &ast ) ) {
+       free( key );
+       if( keyString != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jkeyString, keyString);
+       }
+       throwAFSException( env, ast );
+       return;
+    }
+
+    if( !bos_KeyCreate( (void *) serverHandle, version, key, &ast ) ) {
+       free( key );
+       if( keyString != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jkeyString, keyString);
+       }
+       throwAFSException( env, ast );
+       return;
+    }
+
+    free( key );
+    if( keyString != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jkeyString, keyString);
+    }
+}
+
+/**
+ * Delete a server key.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key belongs
+ * versionNumber   the version number of the key to remove (0 to 255)
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Key_delete
+  (JNIEnv *env, jclass cls, jint serverHandle, jint version )
+{
+    afs_status_t ast;
+
+    if( !bos_KeyDelete( (void *) serverHandle, version, &ast ) ) {
+       throwAFSException( env, ast );
+       return;
+    }
+}
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_Key_reclaimKeyMemory (JNIEnv *env, jclass cls)
+{
+  if( keyCls ) {
+      (*env)->DeleteGlobalRef(env, keyCls);
+      keyCls = 0;
+  }
+}
+
+
+
+
diff --git a/src/JAVA/libjafs/Makefile.in b/src/JAVA/libjafs/Makefile.in
new file mode 100644 (file)
index 0000000..24bc8e6
--- /dev/null
@@ -0,0 +1,240 @@
+# Copyright 2000, International Business Machines Corporation and others.
+# All Rights Reserved.
+# 
+# This software has been released under the terms of the IBM Public
+# License.  For details, see the LICENSE file in the top-level source
+# directory or online at http://www.openafs.org/dl/license10.html
+
+DEST=@DEST@
+TOP_SRCDIR=@TOP_SRCDIR@
+TOP_INCDIR=@TOP_INCDIR@
+TOP_LIBDIR=@TOP_LIBDIR@
+TOP_JLIBDIR=@TOP_JLIBDIR@
+JAVA_HOME=@JAVA_HOME@
+JNI_INC=@JNI_INC@
+prefix=@prefix@
+exec_prefix=@exec_prefix@
+bindir=@bindir@
+sbindir=@sbindir@
+libexecdir=@libexecdir@
+libdir=@libdir@
+includedir=@includedir@
+mandir=@mandir@
+afssrvbindir=@afssrvbindir@
+afssrvsbindir=@afssrvsbindir@
+afssrvlibexecdir=@afssrvlibexecdir@
+COMPILE_ET=${TOP_SRCDIR}/comerr/compile_et
+RXGEN=${TOP_SRCDIR}/rxgen/rxgen
+SYS_NAME=@AFS_SYSNAME@
+
+
+CC = ${MT_CC}
+SHARED_FLAGS = -shared
+OBJECT_FLAGS = -fPIC -c
+
+ifeq "$(BUILD_TYPE)" "admin"
+       INC := -I${TOP_INCDIR} -I${TOP_INCDIR}/afs/ -I${JAVA_HOME}/include ${JNI_INC}
+       CFLAGS := ${INC} ${OPTMZ} ${DBG} -I${TOP_SRCDIR}/config ${MT_CFLAGS}
+else
+       INC := -I${TOP_SRCDIR}/libuafs -I${TOP_INCDIR} -I${JAVA_HOME}/include ${JNI_INC}
+       CFLAGS := ${INC} ${FSINCLUDES} -D_REENTRANT -DLIBJUAFS ${DBUG} ${MT_CFLAGS}
+endif
+
+
+LIBJAFSADMDIR = ./
+ROOTPACKAGEDIR = ../classes
+RELPACKAGEDIR = org/openafs/jafs/
+PACKAGEDIR = ${ROOTPACKAGEDIR}/${RELPACKAGEDIR}
+JAVADOCSDIR = javadocs/
+
+JAVAH = ${JAVA_HOME}/bin/javah -classpath ${ROOTPACKAGEDIR} -jni -d ${LIBJAFSADMDIR}
+JAVAC = ${JAVA_HOME}/bin/javac -classpath ${ROOTPACKAGEDIR}
+
+J_NATIVE_PREFIX = org.openafs.jafs.
+C_NATIVE_PREFIX = org_openafs_jafs_
+
+PACKAGE =\
+       ${PACKAGEDIR}ACL.class \
+       ${PACKAGEDIR}AFSException.class \
+       ${PACKAGEDIR}AFSFileException.class \
+       ${PACKAGEDIR}AFSSecurityException.class \
+       ${PACKAGEDIR}Cell.class \
+       ${PACKAGEDIR}File.class \
+       ${PACKAGEDIR}FileInputStream.class \
+       ${PACKAGEDIR}FileOutputStream.class \
+       ${PACKAGEDIR}Group.class \
+       ${PACKAGEDIR}Key.class \
+       ${PACKAGEDIR}Partition.class \
+       ${PACKAGEDIR}Process.class \
+       ${PACKAGEDIR}Server.class \
+       ${PACKAGEDIR}Token.class \
+       ${PACKAGEDIR}User.class \
+       ${PACKAGEDIR}Volume.class
+
+LIBJAFS_OBJS =\
+       ${LIBJAFSADMDIR}ACL.o \
+       ${LIBJAFSADMDIR}File.o \
+       ${LIBJAFSADMDIR}FileInputStream.o \
+       ${LIBJAFSADMDIR}FileOutputStream.o \
+       ${LIBJAFSADMDIR}Internal.o \
+       ${LIBJAFSADMDIR}UserToken.o
+
+LIBJAFSADM_OBJS =\
+       ${LIBJAFSADMDIR}AdminToken.o \
+       ${LIBJAFSADMDIR}Cell.o \
+       ${LIBJAFSADMDIR}Group.o \
+       ${LIBJAFSADMDIR}Internal.o \
+       ${LIBJAFSADMDIR}Key.o \
+       ${LIBJAFSADMDIR}Partition.o \
+       ${LIBJAFSADMDIR}Process.o \
+       ${LIBJAFSADMDIR}Server.o \
+       ${LIBJAFSADMDIR}User.o \
+       ${LIBJAFSADMDIR}Volume.o
+
+CORRELATING_SOURCE_FILES =\
+       ${LIBJAFSADMDIR}ACL.c \
+       ${LIBJAFSADMDIR}Cell.c \
+       ${LIBJAFSADMDIR}File.c \
+       ${LIBJAFSADMDIR}FileInputStream.c \
+       ${LIBJAFSADMDIR}FileOutputStream.c \
+       ${LIBJAFSADMDIR}Group.c \
+       ${LIBJAFSADMDIR}Key.c \
+       ${LIBJAFSADMDIR}Partition.c \
+       ${LIBJAFSADMDIR}Process.c \
+       ${LIBJAFSADMDIR}Server.c \
+       ${LIBJAFSADMDIR}User.c \
+       ${LIBJAFSADMDIR}Volume.c
+
+JAVA_HEADERS = ${PACKAGE:${PACKAGEDIR}%.class=${C_NATIVE_PREFIX}%.h}
+
+BOSADMINLIB = ${TOP_LIBDIR}/libbosadmin.a
+VOSADMINLIB = ${TOP_LIBDIR}/libvosadmin.a
+PTSADMINLIB = ${TOP_LIBDIR}/libptsadmin.a
+KASADMINLIB = ${TOP_LIBDIR}/libkasadmin.a
+CFGADMINLIB = ${TOP_LIBDIR}/libcfgadmin.a
+UTILADMINLIB = ${TOP_LIBDIR}/libafsadminutil.a
+CLIENTADMINLIB = ${TOP_LIBDIR}/libclientadmin.a
+
+LIBJAFS_LIBS =\
+       ${TOP_LIBDIR}/libjuafs.a \
+       ${TOP_LIBDIR}/libdes.a \
+       -lresolv \
+       -lpthread
+
+LIBJAFSADM_LIBS =\
+       ${CLIENTADMINLIB} \
+       ${VOSADMINLIB} \
+       ${BOSADMINLIB} \
+       ${PTSADMINLIB} \
+       ${KASADMINLIB} \
+       ${CFGADMINLIB} \
+       ${UTILADMINLIB} \
+       ${TOP_LIBDIR}/libafsauthent.a \
+       ${TOP_LIBDIR}/libafsrpc.a \
+       ${TOP_LIBDIR}/libcmd.a \
+       -lresolv \
+       -lpthread
+
+
+JARFILE = jafs.jar
+
+include ../../config/Makefile.${SYS_NAME}
+
+all:  ${TOP_JLIBDIR} libjafs libjafsadm ${PACKAGE} all_jar
+
+install:  all ${DESTDIR}${libdir}/libjafs.so ${DESTDIR}${libdir}/libjafsadm.so ${PACKAGE} install_jar
+       if [ ! -e /usr/afswsp ]; then \
+         mkdir -p /usr/afswsp/; \
+       fi; \
+       if [ ! -e /usr/afswsp/etc ]; then \
+         mkdir -p /usr/afswsp/etc/; \
+         cp ./etc/CacheConfig /usr/afswsp/etc/; \
+       fi; \
+       if [ ! -e /usr/afswsp/log ]; then \
+         mkdir -p /usr/afswsp/log/; \
+       fi; \
+       if [ ! -e /usr/afswsp/cache ]; then \
+         mkdir -p /usr/afswsp/cache/; \
+       fi; \
+       if [ ! -L /usr/vice/etc/CellServDB ]; then \
+         ln -s /usr/vice/etc/CellServDB /usr/afswsp/etc/; \
+       fi; \
+       if [ ! -L /usr/vice/etc/ThisCell ]; then \
+         ln -s /usr/vice/etc/ThisCell /usr/afswsp/etc/; \
+       fi
+
+clean:
+       ${RM} -f ${PACKAGEDIR}*.class ${LIBJAFSADMDIR}*.o ${LIBJAFSADMDIR}${C_NATIVE_PREFIX}*.h
+
+setup: FORCE
+       if [ ! -e ./h ]; then \
+         ln -s /usr/include/sys h; \
+       fi; \
+
+${TOP_JLIBDIR}:
+       mkdir -p $@
+
+FORCE: ;
+
+############# Shared library ###############################
+
+libjafs: setup
+       ${RM} -f ${LIBJAFSADMDIR}Internal.o; \
+       export BUILD_TYPE=user; \
+       ${MAKE} ${TOP_LIBDIR}/libjafs.so
+
+libjafsadm:
+       ${RM} -f ${LIBJAFSADMDIR}Internal.o; \
+       export BUILD_TYPE=admin; \
+       ${MAKE} ${TOP_LIBDIR}/libjafsadm.so
+
+${TOP_LIBDIR}/libjafs.so: ${LIBJAFS_OBJS}
+       ${CC} ${CFLAGS} ${SHARED_FLAGS} -o $@ $^ ${LIBJAFS_LIBS}
+
+${DESTDIR}${libdir}/libjafs.so: ${LIBJAFS_OBJS}
+       ${CC} ${CFLAGS} ${SHARED_FLAGS} -o $@ $^ ${LIBJAFS_LIBS}
+
+${TOP_LIBDIR}/libjafsadm.so: ${LIBJAFSADM_OBJS}
+       ${CC} ${CFLAGS} ${SHARED_FLAGS} -o $@ $^ ${LIBJAFSADM_LIBS}
+
+${DESTDIR}${libdir}/libjafsadm.so: ${LIBJAFSADM_OBJS}
+       ${CC} ${CFLAGS} ${SHARED_FLAGS} -o $@ $^ ${LIBJAFSADM_LIBS}
+
+############## Object files ################################
+
+${LIBJAFSADM_OBJS}: %.o: %.c
+       ${CC} ${CFLAGS} ${OBJECT_FLAGS} -o $@ $<
+
+############## C files #####################################
+
+${CORRELATING_SOURCE_FILES}: ${LIBJAFSADMDIR}%.c: ${LIBJAFSADMDIR}${C_NATIVE_PREFIX}%.h ${LIBJAFSADMDIR}Internal.h
+
+${LIBJAFSADMDIR}AdminToken.c: ${LIBJAFSADMDIR}${C_NATIVE_PREFIX}Token.h ${LIBJAFSADMDIR}${C_NATIVE_PREFIX}Cell.h
+
+${LIBJAFSADMDIR}Internal.c: ${LIBJAFSADMDIR}Internal.h
+
+${LIBJAFSADMDIR}UserToken.c: ${LIBJAFSADMDIR}${C_NATIVE_PREFIX}Token.h
+
+############## Package javac section #########################
+
+${PACKAGEDIR}%.class: ${PACKAGEDIR}%.java
+       ${JAVAC} $<
+
+############## Javah section ###############################
+
+${JAVA_HEADERS}: ${C_NATIVE_PREFIX}%.h: ${PACKAGEDIR}%.class
+       ${JAVAH} ${J_NATIVE_PREFIX}$*
+
+############# JAR file #####################################
+
+all_jar: clean_jar
+       cd ${ROOTPACKAGEDIR}; ${JAVA_HOME}/bin/jar -cMf ${TOP_JLIBDIR}/${JARFILE} *.properties ${RELPACKAGEDIR}*.class
+
+install_jar: clean_jar
+       cd ${ROOTPACKAGEDIR}; ${JAVA_HOME}/bin/jar -cMf ${JAVA_HOME}/lib/${JARFILE} *.properties ${RELPACKAGEDIR}*.class
+
+clean_jar:
+       ${RM} -f ${TOP_JLIBDIR}/${JARFILE}
+
+
+
diff --git a/src/JAVA/libjafs/Partition.c b/src/JAVA/libjafs/Partition.c
new file mode 100644 (file)
index 0000000..6b35c12
--- /dev/null
@@ -0,0 +1,482 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Partition.h"
+
+#include <afs_vosAdmin.h>
+#include <afs_AdminCommonErrors.h>
+
+//// definitions in Internal.c  //////////////////
+
+extern jclass partitionCls;
+extern jfieldID partition_nameField;
+extern jfieldID partition_idField;
+extern jfieldID partition_deviceNameField;
+extern jfieldID partition_lockFileDescriptorField;
+extern jfieldID partition_totalSpaceField;
+extern jfieldID partition_totalFreeSpaceField;
+
+extern jclass volumeCls;
+extern jfieldID volume_cachedInfoField;
+
+//////////////////////////////////////////////////////////
+
+///// definition in jafs_Volume.c /////////////////
+
+extern void fillVolumeInfo
+            ( JNIEnv *env, jobject volume, vos_volumeEntry_t volEntry );
+
+///////////////////////////////////////////////////
+
+
+/**
+ * Extract the information from the given partition entry and populate the
+ * given object
+ *
+ * env      the Java environment
+ * partition      the Partition object to populate with the info
+ * partEntry     the container of the partition's information
+ */
+void fillPartitionInfo
+  (JNIEnv *env, jobject partition, vos_partitionEntry_t partEntry)
+{
+  jstring jdeviceName;
+  jstring jpartition;
+  jint id;
+  afs_status_t ast;
+
+  // get the class fields if need be
+  if( partitionCls == 0 ) {
+    internal_getPartitionClass( env, partition );
+  }
+
+  // fill name and id in case it's a blank object
+  jpartition = (*env)->NewStringUTF(env, partEntry.name);
+  // get the id
+  if( !vos_PartitionNameToId( partEntry.name, (int *) &id, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+  } 
+  (*env)->SetObjectField(env, partition, partition_nameField, jpartition);
+  (*env)->SetIntField(env, partition, partition_idField, id);
+
+  jdeviceName = (*env)->NewStringUTF(env, partEntry.deviceName);
+  (*env)->SetObjectField(env, partition, partition_deviceNameField, 
+                        jdeviceName);
+
+  (*env)->SetIntField(env, partition, partition_lockFileDescriptorField, 
+                     partEntry.lockFileDescriptor);
+  (*env)->SetIntField(env, partition, partition_totalSpaceField, 
+                     partEntry.totalSpace);
+  (*env)->SetIntField(env, partition, partition_totalFreeSpaceField, 
+                     partEntry.totalFreeSpace);
+  
+}
+
+/**
+ * Fills in the information fields of the provided Partition.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the partition belongs
+ * serverHandle  the vos handle of the server on which the 
+ *                      partition resides
+ * partition   the numeric id of the partition for which to get the 
+ *                    info
+ * jpartitionObject   the Partition object in which to 
+ *                    fill in the information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Partition_getPartitionInfo
+  (JNIEnv *env, jclass cls, jint cellHandle, jint serverHandle, 
+   jint partition, jobject jpartitionObject)
+{
+  afs_status_t ast;
+  vos_partitionEntry_t partEntry;
+
+  // get the partition entry
+  if ( !vos_PartitionGet( (void *) cellHandle, (void *) serverHandle, NULL, 
+                         (unsigned int) partition, &partEntry, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  fillPartitionInfo( env, jpartitionObject, partEntry );
+
+}
+
+/**
+ * Translates a partition name into a partition id
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * jname  the name of the partition in question
+ * returns   the id of the partition in question
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_translateNameToID
+  (JNIEnv *env, jclass cls, jstring jname)
+{
+  afs_status_t ast;
+  jint id;
+  const char *name;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  // get the id
+  if( !vos_PartitionNameToId( name, (unsigned int *) &id, &ast ) ) {
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    throwAFSException( env, ast );
+    return -1;
+  } 
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+  return id;
+
+}
+
+/**
+ * Translates a partition id into a partition name
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * id  the id of the partition in question
+ * returns   the name of the partition in question
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Partition_translateIDToName
+ (JNIEnv *env, jclass cls, jint id)
+{
+  afs_status_t ast;
+  char *name = (char *) malloc( sizeof(char)*VOS_MAX_PARTITION_NAME_LEN);
+  jstring jname;
+
+  if( !name ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  // get the name
+  if( !vos_PartitionIdToName( (unsigned int) id, name, &ast ) ) {
+    free(name);
+    throwAFSException( env, ast );
+    return NULL;
+  } 
+
+  jname = (*env)->NewStringUTF(env, name);
+  free(name);
+  return jname;
+
+}
+
+/**
+ * Returns the total number of volumes hosted by this partition.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the partition belongs
+ * serverHandle  the vos handle of the server to which the partition 
+ *                      belongs
+ * partition   the numeric id of the partition on which the volumes 
+ *                    reside
+ * returns total number of volumes hosted by this partition
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_getVolumeCount
+ (JNIEnv *env, jclass cls, jint cellHandle, jint serverHandle, jint partition)
+{
+  afs_status_t ast;
+  void *iterationId;
+  vos_volumeEntry_t volEntry;
+  int i = 0;
+
+  if( !vos_VolumeGetBegin( (void *) cellHandle, (void *) serverHandle, NULL, 
+                          (unsigned int) partition, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  while ( vos_VolumeGetNext( (void *) iterationId, &volEntry, &ast ) ) i++;
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the volumes on a partition.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getVolumesNext and getVolumesDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the partition belongs
+ * serverHandle  the vos handle of the server to which the partition 
+ *                      belongs
+ * partition   the numeric id of the partition on which the volumes 
+ *                    reside
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesBegin
+ (JNIEnv *env, jclass cls, jint cellHandle, jint serverHandle, jint partition)
+{
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !vos_VolumeGetBegin( (void *) cellHandle, (void *) serverHandle, NULL, 
+                          (unsigned int) partition, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+}
+
+/**
+ * Begin the process of getting the volumes on a partition.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getVolumesNext and getVolumesDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the partition belongs
+ * serverHandle  the vos handle of the server to which the partition 
+ *                      belongs
+ * partition   the numeric id of the partition on which the volumes 
+ *                    reside
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesBeginAt
+  (JNIEnv *env, jclass cls, jint cellHandle, jint serverHandle,
+   jint partition, jint index)
+{
+
+  afs_status_t ast;
+  void *iterationId;
+  vos_volumeEntry_t volEntry;
+  int i;
+
+  if( !vos_VolumeGetBegin( (void *) cellHandle, (void *) serverHandle, NULL, 
+                          (unsigned int) partition, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  for ( i = 1; i < index; i++) {
+    if( !vos_VolumeGetNext( (void *) iterationId, &volEntry, &ast ) ) {
+      if( ast == ADMITERATORDONE ) {
+        return 0;
+      } else {
+        throwAFSException( env, ast );
+        return 0;
+      }
+    }
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next volume of the partition.  Returns null 
+ * if there are no more volumes.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next volume of the server
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesNextString
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+  jstring jvolume;
+  vos_volumeEntry_t volEntry;
+
+  if( !vos_VolumeGetNext( (void *) iterationId, &volEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  jvolume = (*env)->NewStringUTF(env, volEntry.name);
+  return jvolume;
+
+}
+
+/**
+ * Fills the next volume object of the partition.  Returns 0 if there
+ * are no more volumes, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * jvolumeObject    the Volume object in which to fill the values 
+ *                         of the next volume
+ * returns 0 if there are no more volumes, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesNext
+  (JNIEnv *env, jclass cls, jint iterationId, jobject jvolumeObject)
+{
+  afs_status_t ast;
+  jstring jvolume;
+  vos_volumeEntry_t volEntry;
+
+  if( !vos_VolumeGetNext( (void *) iterationId, &volEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+
+  fillVolumeInfo( env, jvolumeObject, volEntry );
+
+  // get the class fields if need be
+  if( volumeCls == 0 ) {
+    internal_getVolumeClass( env, jvolumeObject );
+  }
+  (*env)->SetBooleanField( env, jvolumeObject, volume_cachedInfoField, TRUE );
+    
+  return 1;
+
+}
+
+/**
+ * Fills the next volume object of the partition.  Returns 0 if there
+ * are no more volumes, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * jvolumeObject    the Volume object in which to fill the values of the 
+ *                  next volume
+ * advanceCount     the number of volumes to advance past
+ * returns 0 if there are no more volumes, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesAdvanceTo
+  (JNIEnv *env, jclass cls, jint iterationId, jobject jvolumeObject,
+   jint advanceCount)
+{
+  afs_status_t ast;
+  jstring jvolume;
+  vos_volumeEntry_t volEntry;
+  int i;
+
+  for ( i = 0; i < advanceCount; i++) {
+    if( !vos_VolumeGetNext( (void *) iterationId, &volEntry, &ast ) ) {
+      if( ast == ADMITERATORDONE ) {
+        return 0;
+      } else {
+        throwAFSException( env, ast );
+        return 0;
+      }
+    }
+  }
+
+
+  fillVolumeInfo( env, jvolumeObject, volEntry );
+
+  // get the class fields if need be
+  if( volumeCls == 0 ) {
+    internal_getVolumeClass( env, jvolumeObject );
+  }
+  (*env)->SetBooleanField( env, jvolumeObject, volume_cachedInfoField, TRUE );
+    
+  return 1;
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Partition_getVolumesDone
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+
+  if( !vos_VolumeGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+}
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_Partition_reclaimPartitionMemory
+ (JNIEnv *env, jclass cls)
+{
+  if( partitionCls ) {
+      (*env)->DeleteGlobalRef(env, partitionCls);
+      partitionCls = 0;
+  }
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/Process.c b/src/JAVA/libjafs/Process.c
new file mode 100644 (file)
index 0000000..25c0add
--- /dev/null
@@ -0,0 +1,567 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Process.h"
+
+#include <afs_bosAdmin.h>
+
+///// definitions in Internal.c ////////////////////
+
+extern jclass processCls;
+extern jfieldID process_nameField;
+extern jfieldID process_typeField;
+extern jfieldID process_stateField;
+extern jfieldID process_goalField;
+extern jfieldID process_startTimeField;
+extern jfieldID process_numberStartsField;
+extern jfieldID process_exitTimeField;
+extern jfieldID process_exitErrorTimeField;
+extern jfieldID process_errorCodeField;
+extern jfieldID process_errorSignalField;
+extern jfieldID process_stateOkField;
+extern jfieldID process_stateTooManyErrorsField;
+extern jfieldID process_stateBadFileAccessField;
+
+//////////////////////////////////////////////////////////////////
+
+/**
+ * Retrieve the information for the specified process and populate the
+ * given object
+ *
+ * env      the Java environment
+ * serverHandle    the bos handle of the server on which the process resides
+ * processName      the name of the process for which to get the info
+ * process      the Process object to populate with the info
+ */
+void getProcessInfoChar( JNIEnv *env, void *serverHandle, 
+                        const char *processName, jobject process ) {
+
+  afs_status_t ast;
+  bos_ProcessType_t type;
+  bos_ProcessInfo_t infoEntry;
+  bos_ProcessExecutionState_t state;
+  char *auxStatus;
+
+  // get class fields if need be
+  if( processCls == 0 ) {
+    internal_getProcessClass( env, process );
+  }
+
+  if( !bos_ProcessInfoGet( serverHandle, processName, &type, 
+                          &infoEntry, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  // set type variable
+  switch( type ) {
+  case BOS_PROCESS_SIMPLE :
+      (*env)->SetIntField(env, process, process_typeField, 
+                         org_openafs_jafs_Process_SIMPLE_PROCESS);
+      break;
+  case BOS_PROCESS_FS :
+      (*env)->SetIntField(env, process, process_typeField, 
+                         org_openafs_jafs_Process_FS_PROCESS);
+      break;
+  case BOS_PROCESS_CRON :
+      (*env)->SetIntField(env, process, process_typeField, 
+                         org_openafs_jafs_Process_CRON_PROCESS);
+      break;
+  default:
+      throwAFSException( env, type );
+      return;
+  }
+
+  // set goal variable
+  switch( infoEntry.processGoal ) {
+  case BOS_PROCESS_STOPPED :
+      (*env)->SetIntField(env, process, process_goalField, 
+                         org_openafs_jafs_Process_STOPPED);
+      break;
+  case BOS_PROCESS_RUNNING :
+      (*env)->SetIntField(env, process, process_goalField, 
+                         org_openafs_jafs_Process_RUNNING);
+      break;
+  case BOS_PROCESS_STOPPING :
+      (*env)->SetIntField(env, process, process_goalField, 
+                         org_openafs_jafs_Process_STOPPING);
+      break;
+  case BOS_PROCESS_STARTING :
+      (*env)->SetIntField(env, process, process_goalField, 
+                         org_openafs_jafs_Process_STARTING);
+      break;
+  default:
+      throwAFSException( env, infoEntry.processGoal );
+      return;
+  }
+
+  // set state variable
+  auxStatus = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN );
+  if( !auxStatus ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+  if( !bos_ProcessExecutionStateGet( (void *) serverHandle, processName, 
+                                    &state, auxStatus, &ast ) ) {
+      free( auxStatus );
+      throwAFSException( env, ast );
+      return;
+  }
+  free( auxStatus );
+  switch( state ) {
+  case BOS_PROCESS_STOPPED :
+      (*env)->SetIntField(env, process, process_stateField, 
+                         org_openafs_jafs_Process_STOPPED);
+      break;
+  case BOS_PROCESS_RUNNING :
+      (*env)->SetIntField(env, process, process_stateField, 
+                         org_openafs_jafs_Process_RUNNING);
+      break;
+  case BOS_PROCESS_STOPPING :
+      (*env)->SetIntField(env, process, process_stateField, 
+                         org_openafs_jafs_Process_STOPPING);
+      break;
+  case BOS_PROCESS_STARTING :
+      (*env)->SetIntField(env, process, process_stateField, 
+                         org_openafs_jafs_Process_STARTING);
+      break;
+  default:
+      throwAFSException( env, state );
+      return;
+  }
+
+  // set longs
+  (*env)->SetLongField(env, process, process_startTimeField, 
+                      infoEntry.processStartTime );
+  (*env)->SetLongField(env, process, process_numberStartsField, 
+                      infoEntry.numberProcessStarts );
+  (*env)->SetLongField(env, process, process_exitTimeField, 
+                      infoEntry.processExitTime );
+  (*env)->SetLongField(env, process, process_exitErrorTimeField, 
+                      infoEntry.processExitErrorTime );
+  (*env)->SetLongField(env, process, process_errorCodeField, 
+                      infoEntry.processErrorCode );
+  (*env)->SetLongField(env, process, process_errorSignalField, 
+                      infoEntry.processErrorSignal );
+
+  // set stateOk to true if no core dump
+  if( infoEntry.state & BOS_PROCESS_CORE_DUMPED ) {
+    (*env)->SetBooleanField(env, process, process_stateOkField, FALSE );
+  } else {
+    (*env)->SetBooleanField(env, process, process_stateOkField, TRUE );
+  }
+
+  // set stateTooManyErrors
+  if( infoEntry.state & BOS_PROCESS_TOO_MANY_ERRORS ) {
+    (*env)->SetBooleanField(env, process, 
+                           process_stateTooManyErrorsField, TRUE );
+  } else {
+    (*env)->SetBooleanField(env, process, 
+                           process_stateTooManyErrorsField, FALSE );
+  }
+
+  // set stateBadFileAccess
+  if( infoEntry.state & BOS_PROCESS_BAD_FILE_ACCESS ) {
+    (*env)->SetBooleanField(env, process, 
+                           process_stateBadFileAccessField, TRUE );
+  } else {
+    (*env)->SetBooleanField(env, process, 
+                           process_stateBadFileAccessField, FALSE );
+  }
+
+}
+
+/**
+ * Fills in the information fields of the provided Process.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the process belongs
+ * jname     the instance name of the process  for which to get
+ *                  the information
+ * process     the Process object in which to fill 
+ *                    in the information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_getProcessInfo (JNIEnv *env, jclass cls, 
+                                                jint serverHandle, 
+                                                jstring jname, 
+                                                jobject process) {
+
+  const char *name;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  getProcessInfoChar( env, (void *) serverHandle, name, process );
+
+  // set name in case blank object
+  if( name != NULL ) {
+    if( processCls == 0 ) {
+      internal_getProcessClass( env, process );
+    }
+    (*env)->SetObjectField(env, process, process_nameField, jname);
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+}
+
+/**
+ * Creates a process on a server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key will 
+ *                      belong
+ * jname  the instance name to give the process.  See AFS 
+ *                     documentation for a standard list of instance names
+ * jtype  the type of process this will be. 
+ *                     Acceptable values are:
+ *                     org_openafs_jafs_Process_SIMPLE_PROCESS
+ *                     org_openafs_jafs_Process_FS_PROCESS
+ *                     org_openafs_jafs_Process_CRON_PROCESS
+ * jpath  the execution path process to create
+ * jcronTime   a String representing the time a cron process is to 
+ *                   be run.  Acceptable formats are:
+ *                   for daily restarts: "23:10" or "11:10 pm"
+ *                   for weekly restarts: "sunday 11:10pm" or 
+ *                       "sun 11:10pm"
+ *                   Can be null for non-cron processes.
+ * jnotifier   the execution path to a notifier program that should 
+ *                   be called when the process terminates.  Can be 
+ *                   null
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_create (JNIEnv *env, jclass cls, 
+                                        jint serverHandle, jstring jname, 
+                                        jint jtype, jstring jpath, 
+                                        jstring jcronTime, 
+                                        jstring jnotifier) {
+
+    afs_status_t ast;
+    bos_ProcessType_t type;
+    const char *name;
+    const char *path;
+    const char *cronTime;
+    const char *notifier;
+
+    if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      name = NULL;
+    }
+
+    if( jpath != NULL ) {
+      path = (*env)->GetStringUTFChars(env, jpath, 0);
+      if( !path ) {
+       if( name != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jname, name);
+       }
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+      }
+    } else {
+      path = NULL;
+    }
+
+    switch( jtype ) {
+    case org_openafs_jafs_Process_SIMPLE_PROCESS:
+       type = BOS_PROCESS_SIMPLE;
+       break;
+    case org_openafs_jafs_Process_FS_PROCESS:
+       type = BOS_PROCESS_FS;
+       break;
+    case org_openafs_jafs_Process_CRON_PROCESS:
+       type = BOS_PROCESS_CRON;
+       break;
+    default:
+      if( name != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      if( path != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpath, path);
+      }
+      throwAFSException( env, jtype );
+      return;
+    }
+
+    if( jcronTime != NULL ) {
+       cronTime = (*env)->GetStringUTFChars(env, jcronTime, 0);
+       if( !cronTime ) {
+         if( name != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jname, name);
+         }
+         if( path != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jpath, path);
+         }
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+       }
+    } else {
+       cronTime = NULL;
+    }
+
+    if( jnotifier != NULL ) {
+       notifier = (*env)->GetStringUTFChars(env, jnotifier, 0);
+       if( !notifier ) {
+         if( name != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jname, name);
+         }
+         if( path != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jpath, path);
+         }
+         if( cronTime != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jcronTime, cronTime);
+         }
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+       }
+    } else {
+       notifier = NULL;
+    }
+
+    if( !bos_ProcessCreate( (void *) serverHandle, name, type, path, 
+                           cronTime, notifier, &ast ) ) {
+       // release strings
+       if( cronTime != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jcronTime, cronTime);
+       }
+       if( notifier != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jnotifier, notifier);
+       }
+       if( name != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jname, name);
+       }
+       if( path != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jpath, path);
+       }
+       throwAFSException( env, ast );
+       return;
+    }
+
+
+    // release strings
+    if( cronTime != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jcronTime, cronTime);
+    }
+    if( notifier != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jnotifier, notifier);
+    }
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    if( path != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpath, path);
+    }
+
+}
+
+/**
+ * Removes a process from a server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the process 
+ *                      belongs
+ * jname   the name of the process to remove
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_delete (JNIEnv *env, jclass cls, 
+                                        jint serverHandle, jstring jname) {
+
+    afs_status_t ast;
+    const char *name;
+
+    if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      name = NULL;
+    }
+
+    if( !bos_ProcessDelete( (void *) serverHandle, name, &ast ) ) {
+      if( name != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+    }
+
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+
+}
+
+/**
+ * Stop this process.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the process 
+ *                      belongs
+ * jname   the name of the process to stop
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_stop (JNIEnv *env, jclass cls, 
+                                      jint serverHandle, jstring jname) {
+
+    afs_status_t ast;
+    const char *name;
+
+    if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      name = NULL;
+    }
+
+    if( !bos_ProcessExecutionStateSet( (void *) serverHandle, name, 
+                                      BOS_PROCESS_STOPPED, &ast ) ) {
+      if( name != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+    }
+
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+
+}
+
+/**
+ * Start this process.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the process 
+ *                      belongs
+ * jname   the name of the process to start
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_start (JNIEnv *env, jclass cls, 
+                                       jint serverHandle, jstring jname) {
+
+    afs_status_t ast;
+    const char *name;
+
+    if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      name = NULL;
+    }
+
+    if( !bos_ProcessExecutionStateSet( (void *) serverHandle, name, 
+                                      BOS_PROCESS_RUNNING, &ast ) ) {
+      if( name != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+    }
+
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+
+}
+
+/**
+ * Retart this process.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the process 
+ *                      belongs
+ * jname   the name of the process to restart
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Process_restart (JNIEnv *env, jclass cls, 
+                                         jint serverHandle, jstring jname) {
+
+    afs_status_t ast;
+    const char *name;
+
+    if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      name = NULL;
+    }
+
+    if( !bos_ProcessRestart( (void *) serverHandle, name, &ast ) ) {
+      if( name != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+    }
+
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+
+}
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_Process_reclaimProcessMemory (JNIEnv *env, 
+                                                      jclass cls) {
+
+  if( processCls ) {
+      (*env)->DeleteGlobalRef(env, processCls);
+      processCls = 0;
+  }
+
+}
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/Server.c b/src/JAVA/libjafs/Server.c
new file mode 100644 (file)
index 0000000..ce3f504
--- /dev/null
@@ -0,0 +1,1657 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Server.h"
+
+#include <afs_clientAdmin.h>
+#include <afs_vosAdmin.h>
+#include <afs_bosAdmin.h>
+#include <afs_AdminCommonErrors.h>
+#include <rx/rxkad.h>
+#include <bnode.h>
+
+//// definitions in Internal.c  //////////////////
+
+extern jclass serverCls;
+extern jfieldID server_nameField;
+extern jfieldID server_nameField;
+extern jfieldID server_databaseField;
+extern jfieldID server_fileServerField;
+extern jfieldID server_badDatabaseField;
+extern jfieldID server_badFileServerField;
+extern jfieldID server_IPAddressField;
+
+extern jclass exectimeCls;
+extern jfieldID exectime_HourField;
+extern jfieldID exectime_MinField;
+extern jfieldID exectime_SecField;
+extern jfieldID exectime_DayField;
+extern jfieldID exectime_NowField;
+extern jfieldID exectime_NeverField;
+
+extern jclass partitionCls;
+extern jfieldID partition_cachedInfoField;
+
+extern jclass keyCls;
+extern jfieldID key_cachedInfoField;
+
+extern jclass processCls;
+extern jfieldID process_cachedInfoField;
+extern jfieldID process_nameField;
+//extern jfieldID process_serverHandleField;
+
+extern jclass userCls;
+extern jfieldID user_nameField;
+extern jfieldID user_cachedInfoField;
+//////////////////////////////////////////////////////////
+
+///// definition in jafs_Partition.c /////////////////
+
+extern void fillPartitionInfo( JNIEnv *env, jobject partition, 
+                              vos_partitionEntry_t partEntry );
+
+///////////////////////////////////////////////////
+
+///// definition in jafs_Key.c /////////////////
+
+extern void fillKeyInfo( JNIEnv *env, jobject key, bos_KeyInfo_t keyEntry );
+
+///////////////////////////////////////////////////
+
+///// definition in jafs_Process.c /////////////////
+
+extern void getProcessInfoChar( JNIEnv *env, void *serverHandle, 
+                               const char *processName, jobject process );
+
+///////////////////////////////////////////////////
+
+/**
+ * Extract the information from the given server entry and populate the
+ * given object
+ *
+ * env      the Java environment
+ * cellHandle    the handle of the cell to which the server belongs
+ * server      the Server object to populate with the info
+ * servEntry     the container of the server's information
+ */
+void fillServerInfo
+  ( JNIEnv *env, jint cellHandle, jobject server, afs_serverEntry_t servEntry )
+{
+  jstring jip;
+  jobjectArray jaddresses;
+  jstring jserver;
+  int i = 0;
+
+  // get the class fields if need be
+  if( serverCls == 0 ) {
+    internal_getServerClass( env, server );
+  }
+
+  // in case it's blank
+  jserver = (*env)->NewStringUTF(env, servEntry.serverName);
+  (*env)->SetObjectField(env, server, server_nameField, jserver);
+
+  // let's convert just the addresses in the address array into an IP
+  jaddresses = (jobjectArray) (*env)->GetObjectField( env, server, 
+                                                     server_IPAddressField );
+  for (i = 0; i < 16; i++) {
+       if (servEntry.serverAddress[i] != 0) {
+         jip = 
+           (*env)->NewStringUTF(env, (char *) 
+                                inet_ntoa(htonl(servEntry.serverAddress[i])));
+      (*env)->SetObjectArrayElement(env, jaddresses, i, jip);
+       } else {
+         break;
+       }
+  }
+
+  // let's check if this is really a database server
+  (*env)->SetBooleanField(env, server, server_databaseField, 
+                         servEntry.serverType & DATABASE_SERVER);
+  if( servEntry.serverType & DATABASE_SERVER ) {
+    // for now, if it thinks it's a database server than it is
+    // later, add checks for database configuration, and actual 
+    // on-ness of the machine
+    (*env)->SetBooleanField(env, server, server_badDatabaseField, FALSE);
+  } else {
+    (*env)->SetBooleanField(env, server, server_badDatabaseField, FALSE);
+  }
+
+  // we should check to see if this is truly a file server or not
+  // it could just be an old remnant, left over inside the vldb that 
+  // should be removed.
+  // if it is a file server, mark it as such.  If not, mark it as faulty.
+  (*env)->SetBooleanField(env, server, server_fileServerField,  
+                         servEntry.serverType & FILE_SERVER);
+  if( servEntry.serverType & FILE_SERVER ) {
+    
+    // to see if it's really a file server, make sure the 
+    // "fs" process is running
+    void *bosHandle;
+    afs_status_t ast;
+    bos_ProcessType_t processTypeT;
+    bos_ProcessInfo_t processInfoT;
+    char *fileServerProcessName = "fs";
+
+    // set the file server to true (it thinks it's a file server)
+    (*env)->SetBooleanField(env, server, server_fileServerField, TRUE);
+
+    if( !bos_ServerOpen( (void *) cellHandle, servEntry.serverName, 
+                        &bosHandle, &ast ) ) {
+      throwAFSException( env, ast );
+      return;
+    }
+    if( !bos_ProcessInfoGet( bosHandle, fileServerProcessName, &processTypeT, 
+                            &processInfoT, &ast ) ) {
+      // if the machine does not have a fs process or is not responding 
+      // or is part of another cell
+      if( ast == BZNOENT || ast == -1 || ast == RXKADBADTICKET ) {
+        (*env)->SetBooleanField(env, server, server_badFileServerField, TRUE);
+      // otherwise
+      } else {
+        throwAFSException( env, ast );
+        return;
+      }
+    } else {
+      // it's good
+      (*env)->SetBooleanField(env, server, server_badFileServerField, FALSE);
+    }
+  } else {
+    (*env)->SetBooleanField(env, server, server_badFileServerField, FALSE);
+  }
+
+}
+
+/**
+ * Fills in the information fields of the provided Server. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the server belongs
+ * jname     the name of the server for which to get the information
+ * server     the Server object in which to fill in 
+ *                   the information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getServerInfo (JNIEnv *env, jclass cls, 
+                                              jint cellHandle, jstring jname, 
+                                              jobject server) {
+
+  const char *name;
+  afs_status_t ast;
+  afs_serverEntry_t servEntry;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  // get the server entry
+  if ( !afsclient_AFSServerGet( (void *) cellHandle, name, 
+                               &servEntry, &ast ) ) {
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  fillServerInfo( env, cellHandle, server, servEntry );
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+}
+
+/**
+ * Returns the total number of partitions hosted by the server denoted by
+ * serverHandle, if the server is a fileserver.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the server belongs
+ * serverHandle  the vos handle of the server to which the 
+ *                      partitions belong
+ * returns total number of partitions
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getPartitionCount (JNIEnv *env, jclass cls, 
+                                                  jint cellHandle, 
+                                                  jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  vos_partitionEntry_t partEntry;
+  int i = 0;
+
+  if( !vos_PartitionGetBegin( (void *) cellHandle, (void *) serverHandle, 
+                             NULL, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  while ( vos_PartitionGetNext( (void *) iterationId, &partEntry, &ast ) ) i++;
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the partitions on a server.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getPartitionsNext and getPartitionsDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the server belongs
+ * serverHandle  the vos handle of the server to which the 
+ *                      partitions belong
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getPartitionsBegin (JNIEnv *env, jclass cls, 
+                                                   jint cellHandle, 
+                                                   jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !vos_PartitionGetBegin( (void *) cellHandle, (void *) serverHandle, 
+                             NULL, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next partition of the server.  Returns null 
+ * if there are no more partitions.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next partition of the server
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Server_getPartitionsNextString (JNIEnv *env, 
+                                                        jclass cls, 
+                                                        jint iterationId) {
+
+  afs_status_t ast;
+  jstring jpartition;
+  vos_partitionEntry_t partEntry;
+
+  if( !vos_PartitionGetNext( (void *) iterationId, &partEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  jpartition = (*env)->NewStringUTF(env, partEntry.name);
+  return jpartition;
+
+}
+
+/**
+ * Fills the next partition object of the server.  Returns 0 if there
+ * are no more partitions, != 0 otherwise
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * thePartition   the Partition object in which to fill the 
+ *                       values of the next partition
+ * returns 0 if there are no more servers, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getPartitionsNext (JNIEnv *env, jclass cls, 
+                                                  jint iterationId, 
+                                                  jobject jpartitionObject) {
+    
+  afs_status_t ast;
+  vos_partitionEntry_t partEntry;
+
+  if( !vos_PartitionGetNext( (void *) iterationId, &partEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  fillPartitionInfo( env, jpartitionObject, partEntry );
+
+  // get the class fields if need be
+  if( partitionCls == 0 ) {
+    internal_getPartitionClass( env, jpartitionObject );
+  }
+  (*env)->SetBooleanField( env, jpartitionObject, partition_cachedInfoField, 
+                          TRUE );
+
+    
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getPartitionsDone (JNIEnv *env, jclass cls, 
+                                                  jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !vos_PartitionGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Adds the given to name to the list of bos administrators on that server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      partitions belong
+ * jnewAdmin   the name of the admin to add to the list
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_addBosAdmin (JNIEnv *env, jclass cls, 
+                                            jint serverHandle, 
+                                            jstring jnewAdmin) {
+
+  afs_status_t ast;
+  const char *newAdmin;
+
+  if( jnewAdmin != NULL ) {
+    newAdmin = (*env)->GetStringUTFChars(env, jnewAdmin, 0);
+    if( !newAdmin ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    newAdmin = NULL;
+  }
+
+  if( !bos_AdminCreate( (void *) serverHandle, newAdmin, &ast ) ) {
+    if( newAdmin != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jnewAdmin, newAdmin);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( newAdmin != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jnewAdmin, newAdmin);
+  }
+
+}
+
+/**
+ * Removes the given to name from the list of bos administrators on 
+ * that server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      partitions belong
+ * joldAdmin   the name of the admin to remove from the list
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_removeBosAdmin (JNIEnv *env, jclass cls, 
+                                               jint serverHandle, 
+                                               jstring joldAdmin) {
+
+  afs_status_t ast;
+  const char *oldAdmin;
+
+  if( joldAdmin != NULL ) {
+    oldAdmin = (*env)->GetStringUTFChars(env, joldAdmin, 0);
+    if( !oldAdmin ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    oldAdmin = NULL;
+  }
+
+  if( !bos_AdminDelete( (void *) serverHandle, oldAdmin, &ast ) ) {
+    if( oldAdmin != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, joldAdmin, oldAdmin);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( oldAdmin != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, joldAdmin, oldAdmin);
+  }
+
+}
+
+/**
+ * Returns the total number of BOS administrators associated with the server 
+ * denoted by serverHandle.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the vos handle of the server to which the 
+ *                      BOS admins belong
+ * returns total number of BOS administrators
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getBosAdminCount (JNIEnv *env, jclass cls, 
+                                                 jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  char *admin;
+  jstring jadmin;
+  int i = 0;
+
+  if( !bos_AdminGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  admin = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN);
+
+  if( !admin ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;
+  }
+
+  while ( bos_AdminGetNext( (void *) iterationId, admin, &ast ) ) i++;
+
+  free(admin);
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the bos amdinistrators on a server.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getBosAdminsNext and getBosAdminsDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      partitions belong
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getBosAdminsBegin (JNIEnv *env, jclass cls, 
+                                                  jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !bos_AdminGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next bos admin of the server.  Returns null 
+ * if there are no more admins.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next admin of the server
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Server_getBosAdminsNextString (JNIEnv *env, 
+                                                       jclass cls, 
+                                                       jint iterationId) {
+
+  afs_status_t ast;
+  jstring jadmin;
+  char *admin = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN );
+
+  if( !admin ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !bos_AdminGetNext( (void *) iterationId, admin, &ast ) ) {
+    free(admin);
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  jadmin = (*env)->NewStringUTF(env, admin);
+  free(admin);
+  return jadmin;
+
+}
+
+/**
+ * Returns the next bos admin of the server.  Returns 0 if there
+ * are no more admins, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which these admins belong
+ * iterationId   the iteration ID of this iteration
+ * juserObject   the user object in which to fill the values of this admin
+ * returns 0 if no more admins, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getBosAdminsNext (JNIEnv *env, jclass cls, 
+                                                 jint cellHandle, 
+                                                 jint iterationId, 
+                                                 jobject juserObject ) {
+    
+  afs_status_t ast;
+  char *admin;
+  jstring jadmin;
+
+  admin = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN);
+
+  if( !admin ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !bos_AdminGetNext( (void *) iterationId, admin, &ast ) ) {
+    free( admin );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  jadmin = (*env)->NewStringUTF(env, admin);
+
+  if( userCls == 0 ) {
+    internal_getUserClass( env, juserObject );
+  }
+
+  (*env)->SetObjectField(env, juserObject, user_nameField, jadmin);
+
+  getUserInfoChar( env, cellHandle, admin, juserObject );
+  (*env)->SetBooleanField( env, juserObject, user_cachedInfoField, TRUE );
+
+  free( admin );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getBosAdminsDone (JNIEnv *env, jclass cls, 
+                                                 jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !bos_AdminGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Returns the total number of keys hosted by the server denoted by
+ * serverHandle.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the vos handle of the server to which the 
+ *                      keys belong
+ * returns total number of keys
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getKeyCount (JNIEnv *env, jclass cls, 
+                                            jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  bos_KeyInfo_t keyEntry;
+  int i = 0;
+
+  if( !bos_KeyGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  while ( bos_KeyGetNext( (void *) iterationId, &keyEntry, &ast ) ) i++;
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the keys of a server.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getKeysNext and getKeysDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the keys belong
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getKeysBegin (JNIEnv *env, jclass cls, 
+                                             jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !bos_KeyGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next key of the server.  Returns 0 if there
+ * are no more keys, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * jkeyObject   a Key object, in which to fill in the
+ *                 properties of the next key.
+ * returns 0 if there are no more keys, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getKeysNext (JNIEnv *env, jclass cls, 
+                                            jint iterationId, 
+                                            jobject jkeyObject) {
+    
+  afs_status_t ast;
+  bos_KeyInfo_t keyEntry;
+
+  if( !bos_KeyGetNext( (void *) iterationId, &keyEntry, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  fillKeyInfo( env, jkeyObject, keyEntry );
+
+  // get the class fields if need be
+  if( keyCls == 0 ) {
+    internal_getKeyClass( env, jkeyObject );
+  }
+
+  (*env)->SetBooleanField( env, jkeyObject, key_cachedInfoField, TRUE );
+
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getKeysDone (JNIEnv *env, jclass cls, 
+                                            jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !bos_KeyGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Returns the total number of processes hosted by the server denoted by
+ * serverHandle.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the vos handle of the server to which the 
+ *                      processes belong
+ * returns total number of processes
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getProcessCount (JNIEnv *env, jclass cls, 
+                                                jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+  char *process;
+  jstring jprocess;
+  int i = 0;
+
+  if( !bos_ProcessNameGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  process = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN );
+
+  if( !process ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;
+  }
+
+  while ( bos_ProcessNameGetNext( (void *) iterationId, process, &ast ) ) i++;
+
+  free( process );
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the processes on a server.  Returns 
+ * an iteration ID to be used by subsequent calls to 
+ * getProcessesNext and getProcessesDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      processes belong
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getProcessesBegin (JNIEnv *env, jclass cls, 
+                                                  jint serverHandle) {
+
+  afs_status_t ast;
+  void *iterationId;
+
+  if( !bos_ProcessNameGetBegin( (void *) serverHandle, &iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next process of the server.  Returns null 
+ * if there are no more processes.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next process of the cell
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Server_getProcessesNextString (JNIEnv *env, 
+                                                       jclass cls, 
+                                                       jint iterationId) {
+
+  afs_status_t ast;
+  jstring jprocess;
+  char *process = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN );
+
+  if( !process ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !bos_ProcessNameGetNext( (void *) iterationId, process, &ast ) ) {
+    free( process );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+
+  jprocess = (*env)->NewStringUTF(env, process);
+  free( process );
+  return jprocess;
+
+}
+
+/**
+ * Fills the next process object of the server.  Returns 0 if there
+ * are no more processes, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle    the handle of the BOS server that hosts the process
+ * iterationId   the iteration ID of this iteration
+ * jprocessObject    the Process object in which to fill the 
+ *                          values of the next process
+ * returns 0 if there are no more processes, != otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Server_getProcessesNext (JNIEnv *env, jclass cls, 
+                                                 jint serverHandle, 
+                                                 jint iterationId, 
+                                                 jobject jprocessObject) {
+    
+  afs_status_t ast;
+  char *process = (char *) malloc( sizeof(char)*BOS_MAX_NAME_LEN );
+  jstring jprocess;
+
+  if( !process ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !bos_ProcessNameGetNext( (void *) iterationId, process, &ast ) ) {
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      free( process );
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  // get the class fields if need be
+  if( processCls == 0 ) {
+    internal_getProcessClass( env, jprocessObject );
+  }
+
+  jprocess = (*env)->NewStringUTF(env, process);
+  (*env)->SetObjectField(env, jprocessObject, process_nameField, jprocess);
+
+  getProcessInfoChar( env, (void *) serverHandle, process, jprocessObject );
+
+  (*env)->SetBooleanField( env, jprocessObject, 
+                          process_cachedInfoField, TRUE );
+
+  free( process );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getProcessesDone (JNIEnv *env, jclass cls, 
+                                                 jint iterationId) {
+
+  afs_status_t ast;
+
+  if( !bos_ProcessNameGetDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Salvages (restores consistency to) a volume, partition, or server
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the bos handle of the server on which the 
+ *                      volume resides
+ * jpartName  the name of the partition to salvage, 
+ *                   can be null only if volName is 
+ *                   null
+ * jvolName  the name of the volume to salvage, 
+ *                  can be null
+ * numSalvagers   the number of salvager processes to run in parallel
+ * jtempDir   directory to place temporary files, can be 
+ *                  null
+ * jlogFile    where salvager log will be written, can be 
+ *                   null
+ * inspectAllVolumes   whether or not to inspect all volumes, 
+ *                            not just those marked as active at crash
+ * removeBadlyDamaged   whether or not to remove a volume if it's 
+ *                             badly damaged
+ * writeInodes   whether or not to record a list of inodes modified
+ * writeRootInodes   whether or not to record a list of AFS 
+ *                          inodes owned by root
+ * forceDirectory   whether or not to salvage an entire directory 
+ *                         structure
+ * forceBlockReads   whether or not to force the salvager to read 
+ *                          the partition
+ *                          one block at a time and skip badly damaged 
+ *                          blocks.  Use if partition has disk errors
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_salvage (JNIEnv *env, jclass cls, 
+                                        jint cellHandle, jint serverHandle, 
+                                        jstring jpartName, jstring jvolName, 
+                                        jint numSalvagers, jstring jtempDir, 
+                                        jstring jlogFile, 
+                                        jboolean inspectAllVolumes, 
+                                        jboolean removeBadlyDamaged, 
+                                        jboolean writeInodes, 
+                                        jboolean writeRootInodes, 
+                                        jboolean forceDirectory, 
+                                        jboolean forceBlockReads) {
+
+  afs_status_t ast;
+  const char *partName;
+  const char *volName;
+  const char *tempDir;
+  const char *logFile;
+  vos_force_t force;
+  bos_SalvageDamagedVolumes_t sdv;
+  bos_WriteInodes_t wi;
+  bos_WriteRootInodes_t wri;
+  bos_ForceDirectory_t forceD;
+  bos_ForceBlockRead_t forceBR;
+  
+  // convert strings
+  if( jpartName != NULL ) {
+    partName = (*env)->GetStringUTFChars(env, jpartName, 0);    
+    if( !partName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    partName = NULL;
+  }
+  if( jvolName != NULL ) {
+    volName = (*env)->GetStringUTFChars(env, jvolName, 0);    
+    if( !volName ) {
+      if( partName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpartName, partName);    
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    volName = NULL;
+  }
+  if( jtempDir != NULL ) {
+    tempDir = (*env)->GetStringUTFChars(env, jtempDir, 0);    
+    if( !tempDir ) {
+      if( partName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpartName, partName);    
+      }
+      if( volName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jvolName, volName);    
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    tempDir = NULL;
+  }
+  if( jlogFile != NULL ) {
+    logFile = (*env)->GetStringUTFChars(env, jlogFile, 0);    
+    if( !logFile ) {
+      if( partName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpartName, partName);    
+      }
+      if( volName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jvolName, volName);    
+      }
+      if( tempDir != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jtempDir, tempDir);    
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    logFile = NULL;
+  }
+
+  // deal with booleans
+  if( inspectAllVolumes ) {
+    force = VOS_FORCE;
+  } else {
+    force = VOS_NORMAL;
+  }
+  if( removeBadlyDamaged ) {
+    sdv = BOS_DONT_SALVAGE_DAMAGED_VOLUMES;
+  } else {
+    sdv = BOS_SALVAGE_DAMAGED_VOLUMES;
+  }
+  if( writeInodes ) {
+    wi = BOS_SALVAGE_WRITE_INODES;
+  } else {
+    wi = BOS_SALVAGE_DONT_WRITE_INODES;
+  }
+  if( writeRootInodes ) {
+    wri = BOS_SALVAGE_WRITE_ROOT_INODES;
+  } else {
+    wri = BOS_SALVAGE_DONT_WRITE_ROOT_INODES;
+  }
+  if( forceDirectory ) {
+    forceD = BOS_SALVAGE_FORCE_DIRECTORIES;
+  } else {
+    forceD = BOS_SALVAGE_DONT_FORCE_DIRECTORIES;
+  }
+  if( forceBlockReads ) {
+    forceBR = BOS_SALVAGE_FORCE_BLOCK_READS;
+  } else {
+    forceBR = BOS_SALVAGE_DONT_FORCE_BLOCK_READS;
+  }
+
+  //salvage!
+  if( !bos_Salvage( (void *) cellHandle, (void *) serverHandle, partName, 
+                   volName, (int) numSalvagers, tempDir, logFile, force, sdv, 
+                   wi, wri, forceD, forceBR, &ast ) ) {
+    if( partName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpartName, partName);    
+    }
+    if( volName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jvolName, volName);    
+    }
+    if( tempDir != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jtempDir, tempDir);    
+    }
+    if( logFile != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);    
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  // release strings
+  if( partName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jpartName, partName);    
+  }
+  if( volName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jvolName, volName);    
+  }
+  if( tempDir != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jtempDir, tempDir);    
+  }
+  if( logFile != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);    
+  }
+
+}
+
+/**
+ *  Fills in the restart time fields of the given Server
+ *  object. 
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key belongs
+ * jtype  whether to get the general or binary restart. 
+ *               Acceptable values are:
+ *               org_opemafs_jafs_Server_RESTART_BINARY
+ *               org_opemafs_jafs_Server_RESTART_GENERAL    
+ * execTime   the ExecutableTime object, in which 
+ *                   to fill the restart time fields
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_getRestartTime
+  (JNIEnv *env, jclass cls, jint serverHandle, jint jtype, jobject exectime)
+{
+  afs_status_t ast;
+  bos_Restart_t type;
+  bos_RestartTime_t time;
+  jfieldID hourField;
+  jfieldID minField;
+  jfieldID secField;
+  jfieldID dayField;
+  jfieldID neverField;
+  jfieldID nowField;
+
+  // get the class fields if need be
+  if( exectimeCls == 0 ) {
+    internal_getExecTimeClass( env, exectime );
+  }
+
+  if( jtype == org_openafs_jafs_Server_RESTART_BINARY ) {
+    type = BOS_RESTART_DAILY;
+  } else {
+    type = BOS_RESTART_WEEKLY;
+  }
+
+  hourField  = exectime_HourField;
+  minField   = exectime_MinField;
+  secField   = exectime_SecField;
+  dayField   = exectime_DayField;
+  neverField = exectime_NeverField;
+  nowField   = exectime_NowField;
+
+  if( !bos_ExecutableRestartTimeGet( (void *) serverHandle, type, 
+                                    &time, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+  
+  // set now
+  (*env)->SetBooleanField(env, exectime, nowField, 
+                         (time.mask & BOS_RESTART_TIME_NOW) );
+  
+  // set never
+  (*env)->SetBooleanField(env, exectime, neverField, 
+                         (time.mask & BOS_RESTART_TIME_NEVER) );
+
+  // set hour
+  (*env)->SetShortField(env, exectime, hourField, time.hour );
+
+  // set minute
+  (*env)->SetShortField(env, exectime, minField, time.min );
+
+  // set second
+  (*env)->SetShortField(env, exectime, secField, time.sec );
+
+  // set day
+  if( time.mask & BOS_RESTART_TIME_DAY ) {
+    (*env)->SetShortField(env, exectime, dayField, time.day );
+  } else {
+    (*env)->SetShortField(env, exectime, dayField, (jshort) -1 );
+  }
+
+}
+
+/**
+ *  Sets the restart time of the bos server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key belongs
+ * jtype  whether this is to be a general or binary restart. 
+ *               Acceptable values are:
+ *               org_opemafs_jafs_Server_RESTART_BINARY
+ *               org_opemafs_jafs_Server_RESTART_GENERAL
+ * executableTime   the ExecutableTime object containing the 
+ *                         desired information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_setRestartTime (JNIEnv *env, jclass cls, 
+                                               jint serverHandle, jint jtype, 
+                                               jobject exectime ) {
+
+  afs_status_t ast;
+  bos_Restart_t type;
+  bos_RestartTime_t time;
+  jboolean doHour;
+  jboolean doMinute;
+  jboolean doSecond;
+  jboolean doDay;
+  jboolean doNever;
+  jboolean doNow;  
+  jshort hour;
+  jshort minute;
+  jshort second;
+  jshort day;
+  jfieldID hourField;
+  jfieldID minField;
+  jfieldID secField;
+  jfieldID dayField;
+  jfieldID neverField;
+  jfieldID nowField;
+
+  // get the class fields if need be
+  if( exectimeCls == 0 ) {
+    internal_getExecTimeClass( env, exectime );
+  }
+
+  if( jtype == org_openafs_jafs_Server_RESTART_BINARY ) {
+    type = BOS_RESTART_DAILY;
+  } else {
+    type = BOS_RESTART_WEEKLY;
+  }
+
+  hourField  = exectime_HourField;
+  minField   = exectime_MinField;
+  secField   = exectime_SecField;
+  dayField   = exectime_DayField;
+  neverField = exectime_NeverField;
+  nowField   = exectime_NowField;
+
+  hour = (*env)->GetShortField(env, exectime, hourField );
+  if( hour != 0 ) {
+    doHour = TRUE;
+  } else {
+    doHour = FALSE;
+  }
+  minute = (*env)->GetShortField(env, exectime, minField );
+  if( minute != 0 ) {
+    doMinute = TRUE;
+  } else {
+    doMinute = FALSE;
+  }
+  second = (*env)->GetShortField(env, exectime, secField );
+  if( second != 0 ) {
+    doSecond = TRUE;
+  } else {
+    doSecond = FALSE;
+  }
+  day = (*env)->GetShortField(env, exectime, dayField );
+  if( day != -1 ) {
+    doDay = TRUE;
+  } else {
+    doDay = FALSE;
+  }
+  doNever = (*env)->GetBooleanField(env, exectime, neverField );
+  doNow = (*env)->GetBooleanField(env, exectime, nowField );
+
+  bzero(&time, sizeof(time));
+
+  if( jtype == org_openafs_jafs_Server_RESTART_BINARY ) {
+    type = BOS_RESTART_DAILY;
+  } else {
+    type = BOS_RESTART_WEEKLY;
+  }  
+
+  if( doHour ) {
+    time.mask |= BOS_RESTART_TIME_HOUR;
+  }
+  if( doMinute ) {
+    time.mask |= BOS_RESTART_TIME_MINUTE;
+  }
+  if( doSecond ) {
+    time.mask |= BOS_RESTART_TIME_SECOND;
+  }
+  if( doDay ) {
+    time.mask |= BOS_RESTART_TIME_DAY;
+  }
+  if( doNever ) {
+    time.mask |= BOS_RESTART_TIME_NEVER;
+  }
+  if( doNow ) {
+    time.mask |= BOS_RESTART_TIME_NOW;
+  }
+
+  time.hour = hour;
+  time.min = minute;
+  time.sec = second;
+  time.day = day;
+
+  if( !bos_ExecutableRestartTimeSet( (void *) serverHandle, type, 
+                                    time, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ *  Synchronizes a particular server with the volume location database.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the server belongs
+ * serverHandle  the vos handle of the server     
+ * partition   the id of the partition to sync, can be -1 to ignore
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_syncServerWithVLDB (JNIEnv *env, jclass cls, 
+                                                   jint cellHandle, 
+                                                   jint serverHandle, 
+                                                   jint partition) {
+
+  afs_status_t ast;
+  int *part;
+
+  if( partition == -1 ) {
+    part = NULL;
+  } else {
+    part = (int *) &partition;
+  }
+
+  if( !vos_ServerSync( (void *) cellHandle, (void *) serverHandle, 
+                      NULL, part, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ *  Synchronizes the volume location database with a particular server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the server belongs
+ * serverHandle  the vos handle of the server     
+ * partition   the id of the partition to sync, can be -1 to ignore
+ * forceDeletion   whether or not to force the deletion of bad volumes
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_syncVLDBWithServer (JNIEnv *env, jclass cls, 
+                                                   jint cellHandle, 
+                                                   jint serverHandle, 
+                                                   jint partition, 
+                                                   jboolean forceDeletion) {
+
+  afs_status_t ast;
+  int *part;
+  vos_force_t force;
+
+  if( partition == -1 ) {
+    part = NULL;
+  } else {
+    part = (int *) &partition;
+  }
+  
+  if( forceDeletion ) {
+    force = VOS_FORCE;
+  } else {
+    force = VOS_NORMAL;
+  }
+
+  if( !vos_VLDBSync( (void *) cellHandle, (void *) serverHandle, NULL, part, 
+                    force, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Start all server processes.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      processes belong
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_startAllProcesses (JNIEnv *env, jclass cls, 
+                                                  jint serverHandle) {
+
+  afs_status_t ast;
+
+  if( !bos_ProcessAllStart( (void *) serverHandle, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Stop all server processes.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      processes belong
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_stopAllProcesses (JNIEnv *env, jclass cls, 
+                                                 jint serverHandle) {
+
+  afs_status_t ast;
+
+  if( !bos_ProcessAllStop( (void *) serverHandle, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Restart all server processes.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the 
+ *                      processes belong
+ * restartBosServer   whether or not to restart the bos server as well
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_restartAllProcesses (JNIEnv *env, jclass cls, 
+                                                    jint serverHandle, 
+                                                  jboolean restartBosServer) {
+
+  afs_status_t ast;
+  bos_RestartBosServer_t rbs;
+  
+  if( restartBosServer ) {
+    rbs = BOS_RESTART_BOS_SERVER;
+  } else {
+    rbs = BOS_DONT_RESTART_BOS_SERVER;
+  }
+
+  if( !bos_ProcessAllStopAndRestart( (void *) serverHandle, rbs, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Retrieves a specified bos log from a server.  Right now this 
+ * method will simply return a huge String containing the log, but 
+ * hopefully we can devise a better way to make this work more efficiently.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key belongs
+ * jlogFile   the full path and name of the desired bos log
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_Server_getLog(JNIEnv *env, jclass cls, 
+                                      jint serverHandle, jstring jlogFile) {
+
+    afs_status_t ast;
+    const char *logFile;
+    char *logData;
+    unsigned long currInLogSize = 1;
+    unsigned long currOutLogSize = 0;
+    jstring logOut;
+
+    if( jlogFile != NULL ) {
+      logFile = (*env)->GetStringUTFChars(env, jlogFile, 0);
+      if( !logFile ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+    } else {
+      logFile = NULL;
+    }
+
+    logData = (char *) malloc( sizeof(char)*currInLogSize );
+    if( !logData ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    // check how big the log is . . .
+    if( !bos_LogGet( (void *) serverHandle, logFile, 
+                    &currOutLogSize, logData, &ast ) ) {
+      // anything but not enough room in buffer
+      if( ast != ADMMOREDATA ) {
+       free( logData );
+       if( logFile != NULL ) { 
+         (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);
+       }
+       throwAFSException( env, ast );
+       return NULL;
+      }
+    }
+
+    free( logData );
+
+    // increase log size (plus one for terminator)
+    currInLogSize = currOutLogSize + 1;
+    // allocate buffer
+    logData = (char *) malloc( sizeof(char)*currInLogSize );
+    if( !logData ) {
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    if( !logData ) {
+      // memory exception
+      if( logFile != NULL ) { 
+         (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);
+      }
+      throwAFSException( env, ast );
+      return NULL;
+    }
+
+    // get the log for real
+    if( !bos_LogGet( (void *) serverHandle, logFile, &currOutLogSize, 
+                    logData, &ast ) ) {
+       free( logData );
+       if( logFile != NULL ) { 
+         (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);
+       }
+       (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);
+       throwAFSException( env, ast );
+       return NULL;
+    }
+    
+    logData[currOutLogSize] == '\0';
+
+    logOut = (*env)->NewStringUTF(env, logData);
+    
+    free( logData );
+    if( logFile != NULL ) { 
+      (*env)->ReleaseStringUTFChars(env, jlogFile, logFile);
+    }
+    return logOut;
+
+}
+
+
+/**
+ * Executes any command on the specified server.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the bos handle of the server to which the key belongs
+ * jcommand     the text of the commmand to execute
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Server_executeCommand (JNIEnv *env, jclass cls, 
+                                               jint serverHandle, 
+                                               jstring jcommand) {
+
+  afs_status_t ast;
+  const char *command;
+
+  if( jcommand != NULL ) {
+    command = (*env)->GetStringUTFChars(env, jcommand, 0);
+    if( !command ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    command = NULL;
+  }
+
+  if( !bos_CommandExecute( (void *) serverHandle, command, &ast ) ) {
+    if( command != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jcommand, command);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( command != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jcommand, command);
+  }
+
+}
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_Server_reclaimServerMemory (JNIEnv *env, jclass cls) {
+
+  if( serverCls ) {
+      (*env)->DeleteGlobalRef(env, serverCls);
+      serverCls = 0;
+  }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/User.c b/src/JAVA/libjafs/User.c
new file mode 100644 (file)
index 0000000..cf57acb
--- /dev/null
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_User.h"
+
+#include <stdio.h>
+#include <afs_ptsAdmin.h>
+#include <afs_kasAdmin.h>
+#include <kautils.h>
+#include <afs_AdminPtsErrors.h>
+#include <afs_AdminClientErrors.h>
+#include <afs_AdminCommonErrors.h>
+#include <prerror.h>
+
+///// definitions in Internal.c ////////////////////
+
+extern jclass userCls;
+extern jfieldID user_nameField;
+extern jfieldID user_ptsField;
+extern jfieldID user_kasField;
+//pts fields
+extern jfieldID user_nameUidField;
+extern jfieldID user_ownerUidField;
+extern jfieldID user_creatorUidField;
+extern jfieldID user_listStatusField;
+extern jfieldID user_listGroupsOwnedField;
+extern jfieldID user_listMembershipField;
+extern jfieldID user_groupCreationQuotaField;
+extern jfieldID user_groupMembershipCountField;
+extern jfieldID user_ownerField;
+extern jfieldID user_creatorField;
+// kas fields
+extern jfieldID user_adminSettingField;
+extern jfieldID user_tgsSettingField;
+extern jfieldID user_encSettingField;
+extern jfieldID user_cpwSettingField;
+extern jfieldID user_rpwSettingField;
+extern jfieldID user_userExpirationField;
+extern jfieldID user_lastModTimeField;
+extern jfieldID user_lastModNameField;
+extern jfieldID user_lastChangePasswordTimeField;
+extern jfieldID user_maxTicketLifetimeField;
+extern jfieldID user_keyVersionField;
+extern jfieldID user_encryptionKeyField;
+extern jfieldID user_keyCheckSumField;
+extern jfieldID user_daysToPasswordExpireField;
+extern jfieldID user_failLoginCountField;
+extern jfieldID user_lockTimeField;
+extern jfieldID user_lockedUntilField;
+
+extern jclass groupCls;
+//extern jfieldID group_cellHandleField;
+extern jfieldID group_nameField;
+extern jfieldID group_cachedInfoField;
+
+//////////////////////////////////////////////////////////////////
+
+/**
+ * Creates the kas and pts entries for a new user.  Pass in 0 for the uid 
+ * if pts is to automatically assign the user id.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * juserName      the name of the user to create
+ * jpassword      the password for the new user
+ * uid     the user id to assign to the user (0 to have one 
+ *                automatically assigned)
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_create
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring juserName, 
+   jstring jpassword, jint uid )
+{
+  afs_status_t ast;
+  const char *userName;
+  const char *password;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+  
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  // convert java strings
+  if( juserName != NULL ) {
+    userName = (*env)->GetStringUTFChars(env, juserName, 0);
+    if( !userName ) {
+      free( who );
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    userName = NULL;
+  }
+  if( jpassword != NULL ) {
+    password = (*env)->GetStringUTFChars(env, jpassword, 0);
+    if( !password ) {
+      free( who );
+      if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    password = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( userName != NULL && strlen( userName ) > KAS_MAX_NAME_LEN ) {
+    free( who );
+    // release converted java strings
+    if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    if( password != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpassword, password);
+    }
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG  );
+    return;
+  }
+
+  // make sure name doesn't have ":" in it
+  if( userName != NULL && strchr( userName, ':' ) != (int) NULL ) {
+      free(who);
+      // release converted java strings
+      if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( password != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpassword, password);
+      }
+      throwAFSException( env, PRBADNAM );
+      return;
+  }
+
+  // make sure the id isn't negative
+  if( uid < 0 ) {
+      free(who);
+      // release converted java strings
+      if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( password != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jpassword, password);
+      }
+      // use the "bad arg" error code even though it's an ID exception.  
+      // There isn't a bad user ID error code
+      throwAFSException( env, PRBADARG );
+      return;
+  }
+
+  if( userName != NULL ) {
+    internal_makeKasIdentity( userName, who );
+  }
+
+  // create the kas entry
+  if (!kas_PrincipalCreate( (void *) cellHandle, NULL, who, 
+                           password, &ast ) ) {
+    free(who);
+    // release converted java strings
+    if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    if( password != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpassword, password);
+    }
+    throwAFSException( env, ast );
+    return;
+  } 
+
+  // create the pts entry - if there's an error, make sure to delete 
+  // the kas entry 
+  if( !pts_UserCreate( (void *) cellHandle, userName, (int *) &uid, &ast ) ) {
+    afs_status_t ast_kd;
+    kas_PrincipalDelete( (void *) cellHandle, NULL, who, &ast_kd );
+    free( who );
+    // release converted java strings
+    if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    if( password != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpassword, password);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  free( who );
+  // release converted java strings
+  if( userName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+  if( password != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jpassword, password);
+  }
+
+}
+
+/**
+ * Deletes the pts and kas entry for a user.  Deletes this user from the 
+ * membership list of the groups to which it belonged, but does not delete 
+ * the groups owned by this user.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * juserName      the name of the user to delete
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_delete
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring juserName )
+{
+  afs_status_t ast;
+  const char *userName;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );  
+  int kas;
+  
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( juserName != NULL ) {
+      userName = (*env)->GetStringUTFChars(env, juserName, 0);
+      if( !userName ) {
+       free( who );
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+      }
+  } else {
+      userName = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( userName != NULL && strlen( userName ) > KAS_MAX_NAME_LEN ) {
+    free( who );
+    if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG  );
+    return;
+  }
+
+  if( userName != NULL ) {
+      internal_makeKasIdentity( userName, who );
+  }
+
+  // delete the kas entry
+  if( !kas_PrincipalDelete( (void *) cellHandle, NULL, who, &ast ) ) {
+      if( ast != KANOENT ) {
+         free(who);
+         if( userName != NULL ) {
+             (*env)->ReleaseStringUTFChars(env, juserName, userName);
+         }
+         throwAFSException( env, ast );
+         return;
+      } else {
+         kas = FALSE;
+      }
+  }
+
+  //delete the pts entry
+  if( !pts_UserDelete( (void *) cellHandle, userName, &ast ) ) {
+      // throw exception if there was no such pts user only if there was 
+      // also no such kas user
+      if( (ast == ADMPTSFAILEDNAMETRANSLATE && !kas ) || 
+         ast != ADMPTSFAILEDNAMETRANSLATE ) {
+         free( who );
+         if( userName != NULL ) {
+             (*env)->ReleaseStringUTFChars(env, juserName, userName);
+         }
+         throwAFSException( env, ast );
+         return;
+      }
+  }
+
+  free( who );
+  // release converted java strings
+  if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+}
+
+/**
+ * Unlocks a user.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * juserName      the name of the user to unlock
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_unlock
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring juserName )
+{
+  afs_status_t ast;
+  const char *userName;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );  
+  
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  // convert java strings
+  if( juserName != NULL ) {
+      userName = (*env)->GetStringUTFChars(env, juserName, 0);
+    if( !userName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+      userName = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( userName != NULL && strlen( userName ) > KAS_MAX_NAME_LEN ) {
+    free( who );
+    if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG  );
+    return;
+  }  
+
+  if( userName != NULL ) {
+    internal_makeKasIdentity( userName, who );
+  }
+
+  if( !kas_PrincipalUnlock( (void *) cellHandle, NULL, who, &ast ) ) {
+    free( who );
+    if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  free( who );
+  // release converted java strings
+  if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+}
+
+/**
+ * Retrieve the information for the specified user and populate the
+ * given object
+ *
+ * env      the Java environment
+ * cellHandle    the handle of the cell to which the user belongs
+ * name      the name of the user for which to get the info
+ * user      the User object to populate with the info
+ */
+void getUserInfoChar
+  (JNIEnv *env, int cellHandle, const char *name, jobject user)
+{
+  jstring jowner;
+  jstring jcreator;
+  jstring jlastModName;
+  jstring jencryptionKey;
+  jboolean pts;
+  jboolean kas;
+  pts_UserEntry_t ptsEntry;
+  afs_status_t ast;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+  kas_principalEntry_t kasEntry;
+  unsigned int lockedUntil;
+
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  // make sure the name is within the allowed bounds
+  if( name != NULL && strlen( name ) > KAS_MAX_NAME_LEN ) {
+    free( who );
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+    return;
+  }
+  
+  if( name != NULL ) {
+      internal_makeKasIdentity( name, who );
+  }
+
+  // get all the field ids, if you haven't done so already
+  if( userCls == 0 ) {
+    internal_getUserClass( env, user );
+  }
+
+  // get the pts entry
+  if ( !pts_UserGet( (void *) cellHandle, name, &ptsEntry, &ast ) ) {
+    // if the user has no pts ptsEntry
+    if( ast == ADMPTSFAILEDNAMETRANSLATE ) {
+       pts = FALSE;    
+    } else {
+       free( who );
+       throwAFSException( env, ast );
+       return;
+    }
+  } else {
+      pts = TRUE;
+  }
+  
+
+  // get the kas entry
+  if( !kas_PrincipalGet( (void *) cellHandle, NULL, who, &kasEntry, &ast ) ) {
+    // no kas entry
+    if( ast == KANOENT ) { 
+       if( !pts ) {
+           free( who );
+           throwAFSException( env, ast );
+           return;
+       } else {
+           kas = FALSE;
+       }
+    // other
+    } else {
+       free( who );
+       throwAFSException( env, ast );
+       return;
+    }
+  } else {
+      kas = TRUE;
+  }
+
+  // get the lock status
+  if( kas && !kas_PrincipalLockStatusGet( (void *) cellHandle, NULL, who, 
+                                         &lockedUntil, &ast ) ) {
+    free( who );
+    throwAFSException( env, ast );
+    return;
+  }
+
+  (*env)->SetBooleanField(env, user, user_ptsField, pts);
+  (*env)->SetBooleanField(env, user, user_kasField, kas);
+
+  // set the pts fields
+  if( pts ) {
+      (*env)->SetIntField(env, user, user_nameUidField, ptsEntry.nameUid);
+      (*env)->SetIntField(env, user, user_ownerUidField, ptsEntry.ownerUid);
+      (*env)->SetIntField(env, user, user_creatorUidField, 
+                         ptsEntry.creatorUid);
+      (*env)->SetIntField(env, user, user_groupCreationQuotaField, 
+                         ptsEntry.groupCreationQuota);
+      (*env)->SetIntField(env, user, user_groupMembershipCountField, 
+                         ptsEntry.groupMembershipCount);
+      
+      if( ptsEntry.listStatus == PTS_USER_OWNER_ACCESS ) {
+         (*env)->SetIntField(env, user, user_listStatusField, 
+                             org_openafs_jafs_User_USER_OWNER_ACCESS);
+      } else {
+         (*env)->SetIntField(env, user, user_listStatusField, 
+                             org_openafs_jafs_User_USER_ANYUSER_ACCESS);
+      }
+      if( ptsEntry.listGroupsOwned == PTS_USER_OWNER_ACCESS ) {
+         (*env)->SetIntField(env, user, user_listGroupsOwnedField, 
+                             org_openafs_jafs_User_USER_OWNER_ACCESS);
+      } else {
+         (*env)->SetIntField(env, user, user_listGroupsOwnedField, 
+                             org_openafs_jafs_User_USER_ANYUSER_ACCESS);
+      }
+      if( ptsEntry.listMembership == PTS_USER_OWNER_ACCESS ) {
+         (*env)->SetIntField(env, user, user_listMembershipField, 
+                             org_openafs_jafs_User_USER_OWNER_ACCESS);
+      } else {
+         (*env)->SetIntField(env, user, user_listMembershipField, 
+                             org_openafs_jafs_User_USER_ANYUSER_ACCESS);
+      }
+      
+      jowner = (*env)->NewStringUTF(env, ptsEntry.owner);
+      jcreator =  (*env)->NewStringUTF(env, ptsEntry.creator);
+      
+      (*env)->SetObjectField(env, user, user_ownerField, jowner);
+      (*env)->SetObjectField(env, user, user_creatorField, jcreator);
+
+  }
+
+  // set the kas fields
+  if( kas ) {
+      char *convertedKey;
+      int i;
+      if( kasEntry.adminSetting == KAS_ADMIN ) {
+         (*env)->SetIntField(env, user, user_adminSettingField, 
+                             org_openafs_jafs_User_ADMIN);
+      } else {
+         (*env)->SetIntField(env, user, user_adminSettingField, 
+                             org_openafs_jafs_User_NO_ADMIN);
+      }
+      if( kasEntry.tgsSetting == TGS ) {
+         (*env)->SetIntField(env, user, user_tgsSettingField, 
+                             org_openafs_jafs_User_GRANT_TICKETS);
+      } else {
+         (*env)->SetIntField(env, user, user_tgsSettingField, 
+                             org_openafs_jafs_User_NO_GRANT_TICKETS);
+      }
+      if( kasEntry.encSetting != NO_ENCRYPT ) {
+         (*env)->SetIntField(env, user, user_encSettingField, 
+                             org_openafs_jafs_User_ENCRYPT);
+      } else {
+         (*env)->SetIntField(env, user, user_encSettingField, 
+                             org_openafs_jafs_User_NO_ENCRYPT);
+      }
+      if( kasEntry.cpwSetting == CHANGE_PASSWORD ) {
+         (*env)->SetIntField(env, user, user_cpwSettingField, 
+                             org_openafs_jafs_User_CHANGE_PASSWORD);
+      } else {
+         (*env)->SetIntField(env, user, user_cpwSettingField, 
+                             org_openafs_jafs_User_NO_CHANGE_PASSWORD);
+      }
+      if( kasEntry.rpwSetting == REUSE_PASSWORD ) {
+         (*env)->SetIntField(env, user, user_rpwSettingField, 
+                             org_openafs_jafs_User_REUSE_PASSWORD);
+      } else {
+         (*env)->SetIntField(env, user, user_rpwSettingField, 
+                             org_openafs_jafs_User_NO_REUSE_PASSWORD);
+      }
+      (*env)->SetIntField(env, user, user_userExpirationField, 
+                         kasEntry.userExpiration);
+      (*env)->SetIntField(env, user, user_lastModTimeField, 
+                         kasEntry.lastModTime);
+      (*env)->SetIntField(env, user, user_lastChangePasswordTimeField, 
+                         kasEntry.lastChangePasswordTime);
+      (*env)->SetIntField(env, user, user_maxTicketLifetimeField, 
+                         kasEntry.maxTicketLifetime);
+      (*env)->SetIntField(env, user, user_keyVersionField, 
+                         kasEntry.keyVersion);
+      (*env)->SetLongField(env, user, user_keyCheckSumField, 
+                          (unsigned int) kasEntry.keyCheckSum);
+      (*env)->SetIntField(env, user, user_daysToPasswordExpireField, 
+                         kasEntry.daysToPasswordExpire);
+      (*env)->SetIntField(env, user, user_failLoginCountField, 
+                         kasEntry.failLoginCount);
+      (*env)->SetIntField(env, user, user_lockTimeField, kasEntry.lockTime);
+      (*env)->SetIntField(env, user, user_lockedUntilField, lockedUntil);
+      
+      jlastModName = (*env)->NewStringUTF(env, 
+                                         kasEntry.lastModPrincipal.principal);
+      (*env)->SetObjectField(env, user, user_lastModNameField, jlastModName);
+
+      convertedKey = (char *) malloc( sizeof(char *)*
+                                     (sizeof(kasEntry.key.key)*4+1) );
+      if( !convertedKey ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+      }
+      for( i = 0; i < sizeof(kasEntry.key.key); i++ ) {
+       sprintf( &(convertedKey[i*4]), "\\%0.3o", kasEntry.key.key[i] );
+      }
+      jencryptionKey =  (*env)->NewStringUTF(env, convertedKey);
+      (*env)->SetObjectField(env, user, user_encryptionKeyField, 
+                            jencryptionKey);
+      free( convertedKey );
+  }
+  free(who);
+}
+
+/**
+ * Fills in the information fields of the provided User.  
+ * Fills in values based on the current pts and kas information of the user.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * jname     the name of the user for which to get the information
+ * user     the User object in which to fill in the 
+ *                 information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_getUserInfo
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname, jobject user)
+{
+  const char *name;
+
+  if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+      name = NULL;
+  }
+
+  getUserInfoChar( env, cellHandle, name, user );
+  // get class fields if need be
+  if( userCls == 0 ) {
+    internal_getUserClass( env, user );
+  }
+  
+  // set name in case blank object
+  (*env)->SetObjectField(env, user, user_nameField, jname);
+  
+  if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+  }      
+}
+
+/**
+ * Sets the information values of this AFS user to be the parameter values.  
+ * Sets both kas and pts fields.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * jname     the name of the user for which to set the information
+ * user  the User object containing the desired 
+ *                 information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_setUserInfo
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname, jobject user )
+{
+  const char *name;
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+  pts_UserUpdateEntry_t ptsEntry;
+  afs_status_t ast;
+  kas_admin_t isAdmin;
+  kas_tgs_t grantTickets;
+  kas_enc_t canEncrypt;
+  kas_cpw_t canChangePassword;
+  kas_rpw_t passwordReuse;
+  unsigned int expirationDate;
+  unsigned int maxTicketLifetime;
+  unsigned int passwordExpires;
+  unsigned int failedPasswordAttempts;
+  unsigned int failedPasswordLockTime;
+  int kas;
+  int pts;
+
+  if( !who ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+      name = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( name != NULL && strlen( name ) > KAS_MAX_NAME_LEN ) {
+    free( who );
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+    return;
+  }
+
+  if( name != NULL ) {
+      internal_makeKasIdentity( name, who );
+  }
+
+  // get class fields if need be
+  if( userCls == 0 ) {
+    internal_getUserClass( env, user );
+  }
+
+  kas = (*env)->GetBooleanField(env, user, user_kasField);
+  pts = (*env)->GetBooleanField(env, user, user_ptsField);
+
+  if( pts ) {
+      // set the pts fields: 
+      ptsEntry.flag = PTS_USER_UPDATE_GROUP_CREATE_QUOTA | 
+       PTS_USER_UPDATE_PERMISSIONS;
+      ptsEntry.groupCreationQuota = 
+       (*env)->GetIntField(env, user, user_groupCreationQuotaField);
+      if( (*env)->GetIntField(env, user, user_listStatusField) == 
+         org_openafs_jafs_User_USER_OWNER_ACCESS ) {
+         ptsEntry.listStatus = PTS_USER_OWNER_ACCESS;
+      } else {
+         ptsEntry.listStatus = PTS_USER_ANYUSER_ACCESS;
+      }
+      if( (*env)->GetIntField(env, user, user_listGroupsOwnedField) == 
+         org_openafs_jafs_User_USER_OWNER_ACCESS ) {
+         ptsEntry.listGroupsOwned = PTS_USER_OWNER_ACCESS;
+      } else {
+         ptsEntry.listGroupsOwned = PTS_USER_ANYUSER_ACCESS;
+      }
+      if( (*env)->GetIntField(env, user, user_listMembershipField) == 
+         org_openafs_jafs_User_USER_OWNER_ACCESS ) {
+         ptsEntry.listMembership = PTS_USER_OWNER_ACCESS;
+      } else {
+         ptsEntry.listMembership = PTS_USER_ANYUSER_ACCESS;
+      }
+      if( !pts_UserModify( (void *) cellHandle, name, &ptsEntry, &ast ) ) {
+         free( who );
+         if( name != NULL ) {
+             (*env)->ReleaseStringUTFChars(env, jname, name);
+         }
+         throwAFSException( env, ast );
+         return;    
+      }
+  }
+
+  if( kas ) {
+      // set the kas fields:
+      if( (*env)->GetIntField(env, user, user_adminSettingField) == 
+         org_openafs_jafs_User_ADMIN ) {
+         isAdmin = KAS_ADMIN;
+      } else {
+         isAdmin = NO_KAS_ADMIN;
+      }
+      if( (*env)->GetIntField(env, user, user_tgsSettingField) == 
+         org_openafs_jafs_User_GRANT_TICKETS ) {
+         grantTickets = TGS;
+      } else {
+         grantTickets = NO_TGS;
+      }
+      if( (*env)->GetIntField(env, user, user_encSettingField) == 
+         org_openafs_jafs_User_ENCRYPT ) {
+         canEncrypt = 0;
+      } else {
+         canEncrypt = NO_ENCRYPT;
+      }
+      if( (*env)->GetIntField(env, user, user_cpwSettingField) == 
+         org_openafs_jafs_User_CHANGE_PASSWORD ) {
+         canChangePassword = CHANGE_PASSWORD;
+      } else {
+         canChangePassword = NO_CHANGE_PASSWORD;
+      }
+      if( (*env)->GetIntField(env, user, user_rpwSettingField) == 
+         org_openafs_jafs_User_REUSE_PASSWORD ) {
+         passwordReuse = REUSE_PASSWORD;
+      } else {
+         passwordReuse = NO_REUSE_PASSWORD;
+      }
+      expirationDate = (*env)->GetIntField(env, user, 
+                                          user_userExpirationField);
+      maxTicketLifetime = (*env)->GetIntField(env, user, 
+                                             user_maxTicketLifetimeField);
+      passwordExpires = (*env)->GetIntField(env, user, 
+                                           user_daysToPasswordExpireField);
+      failedPasswordAttempts = (*env)->GetIntField(env, user, 
+                                                  user_failLoginCountField);
+      failedPasswordLockTime =  (*env)->GetIntField(env, user, 
+                                                   user_lockTimeField);
+      
+      if( !kas_PrincipalFieldsSet( (void *) cellHandle, NULL, who, &isAdmin, 
+                                  &grantTickets, &canEncrypt, 
+                                  &canChangePassword, &expirationDate, 
+                                  &maxTicketLifetime, &passwordExpires, 
+                                  &passwordReuse, &failedPasswordAttempts, 
+                                  &failedPasswordLockTime, &ast ) ) {
+         free( who );
+         if( name != NULL ) {
+             (*env)->ReleaseStringUTFChars(env, jname, name);
+         }
+         throwAFSException( env, ast );
+         return;    
+      }
+  }      
+
+  free( who );
+  if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+}
+
+/**
+ * Renames the given user.  Does not update the info fields of the kas entry
+ *  -- the calling code is responsible for that.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * joldName     the name of the user to rename
+ * jnewName     the new name for the user
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_rename
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring joldName, jstring jnewName)
+{
+
+    const char *oldName;
+    const char *newName;
+    kas_identity_p whoOld = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+    kas_identity_p whoNew = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+    kas_principalEntry_t kasEntry;
+    pts_UserEntry_t ptsEntry;
+    afs_status_t ast;
+    int kas;
+
+    if( !whoOld || !whoNew ) {
+      if( whoOld ) {
+       free( whoOld );
+      }
+      if( whoNew ) {
+       free( whoNew );
+      } 
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+
+    if( joldName != NULL ) {
+       oldName = (*env)->GetStringUTFChars(env, joldName, 0);
+       if( !oldName ) {
+           throwAFSException( env, JAFSADMNOMEM );
+           return;    
+       }
+    } else {
+       oldName = NULL;
+    }
+    if( jnewName != NULL ) {
+       newName = (*env)->GetStringUTFChars(env, jnewName, 0);
+       if( !newName ) {
+         if( oldName != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+         }
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+       }
+    } else {
+       newName = NULL;
+    }
+
+    // make sure the names are within the allowed bounds
+    if( oldName != NULL && strlen( oldName ) > KAS_MAX_NAME_LEN ) {
+       free( whoOld );
+       free( whoNew );
+       if( oldName != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+       }
+       if( newName != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+       }
+       throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+       return;
+    }
+    if( newName != NULL && strlen( newName ) > KAS_MAX_NAME_LEN ) {
+       free( whoOld );
+       free( whoNew );
+       if( oldName != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+       }
+       if( newName != NULL ) {
+           (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+       }
+       throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+       return;
+    }
+    
+    if( oldName != NULL ) {
+       internal_makeKasIdentity( oldName, whoOld );
+    }
+    if( newName != NULL ) {
+       internal_makeKasIdentity( newName, whoNew );
+    }
+
+    // retrieve the old kas info
+    if( !kas_PrincipalGet( (void *) cellHandle, NULL, whoOld, 
+                          &kasEntry, &ast ) ) {
+       if( ast != KANOENT ) {
+           free( whoOld );
+           free( whoNew );
+           if( oldName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+           }
+           if( newName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+           }
+           throwAFSException( env, ast );
+           return;
+       } else {
+           kas = FALSE;
+       }
+    } else {
+       kas = TRUE;
+    }   
+       
+    if( kas ) {
+       // create a new kas entry
+       // temporarily set the password equal to the new name
+       if (!kas_PrincipalCreate( (void *) cellHandle, NULL, whoNew, 
+                                 newName, &ast ) ) {       
+           free( whoOld );
+           free( whoNew );
+           if( oldName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+           }
+           if( newName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+           }
+           throwAFSException( env, ast );
+           return;
+       } 
+
+       // set the password 
+       ast = 0;
+       // For some reason kas_PrincipalKeySet doesn't set the return code 
+       // correctly.  It always returns 0.
+       // So instead of checking the return code, we see if there's an 
+       // error in the status variable.
+       kas_PrincipalKeySet( (void *) cellHandle, NULL, whoNew, 0, 
+                            &(kasEntry.key), &ast );
+       if( ast ) {
+           afs_status_t ast_kd;
+           kas_PrincipalDelete( (void *) cellHandle, NULL, whoNew, &ast_kd );
+           free( whoOld );
+           free( whoNew );
+           if( oldName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+           }
+           if( newName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+           }
+           throwAFSException( env, ast );
+           return;
+       }
+    }
+
+    // rename the pts entry
+    if( !pts_UserRename( (void *) cellHandle, oldName, newName, &ast ) ) {
+       // throw exception if there was no such pts user only if 
+        // there was also no such kas user
+       if( (ast == ADMPTSFAILEDNAMETRANSLATE && !kas ) || 
+           ast != ADMPTSFAILEDNAMETRANSLATE ) {
+           afs_status_t ast_kd;
+           if( kas ) {
+               kas_PrincipalDelete( (void *) cellHandle, NULL, whoNew, 
+                                    &ast_kd );
+           }
+           free( whoOld );
+           free( whoNew );
+           if( oldName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+           }
+           if( newName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+           }
+           throwAFSException( env, ast );
+           return;
+       }
+    }
+
+    if( kas ) {
+       // delete the old kas entry
+       if( !kas_PrincipalDelete( (void *) cellHandle, NULL, whoOld, &ast ) ) {
+           free( whoOld );
+           free( whoNew );
+           if( oldName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+           }
+           if( newName != NULL ) {
+               (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+           }
+           throwAFSException( env, ast );
+           return;
+       }
+    }    
+
+    free( whoOld );
+    free( whoNew );
+    if( oldName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, joldName, oldName);
+    }
+    if( newName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+    }
+}
+
+/**
+ * Sets the password of the given user.  Sets the key version to 0.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * juserName     the name of the user for which to set the password
+ * jnewPassword     the new password for the user
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_setPassword
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring juserName,
+   jstring jnewPassword)
+{
+  afs_status_t ast;
+  char *cellName;
+  const char *userName;
+  const char *newPassword;
+  kas_encryptionKey_p newKey = 
+    (kas_encryptionKey_p) malloc( sizeof(kas_encryptionKey_t) );
+  kas_identity_p who = (kas_identity_p) malloc( sizeof(kas_identity_t) );
+
+  if( !who || !newKey ) {
+    if( who ) {
+      free( who );
+    }
+    if( newKey ) {
+      free( newKey );
+    }
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( juserName != NULL ) {
+      userName = (*env)->GetStringUTFChars(env, juserName, 0);
+      if( !userName ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      userName = NULL;
+  }
+  if( jnewPassword != NULL ) {
+      newPassword = (*env)->GetStringUTFChars(env, jnewPassword, 0);
+      if( !newPassword ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      newPassword = NULL;
+  }
+
+  // make sure the name is within the allowed bounds
+  if( userName != NULL && strlen( userName ) > KAS_MAX_NAME_LEN ) {
+    free(who);
+    free( newKey );
+    if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    if( newPassword != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jnewPassword, newPassword);
+    }
+    throwAFSException( env, ADMPTSUSERNAMETOOLONG );
+    return;
+  }
+
+  if( !afsclient_CellNameGet( (void *) cellHandle, &cellName, &ast ) ) {
+      free(who);
+      free( newKey );
+      if( userName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( newPassword != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jnewPassword, newPassword);
+      }
+      throwAFSException( env, ast );
+      return;
+  }
+  
+  if( !kas_StringToKey( cellName, newPassword, newKey, &ast ) ) {
+      free(who);
+      free( newKey );
+      if( userName != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, juserName, userName);
+      }
+      if( newPassword != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jnewPassword, newPassword);
+      }
+      throwAFSException( env, ast );
+      return;
+  }
+
+  if( userName != NULL ) {
+      internal_makeKasIdentity( userName, who );
+  }
+
+  ast = 0;
+  // For some reason kas_PrincipalKeySet doesn't set the return code correctly.
+  //  It always returns 0.
+  // So instead of checking the return code, we see if there's an error 
+  // in the status variable.
+  kas_PrincipalKeySet( (void *) cellHandle, NULL, who, 0, newKey, &ast );
+  if( ast ) {
+    free( who );
+    free( newKey );
+    if( userName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, juserName, userName);
+    }
+    if( newPassword != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jnewPassword, newPassword);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  free( who );
+  free( newKey );
+  if( userName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, juserName, userName);
+  }
+  if( newPassword != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jnewPassword, newPassword);
+  }
+
+}
+
+/**
+ * Begin the process of getting the groups to which the user belongs.  
+ * Returns an iteration ID to be used by subsequent calls to 
+ * getUserGroupsNext and getUserGroupsDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * jname          the name of the user for which to get the groups
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_User_getUserGroupsBegin
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname)
+{
+  const char *name;
+  afs_status_t ast;
+  void *iterationId;
+
+  if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      name = NULL;
+  }
+
+  if( !pts_UserMemberListBegin( (void *) cellHandle, name, &iterationId, 
+                               &ast ) ) {
+      if( name != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+  }
+
+  if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next group to which the user belongs.  Returns 
+ * null if there are no more groups.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next group
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_User_getUserGroupsNextString
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+  char *groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+  jstring jgroup;
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_UserMemberListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+  
+  jgroup = (*env)->NewStringUTF(env, groupName);
+  free( groupName );
+  return jgroup;
+}
+
+/**
+ * Fills the next group object of which the user belongs.  Returns 0 if there
+ * are no more groups, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the users belong
+ * iterationId   the iteration ID of this iteration
+ * jgroupObject   a Group object to be populated with the values of the 
+ *                   next group
+ * returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_User_getUserGroupsNext
+  (JNIEnv *env, jclass cls, jint cellHandle, jint iterationId,
+   jobject jgroupObject)
+{
+  afs_status_t ast;
+  char *groupName;
+  jstring jgroup;
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_UserMemberListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  jgroup = (*env)->NewStringUTF(env, groupName);
+
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, jgroupObject );
+  }
+
+  (*env)->SetObjectField(env, jgroupObject, group_nameField, jgroup);
+
+  getGroupInfoChar( env, (void *) cellHandle, groupName, jgroupObject );
+  (*env)->SetBooleanField( env, jgroupObject, group_cachedInfoField, TRUE );
+
+  free( groupName );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_getUserGroupsDone
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+
+  if( !pts_UserMemberListDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+}
+
+/**
+ * Returns the total number of groups owned by the user.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * jname          the name of the user for which to get the groups
+ * returns total number of groups owned by the user
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_User_getGroupsOwnedCount
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname)
+{
+  afs_status_t ast;
+  void *iterationId;
+  char *groupName;
+  int i = 0;
+
+  iterationId = 
+    (void *) Java_org_openafs_jafs_User_getGroupsOwnedBegin( env, cls, 
+                                                               cellHandle, 
+                                                               jname );
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return -1;    
+  }
+
+  while ( pts_OwnedGroupListNext( (void *) iterationId, groupName, &ast ) ) 
+    i++;
+
+  free( groupName );  
+
+  if( ast != ADMITERATORDONE ) {
+    throwAFSException( env, ast );
+    return -1;
+  }
+
+  return i;
+}
+
+/**
+ * Begin the process of getting the groups that a user or group owns.  
+ * Returns an iteration ID to be used by subsequent calls to 
+ * getGroupsOwnedNext and getGroupsOwnedDone.  
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the user belongs
+ * jname  the name of the user or group for which to get the groups
+ * returns an iteration ID
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_User_getGroupsOwnedBegin
+  (JNIEnv *env, jclass cls, jint cellHandle, jstring jname)
+{
+  const char *name;
+  afs_status_t ast;
+  void *iterationId;
+
+  if( jname != NULL ) {
+      name = (*env)->GetStringUTFChars(env, jname, 0);
+      if( !name ) {
+         throwAFSException( env, JAFSADMNOMEM );
+         return;    
+      }
+  } else {
+      name = NULL;
+  }
+
+  if( !pts_OwnedGroupListBegin( (void *) cellHandle, name, 
+                               &iterationId, &ast ) ) {
+      if( jname != NULL ) {
+         (*env)->ReleaseStringUTFChars(env, jname, name);
+      }
+      throwAFSException( env, ast );
+      return;
+  }
+
+  if( jname != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+  return (jint) iterationId;
+
+}
+
+/**
+ * Returns the next group the user or group owns.  Returns null 
+ * if there are no more groups.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ * returns the name of the next group
+ */
+JNIEXPORT jstring JNICALL 
+Java_org_openafs_jafs_User_getGroupsOwnedNextString
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+  char *groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+  jstring jgroup;
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_OwnedGroupListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return NULL;
+    } else {
+      throwAFSException( env, ast );
+      return;
+    }
+  }
+  
+  jgroup = (*env)->NewStringUTF(env, groupName);
+  free( groupName );
+  return jgroup;
+
+}
+
+/**
+ * Fills the next group object that the user or group owns.  Returns 0 if 
+ * there are no more groups, != 0 otherwise.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the users belong
+ * iterationId   the iteration ID of this iteration
+ * jgroupObject   a Group object to be populated with the values of the 
+ *                   next group
+ * returns 0 if there are no more users, != 0 otherwise
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_User_getGroupsOwnedNext
+  (JNIEnv *env, jclass cls, jint cellHandle, jint iterationId, 
+   jobject jgroupObject)
+{
+  afs_status_t ast;
+  char *groupName;
+  jstring jgroup;
+
+  groupName = (char *) malloc( sizeof(char)*PTS_MAX_NAME_LEN);
+
+  if( !groupName ) {
+    throwAFSException( env, JAFSADMNOMEM );
+    return;    
+  }
+
+  if( !pts_OwnedGroupListNext( (void *) iterationId, groupName, &ast ) ) {
+    free( groupName );
+    if( ast == ADMITERATORDONE ) {
+      return 0;
+    } else {
+      throwAFSException( env, ast );
+      return 0;
+    }
+  }
+
+  jgroup = (*env)->NewStringUTF(env, groupName);
+
+  if( groupCls == 0 ) {
+    internal_getGroupClass( env, jgroupObject );
+  }
+
+  (*env)->SetObjectField(env, jgroupObject, group_nameField, jgroup);
+
+  getGroupInfoChar( env, (void *) cellHandle, groupName, jgroupObject );
+  (*env)->SetBooleanField( env, jgroupObject, group_cachedInfoField, TRUE );
+
+  free( groupName );
+  return 1;
+
+}
+
+/**
+ * Signals that the iteration is complete and will not be accessed anymore.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * iterationId   the iteration ID of this iteration
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_User_getGroupsOwnedDone
+  (JNIEnv *env, jclass cls, jint iterationId)
+{
+  afs_status_t ast;
+
+  if( !pts_OwnedGroupListDone( (void *) iterationId, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+  
+}
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_User_reclaimUserMemory
+  (JNIEnv *env, jclass cls)
+{
+  if( userCls ) {
+      (*env)->DeleteGlobalRef(env, userCls);
+      userCls = 0;
+  }
+
+}
+
+
+
diff --git a/src/JAVA/libjafs/UserToken.c b/src/JAVA/libjafs/UserToken.c
new file mode 100644 (file)
index 0000000..86a91d2
--- /dev/null
@@ -0,0 +1,321 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Token.h"
+
+#include <sys/stat.h>
+#include <sys/ioctl.h>
+#include <afs/vice.h>
+#include <afs/venus.h>
+#include <afs/afs_args.h>
+#include <afs/afs_osi.h>
+#include <afs/afs_usrops.h>
+#include <pthread.h>
+
+#ifdef DMALLOC
+#include "dmalloc.h"
+#endif
+
+pthread_mutex_t jafs_init_lock;
+extern pthread_mutex_t jafs_login_lock;
+extern int readCacheParms(char *afsMountPoint, char *afsConfDir,
+                          char *afsCacheDir,  int *cacheBlocks, 
+                          int *cacheFiles, int *cacheStatEntries,
+                          int *dCacheSize, int *vCacheSize, int *chunkSize,
+                          int *closeSynch, int *debug, int *nDaemons, 
+                          int *cacheFlags, char *logFile);
+
+/**
+ * Be carefull with the memory management:
+ *
+ * - For every GetStringUTFChars call the corresponding ReleaseStringUTFChars.
+ * - For every Get<type>ArrayElements call the corresponding
+ *   Release<type>ArrayElements
+ * - For every malloc call the corresponding free.
+ */
+
+int osi_audit(void)
+{
+   return 0;
+}
+
+JNIEXPORT void JNICALL Java_org_openafs_jafs_Token_callDebugger
+  (JNIEnv *env, jobject obj)
+{
+    fprintf(stderr, "callDebugger called\n");
+    __asm__("int $0x3");
+}
+
+/**
+ * Initialize the user space library.
+ *
+ * The user space client must be initialized prior to any
+ * user space related methods, including: klog, unlog, relog,
+ * and shutdown.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ *
+ * throws AFSException
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_Token_initUserSpace
+  (JNIEnv *env, jclass cls)
+{
+  char afsMountPoint[100], afsConfDir[100], afsCacheDir[100], logFile[100];
+  jfieldID fid;
+  int pagval;
+
+  /* Initialize each init parameter with its associated default value */
+  int cacheBlocks = 100000;
+  int cacheFiles  = 12500;
+  int cacheStatEntries = 8192;
+  int dCacheSize  = 11398;
+  int vCacheSize  = 128;
+  int chunkSize   = 0;
+  int closeSynch  = 0;
+  int debug       = 0;
+  int nDaemons    = 3; 
+  int cacheFlags  = -1;
+
+  /* Initialize each init parameter with its associated default value */
+  strcpy(afsMountPoint, "/afs");
+  strcpy(afsConfDir,  "/usr/afswsp/etc");
+  strcpy(afsCacheDir, "/usr/afswsp/cache");
+  strcpy(logFile,     "/usr/afswsp/log/libjafs.log");
+
+  pthread_mutex_init(&jafs_init_lock, NULL);
+  pthread_mutex_lock(&jafs_init_lock);
+
+  readCacheParms(afsMountPoint, afsConfDir, afsCacheDir,
+                 &cacheBlocks, &cacheFiles, &cacheStatEntries,
+                 &dCacheSize, &vCacheSize, &chunkSize,
+                 &closeSynch, &debug, &nDaemons, &cacheFlags,
+                 logFile);
+
+  /* See cache.tune for configuration details */
+  if (debug) {
+    fprintf(stderr, "uafs_Init(\"init_native\", \"%s\", \"%s\", \"%s\",
+                      %d, %d, %d,
+                      %d, %d, %d,
+                      %d, %d, %d, %d, \"%s\");\n",
+              afsMountPoint, afsConfDir, afsCacheDir,
+              cacheBlocks, cacheFiles, cacheStatEntries,
+              dCacheSize, vCacheSize, chunkSize,
+              closeSynch, debug, nDaemons, cacheFlags, logFile);
+  }
+  uafs_Init("init_native", afsMountPoint, afsConfDir, afsCacheDir,
+             cacheBlocks, cacheFiles, cacheStatEntries,
+             dCacheSize, vCacheSize, chunkSize,
+             closeSynch, debug, nDaemons, cacheFlags, logFile);
+
+
+  /* make the initial pag the unauthenticated pag */
+  afs_setpag();
+  uafs_unlog();
+  pagval = afs_getpag_val();
+
+  fid = (*env)->GetStaticFieldID(env, cls, "ANYUSER_PAG_ID", "I");
+  if (fid == 0) {
+    fprintf(stderr,
+    "UserToken::init(): GetFieldID (ANYUSER_PAG_ID) failed\n");
+    return;
+  }
+    
+  (*env)->SetStaticIntField(env, cls, fid, pagval);
+
+  pthread_mutex_unlock(&jafs_init_lock);
+}
+
+/**
+ * Authenticates a user in kas, and binds that authentication
+ * to the current process.
+ *
+ * env      the Java environment
+ * obj      the current Java class
+ * loginUTF    the login to authenticate (expected as username@cellname)
+ * passwordUTF the password of the login
+ * id          the existing pag (or 0)
+ *
+ * returns the assigned pag
+ *
+ * throws AFSException
+ */
+JNIEXPORT jint JNICALL
+Java_org_openafs_jafs_Token_klog (JNIEnv *env, jobject obj,
+  jstring jusername, jstring jpassword, jstring jcell, jint id)
+{
+  char *username;
+  char *password;
+  char *cell;
+  char *reason;
+  jint rc = -1;
+  int code;
+
+  if( jcell != NULL ) { 
+    cell = (char*) (*env)->GetStringUTFChars(env, jcell, 0);
+    if( !cell ) {
+      char *error = "UserToken::klog(): failed to get cell name\n";
+      fprintf(stderr, error);
+      throwMessageException( env, error );
+      return -1;
+    }
+  } else {
+    cell = NULL;
+  }
+
+  if( jusername != NULL ) {
+    username = (char*) (*env)->GetStringUTFChars(env, jusername, 0);
+    if( !username ) {
+      char *error = "UserToken::klog(): failed to get username\n";
+      (*env)->ReleaseStringUTFChars(env, jcell, cell);
+      fprintf(stderr, error);
+      throwMessageException( env, error );
+      return -1;
+    }
+  } else {
+    username = NULL;
+  }
+  if( jpassword != NULL ) {
+    password = (char*) (*env)->GetStringUTFChars(env, jpassword, 0);
+    if( !password ) {
+      char *error = "UserToken::klog(): failed to get password\n";
+      (*env)->ReleaseStringUTFChars(env, jcell, cell);
+      (*env)->ReleaseStringUTFChars(env, jusername, username);
+      fprintf(stderr, error);
+      throwMessageException( env, error );
+      return -1;
+    }
+  } else {
+    password = NULL;
+  }
+
+  if (id == 0) {
+    code = uafs_klog(username, cell, password, &reason);
+  } else {
+    /* Use existing PAG for this thread */
+    code = afs_setpag_val(id);
+    if (code != 0) code = 180492L;  /* KABADARGUMENT */
+    if (!code) code = uafs_klog_nopag(username, cell, password, &reason);
+  }
+
+  if (code != 0) {
+    if( cell != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jcell, cell);
+    }
+    if( username != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jusername, username);
+    }
+    if( password != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jpassword, password);
+    }
+    fprintf(stderr, "UserToken::klog(): uafs_klog failed to cell %s: %s\n",
+                     cell, reason);
+    fprintf(stderr, "code = %d\n", code);
+    throwAFSException( env, code );
+  }
+
+  /* Get the PAG we were assigned as the return value */
+  rc = afs_getpag_val();
+
+  /* clean up */
+  if( cell != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jcell, cell);
+  }
+  if( username != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jusername, username);
+  }
+  if( password != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jpassword, password);
+  }
+
+  /* return PAG ID */
+  return rc;
+}
+
+/**
+ * Authenticates a user in KAS by a previously acquired PAG ID, and binds 
+ * that authentication to the current thread or native process.
+ *
+ * <P> This method does not require the user's username and password to
+ * fully authenticate their request.  Rather it utilizes the user's PAG ID
+ * to recapture the user's existing credentials.
+ *
+ * env      the Java environment
+ * obj      the current Java class
+ * id       User's current PAG (process authentication group) ID
+ *
+ * throws AFSException
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_Token_relog
+  (JNIEnv *env, jobject obj, jint id)
+{
+  int rc;
+
+  rc = afs_setpag_val(id);
+
+  if (rc != 0) {
+    throwAFSException( env, rc );
+  }
+}
+
+/**
+ * Authenticates a user in KAS, and binds that authentication
+ * to the current process.
+ *
+ * env      the Java environment
+ * obj      the current Java class
+ *
+ * throws AFSException
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_Token_unlog
+  (JNIEnv *env, jobject obj)
+{
+  int rc;
+
+  rc = uafs_unlog();
+
+  if (rc != 0) {
+    throwAFSException( env, rc );
+  }
+}
+
+/**
+ * Inform the native library that the application is 
+ * shutting down and will be unloading.
+ *
+ * <p> The library will make a call informing the file server that it will 
+ * no longer be available for callbacks.
+ *
+ * env      the Java environment
+ * obj      the current Java class
+ *
+ * throws AFSException
+ */
+JNIEXPORT void JNICALL Java_org_openafs_jafs_Token_shutdown
+  (JNIEnv *env, jobject obj)
+{
+  uafs_Shutdown();
+}
+
+
+
+
diff --git a/src/JAVA/libjafs/Volume.c b/src/JAVA/libjafs/Volume.c
new file mode 100644 (file)
index 0000000..de687b2
--- /dev/null
@@ -0,0 +1,901 @@
+/*
+ * Copyright (c) 2001-2002 International Business Machines Corp.
+ * All rights reserved.
+ *
+ * This software has been released under the terms of the IBM Public
+ * License.  For details, see the LICENSE file in the top-level source
+ * directory or online at http://www.openafs.org/dl/license10.html
+ * 
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "Internal.h"
+#include "org_openafs_jafs_Volume.h"
+
+#include <afs_vosAdmin.h>
+#include <afs_AdminCommonErrors.h>
+
+//// definitions in Internal.c  //////////////////
+
+extern jclass volumeCls;
+extern jfieldID volume_nameField;
+extern jfieldID volume_idField;
+extern jfieldID volume_readWriteIdField;
+extern jfieldID volume_readOnlyIdField;
+extern jfieldID volume_backupIdField;
+extern jfieldID volume_creationDateField;
+extern jfieldID volume_lastAccessDateField;
+extern jfieldID volume_lastUpdateDateField;
+extern jfieldID volume_lastBackupDateField;
+extern jfieldID volume_copyCreationDateField;
+extern jfieldID volume_accessesSinceMidnightField;
+extern jfieldID volume_fileCountField;
+extern jfieldID volume_maxQuotaField;
+extern jfieldID volume_currentSizeField;
+extern jfieldID volume_statusField;
+extern jfieldID volume_dispositionField;
+extern jfieldID volume_typeField;
+
+//////////////////////////////////////////////////////////
+
+/**
+ * Extract the information from the given volume entry and populate the
+ * given object
+ *
+ * env      the Java environment
+ * volume      the Volume object to populate with the info
+ * volEntry     the container of the volume's information
+ */
+extern void fillVolumeInfo( JNIEnv *env, jobject volume, 
+                           vos_volumeEntry_t volEntry ) {
+
+  jstring jvolume;
+
+  // get the class fields if need be
+  if( volumeCls == 0 ) {
+    internal_getVolumeClass( env, volume );
+  }
+
+  // set name, just in case it was a completely blank object
+  jvolume = (*env)->NewStringUTF(env, volEntry.name);
+  (*env)->SetObjectField(env, volume, volume_nameField, jvolume);
+
+  (*env)->SetIntField(env, volume, volume_idField, volEntry.id);
+  (*env)->SetIntField(env, volume, volume_readWriteIdField, 
+                     volEntry.readWriteId);
+  (*env)->SetIntField(env, volume, volume_readOnlyIdField, 
+                     volEntry.readOnlyId);
+  (*env)->SetIntField(env, volume, volume_backupIdField, volEntry.backupId);
+  (*env)->SetLongField(env, volume, volume_creationDateField, 
+                      volEntry.creationDate);
+  (*env)->SetLongField(env, volume, volume_lastAccessDateField, 
+                      volEntry.lastAccessDate);
+  (*env)->SetLongField(env, volume, volume_lastUpdateDateField, 
+                      volEntry.lastUpdateDate);
+  (*env)->SetLongField(env, volume, volume_lastBackupDateField, 
+                      volEntry.lastBackupDate);
+  (*env)->SetLongField(env, volume, volume_copyCreationDateField, 
+                      volEntry.copyCreationDate);
+  (*env)->SetIntField(env, volume, volume_accessesSinceMidnightField, 
+                     volEntry.accessesSinceMidnight);
+  (*env)->SetIntField(env, volume, volume_fileCountField, volEntry.fileCount);
+  (*env)->SetIntField(env, volume, volume_maxQuotaField, volEntry.maxQuota);
+  (*env)->SetIntField(env, volume, volume_currentSizeField, 
+                     volEntry.currentSize);
+
+  // set status variable
+  switch( volEntry.status ) {
+  case VOS_OK :
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_OK);
+      break;
+  case VOS_SALVAGE :
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_SALVAGE);
+      break;
+  case VOS_NO_VNODE:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_VNODE);
+      break;
+  case VOS_NO_VOL:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_VOL);
+      break;
+  case VOS_VOL_EXISTS:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_VOL_EXISTS);
+      break;
+  case VOS_NO_SERVICE:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_SERVICE);
+      break;
+  case VOS_OFFLINE:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_OFFLINE);
+      break;
+  case VOS_ONLINE:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_ONLINE);
+      break;
+  case VOS_DISK_FULL:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_DISK_FULL);
+      break;
+  case VOS_OVER_QUOTA:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_OVER_QUOTA);
+      break;
+  case VOS_BUSY:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_BUSY);
+      break;
+  case VOS_MOVED:
+      (*env)->SetIntField(env, volume, volume_statusField, 
+                         org_openafs_jafs_Volume_VOLUME_MOVED);
+      break;
+  default:
+      throwAFSException( env, volEntry.status );
+  }
+
+  // set disposition variable
+  switch( volEntry.volumeDisposition ) {
+  case VOS_OK :
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_OK);
+      break;
+  case VOS_SALVAGE :
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_SALVAGE);
+      break;
+  case VOS_NO_VNODE:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_VNODE);
+      break;
+  case VOS_NO_VOL:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_VOL);
+      break;
+  case VOS_VOL_EXISTS:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_VOL_EXISTS);
+      break;
+  case VOS_NO_SERVICE:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_NO_SERVICE);
+      break;
+  case VOS_OFFLINE:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_OFFLINE);
+      break;
+  case VOS_ONLINE:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_ONLINE);
+      break;
+  case VOS_DISK_FULL:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_DISK_FULL);
+      break;
+  case VOS_OVER_QUOTA:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_OVER_QUOTA);
+      break;
+  case VOS_BUSY:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_BUSY);
+      break;
+  case VOS_MOVED:
+      (*env)->SetIntField(env, volume, volume_dispositionField, 
+                         org_openafs_jafs_Volume_VOLUME_MOVED);
+      break;
+  default:
+      throwAFSException( env, volEntry.volumeDisposition );
+  }
+
+  // set type variable
+  switch( volEntry.type ) {
+  case VOS_READ_WRITE_VOLUME:
+      (*env)->SetIntField(env, volume, volume_typeField, 
+                         org_openafs_jafs_Volume_VOLUME_TYPE_READ_WRITE);
+      break;
+  case VOS_READ_ONLY_VOLUME:
+      (*env)->SetIntField(env, volume, volume_typeField, 
+                         org_openafs_jafs_Volume_VOLUME_TYPE_READ_ONLY);
+      break;
+  case VOS_BACKUP_VOLUME:
+      (*env)->SetIntField(env, volume, volume_typeField, 
+                         org_openafs_jafs_Volume_VOLUME_TYPE_BACKUP);
+      break;
+  default:
+      throwAFSException( env, volEntry.type );
+  }
+
+}
+
+/**
+ * Fills in the information fields of the provided Volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the volume 
+ *                      resides
+ * partition   the numeric id of the partition on which the volume 
+ *                    resides
+ * volId  the numeric id of the volume for which to get the info
+ * jvolumeObject   the Volume object in which to fill in 
+ *                        the information
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_getVolumeInfo (JNIEnv *env, jclass cls, 
+                                              jint cellHandle, 
+                                              jint serverHandle, 
+                                              jint partition, jint volID, 
+                                              jobject jvolumeObject) {
+
+  afs_status_t ast;
+  vos_volumeEntry_t volEntry;
+
+  // get the volume entry
+  if ( !vos_VolumeGet( (void *) cellHandle, (void *) serverHandle, NULL, 
+                      (unsigned int) partition, (unsigned int) volID, 
+                      &volEntry, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+  fillVolumeInfo( env, jvolumeObject, volEntry );
+
+}
+
+/**
+ * Creates a volume on a particular partition.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell in which to create the volume
+ * serverHandle  the vos handle of the server on which to create 
+ *                      the volume
+ * partition   the numeric id of the partition on which to create 
+ *                    the volume
+ * jvolName   the name of the volume to create
+ * quota    the amount of space (in KB) to set as this volume's quota
+ * returns the numeric ID assigned to the volume
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Volume_create (JNIEnv *env, jclass cls, 
+                                       jint cellHandle, jint serverHandle, 
+                                       jint partition, jstring jvolName, 
+                                       jint quota) {
+
+  afs_status_t ast;
+  const char *volName;
+  int id;
+
+  if( jvolName != NULL ) {
+    volName = (*env)->GetStringUTFChars(env, jvolName, 0);
+    if( !volName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    volName = NULL;
+  }
+
+  if( !vos_VolumeCreate( (void *) cellHandle, (void *) serverHandle, NULL, 
+                        (unsigned int) partition, volName, 
+                        (unsigned int) quota, &id, &ast ) ) {
+    if( volName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jvolName, volName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( volName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jvolName, volName);
+  }
+  return (jint) id;
+
+}
+
+/**
+ * Deletes a volume from a particular partition.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell in which to delete the volume
+ * serverHandle  the vos handle of the server from which to delete 
+ *                      the volume
+ * partition   the numeric id of the partition from which to delete 
+ *                    the volume
+ * volId   the numeric id of the volume to delete
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_delete (JNIEnv *env, jclass cls, 
+                                       jint cellHandle, jint serverHandle, 
+                                       jint partition, jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_VolumeDelete( (void *) cellHandle, (void *) serverHandle, NULL, 
+                        (unsigned int) partition, 
+                        (unsigned int) volID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Creates a backup volume for the specified regular volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * volId  the numeric id of the volume for which to create a backup 
+ *               volume
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_createBackupVolume (JNIEnv *env, jclass cls, 
+                                                   jint cellHandle, 
+                                                   jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_BackupVolumeCreate( (void *) cellHandle, NULL, 
+                              (unsigned int) volID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Creates a read-only volume for the specified regular volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the read-only 
+ *                      volume is to reside 
+ * partition   the numeric id of the partition on which the read-only 
+ *             volume is to reside 
+ * volId  the numeric id of the volume for which to 
+ *               create a read-only volume
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_createReadOnlyVolume (JNIEnv *env, jclass cls, 
+                                                     jint cellHandle, 
+                                                     jint serverHandle, 
+                                                     jint partition, 
+                                                     jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_VLDBReadOnlySiteCreate( (void *) cellHandle, (void *) serverHandle, 
+                                  NULL, (unsigned int) partition, 
+                                  (unsigned int) volID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Deletes a read-only volume for the specified regular volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the read-only 
+ *                      volume residea 
+ * partition   the numeric id of the partition on which the read-only
+ *                     volume resides 
+ * volId  the numeric read-write id of the volume for which to 
+ *               delete the read-only volume
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_deleteReadOnlyVolume (JNIEnv *env, jclass cls, 
+                                                     jint cellHandle, 
+                                                     jint serverHandle, 
+                                                     jint partition, 
+                                                     jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_VLDBReadOnlySiteDelete( (void *) cellHandle, (void *) serverHandle, 
+                                  NULL, (unsigned int) partition, 
+                                  (unsigned int) volID, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Changes the quota of the specified volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the volume 
+ *                      resides
+ * partition   the numeric id of the partition on which the volume 
+ *                    resides
+ * volId  the numeric id of the volume for which to change the quota
+ * newQuota    the new quota (in KB) to assign the volume
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_changeQuota (JNIEnv *env, jclass cls, 
+                                            jint cellHandle, 
+                                            jint serverHandle, 
+                                            jint partition, jint volID, 
+                                            jint newQuota) {
+
+  afs_status_t ast;
+
+  if( !vos_VolumeQuotaChange( (void *) cellHandle, (void *) serverHandle, 
+                             NULL, (unsigned int) partition, 
+                             (unsigned int) volID, (unsigned int) newQuota, 
+                             &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Move the specified volume to a different site.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * fromServerHandle  the vos handle of the server on which the volume
+ *                          currently resides
+ * fromPartition   the numeric id of the partition on which the volume
+ *                        currently resides
+ * toServerHandle  the vos handle of the server to which the volume 
+ *                        should be moved
+ * toPartition   the numeric id of the partition to which the volume 
+ *                      should be moved
+ * volId  the numeric id of the volume to move
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_move
+  (JNIEnv *env, jclass cls, jint cellHandle, jint fromServerHandle, 
+   jint fromPartition, jint toServerHandle, jint toPartition, jint volID)
+{
+  afs_status_t ast;
+
+  if( !vos_VolumeMove( (void *) cellHandle, NULL, (unsigned int) volID, 
+                      (void *) fromServerHandle, (unsigned int) fromPartition,
+                      (void *) toServerHandle, (unsigned int) toPartition, 
+                      &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Releases the specified volume that has readonly volume sites.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * volId  the numeric id of the volume to release
+ * forceComplete  whether or not to force a complete release
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_release
+  (JNIEnv *env, jclass cls, jint cellHandle, jint volID, jboolean forceComplete)
+{
+  afs_status_t ast;
+  vos_force_t force;
+
+  if( forceComplete ) {
+    force = VOS_FORCE;
+  } else {
+    force = VOS_NORMAL;
+  }
+
+  if( !vos_VolumeRelease( (void *) cellHandle, NULL, (unsigned int) volID,
+                          force, &ast )) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Dumps the specified volume to a file.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the volume 
+ *                      resides
+ * partition   the numeric id of the partition on which the 
+ *                    volume resides
+ * volId  the numeric id of the volume to dump
+ * startTime   files with a modification time >= to this time will 
+ *                    be dumped
+ * jdumpFile   the full path of the file to which to dump
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_dump (JNIEnv *env, jclass cls, 
+                                     jint cellHandle, jint serverHandle, 
+                                     jint partition, jint volID, 
+                                     jint startTime, jstring jdumpFile) {
+
+  afs_status_t ast;
+  const char *dumpFile;
+
+  if( jdumpFile != NULL ) {
+    dumpFile = (*env)->GetStringUTFChars(env, jdumpFile, 0);
+    if( !dumpFile ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    dumpFile = NULL;
+  }
+
+  if( !vos_VolumeDump( (void *) cellHandle, (void *) serverHandle, NULL, 
+                      (unsigned int *) &partition, (unsigned int) volID, 
+                      (unsigned int) startTime, dumpFile, &ast ) ) {
+    if( dumpFile != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jdumpFile, dumpFile);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( dumpFile != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jdumpFile, dumpFile);
+  }
+
+}
+
+/**
+ * Restores the specified volume from a dump file.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * serverHandle  the vos handle of the server on which the volume is 
+ *                      to reside
+ * partition   the numeric id of the partition on which the volume is
+ *                    to reside
+ * volId  the numeric id to assign the restored volume (can be 0)
+ * jvolName  the name of the volume to restore as
+ * jdumpFile   the full path of the dump file from which to restore
+ * incremental  if true, restores an incremental dump over an existing
+ *                     volume (server and partition values must correctly 
+ *                     indicate the current position of the existing volume),
+ *                     otherwise restores a full dump
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_restore (JNIEnv *env, jclass cls, 
+                                        jint cellHandle, jint serverHandle, 
+                                        jint partition, jint volID, 
+                                        jstring jvolName, jstring jdumpFile, 
+                                        jboolean incremental) {
+
+  afs_status_t ast;
+  const char *volName;
+  const char *dumpFile;
+  int *volumeIDp;
+  vos_volumeRestoreType_t vrt;
+
+  if( jvolName != NULL ) {
+    volName = (*env)->GetStringUTFChars(env, jvolName, 0);
+    if( !volName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    volName = NULL;
+  }
+
+  if( jdumpFile != NULL ) {
+    dumpFile = (*env)->GetStringUTFChars(env, jdumpFile, 0);
+    if( !dumpFile ) {
+      if( volName != NULL ) {
+       (*env)->ReleaseStringUTFChars(env, jvolName, volName);
+      }
+      throwAFSException( env, JAFSADMNOMEM );
+      return;    
+    }
+  } else {
+    dumpFile = NULL;
+  }
+
+  if( volID == 0 ) {
+    volumeIDp = NULL;
+  } else {
+    volumeIDp = (int *) &volID;
+  }
+  
+  if( incremental ) {
+    vrt = VOS_RESTORE_INCREMENTAL;
+  } else {
+    vrt = VOS_RESTORE_FULL;
+  }
+
+  if( !vos_VolumeRestore( (void *) cellHandle, (void *) serverHandle, NULL, 
+                         (unsigned int) partition, (unsigned int *) volumeIDp,
+                         volName, dumpFile, vrt, &ast ) ) {
+    if( volName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jvolName, volName);
+    }
+    if( dumpFile != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jdumpFile, dumpFile);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( dumpFile != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jdumpFile, dumpFile);
+  }
+  if( volName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jvolName, volName);
+  }
+
+}
+
+/**
+ * Renames the specified read-write volume.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * volId  the numeric id of the read-write volume to rename
+ * jnewName  the new name for the volume
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_rename (JNIEnv *env, jclass cls, 
+                                       jint cellHandle, jint volID, 
+                                       jstring jnewName) {
+
+  afs_status_t ast;
+  const char *newName;
+
+  if( jnewName != NULL ) {
+    newName = (*env)->GetStringUTFChars(env, jnewName, 0);
+    if( !newName ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    newName = NULL;
+  }
+
+  if( !vos_VolumeRename( (void *) cellHandle, NULL, (unsigned int) volID, 
+                        newName, &ast ) ) {
+    if( newName != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+    }
+    throwAFSException( env, ast );
+    return;
+  }
+
+  if( newName != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jnewName, newName);
+  }
+
+}
+
+/**
+ * "Mounts" the specified volume, bringing it online.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the vos handle of the server on which the volume 
+ *                      resides
+ * partition   the numeric id of the partition on which the volume 
+ *                    resides
+ * volId  the numeric id of the volume to bring online
+ * sleepTime  ?  (not sure what this is yet, possibly a time to wait 
+ *                      before brining it online)
+ * offline   ?  (not sure what this is either, probably the current 
+ *                     status of the volume -- busy or offline)
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_mount (JNIEnv *env, jclass cls, 
+                                      jint serverHandle, jint partition, 
+                                      jint volID, jint sleepTime, 
+                                      jboolean offline) {
+
+  afs_status_t ast;
+  vos_volumeOnlineType_t volumeStatus;
+
+  if( offline ) {
+    volumeStatus = VOS_ONLINE_OFFLINE;
+  } else {
+    volumeStatus = VOS_ONLINE_BUSY;
+  }
+
+  if( !vos_VolumeOnline( (void *) serverHandle, NULL, (unsigned int) partition,
+                        (unsigned int) volID, (unsigned int) sleepTime, 
+                        volumeStatus, &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * "Unmounts" the specified volume, bringing it offline.
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * serverHandle  the vos handle of the server on which the volume 
+ *                      resides
+ * partition   the numeric id of the partition on which the volume 
+ *                    resides
+ * volId  the numeric id of the volume to bring offline
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_unmount (JNIEnv *env, jclass cls, 
+                                        jint serverHandle, jint partition, 
+                                        jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_VolumeOffline( (void *) serverHandle, NULL, 
+                         (unsigned int) partition, (unsigned int) volID, 
+                         &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Locks the VLDB entry specified volume
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle  the handle of the cell on which the volume resides
+ * volId  the numeric id of the volume to lock
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_lock (JNIEnv *env, jclass cls, 
+                                     jint cellHandle, jint volID ) {
+
+  afs_status_t ast;
+
+  if( !vos_VLDBEntryLock( (void *) cellHandle, NULL, (unsigned int) volID, 
+                         &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Unlocks the VLDB entry of the specified volume
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle  the handle of the cell on which the volume resides
+ * volId  the numeric id of the volume to unlock
+ */
+JNIEXPORT void JNICALL 
+Java_org_openafs_jafs_Volume_unlock (JNIEnv *env, jclass cls, 
+                                       jint cellHandle, jint volID) {
+
+  afs_status_t ast;
+
+  if( !vos_VLDBEntryUnlock( (void *) cellHandle, NULL, (unsigned int) volID, 
+                           &ast ) ) {
+    throwAFSException( env, ast );
+    return;
+  }
+
+}
+
+/**
+ * Translates a volume name into a volume id
+ *
+ * env      the Java environment
+ * cls      the current Java class
+ * cellHandle    the handle of the cell to which the volume belongs
+ * jname  the name of the volume in question, cannot end in backup or
+ *              readonly
+ * type  the type of volume: read-write, read-only, or backup.  
+ *              Acceptable values are:
+ *              org_openafs_jafs_Volume_VOLUME_TYPE_READ_WRITE
+ *              org_openafs_jafs_Volume_VOLUME_TYPE_READ_ONLY
+ *              org_openafs_jafs_Volume_VOLUME_TYPE_BACKUP
+ * returns   the id of the volume in question
+ */
+JNIEXPORT jint JNICALL 
+Java_org_openafs_jafs_Volume_translateNameToID (JNIEnv *env, jclass cls, 
+                                                  jint cellHandle, 
+                                                  jstring jname, jint type) {
+
+  afs_status_t ast;
+  const char *name;
+  vos_vldbEntry_t vldbEntry;
+
+  if( jname != NULL ) {
+    name = (*env)->GetStringUTFChars(env, jname, 0);
+    if( !name ) {
+       throwAFSException( env, JAFSADMNOMEM );
+       return;    
+    }
+  } else {
+    name = NULL;
+  }
+
+  // get the id
+  if( !vos_VLDBGet( (void *) cellHandle, NULL, NULL, name, 
+                   &vldbEntry, &ast ) ) {
+    if( name != NULL ) {
+      (*env)->ReleaseStringUTFChars(env, jname, name);
+    }
+    throwAFSException( env, ast );
+    return -1;
+  } 
+
+  if( name != NULL ) {
+    (*env)->ReleaseStringUTFChars(env, jname, name);
+  }
+
+  if( type == org_openafs_jafs_Volume_VOLUME_TYPE_READ_WRITE ) {
+    return vldbEntry.volumeId[VOS_READ_WRITE_VOLUME];
+  } else if( type == org_openafs_jafs_Volume_VOLUME_TYPE_READ_ONLY ) {
+    return vldbEntry.volumeId[VOS_READ_ONLY_VOLUME];
+  } else {
+    return vldbEntry.volumeId[VOS_BACKUP_VOLUME];
+  }
+
+}
+
+
+// reclaim global memory being used by this portion
+JNIEXPORT void JNICALL
+Java_org_openafs_jafs_Volume_reclaimVolumeMemory (JNIEnv *env, jclass cls) {
+
+  if( volumeCls ) {
+      (*env)->DeleteGlobalRef(env, volumeCls);
+      volumeCls = 0;
+  }
+
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/JAVA/libjafs/etc/CacheConfig b/src/JAVA/libjafs/etc/CacheConfig
new file mode 100644 (file)
index 0000000..e184b2e
--- /dev/null
@@ -0,0 +1,16 @@
+MountPoint /afs
+ConfDir /usr/afswsp/etc
+CacheDir /usr/afswsp/cache
+LogFile /usr/afswsp/log/libjafs.log
+CacheBlocks 40000
+CacheFiles 3000
+CacheStatEntries 3000
+DCacheSize 100
+VCacheSize 1000
+ChunkSize 0
+CloseSynch 0
+Debug 0
+NDaemons 3
+CacheFlags -1
+
+
diff --git a/src/JAVA/libjafs/etc/CacheConfig.100MB b/src/JAVA/libjafs/etc/CacheConfig.100MB
new file mode 100644 (file)
index 0000000..e433ca1
--- /dev/null
@@ -0,0 +1,16 @@
+MountPoint /afs
+ConfDir /usr/afswsp/etc
+CacheDir /usr/afswsp/cache
+LogFile /usr/afswsp/log/libjafs.log
+CacheBlocks 100000
+CacheFiles 6000
+CacheStatEntries 6000
+DCacheSize 250
+VCacheSize 2000
+ChunkSize 0
+CloseSynch 0
+Debug 0
+NDaemons 3
+CacheFlags -1
+
+
diff --git a/src/JAVA/libjafs/etc/CacheConfig.40MB b/src/JAVA/libjafs/etc/CacheConfig.40MB
new file mode 100644 (file)
index 0000000..e184b2e
--- /dev/null
@@ -0,0 +1,16 @@
+MountPoint /afs
+ConfDir /usr/afswsp/etc
+CacheDir /usr/afswsp/cache
+LogFile /usr/afswsp/log/libjafs.log
+CacheBlocks 40000
+CacheFiles 3000
+CacheStatEntries 3000
+DCacheSize 100
+VCacheSize 1000
+ChunkSize 0
+CloseSynch 0
+Debug 0
+NDaemons 3
+CacheFlags -1
+
+
diff --git a/src/TechNotes-JavaAPI b/src/TechNotes-JavaAPI
new file mode 100644 (file)
index 0000000..52ddd24
--- /dev/null
@@ -0,0 +1,94 @@
+Java API (Jafs): Technical Notes
+-----------------------------------------
+
+Overview
+--------
+
+The Java API (Jafs) is a Java package with a shared library
+written in C.  It is meant for use by programmers who wish to develop
+tools in Java for OpenAFS administration and use.  This document briefly
+outlines the architecture of Jafs.
+
+Shared library
+--------------
+
+The source code for the shared library resides in the src/JAVA/libjafs
+directory.  See the JAFS_README file in that directory for information
+on how to compile this package.  The code is broken up logically into
+files by the type of AFS object they relate to (i.e. cell, user, volume, 
+etc.), which closely mirrors the different classes of the Java package.
+This library is built on top of the libadmin and libuafs libraries, and
+mainly serves as a translation layer between the Java package and the
+OpenAFS code.  It is closely tied to the Java package, in that it often
+accesses the actual Java objects by calls up through JNI, in order to
+retrieve or populate member fields of those objects. Also, if an error
+occurs in this code or in another C library, a Java exception is 
+constructed with the appropriate error code, and is thrown back to the 
+Java layer to be dealt with.
+
+In order to provide user-level functions such as ACL setting and mount
+point iteration, the shared library needs to be linked with a specialized
+version of libuafs called libjuafs.a. Please view the README file in the 
+src/libuafs directory for more information regarding libjuafs and how it 
+is built. 
+
+Java package
+------------
+
+The code for the org.openafs.jafs package resides in the 
+src/JAVA/org/openafs/jafs/ directory.  It is broken into classes
+in the same way that the OpenAFS file system breaks down into logical 
+components: Cell, User, Group, Server, Partition, Volume, Process, Key,
+Token, ACL, and File.  There are also classes for file input and
+output streams, and specialized exception classes.
+
+Publicly, the developer only has access to these objects and their
+instance functions, which provide a solid, object-oriented view of
+OpenAFS administration and use.  The first thing a programmer would do to
+use this package in his or her code would be to construct a Token object by
+giving it a cell name, a user name, and a password for that user.  From 
+there, the programmer could easily construct a Cell object and then list, 
+for example, all the servers in a cell, create a new user, move a volume to 
+a different partition, etc.
+
+When one of these objects is constructed, it does not actually create
+the corresponding component in OpenAFS.  The object is supposed to
+represent the object.  If the programmer wants to actually create a 
+new user in OpenAFS, for example, he or she would construct a new User
+object with the name of the user to create, and then explicitly call
+the object's create method.  
+
+When an object first accesses information about itself from OpenAFS
+by calling down through JNI to the shared library, it saves the
+information and will return it to the application on subsequent 
+requests.  This minimizes the overhead of expensive JNI calls.  This
+saved information can be updated to reflect the most current state of
+OpenAFS by calling the objects refresh method.
+
+There are usually two ways to list something: getting an array of the
+names of the elements in the list, or getting an array of the actual
+objects themselves.  For example, the Cell object can provide the
+programmer with an array of the names of all users in the cell, or
+an array of actual User objects, with relevant member fields already set
+to the correct values. 
+
+Almost every method in the package declares AFSException in its 
+throws clause.  This is thrown from the shared library whenever an 
+error occurs, and contains an OpenAFS error code as a member.  The 
+programmer is expected to deal with these exceptions however they see fit.
+
+The native methods that act as the interface between the Java layer and
+the shared library are declared protected, so they can be used directly
+by developers who wish to subclass these classes for their applications
+and possibly implement their own versions of the Java API calls.
+
+Known Issues
+------------
+
+Some issues not yet dealt with in this API include:
+  - Alternative methods of authentication, other than Kaserver (i.e.
+    Kerberos V).  There has been some discussion about how to abstract
+    the User object to be more general, but so far it has not been 
+    implemented.
+  - Access to VLDB functionality such as listing VLDB entries and
+    zapping volumes.
index 09fd6f4a31782caea340c8282c2d6b0430779325..b92c8288f31b4f239fc937a71369696c4b8966c3 100644 (file)
@@ -62,6 +62,7 @@ SHLIB_SUFFIX = @SHLIB_SUFFIX@
 SYS_NAME = @AFS_SYSNAME@
 TOP_INCDIR = @TOP_INCDIR@
 TOP_LIBDIR = @TOP_LIBDIR@
+TOP_JLIBDIR= @TOP_JLIBDIR@
 TOP_OBJDIR = @TOP_OBJDIR@
 TOP_SRCDIR = @TOP_SRCDIR@
 TXLIBS = @TXLIBS@