diff options
Diffstat (limited to 'nfs')
-rw-r--r-- | nfs/ChangeLog | 618 | ||||
-rw-r--r-- | nfs/Makefile | 32 | ||||
-rw-r--r-- | nfs/cache.c | 186 | ||||
-rw-r--r-- | nfs/consts.c | 25 | ||||
-rw-r--r-- | nfs/main.c | 403 | ||||
-rw-r--r-- | nfs/mount.c | 220 | ||||
-rw-r--r-- | nfs/mount.h | 35 | ||||
-rw-r--r-- | nfs/name-cache.c | 303 | ||||
-rw-r--r-- | nfs/nfs-spec.h | 164 | ||||
-rw-r--r-- | nfs/nfs.c | 645 | ||||
-rw-r--r-- | nfs/nfs.h | 195 | ||||
-rw-r--r-- | nfs/ops.c | 1757 | ||||
-rw-r--r-- | nfs/pager.c | 456 | ||||
-rw-r--r-- | nfs/rpc.c | 380 |
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 ?: ¤t, + mtime ?: ¤t); + 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); + } +} |