/* * 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 V4 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 static struct timeval zero_timeout = {0, 0}; /* default */ /* * NFS Version 4 specific functions */ ssize_t nfs4read(struct nfs_file *filep, char *buf, size_t size) { enum clnt_stat status; read4arg_t readargs; read4res_t readres; char *buf_offset; uint_t count = 0; uint_t readcnt = 0; bool_t done = FALSE; struct timeval timeout; int framing_errs = 0; static uint_t pos; static char ind[] = "|/-\\"; static int blks_read; utf8string str; char tagname[] = "inetboot read"; bzero(&readres, sizeof (readres)); str.utf8string_len = sizeof (tagname) - 1; str.utf8string_val = tagname; /* * read */ buf_offset = buf; if (nfs_readsize == 0) nfs_readsize = READ4_SIZE; if (size < nfs_readsize) readargs.r_count = size; else readargs.r_count = nfs_readsize; if (filep->fh.fh4.len > 0) compound_init(&readargs.r_arg, &str, 0, 2, &filep->fh.fh4); else compound_init(&readargs.r_arg, &str, 0, 2, NULL); readargs.r_opread = OP_READ; /* * zero out the stateid field */ bzero(&readargs.r_stateid, sizeof (readargs.r_stateid)); readargs.r_offset = filep->offset; do { readres.r_data_val = buf_offset; if ((count + readargs.r_count) > size) readargs.r_count = size - count; timeout.tv_sec = NFS_REXMIT_MIN; timeout.tv_usec = 0; do { status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_read4_args, (caddr_t)&readargs, xdr_read4_res, (caddr_t)&readres, timeout); if (status == RPC_TIMEDOUT) { dprintf("NFS read(%d) timed out. Retrying...\n", readargs.r_count); if (errno == ETIMEDOUT) framing_errs++; if (framing_errs > NFS_MAX_FERRS && readargs.r_count > NFS_READ_DECR) { readargs.r_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; } } } while (status == RPC_TIMEDOUT); if (status != RPC_SUCCESS) return (-1); if (readres.r_status != NFS4_OK) { nfs4_error(readres.r_status); return (-1); } readcnt = readres.r_data_len; if (readres.r_eof == TRUE) done = TRUE; if (readcnt < readargs.r_count) { #ifdef NFS_OPS_DEBUG if ((boothowto & DBFLAGS) == DBFLAGS) printf("nfs4read: partial read %d instead " "of %d\n", readcnt, readargs.count); #endif done = TRUE; } count += readcnt; filep->offset += readcnt; buf_offset += readcnt; readargs.r_offset += readcnt; if ((blks_read++ & 0x3) == 0) printf("%c\b", ind[pos++ & 3]); } while (count < size && !done); return (count); } static vtype_t nf4_to_vt[] = { VBAD, VREG, VDIR, VBLK, VCHR, VLNK, VSOCK, VFIFO }; int nfs4getattr(struct nfs_file *nfp, struct vattr *vap) { enum clnt_stat status; attr4_bitmap1_t bitmap1; attr4_bitmap2_t bitmap2; getattr4arg_t getattrargs; getattr4res_t getattrres; b_fattr4_t *bfattr4; utf8string str; char tagname[] = "inetboot getattr"; bzero(&getattrres, sizeof (getattrres)); /* * Putfh */ str.utf8string_len = sizeof (tagname) - 1; str.utf8string_val = tagname; if (nfp->fh.fh4.len > 0) compound_init(&getattrargs.ga_arg, &str, 0, 2, &nfp->fh.fh4); else compound_init(&getattrargs.ga_arg, &str, 0, 2, NULL); /* * getattr */ getattrargs.ga_opgetattr = OP_GETATTR; /* * Set up the attribute bitmap. We pretty much need everything * except for the filehandle and supported attrs. */ bitmap1.word = 0; bitmap1.bm_fattr4_type = 1; bitmap1.bm_fattr4_size = 1; bitmap1.bm_fattr4_fileid = 1; bitmap2.word = 0; bitmap2.bm_fattr4_mode = 1; bitmap2.bm_fattr4_time_access = 1; bitmap2.bm_fattr4_time_metadata = 1; bitmap2.bm_fattr4_time_modify = 1; getattrargs.ga_attr_req.b_bitmap_len = NFS4_MAX_BITWORDS; getattrargs.ga_attr_req.b_bitmap_val[0] = bitmap1.word; getattrargs.ga_attr_req.b_bitmap_val[1] = bitmap2.word; status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_getattr4_args, (caddr_t)&getattrargs, xdr_getattr4_res, (caddr_t)&getattrres, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs4getattr: RPC error %d\n", status); return (-1); } if (getattrres.gr_attr_status != NFS4_OK) { nfs4_error(getattrres.gr_attr_status); return (getattrres.gr_attr_status); } bfattr4 = &getattrres.gr_attrs; if (vap->va_mask & AT_TYPE) { if (bfattr4->b_fattr4_type < NF4REG || bfattr4->b_fattr4_type > NF4FIFO) vap->va_type = VBAD; else vap->va_type = nf4_to_vt[bfattr4->b_fattr4_type]; } if (vap->va_mask & AT_MODE) vap->va_mode = (mode_t)bfattr4->b_fattr4_mode; if (vap->va_mask & AT_SIZE) vap->va_size = (u_offset_t)bfattr4->b_fattr4_size; if (vap->va_mask & AT_NODEID) vap->va_nodeid = (uint64_t)bfattr4->b_fattr4_fileid; /* * XXX - may need to do something more here. */ if (vap->va_mask & AT_ATIME) { vap->va_atime.tv_sec = bfattr4->b_fattr4_time_access.seconds; vap->va_atime.tv_nsec = bfattr4->b_fattr4_time_access.nseconds; } if (vap->va_mask & AT_CTIME) { vap->va_ctime.tv_sec = bfattr4->b_fattr4_time_metadata.seconds; vap->va_ctime.tv_nsec = bfattr4->b_fattr4_time_metadata.nseconds; } if (vap->va_mask & AT_MTIME) { vap->va_mtime.tv_sec = bfattr4->b_fattr4_time_modify.seconds; vap->va_mtime.tv_nsec = bfattr4->b_fattr4_time_modify.nseconds; } return (NFS4_OK); } /* * Display nfs error messages. */ /*ARGSUSED*/ void nfs4_error(enum nfsstat4 status) { if (!(boothowto & RB_DEBUG)) return; switch (status) { case NFS4_OK: printf("NFS: No error.\n"); break; case NFS4ERR_PERM: printf("NFS: Not owner.\n"); break; case NFS4ERR_NOENT: #ifdef NFS_OPS_DEBUG printf("NFS: No such file or directory.\n"); #endif /* NFS_OPS_DEBUG */ break; case NFS4ERR_IO: printf("NFS: IO ERROR occurred on NFS server.\n"); break; case NFS4ERR_NXIO: printf("NFS: No such device or address.\n"); break; case NFS4ERR_ACCESS: printf("NFS: Permission denied.\n"); break; case NFS4ERR_EXIST: printf("NFS: File exists.\n"); break; case NFS4ERR_XDEV: printf("NFS: Cross device hard link.\n"); break; case NFS4ERR_NOTDIR: printf("NFS: Not a directory.\n"); break; case NFS4ERR_ISDIR: printf("NFS: Is a directory.\n"); break; case NFS4ERR_INVAL: printf("NFS: Invalid argument.\n"); break; case NFS4ERR_FBIG: printf("NFS: File too large.\n"); break; case NFS4ERR_NOSPC: printf("NFS: No space left on device.\n"); break; case NFS4ERR_ROFS: printf("NFS: Read-only filesystem.\n"); break; case NFS4ERR_MLINK: printf("NFS: Too many hard links.\n"); break; case NFS4ERR_NAMETOOLONG: printf("NFS: File name too long.\n"); break; case NFS4ERR_NOTEMPTY: printf("NFS: Directory not empty.\n"); break; case NFS4ERR_DQUOT: printf("NFS: Disk quota exceeded.\n"); break; case NFS4ERR_STALE: printf("NFS: Stale file handle.\n"); break; case NFS4ERR_BADHANDLE: printf("NFS: Illegal NFS file handle.\n"); break; case NFS4ERR_BAD_COOKIE: printf("NFS: Stale Cookie.\n"); break; case NFS4ERR_NOTSUPP: printf("NFS: Operation is not supported.\n"); break; case NFS4ERR_TOOSMALL: printf("NFS: Buffer too small.\n"); break; case NFS4ERR_SERVERFAULT: printf("NFS: Server fault.\n"); break; case NFS4ERR_BADTYPE: printf("NFS: Unsupported object type.\n"); break; case NFS4ERR_BAD_STATEID: printf("NFS: Bad stateid\n"); break; case NFS4ERR_BAD_SEQID: printf("NFS: Bad seqid\n"); break; default: printf("NFS: unknown error.\n"); break; } } /* * lookup one component. for multicomponent lookup use a driver like lookup(). */ struct nfs_file * nfs4lookup(struct nfs_file *dir, char *name, int *nstat) { static struct nfs_file cd; attr4_bitmap1_t bitmap1; lookup4arg_t lookupargs; lookup4res_t lookupres; enum clnt_stat status; utf8string str; char tagname[] = "inetboot lookup"; /* * NFSv4 uses a special LOOKUPP op * for looking up the parent directory. */ if (strcmp(name, "..") == 0) return (nfs4lookupp(dir, nstat, NULL)); *nstat = (int)NFS4_OK; bzero(&lookupres, sizeof (lookupres)); /* * Check if we have a filehandle and initialize the compound * with putfh or putrootfh appropriately. */ str.utf8string_len = sizeof (tagname) - 1; str.utf8string_val = tagname; if (dir->fh.fh4.len > 0) compound_init(&lookupargs.la_arg, &str, 0, 3, &dir->fh.fh4); else compound_init(&lookupargs.la_arg, &str, 0, 3, NULL); /* * lookup */ lookupargs.la_oplookup = OP_LOOKUP; /* * convert the pathname from char * to utf8string */ lookupargs.la_pathname.utf8string_len = strlen(name); lookupargs.la_pathname.utf8string_val = bkmem_alloc(lookupargs.la_pathname.utf8string_len); if (lookupargs.la_pathname.utf8string_val == NULL) { dprintf("nfs4lookup: bkmem_alloc failed\n"); return (NULL); } bcopy(name, lookupargs.la_pathname.utf8string_val, lookupargs.la_pathname.utf8string_len); /* * Setup the attr bitmap. All we need is the type and filehandle info */ lookupargs.la_opgetattr = OP_GETATTR; bitmap1.word = 0; bitmap1.bm_fattr4_type = 1; bitmap1.bm_fattr4_filehandle = 1; lookupargs.la_attr_req.b_bitmap_len = 1; lookupargs.la_attr_req.b_bitmap_val[0] = bitmap1.word; lookupargs.la_attr_req.b_bitmap_val[1] = 0; status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_lookup4_args, (caddr_t)&lookupargs, xdr_lookup4_res, (caddr_t)&lookupres, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs4lookup: RPC error. status %d\n", status); return (NULL); } if (lookupres.lr_lookup_status != NFS4_OK) { #ifdef DEBUG dprintf("nfs4lookup: lookup status = %d\n", lookupres.lr_lookup_status); #endif nfs4_error(lookupres.lr_lookup_status); *nstat = (int)lookupres.lr_lookup_status; if (lookupargs.la_pathname.utf8string_val != NULL) bkmem_free(lookupargs.la_pathname.utf8string_val, lookupargs.la_pathname.utf8string_len); return (NULL); } if (lookupres.lr_attr_status != NFS4_OK) { #ifdef DEBUG dprintf("nfs4lookup: getattr status = %d\n", lookupres.lr_attr_status); #endif nfs4_error(lookupres.lr_attr_status); *nstat = (int)lookupres.lr_attr_status; if (lookupargs.la_pathname.utf8string_val != NULL) bkmem_free(lookupargs.la_pathname.utf8string_val, lookupargs.la_pathname.utf8string_len); return (NULL); } /* * We have all the information we need to update the file pointer */ bzero((caddr_t)&cd, sizeof (struct nfs_file)); cd.version = NFS_V4; cd.ftype.type4 = lookupres.lr_attrs.b_fattr4_type; cd.fh.fh4.len = lookupres.lr_attrs.b_fattr4_filehandle.len; bcopy(lookupres.lr_attrs.b_fattr4_filehandle.data, cd.fh.fh4.data, cd.fh.fh4.len); /* * Free the arg string */ if (lookupargs.la_pathname.utf8string_val != NULL) bkmem_free(lookupargs.la_pathname.utf8string_val, lookupargs.la_pathname.utf8string_len); return (&cd); } /* * lookup parent directory. */ struct nfs_file * nfs4lookupp(struct nfs_file *dir, int *nstat, uint64_t *fileid) { static struct nfs_file cd; attr4_bitmap1_t bitmap1; lookupp4arg_t lookuppargs; lookup4res_t lookupres; enum clnt_stat status; utf8string str; char tagname[] = "inetboot lookupp"; *nstat = (int)NFS4_OK; bzero(&lookupres, sizeof (lookupres)); /* * Check if we have a filehandle and initialize the compound * with putfh or putrootfh appropriately. */ str.utf8string_len = sizeof (tagname) - 1; str.utf8string_val = tagname; if (dir->fh.fh4.len > 0) compound_init(&lookuppargs.la_arg, &str, 0, 3, &dir->fh.fh4); else compound_init(&lookuppargs.la_arg, &str, 0, 3, NULL); /* * lookupp */ lookuppargs.la_oplookupp = OP_LOOKUPP; /* * Setup the attr bitmap. Normally, all we need is the type and * filehandle info, but getdents might require the fileid of the * parent. */ lookuppargs.la_opgetattr = OP_GETATTR; bitmap1.word = 0; bitmap1.bm_fattr4_type = 1; bitmap1.bm_fattr4_filehandle = 1; if (fileid != NULL) bitmap1.bm_fattr4_fileid = 1; lookuppargs.la_attr_req.b_bitmap_len = 1; lookuppargs.la_attr_req.b_bitmap_val[0] = bitmap1.word; lookuppargs.la_attr_req.b_bitmap_val[1] = 0; status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_lookupp4_args, (caddr_t)&lookuppargs, xdr_lookup4_res, (caddr_t)&lookupres, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs4lookupp: RPC error. status %d\n", status); return (NULL); } if (lookupres.lr_lookup_status != NFS4_OK) { #ifdef DEBUG dprintf("nfs4lookupp: lookupp status = %d\n", lookupres.lr_lookup_status); #endif nfs4_error(lookupres.lr_lookup_status); *nstat = (int)lookupres.lr_lookup_status; return (NULL); } if (lookupres.lr_attr_status != NFS4_OK) { #ifdef DEBUG dprintf("nfs4lookupp: getattr status = %d\n", lookupres.lr_attr_status); #endif nfs4_error(lookupres.lr_attr_status); *nstat = (int)lookupres.lr_attr_status; return (NULL); } /* * We have all the information we need to update the file pointer */ bzero((caddr_t)&cd, sizeof (struct nfs_file)); cd.version = NFS_V4; cd.ftype.type4 = lookupres.lr_attrs.b_fattr4_type; cd.fh.fh4.len = lookupres.lr_attrs.b_fattr4_filehandle.len; bcopy(lookupres.lr_attrs.b_fattr4_filehandle.data, cd.fh.fh4.data, cd.fh.fh4.len); /* * Fill in the fileid if the user passed in one */ if (fileid != NULL) *fileid = lookupres.lr_attrs.b_fattr4_fileid; return (&cd); } /* * Gets symbolic link into pathname. */ int nfs4getsymlink(struct nfs_file *cfile, char **path) { enum clnt_stat status; readlink4arg_t readlinkargs; readlink4res_t readlinkres; static char symlink_path[NFS_MAXPATHLEN]; int spathlen; utf8string str; char tagname[] = "inetboot getsymlink"; int error = NFS4_OK; bzero(&readlinkres, sizeof (readlinkres)); /* * readlink */ str.utf8string_len = sizeof (tagname) - 1; str.utf8string_val = tagname; if (cfile->fh.fh4.len > 0) compound_init(&readlinkargs.rl_arg, &str, 0, 2, &cfile->fh.fh4); else compound_init(&readlinkargs.rl_arg, &str, 0, 2, NULL); readlinkargs.rl_opreadlink = OP_READLINK; status = CLNT_CALL(root_CLIENT, NFSPROC4_COMPOUND, xdr_readlink4_args, (caddr_t)&readlinkargs, xdr_readlink4_res, (caddr_t)&readlinkres, zero_timeout); if (status != RPC_SUCCESS) { dprintf("nfs4getsymlink: RPC readlink error %d\n", status); error = -1; goto out; } if (readlinkres.rl_status != NFS4_OK) { nfs4_error(readlinkres.rl_status); error = readlinkres.rl_status; goto out; } /* * Convert the utf8string to a normal character string */ spathlen = readlinkres.rl_link.utf8string_len; if (spathlen <= 0 || readlinkres.rl_link.utf8string_val == NULL) { *path = NULL; error = readlinkres.rl_status; goto out; } bcopy(readlinkres.rl_link.utf8string_val, symlink_path, spathlen); symlink_path[spathlen] = '\0'; *path = symlink_path; out: /* * Free the results */ if (readlinkres.rl_link.utf8string_val != NULL) bkmem_free(readlinkres.rl_link.utf8string_val, spathlen); return (error); } /* * Should just forget about the tag, but will leave in support for the time * being. */ void compound_init(b_compound_t *cp, utf8string *str, uint_t mvers, uint_t arglen, struct nfs_bfh4 *pfh) { if (str == NULL || str->utf8string_len == 0) { cp->ca_tag.utf8string_len = 0; cp->ca_tag.utf8string_val = NULL; } else { cp->ca_tag.utf8string_len = str->utf8string_len; cp->ca_tag.utf8string_val = str->utf8string_val; } cp->ca_minorversion = mvers; cp->ca_argarray_len = arglen; if (pfh == NULL) { cp->ca_isputrootfh = TRUE; cp->ca_opputfh.pf_opnum = OP_PUTROOTFH; } else { cp->ca_isputrootfh = FALSE; cp->ca_opputfh.pf_opnum = OP_PUTFH; cp->ca_opputfh.pf_filehandle.len = pfh->len; bcopy(pfh->data, cp->ca_opputfh.pf_filehandle.data, pfh->len); } }