aboutsummaryrefslogtreecommitdiff
path: root/nfs
diff options
context:
space:
mode:
Diffstat (limited to 'nfs')
-rw-r--r--nfs/ChangeLog618
-rw-r--r--nfs/Makefile32
-rw-r--r--nfs/cache.c186
-rw-r--r--nfs/consts.c25
-rw-r--r--nfs/main.c403
-rw-r--r--nfs/mount.c220
-rw-r--r--nfs/mount.h35
-rw-r--r--nfs/name-cache.c303
-rw-r--r--nfs/nfs-spec.h164
-rw-r--r--nfs/nfs.c645
-rw-r--r--nfs/nfs.h195
-rw-r--r--nfs/ops.c1757
-rw-r--r--nfs/pager.c456
-rw-r--r--nfs/rpc.c380
14 files changed, 5419 insertions, 0 deletions
diff --git a/nfs/ChangeLog b/nfs/ChangeLog
new file mode 100644
index 00000000..9905bb06
--- /dev/null
+++ b/nfs/ChangeLog
@@ -0,0 +1,618 @@
+1999-07-10 Roland McGrath <roland@baalperazim.frob.com>
+
+ * nfs.h: Add #include <sys/mman.h> for munmap decl.
+
+1999-07-09 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * ops.c (netfs_get_dirents): Use mmap instead of vm_allocate.
+ * pager.c (pager_read_page): Likewise.
+
+1999-07-03 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * ops.c (netfs_get_dirents): Use munmap instead of vm_deallocate.
+ * pager.c (pager_read_page): Likewise.
+
+1999-02-01 Mark Kettenis <kettenis@phys.uva.nl>
+
+ * ops.c: Include <maptime.h>.
+ (netfs_attempt_utimes): Fix various typo's.
+
+Sun Jan 31 18:39:09 1999 Thomas Bushnell, BSG <tb@mit.edu>
+
+ * nfs.c (xdr_encode_sattr_times): Convert nanoseconds to
+ microseconds correctly.
+
+ * ops.c (netfs_attempt_utimes): Handle new possibility that ATIME
+ or MTIME might be null.
+
+1999-01-22 Roland McGrath <roland@baalperazim.frob.com>
+
+ * ops.c (netfs_attempt_link): Remove major, minor macro definitions.
+ * nfs.c (xdr_decode_fattr): Remove makedev macro definition.
+
+1998-07-20 Roland McGrath <roland@baalperazim.frob.com>
+
+ * mount.c (mount_root): Make perror explanation strings consistent.
+
+ * mount.c (pmap_initialize_rpc): Make static.
+ (mount_initialize_rpc): Likewise.
+
+Wed Aug 20 14:31:03 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_link): Implement EXCL == 0 case.
+ (netfs_attempt_rename): Implement EXCL != 0 case.
+
+ * pager.c (netfs_get_filemap): If pager_create fails, return
+ error to caller.
+
+Wed Aug 6 15:23:03 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_unlink): Purge cache-held references
+ *before* counting live references. Use new function.
+ * name-cache.c (purge_lookup_cache_node): New function.
+ * nfs.h (purge_lookup_cache_node): New decl.
+
+ * cache.c (netfs_node_norefs): Don't do delete RPC here, fork off
+ a separate thread to do it.
+ (struct fnd): New type.
+ (forked_node_delete): New function.
+
+Mon Aug 4 15:56:37 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * name-cache.c (struct lookup_cache): Drop members `node_cache_fh'
+ and `node_cache_len'. New member `np'.
+ (enter_lookup_cache): Fill C->np instead of C->node_cache_fh.
+ Acquire an additional reference on NP to keep it live. If there
+ was a reference there before, release it.
+ (purge_lookup_cache): If there is a reference to a node on a
+ purged entry, release it.
+ (check_lookup_cache): If there is a reference to a node on an
+ out-of-date entry, release it. When returning live positive hits,
+ use the NP stored instead of looking one up.
+
+Fri Aug 1 15:56:56 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * main.c (netfs_append_args): Append --name-cache-timeout and
+ --name-cache-neg-timeout values.
+
+Tue Jul 29 15:38:15 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * name-cache.c (check_lookup_cache): Use name_cache_neg_timeout in
+ preference to name_cache_timeout for negative hits.
+
+ * nfs.h (name_cache_neg_timeout): New variable.
+ * main.c (DEFAULT_NAME_CACHE_NEG_TIMEOUT): New macro.
+ (name_cache_neg_timeout): Initialize new variable.
+ (OPT_NCACHE_NEG_TO): New option key.
+ (common_options): Add --name-cache-neg-timeout switch.
+ (parse_common_opt): Process OPT_NCACHE_NEG_TO option key.
+
+ * ops.c (netfs_attempt_lookup): Do cast of -1 correctly to match
+ check_lookup_cache and ANSI C rules.
+
+ * name-cache.c (check_lookup_cache): Unlock DIR correctly before
+ returning a negative cache hit.
+
+ * rpc.c (rpc_receive_thread): Don't print "dropping reply"
+ messages any more.
+
+ * ops.c (netfs_attempt_lookup): Cash all lookups, both positive
+ and negative.
+ (netfs_attempt_mkdir): Purge cache before creating directory
+ entry.
+ (netfs_attempt_link): Likewise.
+ (netfs_attempt_create_file): Likewise.
+
+ * ops.c (netfs_attempt_lookup): Pass correct node to
+ enter_lookup_cache as the child.
+
+Thu Jul 24 13:15:56 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * name-cache.c: New file.
+ * ops.c (netfs_attempt_lookup): Check lookup cache before trying
+ RPC. After successful lookup, make cache entry.
+ (netfs_attempt_unlink): Purge cache before doing operation.
+ (netfs_attempt_rmdir): Likewise.
+ (netfs_attempt_rename): Purge cache for both names before operation.
+ * nfs.h (enter_lookup_cache, purge_lookup_cache,
+ check_lookup_cache): New prototypes.
+ * Makefile (SRCS): Add name-cache.c.
+
+ * nfs.h (name_cache_timeout): New variable.
+ * main.c (DEFAULT_NAME_CACHE_TIMEOUT): New macro.
+ (name_cache_timeout): Initialize new variable.
+ (OPT_NCACHE_TO): New option key.
+ (common_options): Add --name-cache-timeout switch.
+ (parse_common_opt): Process OPT_NCACHE_TO option key.
+
+ * cache.c (lookup_fhandle): Don't parse NFS XDR format, instead
+ take a length arg. Return void. All callers changed to use new
+ function xdr_decode_fhandle.
+ * nfs.c (xdr_decode_fhandle): New function.
+ * nfs.h (xdr_decode_fhandle): New prototype.
+ (lookup_fhandle): Update prototype.
+
+1997-07-23 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c: Include <error.h>.
+ (main): Failure to map MAPPED_TIME is a fatal error.
+
+1997-06-09 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ops.c (netfs_validate_stat): Don't clear NP->istranslated.
+
+Wed May 21 12:07:24 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_create_file): Only attempt chown if the
+ create succeeded.
+
+Tue May 20 15:35:39 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs.c (xdr_encode_create_state): New arg OWNER. Set the owner
+ of the new file to it. All callers changed.
+ * nfs.h (xdr_encode_create_state): Update prototype.
+ * ops.c (netfs_attempt_mkdir): If owner didn't get set correctly
+ (some servers ignore it) then try a chown.
+ (netfs_attempt_create_file): Likewise.
+
+Thu Apr 10 13:25:12 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_set_size): Short circuit EACCES to cover
+ one important case.
+
+Fri Feb 21 16:47:35 1997 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (netfs_append_args): Present the remote filesystem as a
+ single argument, in HOST:FS format.
+
+Wed Feb 12 13:53:42 1997 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_check_open_permissions): If this is a new node,
+ don't report an error here. (Though, later I/O requests still
+ might fail.)
+
+Wed Nov 20 17:13:59 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): Add iohelp.
+
+Mon Nov 18 17:01:38 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (guess_mode_use): Delete function.
+ (netfs_report_access): Use fshelp_access instead of
+ guess_mode_use. Change return type.
+
+Sat Nov 16 18:24:55 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (SRCS): Remove `cred.c'.
+ * cred.c: Delete file.
+ * nfs.h (struct netcred): Delete type.
+ (cred_has_uid, cred_has_gid): Delete functions.
+ (nfs_initialize_rpc): netcred arg now iouser.
+ * ops.c: Change struct netcred to struct iouser throughout.
+ * pager.c: Likewise.
+ * cache.c (netfs_node_norefs): Likewise.
+ * nfs.c (nfs_initialize_rpc): Convert use of netcred to iouser.
+
+Mon Nov 4 21:23:58 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * rpc.c (rpc_list_lock): Delete variable. Omit all mention of it
+ throughout this file. Expand the use of outstanding_lock to cover
+ what rpc_list_lock used to handle.
+
+Fri Nov 1 18:12:21 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * rpc.c (conduct_rpc): Unlock OUTSTANDING_LOCK if write fails.
+
+Wed Oct 30 18:25:18 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_statfs): Convert incoming numbers from
+ network to host byte order.
+ Don't convert sizes from IOSIZE to BSIZE units.
+ Return BSIZE in f_bsize field, not IOSIZE.
+ Fill in f_fsid field.
+
+Thu Oct 24 23:01:35 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (runtime_argp_children): New variable.
+ (runtime_argp_parents): Variable removed.
+ (runtime_argp): Use RUNTIME_ARGP_CHILDREN, not RUNTIME_ARGP_PARENTS.
+ (main): Rename ARGP_PARENTS to ARGP_CHILDREN, and change the
+ element type to `struct argp_child'; change the reference in ARGP
+ accordingly.
+
+Fri Oct 4 00:00:12 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * mount.c: Add hacks to avoid idiotic sun definitions of TRUE,
+ FALSE, and malloc in <rpc/types.h>.
+ * nfs.h (bool_t, enum_t): Typedefs removed.
+
+Thu Oct 3 12:04:36 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_write): If we get EINTR after having
+ already written something, return a short count.
+
+ * ops.c (netfs_attempt_create_file): If verify_nonexistent returns
+ an error, bother to properly unlock NP before returning.
+
+Mon Sep 30 15:45:31 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (OPT_RSIZE): Changed to 'R'.
+ (OPT_WSIZE): Changed to 'W'.
+ (doc): Add general description.
+
+Thu Sep 26 14:03:07 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs.h (enum_t): New decl.
+
+Wed Sep 18 13:03:22 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (netfs_append_args): Renamed from netfs_get_options.
+ Don't initialize ARGZ & ARGZ_LEN anymore.
+ Add remote filesystem spec to output.
+ (hold): Variable removed.
+ (main): Don't use HOLD anymore.
+
+Thu Sep 12 16:46:47 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * Makefile (HURDLIBS): New variable.
+ (nfs): Omit special dependency.
+
+Tue Sep 3 14:00:25 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs-spec.h: Renamed from rpcsvc/nfs_prot.h.
+ * mount.c: Include "mount.h" instead of <rpcsvc/mount.h>.
+ * nfs.h: Include "nfs-spec.h" instead of <rpcsvc/nfs_prot.h>.
+ * rpcsvc/mount.h: Deleted file.
+ * rpcsvc: Deleted directory.
+ * mount.h (MOUNTPROG): Renamed from MOUNT_RPC_PROGRAM.
+ (MOUNTVERS): Renamed from MOUNT_RPC_VERSION.
+ * Makefile (RPCSVC_FILES): Deleted var.
+ (lndist, lndist-rpcsvc-files,
+ $(top_srcdir)/hurd-snap/$(dir)/rpcsvc): Deleted targets.
+ (LCLHDRS): Added mount.h and nfs-spec.h.
+
+Thu Aug 29 10:41:27 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * rpcsvc/nfs_prot.h (createmode): Spell EXCLUSIVE correctly.
+ * nfs.c (hurd_mode_to_nfs_type): New function.
+ * nfs.h (hurd_mode_to_nfs_type): New decl.
+ * ops.c (netfs_attempt_rmdir): process_wcc_stat for NP, not DIR.
+ (netfs_attempt_link): Spell netfs_validate_stat correctly.
+ (minor, major): New macros.
+ (netfs_report_access): Don't try and return an error.
+
+ * rpc.c (conduct_rpc): Tolerate and return errors from write.
+
+ * Makefile (RPCSVC_FILES): New variable.
+ (lndist): Depend on lndist-rpcsvc-files.
+ (lndist-rpcsvc-files, $(top_srcdir)/hurd-snap/$(dir)/rpcsvc): New
+ targets.
+
+Fri Aug 16 11:56:53 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (process_wcc_stat): New function.
+ (netfs_attempt_chown): Use process_wcc_stat instead of
+ register_fresh_stat. Pack and unpack v3 SETATTR structure if
+ appropriate.
+ (netfs_attempt_chmod): Likewise.
+ (netfs_attempt_utimes): Likewise.
+ (netfs_attempt_set_size): Likewise.
+ (netfs_attempt_lookup): Use process_returned_stat instead of
+ register_fresh state. Unpack v3 LOOKUP structure if appropriate.
+ (netfs_attempt_link): Likewise.
+ (verify_nonexistent): Assert that we are v2.
+ (netfs_report_access): Use NFS3PROC_ACCESS if we are v3.
+ (netfs_check_open_permissions): Use netfs_report_access.
+ (netfs_attempt_readlink): Unpack v3 READLINK structure if
+ appropriate.
+ (netfs_attempt_read): Pack and unpack v3 READ structure in
+ appropriate. Use process_returned_stat instead of
+ register_fresh_stat.
+ (netfs_attempt_write): Pack and unpack v3 WRITE structure in
+ appropriate. Use process_wcc_stat instead of
+ register_fresh_stat.
+ (netfs_attempt_create_file): Pack and unpack v3 CREATE structure
+ if appropriate. Use process_returned_stat instead of
+ register_fresh_stat.
+ (netfs_attempt_link) [CHRDEV, BLKDEV, FIFO, SOCK]: If v3, use new
+ MKNOD call instead of CREATE with overloaded mode.
+ (netfs_attempt_link) [SYMLINK]: If pack and unpack v3 SYMLINK
+ structure if appropriate.
+ (netfs_attempt_unlink): Unpack v3 REMOVE structure if appropriate.
+ (netfs_attempt_rmdir): Unpack v3 RMDIR structure if appropriate.
+ (netfs_attempt_rename): Unpack v3 RENAME structure if appropriate.
+
+ * rpcsvc/nfs_prot.h (ACCESS3_READ, ACCESS3_LOOKUP, ACCESS3_MODIFY,
+ ACCESS3_EXTEND, ACCESS3_DELETE, ACCESS3_EXECUTE): New macros.
+
+ * ops.c (netfs_attempt_chown): Bother to read NFS error/success
+ value.
+
+Thu Aug 15 15:24:29 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (verify_nonexistent): Don't need to lock DIR; it's already
+ locked.
+ (netfs_attempt_link): Lock DIR *before* calling verify_nonexistent.
+
+ * nfs.c (xdr_encode_fhandle) [protocol_version == 2]: Copy
+ fhandle->data, not fhandle itself.
+
+Wed Aug 14 12:33:37 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs.c (xdr_encode_fhandle): Second arg now a `struct fhandle *'.
+ Encode v3 fhandle if appropriate.
+ * cache.c: Include <netinet/in.h>.
+ (hash): New arg `length'.
+ (lookup_fhandle): Changed to be more like xdr_decode_* functions.
+ All callers changed. Do the right thing for v3.
+ (recache_handle): Likewise.
+ * nfs.h (struct fhandle): New type.
+ (struct netnode): Member `handle' is now a `struct fhandle'.
+ (xdr_encode_fhandle): Second arg now a `struct fhandle *'.
+ (lookup_fhandle, recache_handle): Changed to be more like
+ xdr_decode_* functions.
+
+ * rpcsvc/nfs_prot.h (enum sattr_time_how): New type.
+ * nfs.c (xdr_encode_64bit): New function.
+ (xdr_encode_sattr_mode, xdr_encode_sattr_ids,
+ xdr_encode_sattr_size, xdr_encode_sattr_times,
+ xdr_encode_create_state, xdr_encode_sattr_stat): Encode v3 sattr
+ if appropriate.
+ (xdr_decode_fattr): Decode v3 fattr if appropriate.
+
+ * rpcsvc/nfs_prot.h (NFS_FHSIZE): Deleted macro.
+ (NFS2_FHSIZE, NFS3_FHSIZE, NFS3_COOKIEVERFSIZE, NFS3_CREATEVERFSIZE,
+ NFS3_WRITEVERFSIZE): New macros.
+ (enum ftype): Deleted NFFIFO. Added NF2FIFO, NF3FIFO, and an
+ alias NF2BAD for NF3FIFO.
+ (NFSPROC_NULL, NFSPROC_GETATTR, NFSPROC_SETATTR,
+ NFSPROC_LOOKUP, NFSPROC_READLINK, NFSPROC_READ,
+ NFSPROC_WRITE, NFSPROC_CREATE, NFSPROC_REMOVE,
+ NFSPROC_RENAME, NFSPROC_LINK, NFSPROC_SYMLINK, NFSPROC_MKDIR,
+ NFSPROC_RMDIR, NFSPROC_READDIR): Replace with new
+ macros that take a version arg. All users changed to use new
+ version arg.
+ (NFS_PROTOCOL_FUNC): New macro.
+ (NFS2PROC_NULL, NFS2PROC_GETATTR, NFS2PROC_SETATTR, NFS2PROC_ROOT,
+ NFS2PROC_LOOKUP, NFS2PROC_READLINK, NFS2PROC_READ,
+ NFS2PROC_WRITECACHE, NFS2PROC_WRIT, NFS2PROC_CREATE,
+ NFS2PROC_REMOVE, NFS2PROC_RENAME, NFS2PROC_LINK, NFS2PROC_SYMLINK,
+ NFS2PROC_MKDIR, NFS2PROC_RMDIR, NFS2PROC_READDIR, NFS2PROC_STATFS,
+ NFS3PROC_NULL, NFS3PROC_GETATTR, NFS3PROC_SETATTR,
+ NFS3PROC_LOOKUP, NFS3PROC_ACCESS, NFS3PROC_READLINK,
+ NFS3PROC_READ, NFS3PROC_WRITE, NFS3PROC_CREATE, NFS3PROC_MKDIR,
+ NFS3PROC_SYMLINK, NFS3PROC_MKNOD, NFS3PROC_REMOVE, NFS3PROC_RMDIR,
+ NFS3PROC_RENAME, NFS3PROC_LINK, NFS3PROC_READDIR,
+ NFS3PROC_READDIRPLUS, NFS3PROC_FSSTAT, NFS3PROC_FSINFO,
+ NFS3PROC_PATHCONF, NFS3PROC_COMMIT): New macros. All callers
+ appropriately changed.
+
+ * nfs.c (nfs_error_trans): NFS_SERVERFAULT maps to EIO;
+ NFSERR_BADTYPE maps to EOPNOTSUPP.
+
+ * rpcsvc/nfs_prot.h (nfsstat, ftype, struct nfs_fh, nfs_fh, struct
+ nfstime, nfstime, struct fattr, fattr, struct sattr, sattr,
+ filename, nfspath, struct attrstat, attrstat, struct sattrargs,
+ sattrargs, struct diropargs, diropargs, struct diropokres,
+ diropokres, struct diropres, diropres, struct readlinkres,
+ readlinkres, struct readargs, readargs, struct readokres,
+ readokres, struct readres, readres, struct writeargs, writeargs,
+ struct createargs, createargs, struct renameargs, renameargs,
+ struct linkargs, linkargs, struct symlinkargs, symlinkargs,
+ nfscookie, struct readdirargs, readdirargs, struct entry, entry,
+ struct dirlist, dirlist, struct readdirres, readdirres, struct
+ statfsokres, statfsokres, struct statfsres, statfsres): Delete
+ unused types.
+ (xdr_nfsstat, xdr_ftype, xdr_nfs_fh, xdr_nfstime, xdr_fattr,
+ xdr_sattr, xdr_filename, xdr_nfspath, xdr_attrstat, xdr_sattrargs,
+ xdr_diropargs, xdr_diropokres, xdr_diropres, xdr_readlinkres,
+ xdr_readargs, xdr_readokres, xdr_readres, xdr_writeargs,
+ xdr_createargs, xdr_renameargs, xdr_linkargs, xdr_symlinkargs,
+ xdr_nfscookie, xdr_readdirargs, xdr_entry, xdr_dirlist,
+ xdr_readdirres, xdr_statfsokres, xdr_statfsres, nfsproc_null_2,
+ nfsproc_getattr_2, nfsproc_setattr_2, nfsproc_root_2,
+ nfsproc_lookup_2, nfsproc_readlink_2, rfsproc_read_2,
+ nfsproc_writecache_2, nfsproc_write_2, nfsproc_create_2,
+ nfsproc_remove_2, nfsproc_rename_2, nfsproc_link_2,
+ nfsproc_symlink_2, nfsproc_mkdir_2, nfsproc_rmdir_2,
+ nfsproc_readdir_2, nfsproc_statfs_2): Delete unused function
+ declarations.
+
+Tue Aug 13 14:57:03 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_create_file): Sun's NFS client does not
+ expect NFSPROC_CREATE to be exclusive. Accordingly, on most
+ servers (including ours) it isn't exclusive. (Which, of course,
+ contradicts Sun's own RGC 1094, section 2.2.10.) Which means we
+ have to insert our own test here to make sure the file doesn't
+ exist before attempting NFSPROC_CREATE.
+ (netfs_attempt_link): Likewise.
+ (verify_nonexistent): New function.
+
+Mon Aug 12 11:13:58 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs.c (nfs_error_trans): Repair syntax.
+
+Thu Aug 8 18:48:22 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * nfs.h (protocol_version): New variable.
+ * mount.c (protocol_version): Define and initialize to `2'.
+
+ * rpcsvc/nfs_prot.h (enum nfsstat): Added new nfsv3 error codes:
+ NFSERR_XDEV, NFSERR_INVAL, NFSERR_MLINK, NFSERR_REMOTE,
+ NFSERR_BADHANDLE, NFSERR_NOT_SYNC, NFSERR_BAD_COOKIE,
+ NFSERR_NOTSUPP, NFSERR_TOOSMALL, NFSERR_SERVERFAULT,
+ NFSERR_BADTYPE, NFSERR_JUKEBOX.
+ (NFSERR_TRYLATER): New macro.
+ * nfs.c (nfs_error_trans): Understand v3 error codes if we are
+ runnnig v3.
+
+Wed Jul 31 13:25:00 1996 Thomas Bushnell, n/BSG <thomas@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_statfs): Use NFSPROC_STATFS, not SETATTR to
+ do a statfs.
+
+Tue Jul 23 19:41:07 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * nfs.c (xdr_encode_sattr_times): `struct timespec' now uses a
+ field prefix of `tv_'.
+
+Wed Jul 17 13:12:31 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * main.c (mounted_soft): Initialize to zero.
+
+Thu Jul 4 17:14:42 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_link) [case SYMLINK]: Include directory
+ handle as an RPC arg.
+
+Wed Jun 26 16:41:00 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (netfs_get_options): New function.
+ (netfs_parse_runtime_options, netfs_unparse_runtime_options):
+ Functions removed.
+ (runtime_argp_parents, runtime_argp, netfs_runtime_argp): New variables.
+ (main): Use &NETFS_STD_STARTUP_ARGP insteda of NETFS_STARTUP_ARGP.
+
+Thu Jun 13 09:24:24 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (SRCS): Remove pager.c.
+ * nfs.h (struct netnode): Add member `fileinfo'.
+ * nfs.h (register_fresh_stat): Add decl.
+
+Wed Jun 12 22:37:31 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (SRCS): Add pager.c.
+
+Wed May 22 18:49:16 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (parse_startup_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL.
+
+Tue May 14 14:00:21 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_unlink): Add new arg in call to
+ netfs_attempt_link.
+
+Sat May 11 01:10:05 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (parse_common_opt): Use ARGP_ERR_UNKNOWN instead of EINVAL.
+
+Fri May 10 18:15:11 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_rename, netfs_attempt_link): New parm EXCL,
+ but don't implement the hard case yet.
+
+Thu May 9 20:24:21 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_attempt_statfs): Expect and fill in new statfs
+ buffer.
+
+Fri Apr 19 13:50:25 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * main.c (main): Failure to bind privileged ports is indicated by
+ EACCES, not EPERM.
+
+Thu Apr 11 13:51:33 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (guess_mode_use): New function.
+ (netfs_check_open_permissions, netfs_report_access): Replace old
+ clever versions with less obtrusive one.
+
+Tue Apr 2 09:12:28 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_report_access): Bother to initialize LEN.
+
+Fri Mar 29 17:26:14 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * rpc.c: Define malloc to something random around include of rpc/*
+ header files to avoid bogus definition there.
+
+Fri Mar 29 17:10:58 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_report_access): Make sure netfs_attempt_read return
+ a reasonable LEN.
+ (netfs_attempt_write): Truncate to THISAMT instead of AMT.
+
+Tue Mar 19 11:00:54 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * Makefile (LCLHDRS): Drop rpc.h.
+
+ * consts.c: Doc fixes.
+ * cache.c: Likewise.
+ * cred.c: Likewise.
+ * main.c: Likewise.
+ * mount.c: Likewise.
+ * mount.h: Likewise.
+ * nfs.c: Likewise.
+ * ops.c: Likewise.
+ * rpc.c: Likewise.
+
+ * rpc.c (rpc_receive_thread): Allocate receive buffer big enough
+ for largest read we expect.
+
+ * cache.c (lookup_fhandle): Correctly install new node in hash
+ table slot.
+
+ * main.c (parse_startup_opt): Pass STATE, not STATE->argp in call
+ to argp_error.
+
+ * cache.c (lookup_fhandle): Initialize NN->dead_dir and
+ NN->dead_name.
+
+ * ops.c: Include <unistd.h>.
+ (register_fresh_stat): Repair syntax.
+
+Mon Mar 18 19:49:28 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main, netfs_parse_runtime_options): Pass new arg to
+ argp_parse.
+
+Mon Mar 18 11:19:27 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (register_fresh_stat): Set fs_fsid, st_fstype, st_gen,
+ st_author, and st_flags here.
+ * nfs.c (xdr_decode_fattr): Don't set st_fstype, st_gen,
+ st_author, or st_flags here.
+
+ * ops.c (netfs_attempt_write): Increment OFFSET each time around
+ the loop.
+
+ * nfs.c (xdr_encode_create_state): Call hurd_mode_to_nfs_mode and
+ htonl on MODE.
+
+ * nfs.c (xdr_encode_sattr_stat): New function.
+
+Thu Mar 14 15:11:41 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * nfs.h (struct netnode): New members `dtrans' and `transarg'.
+ * cache.c (lookup_fhandle): Initialize NN->dtrans.
+ (netfs_node_norefs): Free transarg if necessary.
+ (recache_handle): New function.
+ * ops.c (netfs_attempt_mkfile): Make dtrans possible if it
+ isn't already.
+ (netfs_attempt_unlink): Likewise, when doing the rename hack.
+ (netfs_attempt_mksymlink): Implement using dtrans and transarg.
+ (netfs_attempt_mkdev): Likewise.
+ (register_fresh_stat): If NP->nn->dtrans is set, then mutate the
+ mode here.
+ (netfs_attempt_readlink): If NP->nn->dtrans is SYMLINK, then DTRT.
+ (netfs_attempt_link): Only issue NFSPROC_LINK if dtrans is not
+ operative. Otherwise, DTRT.
+ (netfs_attempt_chmod): Implement type-changing using dtrans.
+
+Tue Mar 12 15:23:32 1996 Michael I. Bushnell, p/BSG <mib@gnu.ai.mit.edu>
+
+ * ops.c (netfs_set_translator, netfs_attempt_mksymlink,
+ netfs_attempt_mkdev): New functions.
+ (netfs_attempt_chmod): Detect attempt to change node type.
+ (netfs_validate_stat): Clear NP->istranslated.
+
+Mon Mar 4 16:16:13 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (main): Use NETFS_STARTUP_ARGP.
+ (netfs_parse_runtime_options, netfs_unparse_runtime_options): New funs.
+
+Wed Feb 28 19:24:23 1996 Miles Bader <miles@gnu.ai.mit.edu>
+
+ * main.c (options): New variable.
+ (main): Parse our arguments.
diff --git a/nfs/Makefile b/nfs/Makefile
new file mode 100644
index 00000000..05fbd90c
--- /dev/null
+++ b/nfs/Makefile
@@ -0,0 +1,32 @@
+#
+# Copyright (C) 1995, 1996, 1997 Free Software Foundation
+# Written by Michael I. Bushnell.
+#
+# This file is part of the GNU Hurd.
+#
+# The GNU Hurd is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 2, or (at
+# your option) any later version.
+#
+# The GNU Hurd is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+
+dir := nfs
+makemode := server
+
+target = nfs
+LCLHDRS = nfs.h mount.h nfs-spec.h
+SRCS = ops.c rpc.c mount.c nfs.c cache.c consts.c main.c name-cache.c
+OBJS = $(subst .c,.o,$(SRCS))
+HURDLIBS=ports netfs fshelp threads iohelp
+
+include ../Makeconf
+
+
diff --git a/nfs/cache.c b/nfs/cache.c
new file mode 100644
index 00000000..183660b0
--- /dev/null
+++ b/nfs/cache.c
@@ -0,0 +1,186 @@
+/* Node cache management for NFS client implementation
+ Copyright (C) 1995, 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+#include <string.h>
+#include <netinet/in.h>
+
+/* Hash table containing all the nodes currently active. */
+#define CACHESIZE 512
+static struct node *nodehash [CACHESIZE];
+
+/* Compute and return a hash key for NFS file handle DATA of LEN bytes. */
+static inline int
+hash (int *data, size_t len)
+{
+ unsigned int h = 0;
+ char *cp = (char *)data;
+ int i;
+
+ for (i = 0; i < len; i++)
+ h += cp[i];
+
+ return h % CACHESIZE;
+}
+
+/* Lookup the file handle P (length LEN) in the hash table. If it is
+ not present, initialize a new node structure and insert it into the
+ hash table. Whichever course, a new reference is generated and the
+ node is returned in *NPP. */
+void
+lookup_fhandle (void *p, size_t len, struct node **npp)
+{
+ struct node *np;
+ struct netnode *nn;
+ int h;
+
+ h = hash (p, len);
+
+ spin_lock (&netfs_node_refcnt_lock);
+ for (np = nodehash[h]; np; np = np->nn->hnext)
+ {
+ if (np->nn->handle.size != len
+ || bcmp (np->nn->handle.data, p, len) != 0)
+ continue;
+
+ np->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+ mutex_lock (&np->lock);
+ *npp = np;
+ return;
+ }
+
+ nn = malloc (sizeof (struct netnode));
+ nn->handle.size = len;
+ bcopy (p, nn->handle.data, len);
+ nn->stat_updated = 0;
+ nn->dtrans = NOT_POSSIBLE;
+ nn->dead_dir = 0;
+ nn->dead_name = 0;
+
+ np = netfs_make_node (nn);
+ mutex_lock (&np->lock);
+ nn->hnext = nodehash[h];
+ if (nn->hnext)
+ nn->hnext->nn->hprevp = &nn->hnext;
+ nn->hprevp = &nodehash[h];
+ nodehash[h] = np;
+
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ *npp = np;
+}
+
+/* Package holding args to forked_node_delete. */
+struct fnd
+{
+ struct node *dir;
+ char *name;
+};
+
+/* Worker function to delete nodes that don't have any more local references
+ or links. */
+any_t
+forked_node_delete (any_t arg)
+{
+ struct fnd *args = arg;
+
+ mutex_lock (&args->dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, args->dir, args->name);
+ netfs_nput (args->dir);
+ free (args->name);
+ free (args);
+ return 0;
+};
+
+/* Called by libnetfs when node NP has no more references. (See
+ <hurd/libnetfs.h> for details. Just clear local state and remove
+ from the hash table. */
+void
+netfs_node_norefs (struct node *np)
+{
+ if (np->nn->dead_dir)
+ {
+ struct fnd *args;
+
+ args = malloc (sizeof (struct fnd));
+
+ np->references++;
+ spin_unlock (&netfs_node_refcnt_lock);
+
+ args->dir = np->nn->dead_dir;
+ args->name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ netfs_nput (np);
+
+ /* Do this in a separate thread so that we don't wait for it;
+ it acquires a lock on the dir, which we are not allowed to do. */
+ cthread_detach (cthread_fork (forked_node_delete, (any_t) args));
+
+ /* Caller expects us to leave this locked... */
+ spin_lock (&netfs_node_refcnt_lock);
+ }
+ else
+ {
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ free (np->nn);
+ free (np);
+ }
+}
+
+/* Change the file handle used for node NP to be the handle at P.
+ Make sure the hash table stays up to date. Return the address
+ after the hnadle. */
+int *
+recache_handle (int *p, struct node *np)
+{
+ int h;
+ size_t len;
+
+ if (protocol_version == 2)
+ len = NFS2_FHSIZE;
+ else
+ len = ntohl (*p++);
+
+ spin_lock (&netfs_node_refcnt_lock);
+ *np->nn->hprevp = np->nn->hnext;
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = np->nn->hprevp;
+
+ np->nn->handle.size = len;
+ bcopy (p, np->nn->handle.data, len);
+
+ h = hash (p, len);
+ np->nn->hnext = nodehash[h];
+ if (np->nn->hnext)
+ np->nn->hnext->nn->hprevp = &np->nn->hnext;
+ np->nn->hprevp = &nodehash[h];
+
+ spin_unlock (&netfs_node_refcnt_lock);
+ return p + len / sizeof (int);
+}
+
+
diff --git a/nfs/consts.c b/nfs/consts.c
new file mode 100644
index 00000000..9146615b
--- /dev/null
+++ b/nfs/consts.c
@@ -0,0 +1,25 @@
+/* Definition of constants required by libnetfs
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+/* Maximum number of times to walk through symlinks before returning
+ ELOOP. See <hurd/libnetfs.h> for details. */
+int netfs_maxsymlinks = 8;
diff --git a/nfs/main.c b/nfs/main.c
new file mode 100644
index 00000000..92a49861
--- /dev/null
+++ b/nfs/main.c
@@ -0,0 +1,403 @@
+/*
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include <hurd/netfs.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <device/device.h>
+#include "nfs.h"
+#include <netinet/in.h>
+#include <unistd.h>
+#include <string.h>
+#include <maptime.h>
+#include <argp.h>
+#include <argz.h>
+#include <error.h>
+
+extern char *localhost ();
+
+/* Default number of times to retry RPCs when mounted soft. */
+#define DEFAULT_SOFT_RETRIES 3
+
+/* Default number of seconds to timeout cached stat information. */
+#define DEFAULT_STAT_TIMEOUT 3
+
+/* Default number of seconds to timeout cached file contents. */
+#define DEFAULT_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache positive dir hits. */
+#define DEFAULT_NAME_CACHE_TIMEOUT 3
+
+/* Default number of seconds to timeout cache negative dir hits. */
+#define DEFAULT_NAME_CACHE_NEG_TIMEOUT 3
+
+/* Default maximum number of bytes to read at once. */
+#define DEFAULT_READ_SIZE 8192
+
+/* Default maximum number of bytes to write at once. */
+#define DEFAULT_WRITE_SIZE 8192
+
+
+/* Number of seconds to timeout cached stat information. */
+int stat_timeout = DEFAULT_STAT_TIMEOUT;
+
+/* Number of seconds to timeout cached file contents. */
+int cache_timeout = DEFAULT_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached positive dir hits. */
+int name_cache_timeout = DEFAULT_NAME_CACHE_TIMEOUT;
+
+/* Number of seconds to timeout cached negative dir hits. */
+int name_cache_neg_timeout = DEFAULT_NAME_CACHE_NEG_TIMEOUT;
+
+/* Number of seconds to wait for first retransmission of an RPC. */
+int initial_transmit_timeout = 1;
+
+/* Maximum number of seconds to wait between retransmission of RPCs. */
+int max_transmit_timeout = 30;
+
+/* Maximum number of retries to send when mounted soft. */
+int soft_retries = DEFAULT_SOFT_RETRIES;
+
+/* True iff we are mounted soft. */
+int mounted_soft = 0;
+
+/* Maximum number of bytes to read at once. */
+int read_size = DEFAULT_READ_SIZE;
+
+/* Maximum number of bytes to write at once. */
+int write_size = DEFAULT_WRITE_SIZE;
+
+#define OPT_SOFT 's'
+#define OPT_HARD 'h'
+#define OPT_RSIZE 'R'
+#define OPT_WSIZE 'W'
+#define OPT_STAT_TO -2
+#define OPT_CACHE_TO -3
+#define OPT_INIT_TR_TO -4
+#define OPT_MAX_TR_TO -5
+#define OPT_MNT_PORT -6
+#define OPT_MNT_PORT_D -7
+#define OPT_NFS_PORT -8
+#define OPT_NFS_PORT_D -9
+#define OPT_HOLD -10
+#define OPT_MNT_PROG -11
+#define OPT_NFS_PROG -12
+#define OPT_PMAP_PORT -13
+#define OPT_NCACHE_TO -14
+#define OPT_NCACHE_NEG_TO -15
+
+/* Return a string corresponding to the printed rep of DEFAULT_what */
+#define ___D(what) #what
+#define __D(what) ___D(what)
+#define _D(what) __D(DEFAULT_ ## what)
+
+/* Options usable both at startup and at runtime. */
+static const struct argp_option common_options[] =
+{
+ {0,0,0,0,0,1},
+ {"soft", OPT_SOFT, "RETRIES", OPTION_ARG_OPTIONAL,
+ "File system requests will eventually fail, after RETRIES tries if"
+ " specified, otherwise " _D(SOFT_RETRIES)},
+ {"hard", OPT_HARD, 0, 0,
+ "Retry file systems requests until they succeed"},
+
+ {0,0,0,0,0,2},
+ {"read-size", OPT_RSIZE, "BYTES", 0,
+ "Max packet size for reads (default " _D(READ_SIZE) ")"},
+ {"rsize",0,0,OPTION_ALIAS},
+ {"write-size", OPT_WSIZE, "BYTES", 0,
+ "Max packet size for writes (default " _D(WRITE_SIZE)")"},
+ {"wsize",0,0,OPTION_ALIAS},
+
+ {0,0,0,0,"Timeouts:",3},
+ {"stat-timeout", OPT_STAT_TO, "SEC", 0,
+ "Timeout for cached stat information (default " _D(STAT_TIMEOUT) ")"},
+ {"cache-timeout", OPT_CACHE_TO, "SEC", 0,
+ "Timeout for cached file data (default " _D(CACHE_TIMEOUT) ")"},
+ {"name-cache-timeout", OPT_NCACHE_TO, "SEC", 0,
+ "Timeout for positive directory cache entries (default "
+ _D(NAME_CACHE_TIMEOUT) ")"},
+ {"name-cache-neg-timeout", OPT_NCACHE_NEG_TO, "SEC", 0,
+ "Timeout for negative directory cache entires (default "
+ _D(NAME_CACHE_NEG_TIMEOUT) ")"},
+ {"init-transmit-timeout", OPT_INIT_TR_TO,"SEC", 0},
+ {"max-transmit-timeout", OPT_MAX_TR_TO, "SEC", 0},
+
+ {0}
+};
+
+static error_t
+parse_common_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_SOFT:
+ mounted_soft = 1;
+ if (arg)
+ soft_retries = atoi (arg);
+ break;
+ case OPT_HARD:
+ mounted_soft = 0;
+ break;
+
+ case OPT_RSIZE: read_size = atoi (arg); break;
+ case OPT_WSIZE: write_size = atoi (arg); break;
+
+ case OPT_STAT_TO: stat_timeout = atoi (arg); break;
+ case OPT_CACHE_TO: cache_timeout = atoi (arg); break;
+ case OPT_INIT_TR_TO: initial_transmit_timeout = atoi (arg); break;
+ case OPT_MAX_TR_TO: max_transmit_timeout = atoi (arg); break;
+ case OPT_NCACHE_TO: name_cache_timeout = atoi (arg); break;
+ case OPT_NCACHE_NEG_TO: name_cache_neg_timeout = atoi (arg); break;
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+/* Options usable only at startup. */
+static const struct argp_option startup_options[] = {
+ {0,0,0,0,"Server specification:",10},
+ {"mount-port", OPT_MNT_PORT, "PORT", 0,
+ "Port for mount server"},
+ {"default-mount-port", OPT_MNT_PORT_D,"PORT", 0,
+ "Port for mount server, if none can be found automatically"},
+ {"mount-program", OPT_MNT_PROG, "ID[.VERS]"},
+
+ {"nfs-port", OPT_NFS_PORT, "PORT", 0,
+ "Port for nfs operations"},
+ {"default-nfs-port", OPT_NFS_PORT_D,"PORT", 0,
+ "Port for nfs operations, if none can be found automatically"},
+ {"nfs-program", OPT_NFS_PROG, "ID[.VERS]"},
+
+ {"pmap-port", OPT_PMAP_PORT, "SVC|PORT"},
+
+ {"hold", OPT_HOLD, 0, OPTION_HIDDEN}, /* */
+ { 0 }
+};
+static char *args_doc = "REMOTE_FS [HOST]";
+static char *doc = "Hurd nfs translator"
+"\vIf HOST is not specified, an attempt is made to extract"
+" it from REMOTE_FS, using either the `HOST:FS' or `FS@HOST' notations.";
+
+static const struct argp_child
+runtime_argp_children[] = { {&netfs_std_runtime_argp}, {0} };
+static struct argp
+runtime_argp = { common_options, parse_common_opt, 0, 0,
+ runtime_argp_children };
+
+/* Use by netfs_set_options to handle runtime option parsing. */
+struct argp *netfs_runtime_argp = &runtime_argp;
+
+/* Where to find the remote filesystem. */
+static char *remote_fs = 0;
+static char *host = 0;
+
+/* Return an argz string describing the current options. Fill *ARGZ
+ with a pointer to newly malloced storage holding the list and *LEN
+ to the length of that storage. */
+error_t
+netfs_append_args (char **argz, size_t *argz_len)
+{
+ char buf[80];
+ error_t err = 0;
+
+#define FOPT(fmt, arg) \
+ do { \
+ if (! err) \
+ { \
+ snprintf (buf, sizeof buf, fmt, arg); \
+ err = argz_add (argz, argz_len, buf); \
+ } \
+ } while (0)
+
+ if (mounted_soft)
+ FOPT ("--soft=%d", soft_retries);
+ else
+ err = argz_add (argz, argz_len, "--hard");
+
+ FOPT ("--read-size=%d", read_size);
+ FOPT ("--write-size=%d", write_size);
+
+ FOPT ("--stat-timeout=%d", stat_timeout);
+ FOPT ("--cache-timeout=%d", cache_timeout);
+ FOPT ("--init-transmit-timeout=%d", initial_transmit_timeout);
+ FOPT ("--max-transmit-timeout=%d", max_transmit_timeout);
+ FOPT ("--name-cache-timeout=%d", name_cache_timeout);
+ FOPT ("--name-cache-neg-timeout=%d", name_cache_neg_timeout);
+
+ if (! err)
+ err = netfs_append_std_options (argz, argz_len);
+
+ if (! err)
+ {
+ char *fs;
+ if (asprintf (&fs, "%s:%s", host, remote_fs))
+ {
+ err = argz_add (argz, argz_len, fs);
+ free (fs);
+ }
+ else
+ err = ENOMEM;
+ }
+
+ return err;
+}
+
+/* Extract the host and remote filesystem names from SPEC, which should use
+ either HOST:FS or FS@HOST notation. Returns the malloced storage into
+ which both REMOTE_FS and HOST point, or 0 if SPEC is invalid. */
+static char *
+extract_nfs_args (char *spec, char **remote_fs, char **host)
+{
+ char *sep;
+
+ spec = strdup (spec); /* So we can trash it. */
+
+ sep = index (spec, ':');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = spec;
+ *remote_fs = sep;
+ return spec;
+ }
+
+ sep = index (spec, '@');
+ if (sep)
+ {
+ *sep++ = '\0';
+ *host = sep;
+ *remote_fs = spec;
+ return spec;
+ }
+
+ free (spec);
+
+ return 0;
+}
+
+static error_t
+parse_startup_opt (int key, char *arg, struct argp_state *state)
+{
+ switch (key)
+ {
+ case OPT_MNT_PORT:
+ mount_port_override = 1;
+ /* fall through */
+ case OPT_MNT_PORT_D:
+ mount_port = atoi (arg);
+ break;
+
+ case OPT_NFS_PORT:
+ nfs_port_override = 1;
+ /* fall through */
+ case OPT_NFS_PORT_D:
+ nfs_port = atoi (arg);
+ break;
+
+ case ARGP_KEY_ARG:
+ if (state->arg_num == 0)
+ remote_fs = arg;
+ else if (state->arg_num == 1)
+ host = arg;
+ else
+ return ARGP_ERR_UNKNOWN;
+ break;
+
+ case ARGP_KEY_END:
+ if (!host && !extract_nfs_args (remote_fs, &remote_fs, &host))
+ argp_error (state, "No HOST specified");
+ break;
+
+ case ARGP_KEY_NO_ARGS:
+ argp_error (state, "No REMOTE_FS specified");
+
+ default:
+ return ARGP_ERR_UNKNOWN;
+ }
+ return 0;
+}
+
+
+/* NFS client main program */
+int
+main (int argc, char **argv)
+{
+ struct argp common_argp = { common_options, parse_common_opt };
+ const struct argp_child argp_children[] =
+ { {&common_argp}, {&netfs_std_startup_argp}, {0} };
+ struct argp argp =
+ { startup_options, parse_startup_opt, args_doc, doc, argp_children };
+ mach_port_t bootstrap;
+ struct sockaddr_in addr;
+ int ret;
+
+ argp_parse (&argp, argc, argv, 0, 0, 0);
+
+ task_get_bootstrap_port (mach_task_self (), &bootstrap);
+ netfs_init ();
+
+ main_udp_socket = socket (PF_INET, SOCK_DGRAM, 0);
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = INADDR_ANY;
+ addr.sin_port = htons (IPPORT_RESERVED);
+ do
+ {
+ addr.sin_port = htons (ntohs (addr.sin_port) - 1);
+ ret = bind (main_udp_socket, (struct sockaddr *)&addr,
+ sizeof (struct sockaddr_in));
+ if (ret == -1 && errno == EACCES)
+ {
+ /* We aren't allowed privileged ports; no matter;
+ let the server deny us later if it wants. */
+ ret = 0;
+ break;
+ }
+ }
+ while ((ret == -1) && (errno == EADDRINUSE));
+ if (ret == -1)
+ {
+ perror ("binding main udp socket");
+ exit (1);
+ }
+
+ errno = maptime_map (0, 0, &mapped_time);
+ if (errno)
+ error (2, errno, "mapping time");
+
+ cthread_detach (cthread_fork ((cthread_fn_t) timeout_service_thread, 0));
+ cthread_detach (cthread_fork ((cthread_fn_t) rpc_receive_thread, 0));
+
+ hostname = localhost ();
+
+ netfs_root_node = mount_root (remote_fs, host);
+
+ if (!netfs_root_node)
+ exit (1);
+
+ netfs_startup (bootstrap, 0);
+
+ for (;;)
+ netfs_server_loop ();
+}
+
diff --git a/nfs/mount.c b/nfs/mount.c
new file mode 100644
index 00000000..92af75f8
--- /dev/null
+++ b/nfs/mount.c
@@ -0,0 +1,220 @@
+/*
+ Copyright (C) 1995, 1996, 1997, 1998 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#define malloc a_byte_for_every_bozotic_sun_lossage_and_youd_need_a_lotta_ram
+#include <rpc/types.h>
+#undef TRUE /* Get rid of sun defs. */
+#undef FALSE
+#undef malloc
+#include <rpc/pmap_prot.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+#include "nfs.h"
+#include "mount.h"
+
+/* Service name for portmapper */
+char *pmap_service_name = "sunrpc";
+
+/* Fallback port number for portmapper */
+short pmap_service_number = PMAPPORT;
+
+/* RPC program for mount server. */
+int mount_program = MOUNTPROG;
+
+/* RPC version for mount server. */
+int mount_version = MOUNTVERS;
+
+/* Fallback port number for mount server. */
+short mount_port = 0;
+
+/* True iff MOUNT_PORT should be used even if portmapper present. */
+int mount_port_override = 0;
+
+/* RPC program number for NFS server. */
+int nfs_program = NFS_PROGRAM;
+
+/* RPC version number for NFS server. */
+int nfs_version = NFS_VERSION;
+
+/* Fallback port number for NFS server. */
+short nfs_port = NFS_PORT;
+
+/* True iff NFS_PORT should be used even if portmapper present. */
+int nfs_port_override = 0;
+
+int protocol_version = 2;
+
+/* Set up an RPC for procedure PROCNUM for talking to the portmapper.
+ Allocate storage with malloc and point *BUF at it; caller must free
+ this when done. Return the address where the args for the
+ procedure should be placed. */
+static int *
+pmap_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (PMAPPROG, PMAPVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Set up an RPC for procedure PROCNUM for talking to the mount
+ server. Allocate storage with malloc and point *BUF at it; caller
+ must free this when done. Return the address where the args for
+ the procedure should be placed. */
+static int *
+mount_initialize_rpc (int procnum, void **buf)
+{
+ return initialize_rpc (MOUNTPROG, MOUNTVERS, procnum, 0, buf, 0, 0, -1);
+}
+
+/* Using the mount protocol, lookup NAME at host HOST.
+ Return a node for it or null for an error. */
+struct node *
+mount_root (char *name, char *host)
+{
+ struct sockaddr_in addr;
+ struct hostent *h;
+ struct servent *s;
+ int *p;
+ void *rpcbuf;
+ int port;
+ struct node *np;
+ short pmapport;
+
+ /* Lookup the portmapper port number */
+ if (pmap_service_name)
+ {
+ s = getservbyname ("sunrpc", pmap_service_name);
+ if (s)
+ pmapport = s->s_port;
+ else
+ pmapport = htons (pmap_service_number);
+ }
+ else
+ pmapport = htons (pmap_service_number);
+
+ /* Lookup the host */
+ h = gethostbyname (host);
+ if (!h)
+ {
+ herror (host);
+ return 0;
+ }
+
+ addr.sin_family = h->h_addrtype;
+ bcopy (h->h_addr_list[0], &addr.sin_addr, h->h_length);
+ addr.sin_port = pmapport;
+
+ connect (main_udp_socket,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr_in));
+
+ if (!mount_port_override)
+ {
+ /* Formulate and send a PMAPPROC_GETPORT request
+ to lookup the mount program on the server. */
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ *p++ = htonl (MOUNTPROG);
+ *p++ = htonl (MOUNTVERS);
+ *p++ = htonl (IPPROTO_UDP);
+ *p++ = htonl (0);
+ errno = conduct_rpc (&rpcbuf, &p);
+ if (!errno)
+ {
+ port = ntohl (*p++);
+ addr.sin_port = htons (port);
+ }
+ else if (mount_port)
+ addr.sin_port = htons (mount_port);
+ else
+ {
+ free (rpcbuf);
+ perror ("portmap of mount");
+ return 0;
+ }
+ free (rpcbuf);
+ }
+ else
+ addr.sin_port = htons (mount_port);
+
+
+ /* Now talking to the mount program, fetch the file handle
+ for the root. */
+ connect (main_udp_socket,
+ (struct sockaddr *) &addr, sizeof (struct sockaddr_in));
+ p = mount_initialize_rpc (MOUNTPROC_MNT, &rpcbuf);
+ p = xdr_encode_string (p, name);
+ errno = conduct_rpc (&rpcbuf, &p);
+ if (errno)
+ {
+ free (rpcbuf);
+ perror (name);
+ return 0;
+ }
+ /* XXX Protocol spec says this should be a "unix error code"; we'll
+ pretend that an NFS error code is what's meant, the numbers match
+ anyhow. */
+ errno = nfs_error_trans (htonl (*p++));
+ if (errno)
+ {
+ free (rpcbuf);
+ perror (name);
+ return 0;
+ }
+
+ /* Create the node for root */
+ xdr_decode_fhandle (p, &np);
+ free (rpcbuf);
+ mutex_unlock (&np->lock);
+
+ if (!nfs_port_override)
+ {
+ /* Now send another PMAPPROC_GETPORT request to lookup the nfs server. */
+ addr.sin_port = pmapport;
+ connect (main_udp_socket,
+ (struct sockaddr *) &addr, sizeof (struct sockaddr_in));
+ p = pmap_initialize_rpc (PMAPPROC_GETPORT, &rpcbuf);
+ *p++ = htonl (NFS_PROGRAM);
+ *p++ = htonl (NFS_VERSION);
+ *p++ = htonl (IPPROTO_UDP);
+ *p++ = htonl (0);
+ errno = conduct_rpc (&rpcbuf, &p);
+ if (!errno)
+ port = ntohl (*p++);
+ else if (nfs_port)
+ port = nfs_port;
+ else
+ {
+ free (rpcbuf);
+ perror ("portmap of nfs server");
+ return 0;
+ }
+ free (rpcbuf);
+ }
+ else
+ port = nfs_port;
+
+ addr.sin_port = htons (port);
+ connect (main_udp_socket,
+ (struct sockaddr *) &addr, sizeof (struct sockaddr_in));
+
+ return np;
+}
diff --git a/nfs/mount.h b/nfs/mount.h
new file mode 100644
index 00000000..fce8ee4c
--- /dev/null
+++ b/nfs/mount.h
@@ -0,0 +1,35 @@
+/* Manifest constants describing the mount protocol
+ Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+/* These constants define the RPC mount protocol; see RFC 1094. */
+
+#define MOUNTPROG 100005
+#define MOUNTVERS 1
+
+/* Obnoxious arbitrary limits */
+#define MOUNT_MNTPATHLEN 1024
+#define MOUNT_MNTNAMLEN 255
+
+#define MOUNTPROC_NULL 0
+#define MOUNTPROC_MNT 1
+#define MOUNTPROC_DUMP 2
+#define MOUNTPROC_UMNT 3
+#define MOUNTPROC_UMNTALL 4
+#define MOUNTPROC_EXPORT 5
diff --git a/nfs/name-cache.c b/nfs/name-cache.c
new file mode 100644
index 00000000..bc5babe6
--- /dev/null
+++ b/nfs/name-cache.c
@@ -0,0 +1,303 @@
+/* Directory name lookup caching
+
+ Copyright (C) 1996, 1997 Free Software Foundation, Inc.
+ Written by Thomas Bushnell, n/BSG, & Miles Bader.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+#include <string.h>
+#include <cacheq.h>
+
+
+/* Maximum number of names to cache at once */
+#define MAXCACHE 200
+
+/* Maximum length of file name we bother caching */
+#define CACHE_NAME_LEN 100
+
+/* Cache entry */
+struct lookup_cache
+{
+ struct cacheq_hdr hdr;
+
+ /* File handles and lengths for cache entries. 0 for NODE_CACHE_LEN
+ means a */
+ char dir_cache_fh[NFS3_FHSIZE];
+ size_t dir_cache_len;
+
+ /* Zero means a `negative' entry -- recording that there's
+ definitely no node with this name. */
+ struct node *np;
+
+ /* Name of the node NODE_CACHE_ID in the directory DIR_CACHE_ID. Entries
+ with names too long to fit in this buffer aren't cached at all. */
+ char name[CACHE_NAME_LEN];
+
+ /* Strlen of NAME. If this is zero, it's an unused entry. */
+ size_t name_len;
+
+ /* Time that this cache entry was created. */
+ time_t cache_stamp;
+
+ /* XXX */
+ int stati;
+};
+
+/* The contents of the cache in no particular order */
+static struct cacheq lookup_cache = { sizeof (struct lookup_cache) };
+
+static spin_lock_t cache_lock = SPIN_LOCK_INITIALIZER;
+
+/* Buffer to hold statistics */
+static struct stats
+{
+ long pos_hits;
+ long neg_hits;
+ long miss;
+ long fetch_errors;
+} statistics;
+
+#define PARTIAL_THRESH 100
+#define NPARTIALS MAXCACHE / PARTIAL_THRESH
+struct stats partial_stats [NPARTIALS];
+
+
+/* If there's an entry for NAME, of length NAME_LEN, in directory DIR in the
+ cache, return its entry, otherwise 0. CACHE_LOCK must be held. */
+static struct lookup_cache *
+find_cache (char *dir, size_t len, const char *name, size_t name_len)
+{
+ struct lookup_cache *c;
+ int i;
+
+ /* Search the list. All unused entries are contiguous at the end of the
+ list, so we can stop searching when we see the first one. */
+ for (i = 0, c = lookup_cache.mru;
+ c && c->name_len;
+ c = c->hdr.next, i++)
+ if (c->name_len == name_len
+ && c->dir_cache_len == len
+ && c->name[0] == name[0]
+ && bcmp (c->dir_cache_fh, dir, len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ c->stati = i / 100;
+ return c;
+ }
+
+ return 0;
+}
+
+/* Node NP has just been found in DIR with NAME. If NP is null, that
+ means that this name has been confirmed as absent in the directory.
+ DIR is the fhandle of the directory; its length is LEN. */
+void
+enter_lookup_cache (char *dir, size_t len, struct node *np, char *name)
+{
+ struct lookup_cache *c;
+ size_t name_len = strlen (name);
+
+ if (name_len > CACHE_NAME_LEN - 1)
+ return;
+
+ spin_lock (&cache_lock);
+
+ if (lookup_cache.length == 0)
+ /* There should always be an lru_cache; this being zero means that the
+ cache hasn't been initialized yet. Do so. */
+ cacheq_set_length (&lookup_cache, MAXCACHE);
+
+ /* See if there's an old entry for NAME in DIR. If not, replace the least
+ recently used entry. */
+ c = find_cache (dir, len, name, name_len) ?: lookup_cache.lru;
+
+ /* Fill C with the new entry. */
+ bcopy (dir, c->dir_cache_fh, len);
+ c->dir_cache_len = len;
+ if (c->np)
+ netfs_nrele (c->np);
+ c->np = np;
+ if (c->np)
+ netfs_nref (c->np);
+ strcpy (c->name, name);
+ c->name_len = name_len;
+ c->cache_stamp = mapped_time->seconds;
+
+ /* Now C becomes the MRU entry! */
+ cacheq_make_mru (&lookup_cache, c);
+
+ spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to NAME within directory DIR. */
+void
+purge_lookup_cache (struct node *dp, char *name, size_t namelen)
+{
+ struct lookup_cache *c, *next;
+
+ spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ /* Save C->hdr.next, since we may move C from this position. */
+ next = c->hdr.next;
+
+ if (c->name_len == namelen
+ && c->dir_cache_len == dp->nn->handle.size
+ && bcmp (c->dir_cache_fh, dp->nn->handle.data, c->dir_cache_len) == 0
+ && strcmp (c->name, name) == 0)
+ {
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c); /* Use C as the next free
+ entry. */
+ }
+ }
+ spin_unlock (&cache_lock);
+}
+
+/* Purge all references in the cache to node NP. */
+void
+purge_lookup_cache_node (struct node *np)
+{
+ struct lookup_cache *c, *next;
+
+ spin_lock (&cache_lock);
+ for (c = lookup_cache.mru; c; c = next)
+ {
+ next = c->hdr.next;
+
+ if (c->np == np)
+ {
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ }
+ }
+ spin_unlock (&cache_lock);
+}
+
+
+
+/* Register a negative hit for an entry in the Nth stat class */
+void
+register_neg_hit (int n)
+{
+ int i;
+
+ statistics.neg_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].neg_hits++;
+}
+
+/* Register a positive hit for an entry in the Nth stat class */
+void
+register_pos_hit (int n)
+{
+ int i;
+
+ statistics.pos_hits++;
+
+ for (i = 0; i < n; i++)
+ partial_stats[i].miss++;
+ for (; i < NPARTIALS; i++)
+ partial_stats[i].pos_hits++;
+}
+
+/* Register a miss */
+void
+register_miss ()
+{
+ int i;
+
+ statistics.miss++;
+ for (i = 0; i < NPARTIALS; i++)
+ partial_stats[i].miss++;
+}
+
+
+
+/* Scan the cache looking for NAME inside DIR. If we don't know
+ anything entry at all, then return 0. If the entry is confirmed to
+ not exist, then return -1. Otherwise, return NP for the entry, with
+ a newly allocated reference. For any return value but 0, unlock
+ DP before returning. */
+struct node *
+check_lookup_cache (struct node *dir, char *name)
+{
+ struct lookup_cache *c;
+
+ spin_lock (&cache_lock);
+
+ c = find_cache (dir->nn->handle.data, dir->nn->handle.size,
+ name, strlen (name));
+ if (c)
+ {
+ int timeout = c->np
+ ? name_cache_timeout
+ : name_cache_neg_timeout;
+
+ /* Make sure the entry is still usable; if not, zap it now. */
+ if (mapped_time->seconds - c->cache_stamp >= timeout)
+ {
+ register_neg_hit (c->stati);
+ if (c->np)
+ netfs_nrele (c->np);
+ c->name_len = 0;
+ c->np = 0;
+ cacheq_make_lru (&lookup_cache, c);
+ spin_unlock (&cache_lock);
+ return 0;
+ }
+
+ cacheq_make_mru (&lookup_cache, c); /* Record C as recently used. */
+
+ if (c->np == 0)
+ /* A negative cache entry. */
+ {
+ register_neg_hit (c->stati);
+ spin_unlock (&cache_lock);
+ mutex_unlock (&dir->lock);
+ return (struct node *)-1;
+ }
+ else
+ {
+ struct node *np;
+
+ np = c->np;
+ netfs_nref (np);
+ register_pos_hit (c->stati);
+ spin_unlock (&cache_lock);
+
+ mutex_unlock (&dir->lock);
+ mutex_lock (&np->lock);
+
+ return np;
+ }
+ }
+
+ register_miss ();
+ spin_unlock (&cache_lock);
+
+ return 0;
+}
diff --git a/nfs/nfs-spec.h b/nfs/nfs-spec.h
new file mode 100644
index 00000000..32f5b09d
--- /dev/null
+++ b/nfs/nfs-spec.h
@@ -0,0 +1,164 @@
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS2_FHSIZE 32
+#define NFS3_FHSIZE 64
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV -1
+#define NFS3_COOKIEVERFSIZE 8
+#define NFS3_CREATEVERFSIZE 8
+#define NFS3_WRITEVERFSIZE 8
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+enum nfsstat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_XDEV = 18, /* v3 only */
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22, /* v3 only */
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_MLINK = 31, /* v3 only */
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_REMOTE = 71, /* v3 only */
+ NFSERR_WFLUSH = 99, /* v2 only */
+ NFSERR_BADHANDLE = 10001, /* v3 only */
+ NFSERR_NOT_SYNC = 10002, /* v3 only */
+ NFSERR_BAD_COOKIE = 10003, /* v3 only */
+ NFSERR_NOTSUPP = 10004, /* v3 only */
+ NFSERR_TOOSMALL = 10005, /* v3 only */
+ NFSERR_SERVERFAULT = 10006, /* v3 only */
+ NFSERR_BADTYPE = 10007, /* v3 only */
+ NFSERR_JUKEBOX = 10008, /* v3 only */
+#define NFSERR_TRYLATER NFSERR_JUKEBOX
+};
+
+
+enum ftype {
+ NF2NON = 0, /* v2 only */
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NF3FIFO = 7, /* v3 only */
+#define NF2BAD NF3FIFO /* v2 only */
+ NF2FIFO = 8, /* v2 only */
+};
+
+/* Ways to set the time in setattr structures */
+enum sattr_time_how
+{
+ DONT_CHANGE = 0,
+ SET_TO_SERVER_TIME = 1,
+ SET_TO_CLIENT_TIME = 2,
+};
+
+/* Construction of ACCESS arg to NFS3PROC_ACCESS. */
+#define ACCESS3_READ 0x01
+#define ACCESS3_LOOKUP 0x02
+#define ACCESS3_MODIFY 0x04
+#define ACCESS3_EXTEND 0x08
+#define ACCESS3_DELETE 0x10
+#define ACCESS3_EXECUTE 0x20
+
+/* STABLE arg to NFS3PROC_READ */
+enum stable_how {
+ UNSTABLE = 0,
+ DATA_SYNC = 1,
+ FILE_SYNC = 2,
+};
+
+/* MODE arg to NFS3PROC_CREATE */
+enum createmode
+{
+ UNCHECKED = 0,
+ GUARDED = 1,
+ EXCLUSIVE = 2,
+};
+
+#define NFS_PROGRAM ((u_long)100003)
+#define NFS_VERSION ((u_long)2)
+
+#define NFS_PROTOCOL_FUNC(proc,vers) \
+ (vers == 2 ? NFS2PROC_ ## proc : NFS3PROC_ ## proc)
+
+#define NFSPROC_NULL(v) NFS_PROTOCOL_FUNC (NULL,v)
+#define NFSPROC_GETATTR(v) NFS_PROTOCOL_FUNC (GETATTR, v)
+#define NFSPROC_SETATTR(v) NFS_PROTOCOL_FUNC (SETATTR, v)
+#define NFSPROC_LOOKUP(v) NFS_PROTOCOL_FUNC (LOOKUP, v)
+#define NFSPROC_READLINK(v) NFS_PROTOCOL_FUNC (READLINK, v)
+#define NFSPROC_READ(v) NFS_PROTOCOL_FUNC (READ, v)
+#define NFSPROC_WRITE(v) NFS_PROTOCOL_FUNC (WRITE, v)
+#define NFSPROC_CREATE(v) NFS_PROTOCOL_FUNC (CREATE, v)
+#define NFSPROC_REMOVE(v) NFS_PROTOCOL_FUNC (REMOVE, v)
+#define NFSPROC_RENAME(v) NFS_PROTOCOL_FUNC (RENAME, v)
+#define NFSPROC_LINK(v) NFS_PROTOCOL_FUNC (LINK, v)
+#define NFSPROC_SYMLINK(v) NFS_PROTOCOL_FUNC (SYMLINK, v)
+#define NFSPROC_MKDIR(v) NFS_PROTOCOL_FUNC (MKDIR, v)
+#define NFSPROC_RMDIR(v) NFS_PROTOCOL_FUNC (RMDIR, v)
+#define NFSPROC_READDIR(v) NFS_PROTOCOL_FUNC (READDIR, v)
+
+/* Values for each protocol */
+#define NFS2PROC_NULL 0
+#define NFS2PROC_GETATTR 1
+#define NFS2PROC_SETATTR 2
+#define NFS2PROC_ROOT 3
+#define NFS2PROC_LOOKUP 4
+#define NFS2PROC_READLINK 5
+#define NFS2PROC_READ 6
+#define NFS2PROC_WRITECACHE 7
+#define NFS2PROC_WRITE 8
+#define NFS2PROC_CREATE 9
+#define NFS2PROC_REMOVE 10
+#define NFS2PROC_RENAME 11
+#define NFS2PROC_LINK 12
+#define NFS2PROC_SYMLINK 13
+#define NFS2PROC_MKDIR 14
+#define NFS2PROC_RMDIR 15
+#define NFS2PROC_READDIR 16
+#define NFS2PROC_STATFS 17
+
+#define NFS3PROC_NULL 0
+#define NFS3PROC_GETATTR 1
+#define NFS3PROC_SETATTR 2
+#define NFS3PROC_LOOKUP 3
+#define NFS3PROC_ACCESS 4
+#define NFS3PROC_READLINK 5
+#define NFS3PROC_READ 6
+#define NFS3PROC_WRITE 7
+#define NFS3PROC_CREATE 8
+#define NFS3PROC_MKDIR 9
+#define NFS3PROC_SYMLINK 10
+#define NFS3PROC_MKNOD 11
+#define NFS3PROC_REMOVE 12
+#define NFS3PROC_RMDIR 13
+#define NFS3PROC_RENAME 14
+#define NFS3PROC_LINK 15
+#define NFS3PROC_READDIR 16
+#define NFS3PROC_READDIRPLUS 17
+#define NFS3PROC_FSSTAT 18
+#define NFS3PROC_FSINFO 19
+#define NFS3PROC_PATHCONF 20
+#define NFS3PROC_COMMIT 21
+
diff --git a/nfs/nfs.c b/nfs/nfs.c
new file mode 100644
index 00000000..46c95c22
--- /dev/null
+++ b/nfs/nfs.c
@@ -0,0 +1,645 @@
+/* XDR frobbing and lower level routines for NFS client
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+#include "nfs.h"
+
+#include <string.h>
+#include <netinet/in.h>
+#include <stdio.h>
+
+/* Convert an NFS mode (TYPE and MODE) to a Hurd mode and return it. */
+mode_t
+nfs_mode_to_hurd_mode (int type, int mode)
+{
+ int hurdmode;
+
+ switch (type)
+ {
+ case NFDIR:
+ hurdmode = S_IFDIR;
+ break;
+
+ case NFCHR:
+ hurdmode = S_IFCHR;
+ break;
+
+ case NFBLK:
+ hurdmode = S_IFBLK;
+ break;
+
+ case NFREG:
+ hurdmode = S_IFREG;
+ break;
+
+ case NFLNK:
+ hurdmode = S_IFLNK;
+ break;
+
+ case NFSOCK:
+ hurdmode = S_IFSOCK;
+ break;
+
+ default:
+ if (protocol_version == 2)
+ switch (type)
+ {
+ case NF2NON:
+ case NF2BAD:
+ default:
+ hurdmode = S_IFREG;
+ break;
+
+ case NF2FIFO:
+ hurdmode = S_IFIFO;
+ break;
+ }
+ else
+ switch (type)
+ {
+ case NF3FIFO:
+ hurdmode = S_IFIFO;
+ break;
+
+ default:
+ hurdmode = S_IFREG;
+ break;
+ }
+ break;
+ }
+
+ hurdmode |= mode & ~NFSMODE_FMT;
+ return hurdmode;
+}
+
+/* Convert a Hurd mode to an NFS mode */
+int
+hurd_mode_to_nfs_mode (mode_t mode)
+{
+ /* This function is used only for chmod; just trim the bits that NFS
+ doesn't support. */
+ return mode & 07777;
+}
+
+/* Convert a Hurd mode to an NFS type */
+int
+hurd_mode_to_nfs_type (mode_t mode)
+{
+ switch (mode & S_IFMT)
+ {
+ case S_IFDIR:
+ return NFDIR;
+
+ case S_IFCHR:
+ default:
+ return NFCHR;
+
+ case S_IFBLK:
+ return NFBLK;
+
+ case S_IFREG:
+ return NFREG;
+
+ case S_IFLNK:
+ return NFLNK;
+
+ case S_IFSOCK:
+ return NFSOCK;
+
+ case S_IFIFO:
+ return protocol_version == 2 ? NF2FIFO : NF3FIFO;
+ }
+}
+
+
+
+/* Each of the functions on this page copies its second arg to *P,
+ converting it to XDR representation along the way. They then
+ return the address after the copied value. */
+
+/* Encode an NFS file handle. */
+int *
+xdr_encode_fhandle (int *p, struct fhandle *fhandle)
+{
+ if (protocol_version == 2)
+ {
+ bcopy (fhandle->data, p, NFS2_FHSIZE);
+ return p + INTSIZE (NFS2_FHSIZE);
+ }
+ else
+ return xdr_encode_data (p, fhandle->data, fhandle->size);
+}
+
+/* Encode uninterpreted bytes. */
+int *
+xdr_encode_data (int *p, char *data, size_t len)
+{
+ int nints = INTSIZE (len);
+
+ p[nints] = 0;
+ *p++ = htonl (len);
+ bcopy (data, p, len);
+ return p + nints;
+}
+
+/* Encode a 64 bit integer */
+int *
+xdr_encode_64bit (int *p, long long n)
+{
+ *p++ = htonl (n & 0xffffffff00000000LL >> 32);
+ *p++ = htonl (n & 0xffffffff);
+ return p;
+}
+
+/* Encode a C string. */
+int *
+xdr_encode_string (int *p, char *string)
+{
+ return xdr_encode_data (p, string, strlen (string));
+}
+
+/* Encode a MODE into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_mode (int *p, mode_t mode)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = -1; /* size */
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime usecs */
+ }
+ else
+ {
+ *p++ = htonl (1); /* set mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = 0; /* no size */
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode UID and GID into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_ids (int *p, u_int uid, u_int gid)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = htonl (uid);
+ *p++ = htonl (gid);
+ *p++ = -1; /* size */
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime usecs */
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (uid);
+ *p++ = htonl (1); /* set gid */
+ *p++ = htonl (gid);
+ *p++ = 0; /* no size */
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode a file size into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_size (int *p, off_t size)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = htonl (size);
+ *p++ = -1; /* atime secs */
+ *p++ = -1; /* atime usecs */
+ *p++ = -1; /* mtime secs */
+ *p++ = -1; /* mtime secs */
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = htonl (1); /* size */
+ p = xdr_encode_64bit (p, size);
+ *p++ = DONT_CHANGE; /* no atime */
+ *p++ = DONT_CHANGE; /* no mtime */
+ }
+ return p;
+}
+
+/* Encode ATIME and MTIME into an otherwise empty sattr. */
+int *
+xdr_encode_sattr_times (int *p, struct timespec *atime, struct timespec *mtime)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = -1; /* mode */
+ *p++ = -1; /* uid */
+ *p++ = -1; /* gid */
+ *p++ = -1; /* size */
+ *p++ = htonl (atime->tv_sec);
+ *p++ = htonl (atime->tv_nsec / 1000);
+ *p++ = htonl (mtime->tv_sec);
+ *p++ = htonl (mtime->tv_nsec / 1000);
+ }
+ else
+ {
+ *p++ = 0; /* no mode */
+ *p++ = 0; /* no uid */
+ *p++ = 0; /* no gid */
+ *p++ = 0; /* no size */
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* atime */
+ *p++ = htonl (atime->tv_sec);
+ *p++ = htonl (atime->tv_nsec);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* mtime */
+ *p++ = htonl (mtime->tv_sec);
+ *p++ = htonl (mtime->tv_nsec);
+ }
+ return p;
+}
+
+/* Encode MODE, a size of zero, and the specified owner into an otherwise
+ empty sattr. */
+int *
+xdr_encode_create_state (int *p,
+ mode_t mode,
+ uid_t owner)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = htonl (owner); /* uid */
+ *p++ = -1; /* gid */
+ *p++ = 0; /* size */
+ *p++ = -1; /* atime sec */
+ *p++ = -1; /* atime usec */
+ *p++ = -1; /* mtime sec */
+ *p++ = -1; /* mtime usec */
+ }
+ else
+ {
+ *p++ = htonl (1); /* mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (mode));
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (owner);
+ *p++ = 0; /* no gid */
+ *p++ = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, 0);
+ *p++ = htonl (SET_TO_SERVER_TIME); /* atime */
+ *p++ = htonl (SET_TO_SERVER_TIME); /* mtime */
+ }
+ return p;
+}
+
+/* Encode ST into an sattr. */
+int *
+xdr_encode_sattr_stat (int *p,
+ struct stat *st)
+{
+ if (protocol_version == 2)
+ {
+ *p++ = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *p++ = htonl (st->st_uid);
+ *p++ = htonl (st->st_gid);
+ *p++ = htonl (st->st_size);
+ *p++ = htonl (st->st_atime);
+ *p++ = htonl (st->st_atime_usec);
+ *p++ = htonl (st->st_mtime);
+ *p++ = htonl (st->st_mtime_usec);
+ }
+ else
+ {
+ *p++ = htonl (1); /* set mode */
+ *p++ = htonl (hurd_mode_to_nfs_mode (st->st_mode));
+ *p++ = htonl (1); /* set uid */
+ *p++ = htonl (st->st_uid);
+ *p++ = htonl (1); /* set gid */
+ *p++ = htonl (st->st_gid);
+ *p++ = htonl (1); /* set size */
+ p = xdr_encode_64bit (p, st->st_size);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* set atime */
+ *p++ = htonl (st->st_atime);
+ *p++ = htonl (st->st_atime_usec * 1000);
+ *p++ = htonl (SET_TO_CLIENT_TIME); /* set mtime */
+ *p++ = htonl (st->st_mtime);
+ *p++ = htonl (st->st_mtime_usec * 1000);
+ }
+ return p;
+}
+
+
+/* Decode *P into a long long; return the address of the following data. */
+int *
+xdr_decode_64bit (int *p, long long *n)
+{
+ long long high, low;
+ high = ntohl (*p++);
+ low = ntohl (*p++);
+ *n = ((high & 0xffffffff) << 32) | (low & 0xffffffff);
+ return p;
+}
+
+/* Decode *P into an fhandle and look up the associated node. Return
+ the address of the following data. */
+int *
+xdr_decode_fhandle (int *p, struct node **npp)
+{
+ size_t len;
+
+ len = protocol_version == 2 ? NFS2_FHSIZE : ntohl (*p++);
+ lookup_fhandle (p, len, npp);
+ return p + len / sizeof (int);
+}
+
+/* Decode *P into a stat structure; return the address of the
+ following data. */
+int *
+xdr_decode_fattr (int *p, struct stat *st)
+{
+ int type, mode;
+
+ type = ntohl (*p++);
+ mode = ntohl (*p++);
+ st->st_mode = nfs_mode_to_hurd_mode (type, mode);
+ st->st_nlink = ntohl (*p++);
+ st->st_uid = ntohl (*p++);
+ st->st_gid = ntohl (*p++);
+ if (protocol_version == 2)
+ {
+ st->st_size = ntohl (*p++);
+ st->st_blksize = ntohl (*p++);
+ st->st_rdev = ntohl (*p++);
+ st->st_blocks = ntohl (*p++);
+ }
+ else
+ {
+ long long size;
+ int major, minor;
+ p = xdr_decode_64bit (p, &size);
+ st->st_size = size;
+ p = xdr_decode_64bit (p, &size);
+ st->st_blocks = size / 512;
+ st->st_blksize = read_size < write_size ? read_size : write_size;
+ major = ntohl (*p++);
+ minor = ntohl (*p++);
+ st->st_rdev = makedev (major, minor);
+ }
+ st->st_fsid = ntohl (*p++);
+ st->st_ino = ntohl (*p++);
+ st->st_atime = ntohl (*p++);
+ st->st_atime_usec = ntohl (*p++);
+ st->st_mtime = ntohl (*p++);
+ st->st_mtime_usec = ntohl (*p++);
+ st->st_ctime = ntohl (*p++);
+ st->st_ctime_usec = ntohl (*p++);
+
+ if (protocol_version == 3)
+ {
+ st->st_atime_usec /= 1000;
+ st->st_mtime_usec /= 1000;
+ st->st_ctime_usec /= 1000;
+ }
+
+ return p;
+
+}
+
+/* Decode *P into a string, stored at BUF; return the address
+ of the following data. */
+int *
+xdr_decode_string (int *p, char *buf)
+{
+ int len;
+
+ len = ntohl (*p++);
+ bcopy (p, buf, len);
+ buf[len] = '\0';
+ return p + INTSIZE (len);
+}
+
+
+/* Set up an RPC for procedure RPC_PROC for talking to the NFS server.
+ Allocate storage with malloc and point *BUFP at it; caller must
+ free this when done. Initialize RPC credential information with
+ information from CRED (identifying the user making this call; -1
+ means superuser), NP (identifying the node we are operating on), and
+ SECOND_GID (specifying another GID the server might be interested
+ in). Allocate at least LEN bytes of space for bulk data in
+ addition to the normal amount for an RPC. */
+int *
+nfs_initialize_rpc (int rpc_proc, struct iouser *cred,
+ size_t len, void **bufp, struct node *np,
+ uid_t second_gid)
+{
+ uid_t uid;
+ uid_t gid;
+ error_t err;
+
+ /* Use heuristics to figure out what ids to present to the server.
+ Don't lie, but adjust ids as necessary to secure the desired result. */
+
+ if (cred == (struct iouser *) -1)
+ {
+ uid = gid = 0;
+ second_gid = -1;
+ }
+ else if (cred
+ && (cred->uids->num || cred->gids->num))
+ {
+ if (idvec_contains (cred->uids, 0))
+ {
+ err = netfs_validate_stat (np, 0);
+ uid = 0;
+ gid = err ? -2 : 0;
+ if (err)
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (cred->uids->num == 0)
+ uid = -2;
+ else if (cred->uids->num == 1)
+ uid = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ uid = cred->uids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->uids, np->nn_stat.st_uid))
+ uid = np->nn_stat.st_uid;
+ else
+ uid = cred->uids->ids[0];
+ }
+ }
+
+ if (cred->gids->num == 0)
+ {
+ gid = -2;
+ second_gid = -1;
+ }
+ else if (cred->gids->num == 1)
+ {
+ gid = cred->gids->ids[0];
+ second_gid = -1;
+ }
+ else
+ {
+ err = netfs_validate_stat (np, 0);
+ if (err)
+ {
+ gid = cred->gids->ids[0];
+ printf ("NFS warning, internal stat failure\n");
+ }
+ else
+ {
+ if (idvec_contains (cred->gids, np->nn_stat.st_gid))
+ gid = np->nn_stat.st_gid;
+ else
+ gid = cred->gids->ids[0];
+ }
+ if (second_gid != -1
+ && !idvec_contains (cred->gids, second_gid))
+ second_gid = -1;
+ }
+ }
+ }
+ else
+ uid = gid = second_gid = -1;
+
+ return initialize_rpc (NFS_PROGRAM, NFS_VERSION, rpc_proc, len, bufp,
+ uid, gid, second_gid);
+}
+
+/* ERROR is an NFS error code; return the correspending Hurd error. */
+error_t
+nfs_error_trans (int error)
+{
+ switch (error)
+ {
+ case NFS_OK:
+ return 0;
+
+ case NFSERR_PERM:
+ return EPERM;
+
+ case NFSERR_NOENT:
+ return ENOENT;
+
+ case NFSERR_IO:
+ return EIO;
+
+ case NFSERR_NXIO:
+ return ENXIO;
+
+ case NFSERR_ACCES:
+ return EACCES;
+
+ case NFSERR_EXIST:
+ return EEXIST;
+
+ case NFSERR_NODEV:
+ return ENODEV;
+
+ case NFSERR_NOTDIR:
+ return ENOTDIR;
+
+ case NFSERR_ISDIR:
+ return EISDIR;
+
+ case NFSERR_FBIG:
+ return E2BIG;
+
+ case NFSERR_NOSPC:
+ return ENOSPC;
+
+ case NFSERR_ROFS:
+ return EROFS;
+
+ case NFSERR_NAMETOOLONG:
+ return ENAMETOOLONG;
+
+ case NFSERR_NOTEMPTY:
+ return ENOTEMPTY;
+
+ case NFSERR_DQUOT:
+ return EDQUOT;
+
+ case NFSERR_STALE:
+ return ESTALE;
+
+ case NFSERR_WFLUSH:
+ /* Not known in v3, but we just give EINVAL for unknown errors
+ so it's the same. */
+ return EINVAL;
+
+ default:
+ if (protocol_version == 2)
+ return EINVAL;
+ else
+ switch (error)
+ {
+ case NFSERR_XDEV:
+ return EXDEV;
+
+ case NFSERR_INVAL:
+ case NFSERR_REMOTE: /* not sure about this one */
+ default:
+ return EINVAL;
+
+ case NFSERR_MLINK:
+ return EMLINK;
+
+ case NFSERR_NOTSUPP:
+ case NFSERR_BADTYPE:
+ return EOPNOTSUPP;
+
+ case NFSERR_SERVERFAULT:
+ return EIO;
+
+ case NFSERR_BADHANDLE:
+ case NFSERR_NOT_SYNC:
+ case NFSERR_BAD_COOKIE:
+ case NFSERR_TOOSMALL:
+ case NFSERR_JUKEBOX: /* ??? */
+ /* These indicate bugs in the client, so EGRATUITOUS is right. */
+ return EGRATUITOUS;
+ }
+ }
+}
diff --git a/nfs/nfs.h b/nfs/nfs.h
new file mode 100644
index 00000000..2c32ff9b
--- /dev/null
+++ b/nfs/nfs.h
@@ -0,0 +1,195 @@
+/* Data structures and global variables for NFS client
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include "nfs-spec.h"
+#include <hurd/netfs.h>
+
+/* A file handle */
+struct fhandle
+{
+ size_t size;
+
+ /* Leave enough room for the biggest possible fhandle. */
+ char data[NFS3_FHSIZE];
+};
+
+/* One of these exists for private data needed by the client for each
+ node. */
+struct netnode
+{
+ struct fhandle handle;
+ time_t stat_updated;
+ struct node *hnext, **hprevp;
+
+ /* These two fields handle translators set internally but
+ unknown to the server. */
+ enum
+ {
+ NOT_POSSIBLE,
+ POSSIBLE,
+ SYMLINK,
+ CHRDEV,
+ BLKDEV,
+ FIFO,
+ SOCK,
+ } dtrans;
+ union
+ {
+ char *name;
+ dev_t indexes;
+ } transarg;
+
+#ifdef notyet
+ /* This indicates that the length of the file must be at
+ least this big because we've written this much locally,
+ even if the server thinks we haven't gone this far. */
+ off_t extend_len;
+#endif
+
+ struct user_pager_info *fileinfo;
+
+ /* If this node has been renamed by "deletion" then
+ this is the directory and name in that directory which
+ is holding the node */
+ struct node *dead_dir;
+ char *dead_name;
+};
+
+/* Socket file descriptor for talking to RPC servers. */
+int main_udp_socket;
+
+/* Our hostname */
+char *hostname;
+
+/* The current time */
+volatile struct mapped_time_value *mapped_time;
+
+/* Some tunable parameters */
+
+/* How long to keep around stat information */
+extern int stat_timeout;
+
+/* How long to keep around file contents caches */
+extern int cache_timeout;
+
+/* How long to keep around positive dir cache entries */
+extern int name_cache_timeout;
+
+/* How long to keep around negative dir cache entries */
+extern int name_cache_neg_timeout;
+
+/* How long to wait for replies before re-sending RPC's. */
+extern int initial_transmit_timeout;
+extern int max_transmit_timeout;
+
+/* How many attempts to send before giving up on soft mounts */
+extern int soft_retries;
+
+/* Whether we are a hard or soft mount */
+extern int mounted_soft;
+
+/* Maximum amount to read at once */
+extern int read_size;
+
+/* Maximum amout to write at once */
+extern int write_size;
+
+/* Service name for portmapper */
+extern char *pmap_service_name;
+
+/* If pmap_service is null, then this is the port to use for the portmapper;
+ if the lookup of pmap_service_name fails, use this number. */
+extern short pmap_service_number;
+
+/* RPC program for the mount agent */
+extern int mount_program;
+
+/* RPC program version for the mount agent */
+extern int mount_version;
+
+/* If this is nonzero, it's the port to use for the mount agent if
+ the portmapper fails or can't be found. */
+extern short mount_port;
+
+/* If this is nonzero use mount_port without attempting to contact
+ the portmapper */
+extern int mount_port_override;
+
+/* RPC program for the NFS server */
+extern int nfs_program;
+
+/* RPC program version for the NFS server */
+extern int nfs_version;
+
+/* If this is nonzero, it's the port to be used to find the nfs agent
+ if the portmapper fails or can't be found */
+extern short nfs_port;
+
+/* If this is nonzero use nfs_port without attepting to contact the
+ portmapper. */
+extern int nfs_port_override;
+
+/* Which NFS protocol version we are using */
+extern int protocol_version;
+
+
+/* Count how many four-byte chunks it takss to hold LEN bytes. */
+#define INTSIZE(len) (((len)+3)>>2)
+
+
+/* nfs.c */
+int hurd_mode_to_nfs_type (mode_t);
+int *xdr_encode_fhandle (int *, struct fhandle *);
+int *xdr_encode_data (int *, char *, size_t);
+int *xdr_encode_string (int *, char *);
+int *xdr_encode_sattr_mode (int *, mode_t);
+int *xdr_encode_sattr_ids (int *, u_int, u_int);
+int *xdr_encode_sattr_size (int *, off_t);
+int *xdr_encode_sattr_times (int *, struct timespec *, struct timespec *);
+int *xdr_encode_sattr_stat (int *, struct stat *);
+int *xdr_encode_create_state (int *, mode_t, uid_t);
+int *xdr_decode_fattr (int *, struct stat *);
+int *xdr_decode_string (int *, char *);
+int *xdr_decode_fhandle (int *, struct node **);
+int *nfs_initialize_rpc (int, struct iouser *, size_t, void **,
+ struct node *, uid_t);
+error_t nfs_error_trans (int);
+
+/* mount.c */
+struct node *mount_root (char *, char *);
+
+/* ops.c */
+int *register_fresh_stat (struct node *, int *);
+
+/* rpc.c */
+int *initialize_rpc (int, int, int, size_t, void **, uid_t, gid_t, gid_t);
+error_t conduct_rpc (void **, int **);
+void timeout_service_thread (void);
+void rpc_receive_thread (void);
+
+/* cache.c */
+void lookup_fhandle (void *, size_t, struct node **);
+int *recache_handle (int *, struct node *);
+
+/* name-cache.c */
+void enter_lookup_cache (char *, size_t, struct node *, char *);
+void purge_lookup_cache (struct node *, char *, size_t);
+struct node *check_lookup_cache (struct node *, char *);
+void purge_lookup_cache_node (struct node *);
diff --git a/nfs/ops.c b/nfs/ops.c
new file mode 100644
index 00000000..c8e7615b
--- /dev/null
+++ b/nfs/ops.c
@@ -0,0 +1,1757 @@
+/* Libnetfs callbacks for node operations in NFS client
+ Copyright (C) 1994, 1995, 1996, 1997, 1999 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "nfs.h"
+#include <hurd/netfs.h>
+#include <netinet/in.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <dirent.h>
+#include <unistd.h>
+#include <maptime.h>
+
+/* We have fresh stat information for NP; the fattr structure is at
+ P. Update our entry. Return the address of the next int after
+ the fattr structure. */
+int *
+register_fresh_stat (struct node *np, int *p)
+{
+ int *ret;
+
+ ret = xdr_decode_fattr (p, &np->nn_stat);
+ np->nn->stat_updated = mapped_time->seconds;
+
+ switch (np->nn->dtrans)
+ {
+ case NOT_POSSIBLE:
+ case POSSIBLE:
+ break;
+
+ case SYMLINK:
+ np->nn_stat.st_size = strlen (np->nn->transarg.name);
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFLNK);
+ break;
+
+ case CHRDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFCHR);
+ break;
+
+ case BLKDEV:
+ np->nn_stat.st_rdev = np->nn->transarg.indexes;
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFBLK);
+ break;
+
+ case FIFO:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFIFO);
+ break;
+
+ case SOCK:
+ np->nn_stat.st_mode = ((np->nn_stat.st_mode & ~S_IFMT) | S_IFSOCK);
+ break;
+ }
+
+ np->nn_stat.st_fsid = getpid ();
+ np->nn_stat.st_fstype = FSTYPE_NFS;
+ np->nn_stat.st_gen = 0;
+ np->nn_stat.st_author = np->nn_stat.st_uid;
+ np->nn_stat.st_flags = 0;
+
+ return ret;
+}
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it
+ checks to see if stat information is present too. If this follows
+ an operation that we expect has modified the attributes, MOD should
+ be set. (This unpacks the post_op_attr XDR type.) */
+int *
+process_returned_stat (struct node *np, int *p, int mod)
+{
+ int attrs_exist;
+
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ attrs_exist = ntohl (*p++);
+ if (attrs_exist)
+ p = register_fresh_stat (np, p);
+ else if (mod)
+ /* We know that our values are now wrong */
+ np->nn->stat_updated = 0;
+ return p;
+ }
+}
+
+
+/* Handle returned wcc information for various calls. In protocol
+ version 2, this is just register_fresh_stat. In version 3, it does
+ the wcc_data interpretation too. If this follows an operation that
+ we expect has modified the attributes, MOD should be set.
+ (This unpacks the wcc_data XDR type.) */
+int *
+process_wcc_stat (struct node *np, int *p, int mod)
+{
+ if (protocol_version == 2)
+ return register_fresh_stat (np, p);
+ else
+ {
+ int attrs_exist;
+
+ /* First the pre_op_attr */
+ attrs_exist = ntohl (*p++);
+ if (attrs_exist)
+ {
+ /* Just skip them for now */
+ p += 2 * sizeof (int); /* size */
+ p += 2 * sizeof (int); /* mtime */
+ p += 2 * sizeof (int); /* atime */
+ }
+
+ /* Now the post_op_attr */
+ return process_returned_stat (np, p, mod);
+ }
+}
+
+
+/* Implement the netfs_validate_stat callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_validate_stat (struct node *np, struct iouser *cred)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (mapped_time->seconds - np->nn->stat_updated < stat_timeout)
+ return 0;
+
+ p = nfs_initialize_rpc (NFSPROC_GETATTR (protocol_version),
+ (struct iouser *) -1, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ register_fresh_stat (np, p);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chown callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chown (struct iouser *cred, struct node *np,
+ uid_t uid, gid_t gid)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, gid);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_ids (p, uid, gid);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_chauthor callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chauthor (struct iouser *cred, struct node *rp,
+ uid_t author)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_chmod callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chmod (struct iouser *cred, struct node *np,
+ mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if ((mode & S_IFMT) != 0)
+ {
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+ if ((mode & S_IFMT) != (np->nn_stat.st_mode & S_IFMT))
+ {
+ char *f = 0;
+
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ f = np->nn->transarg.name;
+
+ switch (mode & S_IFMT)
+ {
+ default:
+ return EOPNOTSUPP;
+
+ case S_IFIFO:
+ np->nn->dtrans = FIFO;
+ np->nn->stat_updated = 0;
+ break;
+
+ case S_IFSOCK:
+ np->nn->dtrans = SOCK;
+ np->nn->stat_updated = 0;
+ }
+ if (f)
+ free (f);
+ return 0;
+ }
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_mode (p, mode);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_chflags callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_chflags (struct iouser *cred, struct node *np,
+ int flags)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_utimes callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_utimes (struct iouser *cred, struct node *np,
+ struct timespec *atime, struct timespec *mtime)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct timeval tv;
+ struct timespec current;
+
+ /* XXX For version 3 we can actually do this right, but we don't
+ just yet. */
+ if (!atime || !mtime)
+ {
+ maptime_read (mapped_time, &tv);
+ current.tv_sec = tv.tv_sec;
+ current.tv_nsec = tv.tv_usec * 1000;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_times (p,
+ atime ?: &current,
+ mtime ?: &current);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_set_size callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_set_size (struct iouser *cred, struct node *np,
+ off_t size)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFSPROC_SETATTR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_sattr_size (p, size);
+ if (protocol_version == 3)
+ *p++ = 0; /* guard_check == 0 */
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ /* If we got EACCES, but the user has the file open for writing,
+ then the NFS protocol has screwed us. There's nothing we can do,
+ except in the important case of opens with
+ O_TRUNC|O_CREAT|O_WRONLY|O_EXCL where the new mode does not allow
+ writing. RCS, for example, uses this to create lock files. So permit
+ cases where the O_TRUNC isn't doing anything to succeed if the user
+ does have the file open for writing. */
+ if (err == EACCES)
+ {
+ err = netfs_validate_stat (np, cred);
+ if (!err && np->nn_stat.st_size == size)
+ err = 0;
+ else
+ /* Never mind, put the old error back */
+ err = EACCES;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_statfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_statfs (struct iouser *cred, struct node *np,
+ struct statfs *st)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ p = nfs_initialize_rpc (NFS2PROC_STATFS, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ {
+ p++; /* skip IOSIZE field */
+ st->f_bsize = ntohl (*p++);
+ st->f_blocks = ntohl (*p++);
+ st->f_bfree = ntohl (*p++);
+ st->f_bavail = ntohl (*p++);
+ st->f_type = FSTYPE_NFS;
+ st->f_files = 0;
+ st->f_ffree = 0;
+ st->f_fsid = getpid ();
+ st->f_namelen = 0;
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_sync callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_sync (struct iouser *cred, struct node *np, int wait)
+{
+ /* We are already completely synchronous. */
+ return 0;
+}
+
+/* Implement the netfs_attempt_syncfs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_syncfs (struct iouser *cred, int wait)
+{
+ return 0;
+}
+
+/* Implement the netfs_attempt_read callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_read (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ size_t trans_len;
+ error_t err;
+ size_t amt, thisamt;
+ int eof;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > read_size)
+ thisamt = read_size;
+
+ p = nfs_initialize_rpc (NFSPROC_READ (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (offset);
+ *p++ = htonl (thisamt);
+ if (protocol_version == 2)
+ *p++ = 0;
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err || protocol_version == 3)
+ p = process_returned_stat (np, p, !err);
+
+ if (err)
+ {
+ free (rpcbuf);
+ return err;
+ }
+
+ trans_len = ntohl (*p++);
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ if (protocol_version == 3)
+ eof = ntohl (*p++);
+ else
+ eof = (trans_len < thisamt);
+
+ bcopy (p, data, trans_len);
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ if (eof)
+ {
+ *len -= amt;
+ return 0;
+ }
+ }
+ }
+ return 0;
+}
+
+/* Implement the netfs_attempt_write callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_write (struct iouser *cred, struct node *np,
+ off_t offset, size_t *len, void *data)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+ size_t count;
+
+ for (amt = *len; amt;)
+ {
+ thisamt = amt;
+ if (thisamt > write_size)
+ thisamt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE (protocol_version),
+ cred, thisamt, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ if (protocol_version == 2)
+ *p++ = 0;
+ *p++ = htonl (offset);
+ if (protocol_version == 2)
+ *p++ = 0;
+ if (protocol_version == 3)
+ *p++ = htonl (FILE_SYNC);
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err || protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ if (!err)
+ {
+ if (protocol_version == 3)
+ {
+ count = ntohl (*p++);
+ p++; /* ignore COMMITTED */
+ /* ignore verf for now */
+ p += NFS3_WRITEVERFSIZE / sizeof (int);
+ }
+ else
+ /* assume it wrote the whole thing */
+ count = thisamt;
+
+ free (rpcbuf);
+ amt -= count;
+ data += count;
+ offset += count;
+ }
+ }
+
+ if (err == EINTR && amt != *len)
+ {
+ *len -= amt;
+ free (rpcbuf);
+ return 0;
+ }
+
+ if (err)
+ {
+ *len = 0;
+ free (rpcbuf);
+ return err;
+ }
+ }
+ return 0;
+}
+
+/* See if NAME exists in DIR for CRED. If so, return EEXIST. */
+error_t
+verify_nonexistent (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Don't use the lookup cache for this; we want a full sync to
+ get as close to real exclusive create behavior as possible. */
+
+ assert (protocol_version == 2);
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (!err)
+ return EEXIST;
+ else
+ return 0;
+}
+
+/* Implement the netfs_attempt_lookup callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_lookup (struct iouser *cred, struct node *np,
+ char *name, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ char dirhandle[NFS3_FHSIZE];
+ size_t dirlen;
+
+ /* Check the cache first. */
+ *newnp = check_lookup_cache (np, name);
+ if (*newnp)
+ {
+ if (*newnp == (struct node *) -1)
+ {
+ *newnp = 0;
+ return ENOENT;
+ }
+ else
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ /* Remember the directory handle for later cache use. */
+
+ dirlen = np->nn->handle.size;
+ bcopy (np->nn->handle.data, dirhandle, dirlen);
+
+ mutex_unlock (&np->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ mutex_unlock (&(*newnp)->lock);
+ mutex_lock (&np->lock);
+ p = process_returned_stat (np, p, 0);
+ mutex_unlock (&np->lock);
+ if (*newnp)
+ mutex_lock (&(*newnp)->lock);
+ }
+ }
+ else
+ *newnp = 0;
+
+ /* Notify the cache of the hit or miss. */
+ enter_lookup_cache (dirhandle, dirlen, *newnp, name);
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_mkdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdir (struct iouser *cred, struct node *np,
+ char *name, mode_t mode)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+ struct node *newnp;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_MKDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+
+ p = xdr_decode_fhandle (p, &newnp);
+ p = process_returned_stat (newnp, p, 1);
+
+ /* Did we set the owner correctly? If not, try, but ignore failures. */
+ if (!netfs_validate_stat (newnp, -1) && newnp->nn_stat.st_uid != owner)
+ netfs_attempt_chown (-1, newnp, owner, newnp->nn_stat.st_gid);
+
+ /* We don't actually return this. */
+ netfs_nput (newnp);
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_rmdir callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rmdir (struct iouser *cred, struct node *np,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ /* Should we do the same sort of thing here as with attempt_unlink? */
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_RMDIR (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_wcc_stat (np, p, !err);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_link callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_link (struct iouser *cred, struct node *dir,
+ struct node *np, char *name, int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err = 0;
+
+ if (!excl)
+ {
+ /* We have no RPC available that will do an atomic replacement,
+ so we settle for second best; just doing an unlink and ignoring
+ any errors. */
+ mutex_lock (&dir->lock);
+ netfs_attempt_unlink (cred, dir, name);
+ mutex_unlock (&dir->lock);
+ }
+
+ /* If we have postponed a translator setting on an unlinked node,
+ then here's where we set it, by creating the new node instead of
+ doing a normal link. */
+
+ switch (np->nn->dtrans)
+ {
+ case POSSIBLE:
+ case NOT_POSSIBLE:
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_LINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ mutex_unlock (&np->lock);
+
+ mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+
+ break;
+
+ case SYMLINK:
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFSPROC_SYMLINK (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ mutex_unlock (&dir->lock);
+
+ p = xdr_encode_string (p, name);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ if (protocol_version == 2)
+ {
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ }
+ else
+ {
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ p = xdr_encode_string (p, np->nn->transarg.name);
+ }
+ mutex_unlock (&np->lock);
+
+ mutex_lock (&dir->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+
+ if (protocol_version == 2 && !err)
+ {
+ free (rpcbuf);
+
+ /* NFSPROC_SYMLINK stupidly does not pass back an
+ fhandle, so we have to fetch one now. */
+ p = nfs_initialize_rpc (NFSPROC_LOOKUP (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ mutex_unlock (&dir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ }
+ if (err)
+ err = EGRATUITOUS; /* damn */
+ }
+ else if (protocol_version == 3)
+ {
+ if (!err)
+ {
+ mutex_unlock (&dir->lock);
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ mutex_lock (&dir->lock);
+ }
+ p = process_wcc_stat (dir, p, !err);
+ mutex_unlock (&dir->lock);
+ }
+ else
+ mutex_unlock (&dir->lock);
+ }
+ else
+ mutex_unlock (&dir->lock);
+
+ free (rpcbuf);
+ break;
+
+ case CHRDEV:
+ case BLKDEV:
+ case FIFO:
+ case SOCK:
+
+ if (protocol_version == 2)
+ {
+ mutex_lock (&dir->lock);
+ err = verify_nonexistent (cred, dir, name);
+ if (err)
+ return err;
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ mutex_unlock (&np->lock);
+
+ mutex_lock (&dir->lock);
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ register_fresh_stat (np, p);
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ }
+ else
+ {
+ mutex_lock (&dir->lock);
+ p = nfs_initialize_rpc (NFS3PROC_MKNOD, cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+ mutex_unlock (&dir->lock);
+
+ mutex_lock (&np->lock);
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ return err;
+ }
+ *p++ = htonl (hurd_mode_to_nfs_type (np->nn_stat.st_mode));
+ p = xdr_encode_sattr_stat (p, &np->nn_stat);
+ if (np->nn->dtrans == BLKDEV || np->nn->dtrans == CHRDEV)
+ {
+ *p++ = htonl (major (np->nn_stat.st_rdev));
+ *p++ = htonl (minor (np->nn_stat.st_rdev));
+ }
+ mutex_unlock (&np->lock);
+
+ purge_lookup_cache (dir, name, strlen (name));
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ mutex_lock (&np->lock);
+ p = recache_handle (p, np);
+ p = process_returned_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ }
+ mutex_lock (&dir->lock);
+ p = process_wcc_stat (dir, p, !err);
+ mutex_unlock (&dir->lock);
+ }
+ free (rpcbuf);
+ }
+ break;
+ }
+
+ if (err)
+ return err;
+
+ mutex_lock (&np->lock);
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+ np->nn->dtrans = NOT_POSSIBLE;
+
+ /* If there is a dead-dir tag lying around, it's time to delete it now. */
+ if (np->nn->dead_dir)
+ {
+ struct node *dir = np->nn->dead_dir;
+ char *name = np->nn->dead_name;
+ np->nn->dead_dir = 0;
+ np->nn->dead_name = 0;
+ mutex_unlock (&np->lock);
+
+ mutex_lock (&dir->lock);
+ netfs_attempt_unlink ((struct iouser *)-1, dir, name);
+ mutex_unlock (&dir->lock);
+ }
+ else
+ mutex_unlock (&np->lock);
+
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkfile callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkfile (struct iouser *cred, struct node *dir,
+ mode_t mode, struct node **newnp)
+{
+ error_t err;
+ char *name;
+ static int n = 0;
+
+ /* This is the best we can do. */
+
+ name = malloc (50);
+
+ do
+ {
+ sprintf (name, ".nfstmpgnu.%d", n++);
+ err = netfs_attempt_create_file (cred, dir, name, mode, newnp);
+ if (err == EEXIST)
+ mutex_lock (&dir->lock);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (name);
+ return err;
+ }
+
+ assert (!(*newnp)->nn->dead_dir);
+ assert (!(*newnp)->nn->dead_name);
+ netfs_nref (dir);
+ (*newnp)->nn->dead_dir = dir;
+ (*newnp)->nn->dead_name = name;
+ if ((*newnp)->nn->dtrans == NOT_POSSIBLE)
+ (*newnp)->nn->dtrans = POSSIBLE;
+ return 0;
+}
+
+/* Implement the netfs_attempt_create_file callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_create_file (struct iouser *cred, struct node *np,
+ char *name, mode_t mode, struct node **newnp)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ uid_t owner;
+
+ if (cred->uids->num)
+ owner = cred->uids->ids[0];
+ else
+ {
+ err = netfs_validate_stat (np, cred);
+ owner = err ? 0 : np->nn_stat.st_uid;
+ mode &= ~S_ISUID;
+ }
+
+ /* RFC 1094 says that create is always exclusive. But Sun doesn't
+ actually *implement* the spec. No, of course not. So we have to do
+ it for them. */
+ if (protocol_version == 2)
+ {
+ err = verify_nonexistent (cred, np, name);
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ return err;
+ }
+ }
+
+ purge_lookup_cache (np, name, strlen (name));
+
+ p = nfs_initialize_rpc (NFSPROC_CREATE (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ p = xdr_encode_string (p, name);
+ if (protocol_version == 3)
+ {
+ /* We happen to know this is where the XID is. */
+ int verf = *(int *)rpcbuf;
+
+ *p++ = ntohl (EXCLUSIVE);
+ /* 8 byte verf */
+ *p++ = ntohl (verf);
+ p++;
+ }
+ else
+ p = xdr_encode_create_state (p, mode, owner);
+
+ err = conduct_rpc (&rpcbuf, &p);
+
+ mutex_unlock (&np->lock);
+
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (!err)
+ {
+ p = xdr_decode_fhandle (p, newnp);
+ p = process_returned_stat (*newnp, p, 1);
+ }
+ if (err)
+ *newnp = 0;
+ if (protocol_version == 3)
+ {
+ if (*newnp)
+ mutex_unlock (&(*newnp)->lock);
+ mutex_lock (&np->lock);
+ p = process_wcc_stat (np, p, 1);
+ mutex_unlock (&np->lock);
+ if (*newnp)
+ mutex_lock (&(*newnp)->lock);
+ }
+
+ if (*newnp && !netfs_validate_stat (*newnp, -1)
+ && (*newnp)->nn_stat.st_uid != owner)
+ netfs_attempt_chown (-1, *newnp, owner, (*newnp)->nn_stat.st_gid);
+ }
+ else
+ *newnp = 0;
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_unlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_unlink (struct iouser *cred, struct node *dir,
+ char *name)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ struct node *np;
+
+ /* First lookup the node being removed */
+ err = netfs_attempt_lookup (cred, dir, name, &np);
+ if (err)
+ {
+ mutex_lock (&dir->lock);
+ return err;
+ }
+
+ /* Restore the locks to sanity. */
+ mutex_unlock (&np->lock);
+ mutex_lock (&dir->lock);
+
+ /* Purge the cache of entries for this node, so that we don't
+ regard cache-held references as live. */
+ purge_lookup_cache_node (np);
+
+ /* See if there are any other users of this node than the
+ one we just got; if so, we must give this file another link
+ so that when we delete the one we are asked for it doesn't go
+ away entirely. */
+ if (np->references > 1)
+ {
+ char *newname;
+ int n = 0;
+
+ mutex_unlock (&dir->lock);
+
+ newname = malloc (50);
+ do
+ {
+ sprintf (newname, ".nfs%xgnu.%d", (int) np, n++);
+ err = netfs_attempt_link (cred, dir, np, newname, 1);
+ }
+ while (err == EEXIST);
+
+ if (err)
+ {
+ free (newname);
+ mutex_lock (&dir->lock);
+ netfs_nrele (np);
+ return err;
+ }
+
+ /* Write down what name we gave it; we'll delete this when all
+ our uses vanish. */
+ mutex_lock (&np->lock);
+
+ if (np->nn->dead_dir)
+ netfs_nrele (np->nn->dead_dir);
+ netfs_nref (dir);
+ np->nn->dead_dir = dir;
+ if (np->nn->dead_name)
+ free (np->nn->dead_name);
+ np->nn->dead_name = newname;
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ np->nn->dtrans = POSSIBLE;
+
+ netfs_nput (np);
+ mutex_lock (&dir->lock);
+ }
+ else
+ netfs_nrele (np);
+
+ p = nfs_initialize_rpc (NFSPROC_REMOVE (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ p = xdr_encode_string (p, name);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_wcc_stat (dir, p, !err);
+ }
+
+ free (rpcbuf);
+
+ return err;
+}
+
+/* Implement the netfs_attempt_rename callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_rename (struct iouser *cred, struct node *fromdir,
+ char *fromname, struct node *todir, char *toname,
+ int excl)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (excl)
+ {
+ struct node *np;
+
+ /* Just do a lookup/link/unlink sequence. */
+
+ mutex_lock (&fromdir->lock);
+ err = netfs_attempt_lookup (cred, fromdir, fromname, &np);
+ mutex_unlock (&fromdir->lock);
+ if (err)
+ return err;
+
+ err = netfs_attempt_link (cred, todir, np, toname, 1);
+ netfs_nput (np);
+ if (err)
+ return err;
+
+ mutex_lock (&fromdir->lock);
+ err = netfs_attempt_unlink (cred, fromdir, fromname);
+ mutex_unlock (&fromdir->lock);
+
+ /* If the unlink failed, then back out the link */
+ if (err)
+ {
+ mutex_lock (&todir->lock);
+ netfs_attempt_unlink (cred, todir, toname);
+ mutex_unlock (&todir->lock);
+ return err;
+ }
+
+ return 0;
+ }
+
+ mutex_lock (&fromdir->lock);
+ purge_lookup_cache (fromdir, fromname, strlen (fromname));
+ p = nfs_initialize_rpc (NFSPROC_RENAME (protocol_version),
+ cred, 0, &rpcbuf, fromdir, -1);
+ p = xdr_encode_fhandle (p, &fromdir->nn->handle);
+ p = xdr_encode_string (p, fromname);
+ mutex_unlock (&fromdir->lock);
+
+ mutex_lock (&todir->lock);
+ purge_lookup_cache (todir, toname, strlen (toname));
+ p = xdr_encode_fhandle (p, &todir->nn->handle);
+ p = xdr_encode_string (p, toname);
+ mutex_unlock (&todir->lock);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ {
+ mutex_lock (&fromdir->lock);
+ p = process_wcc_stat (fromdir, p, !err);
+ p = process_wcc_stat (todir, p, !err);
+ }
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_attempt_readlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_readlink (struct iouser *cred, struct node *np,
+ char *buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+
+ if (np->nn->dtrans == SYMLINK)
+ {
+ strcpy (buf, np->nn->transarg.name);
+ return 0;
+ }
+
+ p = nfs_initialize_rpc (NFSPROC_READLINK (protocol_version),
+ cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ if (protocol_version == 3)
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ p = xdr_decode_string (p, buf);
+ }
+
+ free (rpcbuf);
+ return err;
+}
+
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ int modes;
+
+ if (newnode || (flags & (O_READ|O_WRITE|O_EXEC)) == 0)
+ return 0;
+
+ netfs_report_access (cred, np, &modes);
+ if ((flags & (O_READ|O_WRITE|O_EXEC)) == (flags & modes))
+ return 0;
+ else
+ return EACCES;
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ error_t err;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ if (protocol_version == 2)
+ {
+ /* Hope the server means the same thing be the bits as we do. */
+ *types = 0;
+ if (fshelp_access (&np->nn_stat, S_IREAD, cred) == 0)
+ *types |= O_READ;
+ if (fshelp_access (&np->nn_stat, S_IWRITE, cred) == 0)
+ *types |= O_WRITE;
+ if (fshelp_access (&np->nn_stat, S_IEXEC, cred) == 0)
+ *types |= O_EXEC;
+ return 0;
+ }
+ else
+ {
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ int ret;
+ int write_check, execute_check;
+
+ if (S_ISDIR (np->nn_stat.st_mode))
+ {
+ write_check = ACCESS3_MODIFY | ACCESS3_DELETE | ACCESS3_EXTEND;
+ execute_check = ACCESS3_LOOKUP;
+ }
+ else
+ {
+ write_check = ACCESS3_MODIFY;
+ execute_check = ACCESS3_EXECUTE;
+ }
+
+ p = nfs_initialize_rpc (NFS3PROC_ACCESS, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (ACCESS3_READ | write_check | execute_check);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ {
+ err = nfs_error_trans (ntohl (*p++));
+ p = process_returned_stat (np, p, 0);
+ if (!err)
+ {
+ ret = ntohl (*p++);
+ *types = ((ret & ACCESS3_READ ? O_READ : 0)
+ | (ret & write_check ? O_WRITE : 0)
+ | (ret & execute_check ? O_EXEC : 0));
+ }
+ }
+ return err;
+ }
+}
+
+/* These definitions have unfortunate side effects, don't use them,
+ clever though they are. */
+#if 0
+/* Implement the netfs_check_open_permissions callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_check_open_permissions (struct iouser *cred, struct node *np,
+ int flags, int newnode)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Sun derived nfs client implementations attempt to reproduce the
+ server's permission restrictions by hoping they look like Unix,
+ and using that to give errors at open time. Sadly, that loses
+ here. So instead what we do is try and do what the user
+ requested; if we can't, then we fail. Otherwise, we allow the
+ open, but acknowledge that the server might still give an error
+ later. (Even with our check, the server can revoke access, thus
+ violiting Posix semantics; this means that erring on the side of
+ permitting illegal opens won't harm otherwise correct programs,
+ because they need to deal with revocation anyway.) We thus here
+ have the advantage of working correctly with servers that allow
+ things Unix denies. */
+
+ if ((flags & O_READ) == 0
+ && (flags & O_WRITE) == 0
+ && (flags & O_EXEC) == 0)
+ return 0;
+
+ err = netfs_validate_stat (np, cred);
+ if (err)
+ return err;
+
+ switch (np->nn_stat.st_mode & S_IFMT)
+ {
+ /* Don't know how to check, so return provisional success. */
+ default:
+ return 0;
+
+ case S_IFREG:
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ {
+ if ((flags & O_READ) || (flags & O_EXEC))
+ return err;
+ else
+ /* If we couldn't read a byte, but the user wasn't actually asking
+ for read, then we shouldn't inhibit the open now. */
+ return 0;
+ }
+
+ if (len != 1)
+ /* The file is empty; reads are known to be OK, but writes can't be
+ tested, so no matter what, return success. */
+ return 0;
+
+ if (flags & O_WRITE)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ return err;
+ }
+
+ /* Try as we might, we couldn't get the server to bump us, so
+ give (provisional) success. */
+ return 0;
+
+ case S_IFDIR:
+ if (flags & O_READ)
+ {
+ void *rpcbuf;
+ int *p;
+
+ /* Issue a readdir request; if it fails, then we can
+ return failure. Otherwise, succeed. */
+ p = nfs_initialize_rpc (NFSPROC_READDIR, cred, 0, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = 0;
+ *p++ = htonl (50);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ free (rpcbuf);
+
+ if (err)
+ return err;
+ }
+ return 0;
+ }
+}
+
+/* Implement the netfs_report_access callback as described in
+ <hurd/netfs.h>. */
+void
+netfs_report_access (struct iouser *cred,
+ struct node *np,
+ int *types)
+{
+ char byte;
+ error_t err;
+ size_t len;
+
+ /* Much the same logic as netfs_check_open_permissions, except that
+ here we err on the side of denying access, and that we always
+ have to check everything. */
+
+ *types = 0;
+
+ len = 1;
+ err = netfs_attempt_read (cred, np, 0, &len, &byte);
+ if (err)
+ return;
+ assert (len == 1 || len == 0);
+
+ *types |= O_READ | O_EXEC;
+
+ if (len == 1)
+ {
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ }
+ else
+ {
+ /* Oh, ugh. We have to try and write a byte and then truncate
+ back. God help us if the file becomes unwritable in-between.
+ But because of the use of this function (by setuid programs
+ wanting to see if they should write user's files) we must
+ check this and not just return a presumptive error. */
+ byte = 0;
+ err = netfs_attempt_write (cred, np, 0, &len, &byte);
+ if (!err)
+ *types |= O_WRITE;
+ netfs_attempt_set_size (cred, np, 0);
+ }
+}
+#endif
+
+/* Fetch the complete contents of DIR into a buffer of directs. Set
+ *BUFP to that buffer. *BUFP must be freed by the caller when no
+ longer needed. If an error occurs, don't touch *BUFP and return
+ the error code. Set BUFSIZEP to the amount of data used inside
+ *BUFP and TOTALENTRIES to the total number of entries copied. */
+static error_t
+fetch_directory (struct iouser *cred, struct node *dir,
+ void **bufp, size_t *bufsizep, int *totalentries)
+{
+ void *buf;
+ int cookie;
+ int *p;
+ void *rpcbuf;
+ struct dirent *entry;
+ void *bp;
+ int bufmalloced;
+ int eof;
+ error_t err;
+ int isnext;
+
+ bufmalloced = read_size;
+ buf = malloc (bufmalloced);
+ bp = buf;
+ cookie = 0;
+ eof = 0;
+ *totalentries = 0;
+
+ while (!eof)
+ {
+ /* Fetch new directory entries */
+ p = nfs_initialize_rpc (NFSPROC_READDIR (protocol_version),
+ cred, 0, &rpcbuf, dir, -1);
+ p = xdr_encode_fhandle (p, &dir->nn->handle);
+ *p++ = cookie;
+ *p++ = ntohl (read_size);
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ free (buf);
+ return err;
+ }
+
+ isnext = ntohl (*p++);
+
+ /* Now copy them one at a time. */
+ while (isnext)
+ {
+ ino_t fileno;
+ int namlen;
+ int reclen;
+
+ fileno = ntohl (*p++);
+ namlen = ntohl (*p++);
+
+ /* There's a hidden +1 here for the null byte and -1 because d_name
+ has a size of one already in the sizeof. */
+ reclen = sizeof (struct dirent) + namlen;
+ reclen = (reclen + 3) & ~3; /* make it a multiple of four */
+
+ /* Expand buffer if necessary */
+ if (bp + reclen > buf + bufmalloced)
+ {
+ char *newbuf;
+
+ newbuf = realloc (buf, bufmalloced *= 2);
+ if (newbuf != buf)
+ bp = newbuf + (bp - buf);
+ buf = newbuf;
+ }
+
+ /* Fill in new entry */
+ entry = (struct dirent *) bp;
+ entry->d_fileno = fileno;
+ entry->d_reclen = reclen;
+ entry->d_type = DT_UNKNOWN;
+ entry->d_namlen = namlen;
+ bcopy (p, entry->d_name, namlen);
+ entry->d_name[namlen] = '\0';
+
+ p += INTSIZE (namlen);
+ bp = bp + entry->d_reclen;
+
+ ++*totalentries;
+
+ cookie = *p++;
+ isnext = ntohl (*p++);
+ }
+
+ eof = ntohl (*p++);
+ free (rpcbuf);
+ }
+
+ /* Return it all to the user */
+ *bufp = buf;
+ *bufsizep = bufmalloced;
+ return 0;
+}
+
+
+/* Implement the netfs_get_directs callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_get_dirents (struct iouser *cred, struct node *np,
+ int entry, int nentries, char **data,
+ mach_msg_type_number_t *datacnt,
+ vm_size_t bufsiz, int *amt)
+{
+ void *buf;
+ size_t our_bufsiz, allocsize;
+ void *bp;
+ char *userdp;
+ error_t err;
+ int totalentries;
+ int thisentry;
+
+ err = fetch_directory (cred, np, &buf, &our_bufsiz, &totalentries);
+ if (err)
+ return err;
+
+ /* Allocate enough space to hold the maximum we might return. */
+ if (!bufsiz || bufsiz > our_bufsiz)
+ allocsize = round_page (our_bufsiz);
+ else
+ allocsize = round_page (bufsiz);
+ if (allocsize > *datacnt)
+ *data = mmap (0, allocsize, PROT_READ|PROT_WRITE, MAP_ANON, 0, 0);
+
+ /* Skip ahead to the correct entry. */
+ bp = buf;
+ for (thisentry = 0; thisentry < entry;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ bp += entry->d_reclen;
+ thisentry++;
+ }
+
+ /* Now copy them one at a time */
+ {
+ int entries_copied;
+
+ for (entries_copied = 0, userdp = *data;
+ (nentries == -1 || entries_copied < nentries)
+ && (!bufsiz || userdp - *data < bufsiz)
+ && thisentry < totalentries;)
+ {
+ struct dirent *entry = (struct dirent *) bp;
+ bcopy (bp, userdp, entry->d_reclen);
+ bp += entry->d_reclen;
+ userdp += entry->d_reclen;
+ entries_copied++;
+ thisentry++;
+ }
+ *amt = entries_copied;
+ }
+
+ free (buf);
+
+ /* If we allocated the buffer ourselves, but didn't use
+ all the pages, free the extra. */
+ if (allocsize > *datacnt
+ && round_page (userdp - *data) < round_page (allocsize))
+ munmap ((caddr_t) round_page (userdp),
+ round_page (allocsize) - round_page (userdp - *data));
+
+ *datacnt = userdp - *data;
+ return 0;
+}
+
+
+/* Implement the netfs_set_translator callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_set_translator (struct iouser *cred,
+ struct node *np,
+ char *argz,
+ size_t argzlen)
+{
+ return EOPNOTSUPP;
+}
+
+/* Implement the netfs_attempt_mksymlink callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mksymlink (struct iouser *cred,
+ struct node *np,
+ char *arg)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.name = malloc (strlen (arg) + 1);
+ strcpy (np->nn->transarg.name, arg);
+ np->nn->dtrans = SYMLINK;
+ np->nn->stat_updated = 0;
+ return 0;
+}
+
+/* Implement the netfs_attempt_mkdev callback as described in
+ <hurd/netfs.h>. */
+error_t
+netfs_attempt_mkdev (struct iouser *cred,
+ struct node *np,
+ mode_t type,
+ dev_t indexes)
+{
+ if (np->nn->dtrans == NOT_POSSIBLE)
+ return EOPNOTSUPP;
+
+ if (np->nn->dtrans == SYMLINK)
+ free (np->nn->transarg.name);
+
+ np->nn->transarg.indexes = indexes;
+ if (type == S_IFBLK)
+ np->nn->dtrans = BLKDEV;
+ else
+ np->nn->dtrans = CHRDEV;
+ np->nn->stat_updated = 0;
+ return 0;
+}
diff --git a/nfs/pager.c b/nfs/pager.c
new file mode 100644
index 00000000..92df7955
--- /dev/null
+++ b/nfs/pager.c
@@ -0,0 +1,456 @@
+/*
+ Copyright (C) 1995, 1996, 1997, 1999 Free Software Foundation, Inc.
+ Written by Michael I. Bushnell, p/BSG.
+
+ This file is part of the GNU Hurd.
+
+ The GNU Hurd is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ The GNU Hurd is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. */
+
+
+#include "nfs.h"
+#include <unistd.h>
+#include <hurd/pager.h>
+#include <netinet/in.h>
+#include <string.h>
+
+struct user_pager_info
+{
+ struct node *np;
+ struct pager *p;
+ int max_prot;
+};
+
+struct pager_cache_rec
+{
+ struct pager_cache_rec *next;
+ vm_offset_t offset;
+ struct pager *p;
+ time_t fetched;
+};
+
+static struct pager_cache_rec *pager_cache_recs;
+static spin_lock_t pager_cache_rec_lock = SPIN_LOCK_INITIALIZER;
+static spin_lock_t node2pagelock = SPIN_LOCK_INITIALIZER;
+static struct port_bucket *pager_bucket;
+
+void
+register_new_page (struct pager *p, vm_offset_t offset)
+{
+ struct pager_cache_rec *pc;
+
+ pc = malloc (sizeof (struct pager_cache_rec));
+ pc->offset = offset;
+ pc->p = p;
+ ports_port_ref (p);
+ pc->fetched = mapped_time->seconds;
+
+ spin_lock (&pager_cache_rec_lock);
+ pc->next = pager_cache_recs;
+ pager_cache_recs = pc;
+ spin_unlock (&pager_cache_rec_lock);
+}
+
+any_t
+flush_pager_cache_thread (any_t foo2)
+{
+ struct pager_cache_rec *pc, *next, **ppc, *list;
+
+ for (;;)
+ {
+ sleep (1);
+
+ /* Dequeue from the main list and queue locally the recs
+ for expired pages. */
+ list = 0;
+ spin_lock (&pager_cache_rec_lock);
+ for (pc = pager_cache_recs, ppc = &pager_cache_recs;
+ pc;
+ ppc = &pc->next, pc = next)
+ {
+ next = pc->next;
+ if (mapped_time->seconds - pc->fetched > cache_timeout)
+ {
+ *ppc = pc->next;
+ pc->next = list;
+ list = pc;
+ }
+ }
+ spin_unlock (&pager_cache_rec_lock);
+
+ /* And now, one at a time, expire them */
+ for (pc = list; pc; pc = next)
+ {
+ pager_return_some (pc->p, pc->offset, vm_page_size, 0);
+ next = pc->next;
+ ports_port_deref (pc->p);
+ free (pc);
+ }
+ }
+}
+
+error_t
+pager_read_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t *buf,
+ int *writelock)
+{
+ error_t err;
+ int *p;
+ void *rpcbuf;
+ struct node *np;
+ size_t amt, thisamt, trans_len;
+ void *data;
+ off_t offset;
+
+ np = pager->np;
+
+ mutex_lock (&np->lock);
+
+ *buf = (vm_address_t) mmap (0, vm_page_size, PROT_READ|PROT_WRITE,
+ MAP_ANON, 0, 0);
+ data = (char *) *buf;
+ amt = vm_page_size;
+ offset = page;
+
+ while (amt)
+ {
+ thisamt = amt;
+ if (thisamt > read_size)
+ thisamt = read_size;
+
+ p = nfs_initialize_rpc (NFSPROC_READ, (struct iouser *)-1, 0,
+ &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = htonl (offset);
+ *p++ = htonl (vm_page_size);
+ *p++ = 0;
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ mutex_unlock (&np->lock);
+ free (rpcbuf);
+ munmap ((caddr_t) *buf, vm_page_size);
+ return err;
+ }
+
+ p = register_fresh_stat (np, p);
+ trans_len = ntohl (*p++);
+ if (trans_len > thisamt)
+ trans_len = thisamt; /* ??? */
+
+ bcopy (p, data, trans_len);
+
+ free (rpcbuf);
+
+ data += trans_len;
+ offset += trans_len;
+ amt -= trans_len;
+
+ /* If we got a short count, we're all done. */
+ if (trans_len < thisamt)
+ break;
+ }
+
+ register_new_page (pager->p, page);
+ mutex_unlock (&np->lock);
+ return 0;
+}
+
+
+error_t
+pager_write_page (struct user_pager_info *pager,
+ vm_offset_t page,
+ vm_address_t buf)
+{
+ int *p;
+ void *rpcbuf;
+ error_t err;
+ size_t amt, thisamt;
+ off_t offset;
+ struct node *np;
+ void *data;
+
+ np = pager->np;
+ mutex_lock (&np->lock);
+
+ amt = vm_page_size;
+ offset = page;
+ data = (void *) buf;
+
+ while (amt)
+ {
+ thisamt = amt;
+ if (amt > write_size)
+ amt = write_size;
+
+ p = nfs_initialize_rpc (NFSPROC_WRITE, (struct iouser *) -1,
+ amt, &rpcbuf, np, -1);
+ p = xdr_encode_fhandle (p, &np->nn->handle);
+ *p++ = 0;
+ *p++ = htonl (offset);
+ *p++ = 0;
+ p = xdr_encode_data (p, data, thisamt);
+
+ err = conduct_rpc (&rpcbuf, &p);
+ if (!err)
+ err = nfs_error_trans (ntohl (*p++));
+ if (err)
+ {
+ free (rpcbuf);
+ vm_deallocate (mach_task_self (), buf, vm_page_size);
+ return err;
+ }
+ register_fresh_stat (np, p);
+ free (rpcbuf);
+ amt -= thisamt;
+ data += thisamt;
+ offset += thisamt;
+ }
+
+ vm_deallocate (mach_task_self (), buf, vm_page_size);
+ mutex_unlock (&np->lock);
+ return 0;
+}
+
+error_t
+pager_unlock_page (struct user_pager_info *pager,
+ vm_offset_t address)
+{
+ abort ();
+}
+
+error_t
+pager_report_extent (struct user_pager_info *pager,
+ vm_address_t *offset,
+ vm_size_t *size)
+{
+ struct node *np;
+ error_t err;
+
+ np = pager->np;
+ mutex_lock (&np->lock);
+
+ err = netfs_validate_stat (np, 0);
+ if (!err)
+ *size = round_page (np->nn_stat.st_size);
+ mutex_unlock (&np->lock);
+ return err;
+}
+
+void
+pager_clear_user_data (struct user_pager_info *upi)
+{
+ spin_lock (&node2pagelock);
+ if (upi->np->nn->fileinfo == upi)
+ upi->np->nn->fileinfo = 0;
+ spin_unlock (&node2pagelock);
+ netfs_nrele (upi->np);
+ free (upi);
+}
+
+void
+pager_dropweak (struct user_pager_info *upi)
+{
+ abort ();
+}
+
+mach_port_t
+netfs_get_filemap (struct node *np, vm_prot_t prot)
+{
+ struct user_pager_info *upi;
+ mach_port_t right;
+
+ spin_lock (&node2pagelock);
+ do
+ if (!np->nn->fileinfo)
+ {
+ upi = malloc (sizeof (struct user_pager_info));
+ upi->np = np;
+ netfs_nref (np);
+ upi->max_prot = prot;
+ upi->p = pager_create (upi, pager_bucket, 1, MEMORY_OBJECT_COPY_NONE);
+ if (upi->p == 0)
+ {
+ netfs_nrele (np);
+ free (upi);
+ spin_unlock (&node2pagelock);
+ return MACH_PORT_NULL;
+ }
+ np->nn->fileinfo = upi;
+ right = pager_get_port (np->nn->fileinfo->p);
+ ports_port_deref (np->nn->fileinfo->p);
+ }
+ else
+ {
+ np->nn->fileinfo->max_prot |= prot;
+ /* Because NP->dn->fileinfo->p is not a real reference,
+ this might be nearly deallocated. If that's so, then
+ the port right will be null. In that case, clear here
+ and loop. The deallocation will complete separately. */
+ right = pager_get_port (np->nn->fileinfo->p);
+ if (right == MACH_PORT_NULL)
+ np->nn->fileinfo = 0;
+ }
+ while (right == MACH_PORT_NULL);
+
+ spin_unlock (&node2pagelock);
+
+ mach_port_insert_right (mach_task_self (), right, right,
+ MACH_MSG_TYPE_MAKE_SEND);
+ return right;
+}
+
+void
+drop_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->nn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 0, MEMORY_OBJECT_COPY_NONE, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+void
+allow_pager_softrefs (struct node *np)
+{
+ struct user_pager_info *upi;
+
+ spin_lock (&node2pagelock);
+ upi = np->nn->fileinfo;
+ if (upi)
+ ports_port_ref (upi->p);
+ spin_unlock (&node2pagelock);
+
+ if (upi)
+ {
+ pager_change_attributes (upi->p, 1, MEMORY_OBJECT_COPY_NONE, 0);
+ ports_port_deref (upi->p);
+ }
+}
+
+void
+block_caching ()
+{
+ error_t block_cache (void *arg)
+ {
+ struct pager *p = arg;
+ pager_change_attributes (p, 0, MEMORY_OBJECT_COPY_NONE, 1);
+ return 0;
+ }
+ ports_bucket_iterate (pager_bucket, block_cache);
+}
+
+void
+enable_caching ()
+{
+ error_t enable_cache (void *arg)
+ {
+ struct pager *p = arg;
+ struct user_pager_info *upi = pager_get_upi (p);
+
+ pager_change_attributes (p, 1, MEMORY_OBJECT_COPY_NONE, 0);
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, enable_cache);
+}
+
+int
+netfs_pager_users ()
+{
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (!npagers)
+ return 0;
+
+ block_caching ();
+ /* Give it a sec; the kernel doesn't issue the shutdown right away */
+ sleep (1);
+ npagers = ports_count_bucket (pager_bucket);
+ if (!npagers)
+ return 0;
+
+ enable_caching ();
+
+ ports_enable_bucket (pager_bucket);
+}
+
+vm_prot_t
+netfs_max_user_pager_prot ()
+{
+ vm_prot_t max_prot;
+ int npagers = ports_count_bucket (pager_bucket);
+
+ if (npagers)
+ {
+ error_t add_pager_max_prot (void *v_p)
+ {
+ struct pager *p = v_p;
+ struct user_pager_info *upi = pager_get_upi (p);
+ max_prot |= upi->max_prot;
+ return max_prot == (VM_PROT_READ|VM_PROT_WRITE|VM_PROT_EXECUTE);
+ }
+
+ block_caching ();
+ sleep (1);
+
+ ports_bucket_iterate (pager_bucket, add_pager_max_prot);
+ enable_caching ();
+ }
+
+ ports_enable_bucket (pager_bucket);
+ return max_prot;
+}
+
+void
+netfs_shutdown_pager ()
+{
+ error_t shutdown_one (void *arg)
+ {
+ pager_shutdown ((struct pager *) arg);
+ return 0;
+ }
+
+ ports_bucket_iterate (pager_bucket, shutdown_one);
+}
+
+void
+netfs_sync_everything (int wait)
+{
+ error_t sync_one (void *arg)
+ {
+ pager_sync ((struct pager *) arg, wait);
+ return 0;
+ }
+ ports_bucket_iterate (pager_bucket, sync_one);
+}
+
+void
+pager_initialize (void)
+{
+ pager_bucket = ports_create_bucket ();
+ cthread_detach (cthread_fork (flush_pager_cache_thread, 0));
+
diff --git a/nfs/rpc.c b/nfs/rpc.c
new file mode 100644
index 00000000..90d534ba
--- /dev/null
+++ b/nfs/rpc.c
@@ -0,0 +1,380 @@
+/* SunRPC management for NFS client
+ Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation
+
+ This program is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License as
+ published by the Free Software Foundation; either version 2, or (at
+ your option) any later version.
+
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
+
+#include "nfs.h"
+
+/* Needed in order to get the RPC header files to include correctly */
+#undef TRUE
+#undef FALSE
+#define malloc spoiufasdf /* Avoid bogus definition in rpc/types.h */
+
+#include <rpc/types.h>
+#include <rpc/xdr.h>
+#include <rpc/auth.h>
+#include <rpc/rpc_msg.h>
+#include <rpc/auth_unix.h>
+
+#undef malloc /* Get rid protection. */
+
+#include <netinet/in.h>
+#include <assert.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* One of these exists for each pending RPC. */
+struct rpc_list
+{
+ struct rpc_list *next, **prevp;
+ void *reply;
+};
+
+/* A list of all the pending RPCs. */
+static struct rpc_list *outstanding_rpcs;
+
+/* Wake up this condition when an outstanding RPC has received a reply
+ or we should check for timeouts. */
+static struct condition rpc_wakeup = CONDITION_INITIALIZER;
+
+/* Lock the global data and the REPLY fields of outstanding RPC's. */
+static struct mutex outstanding_lock = MUTEX_INITIALIZER;
+
+
+
+/* Generate and return a new transaction ID. */
+static int
+generate_xid ()
+{
+ static int nextxid;
+
+ if (nextxid == 0)
+ nextxid = mapped_time->seconds;
+
+ return nextxid++;
+}
+
+/* Set up an RPC for procdeure RPC_PROC, for talking to the server
+ PROGRAM of version VERSION. Allocate storage with malloc and point
+ *BUF at it; caller must free this when done. Allocate at least LEN
+ bytes more than the usual amount for an RPC. Initialize the RPC
+ credential structure with UID, GID, and SECOND_GID. (Any of those
+ may be -1 to indicate that it does not apply; exactly or two of UID
+ and GID must be -1, however.) */
+int *
+initialize_rpc (int program, int version, int rpc_proc,
+ size_t len, void **bufp,
+ uid_t uid, gid_t gid, gid_t second_gid)
+{
+ void *buf = malloc (len + 1024);
+ int *p, *lenaddr;
+ struct rpc_list *hdr;
+
+ /* First the struct rpc_list bit. */
+ hdr = buf;
+ hdr->reply = 0;
+
+ p = buf + sizeof (struct rpc_list);
+
+ /* RPC header */
+ *p++ = htonl (generate_xid ());
+ *p++ = htonl (CALL);
+ *p++ = htonl (RPC_MSG_VERSION);
+ *p++ = htonl (program);
+ *p++ = htonl (version);
+ *p++ = htonl (rpc_proc);
+
+ assert ((uid == -1) == (gid == -1));
+
+ if (uid != -1)
+ {
+ *p++ = htonl (AUTH_UNIX);
+ lenaddr = p++;
+ *p++ = htonl (mapped_time->seconds);
+ p = xdr_encode_string (p, hostname);
+ *p++ = htonl (uid);
+ *p++ = htonl (gid);
+ if (second_gid != -1)
+ {
+ *p++ = htonl (1);
+ *p++ = htonl (second_gid);
+ }
+ else
+ *p++ = 0;
+ *lenaddr = htonl ((p - (lenaddr + 1)) * sizeof (int));
+ }
+ else
+ {
+ *p++ = htonl (AUTH_NULL);
+ *p++ = 0;
+ }
+
+ /* VERF field */
+ *p++ = htonl (AUTH_NULL);
+ *p++ = 0;
+
+ *bufp = buf;
+ return p;
+}
+
+/* Remove HDR from the list of pending RPC's. rpc_list_lock must be
+ held */
+static void
+unlink_rpc (struct rpc_list *hdr)
+{
+ *hdr->prevp = hdr->next;
+ if (hdr->next)
+ hdr->next->prevp = hdr->prevp;
+}
+
+/* Send the specified RPC message. *RPCBUF is the initialized buffer
+ from a previous initialize_rpc call; *PP points past the filled
+ in args. Set *PP to the address of the reply contents themselves.
+ The user will be expected to free *RPCBUF (which will have changed)
+ when done with the reply contents. The old value of *RPCBUF will
+ be freed by this routine. */
+error_t
+conduct_rpc (void **rpcbuf, int **pp)
+{
+ struct rpc_list *hdr = *rpcbuf;
+ error_t err;
+ size_t cc, nc;
+ int timeout = initial_transmit_timeout;
+ time_t lasttrans;
+ int ntransmit = 0;
+ int *p;
+ int xid;
+ int n;
+ int cancel;
+
+ mutex_lock (&outstanding_lock);
+
+ /* Link it in */
+ hdr->next = outstanding_rpcs;
+ if (hdr->next)
+ hdr->next->prevp = &hdr->next;
+ hdr->prevp = &outstanding_rpcs;
+ outstanding_rpcs = hdr;
+
+ xid = * (int *) (*rpcbuf + sizeof (struct rpc_list));
+
+ do
+ {
+ /* If we've sent enough, give up */
+ if (mounted_soft && ntransmit == soft_retries)
+ {
+ unlink_rpc (hdr);
+ mutex_unlock (&outstanding_lock);
+ return ETIMEDOUT;
+ }
+
+ /* Issue the RPC */
+ lasttrans = mapped_time->seconds;
+ ntransmit++;
+ nc = (void *) *pp - *rpcbuf - sizeof (struct rpc_list);
+ cc = write (main_udp_socket, *rpcbuf + sizeof (struct rpc_list), nc);
+ if (cc == -1)
+ {
+ unlink_rpc (hdr);
+ mutex_unlock (&outstanding_lock);
+ return errno;
+ }
+ else
+ assert (cc == nc);
+
+ /* Wait for reply */
+ cancel = 0;
+ while (!hdr->reply
+ && (mapped_time->seconds - lasttrans < timeout)
+ && !cancel)
+ cancel = hurd_condition_wait (&rpc_wakeup, &outstanding_lock);
+
+ if (cancel)
+ {
+ unlink_rpc (hdr);
+ mutex_unlock (&outstanding_lock);
+ return EINTR;
+ }
+
+ if (!hdr->reply)
+ {
+ timeout *=2;
+ if (timeout > max_transmit_timeout)
+ timeout = max_transmit_timeout;
+ }
+ }
+ while (!hdr->reply);
+
+ mutex_unlock (&outstanding_lock);
+
+ /* Switch to the reply buffer. */
+ *rpcbuf = hdr->reply;
+ free (hdr);
+
+ /* Process the reply, dissecting errors. When we're done, set *PP to
+ the rpc return contents, if there is no error. */
+ p = (int *) *rpcbuf;
+
+ assert (*p == xid);
+ p++;
+
+ switch (ntohl (*p++))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case REPLY:
+ switch (ntohl (*p++))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case MSG_DENIED:
+ switch (ntohl (*p++))
+ {
+ default:
+ err = EBADRPC;
+ break;
+
+ case RPC_MISMATCH:
+ err = ERPCMISMATCH;
+ break;
+
+ case AUTH_ERROR:
+ switch (ntohl (*p++))
+ {
+ case AUTH_BADCRED:
+ case AUTH_REJECTEDCRED:
+ err = EAUTH;
+ break;
+
+ case AUTH_TOOWEAK:
+ err = ENEEDAUTH;
+ break;
+
+ case AUTH_BADVERF:
+ case AUTH_REJECTEDVERF:
+ default:
+ err = EBADRPC;
+ break;
+ }
+ break;
+ }
+ break;
+
+ case MSG_ACCEPTED:
+
+ /* Process VERF field. */
+ p++; /* skip verf type */
+ n = ntohl (*p++); /* size of verf */
+ p += INTSIZE (n); /* skip verf itself */
+
+ switch (ntohl (*p++))
+ {
+ default:
+ case GARBAGE_ARGS:
+ err = EBADRPC;
+ break;
+
+ case PROG_UNAVAIL:
+ err = EPROGUNAVAIL;
+ break;
+
+ case PROG_MISMATCH:
+ err = EPROGMISMATCH;
+ break;
+
+ case PROC_UNAVAIL:
+ err = EPROCUNAVAIL;
+ break;
+
+ case SUCCESS:
+ *pp = p;
+ err = 0;
+ break;
+ }
+ break;
+ }
+ break;
+ }
+ return err;
+}
+
+/* Dedicated thread to wakeup rpc_wakeup once a second. */
+void
+timeout_service_thread ()
+{
+ while (1)
+ {
+ sleep (1);
+ mutex_lock (&outstanding_lock);
+ condition_broadcast (&rpc_wakeup);
+ mutex_unlock (&outstanding_lock);
+ }
+}
+
+/* Dedicate thread to receive RPC replies, register them on the queue
+ of pending wakeups, and deal appropriately. */
+void
+rpc_receive_thread ()
+{
+ int cc;
+ void *buf;
+ struct rpc_list *r;
+ int xid;
+
+ while (1)
+ {
+ buf = malloc (1024 + read_size);
+
+ do
+ {
+ cc = read (main_udp_socket, buf, 1024 + read_size);
+ if (cc == -1)
+ {
+ perror ("nfs read");
+ r = 0;
+ }
+ else
+ {
+ xid = *(int *)buf;
+ mutex_lock (&outstanding_lock);
+ for (r = outstanding_rpcs; r; r = r->next)
+ {
+ if (* (int *) &r[1] == xid)
+ {
+ /* Remove it from the list */
+ *r->prevp = r->next;
+ if (r->next)
+ r->next->prevp = r->prevp;
+
+ r->reply = buf;
+ condition_broadcast (&rpc_wakeup);
+ break;
+ }
+ }
+#if notanymore
+ if (!r)
+ fprintf (stderr, "NFS dropping reply xid %d\n", xid);
+#endif
+ mutex_unlock (&outstanding_lock);
+ }
+ }
+ while (!r);
+ }
+}