nfs2ops.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License, Version 1.0 only
* (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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*
* Simple nfs ops - open, close, read, and lseek.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <rpc/types.h>
#include <rpc/auth.h>
#include <sys/t_lock.h>
#include "clnt.h"
#include <sys/fcntl.h>
#include <sys/vfs.h>
#include <errno.h>
#include <sys/promif.h>
#include <rpc/xdr.h>
#include "nfs_inet.h"
#include <sys/stat.h>
#include <sys/bootvfs.h>
#include <sys/bootdebug.h>
#include <sys/salib.h>
#include <sys/sacache.h>
#include <rpc/rpc.h>
#include "brpc.h"
#include <rpcsvc/nfs_prot.h>
#define dprintf if (boothowto & RB_DEBUG) printf
static struct timeval zero_timeout = {0, 0}; /* default */
/*
* NFS Version 2 specific functions
*/
ssize_t
nfsread(struct nfs_file *filep, char *buf, size_t size)
{
readargs read_args;
readres 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;
#ifndef i386
static uint_t pos; /* progress indicator counter */
static char ind[] = "|/-\\"; /* progress indicator */
static int blks_read;
#endif
read_args.file = filep->fh.fh2; /* structure copy */
read_args.offset = filep->offset;
buf_offset = buf;
/* Optimize for reads of less than one block size */
if (nfs_readsize == 0)
nfs_readsize = READ_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.readres_u.reply.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, NFSPROC_READ,
xdr_readargs, (caddr_t)&read_args,
xdr_readres, (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 -= NFS_READ_DECR;
nfs_readsize -= NFS_READ_DECR;
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);
readcnt = read_res.readres_u.reply.data.data_len;
/*
* Handle the case where the file is simply empty, and
* nothing could be read.
*/
if (readcnt == 0)
break; /* eof */
/*
* 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("nfsread(): 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;
#ifndef i386
/*
* 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]);
#endif
} while (count < size && !done);
return (count);
}
static vtype_t nf_to_vt[] = {
VNON, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK
};
int
nfsgetattr(struct nfs_file *nfp, struct vattr *vap)
{
enum clnt_stat getattr_stat;
attrstat getattr_res;
fattr *na;
struct timeval timeout = {0, 0}; /* default */
getattr_stat = CLNT_CALL(root_CLIENT, NFSPROC_GETATTR,
xdr_nfs_fh, (caddr_t)&(nfp->fh.fh2),
xdr_attrstat, (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 != NFS_OK) {
nfs_error(getattr_res.status);
return (getattr_res.status);
}
/* adapted from nattr_to_vattr() in nfs_client.c */
na = &getattr_res.attrstat_u.attributes;
if (vap->va_mask & AT_TYPE) {
if (na->type < NFNON || na->type > NFSOCK)
vap->va_type = VBAD;
else
vap->va_type = nf_to_vt[na->type];
}
if (vap->va_mask & AT_MODE)
vap->va_mode = na->mode;
if (vap->va_mask & AT_SIZE)
vap->va_size = na->size;
if (vap->va_mask & AT_NODEID)
vap->va_nodeid = na->fileid;
if (vap->va_mask & AT_ATIME) {
vap->va_atime.tv_sec = na->atime.seconds;
vap->va_atime.tv_nsec = na->atime.useconds * 1000;
}
if (vap->va_mask & AT_CTIME) {
vap->va_ctime.tv_sec = na->ctime.seconds;
vap->va_ctime.tv_nsec = na->ctime.useconds * 1000;
}
if (vap->va_mask & AT_MTIME) {
vap->va_mtime.tv_sec = na->mtime.seconds;
vap->va_mtime.tv_nsec = na->mtime.useconds * 1000;
}
#ifdef NFS_OPS_DEBUG
if ((boothowto & DBFLAGS) == DBFLAGS)
printf("nfs_getattr(): done.\n");
#endif
return (getattr_res.status);
}
/*
* Display nfs error messages.
*/
/*ARGSUSED*/
void
nfs_error(enum nfsstat status)
{
if (!(boothowto & RB_DEBUG))
return;
switch (status) {
case NFSERR_PERM:
printf("NFS: Not owner.\n");
break;
case NFSERR_NOENT:
#ifdef NFS_OPS_DEBUG
printf("NFS: No such file or directory.\n");
#endif /* NFS_OPS_DEBUG */
break;
case NFSERR_IO:
printf("NFS: IO ERROR occurred on NFS server.\n");
break;
case NFSERR_NXIO:
printf("NFS: No such device or address.\n");
break;
case NFSERR_ACCES:
printf("NFS: Permission denied.\n");
break;
case NFSERR_EXIST:
printf("NFS: File exists.\n");
break;
case NFSERR_NODEV:
printf("NFS: No such device.\n");
break;
case NFSERR_NOTDIR:
printf("NFS: Not a directory.\n");
break;
case NFSERR_ISDIR:
printf("NFS: Is a directory.\n");
break;
case NFSERR_FBIG:
printf("NFS: File too large.\n");
break;
case NFSERR_NOSPC:
printf("NFS: No space left on device.\n");
break;
case NFSERR_ROFS:
printf("NFS: Read-only filesystem.\n");
break;
case NFSERR_NAMETOOLONG:
printf("NFS: File name too long.\n");
break;
case NFSERR_NOTEMPTY:
printf("NFS: Directory not empty.\n");
break;
case NFSERR_DQUOT:
printf("NFS: Disk quota exceeded.\n");
break;
case NFSERR_STALE:
printf("NFS: Stale file handle.\n");
break;
case NFSERR_WFLUSH:
printf("NFS: server's write cache has been flushed.\n");
break;
default:
printf("NFS: unknown error.\n");
break;
}
}
struct nfs_file *
nfslookup(struct nfs_file *dir, char *name, int *nstat)
{
static struct nfs_file cd;
diropargs dirop;
diropres res_lookup;
enum clnt_stat status;
*nstat = (int)NFS_OK;
bcopy(&dir->fh.fh2, &dirop.dir, NFS_FHSIZE);
dirop.name = name;
status = CLNT_CALL(root_CLIENT, NFSPROC_LOOKUP, xdr_diropargs,
(caddr_t)&dirop, xdr_diropres, (caddr_t)&res_lookup,
zero_timeout);
if (status != RPC_SUCCESS) {
dprintf("lookup: RPC error.\n");
return (NULL);
}
if (res_lookup.status != NFS_OK) {
nfs_error(res_lookup.status);
*nstat = (int)res_lookup.status;
return (NULL);
}
bzero((caddr_t)&cd, sizeof (struct nfs_file));
cd.version = NFS_VERSION;
cd.ftype.type2 = res_lookup.diropres_u.diropres.attributes.type;
bcopy(&res_lookup.diropres_u.diropres.file, &cd.fh.fh2, NFS_FHSIZE);
return (&cd);
}
/*
* Gets symbolic link into pathname.
*/
int
nfsgetsymlink(struct nfs_file *cfile, char **path)
{
enum clnt_stat status;
struct readlinkres linkres;
static char symlink_path[NFS_MAXPATHLEN];
/*
* linkres needs a zeroed buffer to place path data into:
*/
bzero(symlink_path, NFS_MAXPATHLEN);
linkres.readlinkres_u.data = &symlink_path[0];
status = CLNT_CALL(root_CLIENT, NFSPROC_READLINK,
xdr_nfs_fh, (caddr_t)&cfile->fh.fh2,
xdr_readlinkres, (caddr_t)&linkres, zero_timeout);
if (status != RPC_SUCCESS) {
dprintf("nfsgetsymlink: RPC call failed.\n");
return (-1);
}
if (linkres.status != NFS_OK) {
nfs_error(linkres.status);
return (linkres.status);
}
*path = linkres.readlinkres_u.data;
return (NFS_OK);
}