/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2008 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. * * Simple nfs V3 ops */ #pragma ident "%Z%%M% %I% %E% SMI" #include #include #include #include "clnt.h" #include #include #include #include #include #include "nfs_inet.h" #include #include #include #include #include #include #include "brpc.h" #include #define dprintf if (boothowto & RB_DEBUG) printf /* * NFS Version 3 specific functions */ ssize_t nfs3read(struct nfs_file *filep, char *buf, size_t size) { READ3args read_args; READ3res read_res; enum clnt_stat read_stat; uint_t readcnt = 0; /* # bytes read by nfs */ uint_t count = 0; /* # bytes transferred to buf */ int done = FALSE; /* last block has come in */ int framing_errs = 0; /* stack errors */ char *buf_offset; /* current buffer offset */ struct timeval timeout; static uint_t pos; /* progress indicator counter */ static char ind[] = "|/-\\"; /* progress indicator */ static int blks_read; read_args.file.data.data_len = filep->fh.fh3.len; read_args.file.data.data_val = filep->fh.fh3.data; read_args.offset = filep->offset; bzero(&read_res, sizeof (read_res)); buf_offset = buf; /* Optimize for reads of less than one block size */ if (nfs_readsize == 0) nfs_readsize = READ3_SIZE; if (size < nfs_readsize) read_args.count = size; else read_args.count = nfs_readsize; do { /* use the user's buffer to stuff the data into. */ read_res.READ3res_u.resok.data.data_val = buf_offset; /* * Handle the case where the file does not end * on a block boundary. */ if ((count + read_args.count) > size) read_args.count = size - count; timeout.tv_sec = NFS_REXMIT_MIN; /* Total wait for call */ timeout.tv_usec = 0; do { read_stat = CLNT_CALL(root_CLIENT, NFSPROC3_READ, xdr_READ3args, (caddr_t)&read_args, xdr_READ3res, (caddr_t)&read_res, timeout); if (read_stat == RPC_TIMEDOUT) { dprintf("NFS read(%d) timed out. Retrying...\n", read_args.count); /* * If the remote is there and trying to respond, * but our stack is having trouble reassembling * the reply, reduce the read size in an * attempt to compensate. Reset the * transmission and reply wait timers. */ if (errno == ETIMEDOUT) framing_errs++; if (framing_errs > NFS_MAX_FERRS && read_args.count > NFS_READ_DECR) { read_args.count /= 2; nfs_readsize /= 2; dprintf("NFS Read size now %d.\n", nfs_readsize); timeout.tv_sec = NFS_REXMIT_MIN; framing_errs = 0; } else { if (timeout.tv_sec < NFS_REXMIT_MAX) timeout.tv_sec++; else timeout.tv_sec = 0; /* default RPC */ } } } while (read_stat == RPC_TIMEDOUT); if (read_stat != RPC_SUCCESS) return (-1); if (read_res.status != NFS3_OK) return (-1); readcnt = read_res.READ3res_u.resok.data.data_len; /* * If we are at EOF, update counts and exit */ if (read_res.READ3res_u.resok.eof == TRUE) done = TRUE; /* * Handle the case where the file is smaller than * the size of the read request, thus the request * couldn't be completely filled. */ if (readcnt < read_args.count) { #ifdef NFS_OPS_DEBUG if ((boothowto & DBFLAGS) == DBFLAGS) printf("nfs3read(): partial read %d" " instead of %d\n", readcnt, read_args.count); #endif done = TRUE; /* update the counts and exit */ } /* update various offsets */ count += readcnt; filep->offset += readcnt; buf_offset += readcnt; read_args.offset += readcnt; /* * round and round she goes (though not on every block.. * - OBP's take a fair bit of time to actually print stuff) */ if ((blks_read++ & 0x3) == 0) printf("%c\b", ind[pos++ & 3]); } while (count < size && !done); return (count); } int nfs3getattr(struct nfs_file *nfp, struct vattr *vap) { enum clnt_stat getattr_stat; GETATTR3args getattr_args; GETATTR3res getattr_res; fattr3 *na; struct timeval timeout = {0, 0}; /* default */ vtype_t nf3_to_vt[] = { VBAD, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO }; bzero(&getattr_args, sizeof (getattr_args)); getattr_args.object.data.data_len = nfp->fh.fh3.len; getattr_args.object.data.data_val = nfp->fh.fh3.data; bzero(&getattr_res, sizeof (getattr_res)); getattr_stat = CLNT_CALL(root_CLIENT, NFSPROC3_GETATTR, xdr_GETATTR3args, (caddr_t)&getattr_args, xdr_GETATTR3res, (caddr_t)&getattr_res, timeout); if (getattr_stat != RPC_SUCCESS) { dprintf("nfs_getattr: RPC error %d\n", getattr_stat); return (-1); } if (getattr_res.status != NFS3_OK) { nfs3_error(getattr_res.status); return (getattr_res.status); } na = &getattr_res.GETATTR3res_u.resok.obj_attributes; if (vap->va_mask & AT_TYPE) { if (na->type < NF3REG || na->type > NF3FIFO) vap->va_type = VBAD; else vap->va_type = nf3_to_vt[na->type]; } if (vap->va_mask & AT_MODE) vap->va_mode = (mode_t)na->mode; if (vap->va_mask & AT_SIZE) vap->va_size = (u_offset_t)na->size; if (vap->va_mask & AT_NODEID) vap->va_nodeid = (u_longlong_t)na->fileid; if (vap->va_mask & AT_ATIME) { vap->va_atime.tv_sec = na->atime.seconds; vap->va_atime.tv_nsec = na->atime.nseconds; } if (vap->va_mask & AT_CTIME) { vap->va_ctime.tv_sec = na->ctime.seconds; vap->va_ctime.tv_nsec = na->ctime.nseconds; } if (vap->va_mask & AT_MTIME) { vap->va_mtime.tv_sec = na->mtime.seconds; vap->va_mtime.tv_nsec = na->mtime.nseconds; } return (NFS3_OK); } /* * Display nfs error messages. */ /*ARGSUSED*/ void nfs3_error(enum nfsstat3 status) { if (!(boothowto & RB_DEBUG)) return; switch (status) { case NFS3_OK: printf("NFS: No error.\n"); break; case NFS3ERR_PERM: printf("NFS: Not owner.\n"); break; case NFS3ERR_NOENT: #ifdef NFS_OPS_DEBUG printf("NFS: No such file or directory.\n"); #endif /* NFS_OPS_DEBUG */ break; case NFS3ERR_IO: printf("NFS: IO ERROR occurred on NFS server.\n"); break; case NFS3ERR_NXIO: printf("NFS: No such device or address.\n"); break; case NFS3ERR_ACCES: printf("NFS: Permission denied.\n"); break; case NFS3ERR_EXIST: printf("NFS: File exists.\n"); break; case NFS3ERR_XDEV: printf("NFS: Cross device hard link.\n"); break; case NFS3ERR_NODEV: printf("NFS: No such device.\n"); break; case NFS3ERR_NOTDIR: printf("NFS: Not a directory.\n"); break; case NFS3ERR_ISDIR: printf("NFS: Is a directory.\n"); break; case NFS3ERR_INVAL: printf("NFS: Invalid argument.\n"); break; case NFS3ERR_FBIG: printf("NFS: File too large.\n"); break; case NFS3ERR_NOSPC: printf("NFS: No space left on device.\n"); break; case NFS3ERR_ROFS: printf("NFS: Read-only filesystem.\n"); break; case NFS3ERR_MLINK: printf("NFS: Too many hard links.\n"); break; case NFS3ERR_NAMETOOLONG: printf("NFS: File name too long.\n"); break; case NFS3ERR_NOTEMPTY: printf("NFS: Directory not empty.\n"); break; case NFS3ERR_DQUOT: printf("NFS: Disk quota exceeded.\n"); break; case NFS3ERR_STALE: printf("NFS: Stale file handle.\n"); break; case NFS3ERR_REMOTE: printf("NFS: Remote file in path.\n"); break; case NFS3ERR_BADHANDLE: printf("NFS: Illegal NFS file handle.\n"); break; case NFS3ERR_NOT_SYNC: printf("NFS: Synchronization mismatch.\n"); break; case NFS3ERR_BAD_COOKIE: printf("NFS: Stale Cookie.\n"); break; case NFS3ERR_NOTSUPP: printf("NFS: Operation is not supported.\n"); break; case NFS3ERR_TOOSMALL: printf("NFS: Buffer too small.\n"); break; case NFS3ERR_SERVERFAULT: printf("NFS: Server fault.\n"); break; case NFS3ERR_BADTYPE: printf("NFS: Unsupported object type.\n"); break; case NFS3ERR_JUKEBOX: printf("NFS: Resource temporarily unavailable.\n"); break; default: printf("NFS: unknown error.\n"); break; } } struct nfs_file * nfs3lookup(struct nfs_file *dir, char *name, int *nstat) { struct timeval zero_timeout = {0, 0}; /* default */ static struct nfs_file cd; LOOKUP3args dirop; LOOKUP3res res_lookup; enum clnt_stat status; *nstat = (int)NFS3_OK; bzero((caddr_t)&dirop, sizeof (LOOKUP3args)); bzero((caddr_t)&res_lookup, sizeof (LOOKUP3res)); dirop.what.dir.data.data_len = dir->fh.fh3.len; dirop.what.dir.data.data_val = dir->fh.fh3.data; dirop.what.name = name; status = CLNT_CALL(root_CLIENT, NFSPROC3_LOOKUP, xdr_LOOKUP3args, (caddr_t)&dirop, xdr_LOOKUP3res, (caddr_t)&res_lookup, zero_timeout); if (status != RPC_SUCCESS) { dprintf("lookup: RPC error.\n"); return (NULL); } if (res_lookup.status != NFS3_OK) { nfs3_error(res_lookup.status); *nstat = (int)res_lookup.status; (void) CLNT_FREERES(root_CLIENT, xdr_LOOKUP3res, (caddr_t)&res_lookup); return (NULL); } bzero((caddr_t)&cd, sizeof (struct nfs_file)); cd.version = NFS_V3; /* * Server must supply post_op_attr's */ if (res_lookup.LOOKUP3res_u.resok.obj_attributes.attributes_follow == FALSE) { printf("nfs3lookup: server fails to return post_op_attr\n"); (void) CLNT_FREERES(root_CLIENT, xdr_LOOKUP3res, (caddr_t)&res_lookup); return (NULL); } cd.ftype.type3 = res_lookup.LOOKUP3res_u.resok.obj_attributes .post_op_attr_u.attributes.type; cd.fh.fh3.len = res_lookup.LOOKUP3res_u.resok.object.data.data_len; bcopy(res_lookup.LOOKUP3res_u.resok.object.data.data_val, cd.fh.fh3.data, cd.fh.fh3.len); (void) CLNT_FREERES(root_CLIENT, xdr_LOOKUP3res, (caddr_t)&res_lookup); return (&cd); } /* * Gets symbolic link into pathname. */ int nfs3getsymlink(struct nfs_file *cfile, char **path) { struct timeval zero_timeout = {0, 0}; /* default */ enum clnt_stat status; struct READLINK3res linkres; struct READLINK3args linkargs; static char symlink_path[NFS_MAXPATHLEN]; bzero(&linkargs, sizeof (linkargs)); linkargs.symlink.data.data_len = cfile->fh.fh3.len; linkargs.symlink.data.data_val = cfile->fh.fh3.data; /* * linkres needs a zeroed buffer to place path data into: */ bzero(&linkres, sizeof (linkres)); bzero(symlink_path, NFS_MAXPATHLEN); linkres.READLINK3res_u.resok.data = symlink_path; status = CLNT_CALL(root_CLIENT, NFSPROC3_READLINK, xdr_READLINK3args, (caddr_t)&linkargs, xdr_READLINK3res, (caddr_t)&linkres, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs3getsymlink: RPC call failed.\n"); return (-1); } if (linkres.status != NFS3_OK) { nfs3_error(linkres.status); return (linkres.status); } *path = symlink_path; return (NFS3_OK); }