1
0
Files
2022-09-29 17:59:04 +03:00

1009 lines
25 KiB
C

/*
* Copyright 1990 Silicon Graphics, Inc. All rights reserved.
*
* Sun Network File System (NFS).
*/
#include <stdio.h>
#include <rpc/types.h>
#include <sys/socket.h>
#include <rpc/xdr.h>
#ifdef sun
#include <errno.h>
#include <protocols/nfs.h>
#else
#include <sys/fs/nfs.h>
#endif
#include <netinet/in.h> /* for ntohl */
#include "debug.h"
#include "enum.h"
#include "heap.h"
#include "protodefs.h"
#include "protocols/sunrpc.h"
#include "strings.h"
char nfsname[] = "nfs";
enum nfsfid {
PROC, FH, STATUS,
TYPE, MODE, NLINK, UID, GID, SIZE, BLOCKSIZE, RDEV, BLOCKS, FSID,
NODEID, ATIME, MTIME, CTIME,
OFFSET, COUNT, TOTCOUNT, BEGOFF,
TSIZE, BSIZE, BFREE, BAVAIL,
NAME, PATH, TOFH, TONAME, NEOF
};
/*
* Selected NFS fields. Field offsets are not used, so we order by degree
* of commonality and then by procedure number.
*/
#define TVSIZE sizeof(struct timeval)
#define PFI_TIME PFI_ADDR
static ProtoField nfsfields[] = {
/* proc is a pseudo-field used to re-decode the sunrpc proc number */
PFI_VAR("proc", "Procedure", PROC, 0, PV_TERSE),
/* the common arguments and results members */
PFI_BYTE("fh", "File Handle", FH, NFS_FHSIZE,PV_DEFAULT),
PFI_UINT("status", "Status", STATUS, enum nfsstat,PV_TERSE),
/* nfsfattr and sattr members */
PFI_UINT("type", "File Type", TYPE,enum nfsftype,PV_DEFAULT),
PFI_UINT("mode", "Mode", MODE, u_long, PV_DEFAULT),
PFI_UINT("nlink", "Number of Links", NLINK, u_long, PV_DEFAULT),
PFI_SINT("uid", "User ID", UID, long, PV_DEFAULT),
PFI_SINT("gid", "Group ID", GID, long, PV_DEFAULT),
PFI_UINT("size", "Size", SIZE, u_long, PV_DEFAULT),
PFI_UINT("blocksize","Block Size", BLOCKSIZE,u_long,PV_DEFAULT),
PFI_UINT("rdev", "Device Number", RDEV, u_long, PV_DEFAULT),
PFI_UINT("blocks", "Blocks", BLOCKS, u_long, PV_DEFAULT),
PFI_UINT("fsid", "File System ID", FSID, u_long, PV_DEFAULT),
PFI_UINT("nodeid", "File Node ID", NODEID, u_long, PV_DEFAULT),
PFI_TIME("atime", "Access Time", ATIME, TVSIZE, PV_DEFAULT),
PFI_TIME("mtime", "Modification Time",MTIME, TVSIZE, PV_DEFAULT),
PFI_TIME("ctime", "Change Time", CTIME, TVSIZE, PV_DEFAULT),
/* readargs and writeargs members */
PFI_UINT("offset", "Offset", OFFSET, u_long, PV_DEFAULT),
PFI_UINT("count", "Count", COUNT, u_long, PV_DEFAULT),
PFI_UINT("totcount","Total Count", TOTCOUNT,u_long,PV_DEFAULT),
PFI_UINT("begoff", "Beginning Offset", BEGOFF, u_long, PV_DEFAULT),
/* nfsstatfsok members, except for blocks */
PFI_UINT("tsize", "Transfer Size", TSIZE, u_long, PV_DEFAULT),
PFI_UINT("bsize", "Block Size", BSIZE, u_long, PV_DEFAULT),
PFI_UINT("bfree", "Blocks Free", BFREE, u_long, PV_DEFAULT),
PFI_UINT("bavail", "Blocks Available", BAVAIL, u_long, PV_DEFAULT),
/* diropargs, readlinkres, renameargs, etc. */
PFI_VAR("name", "Filename", NAME, NFS_MAXNAMLEN,PV_DEFAULT),
PFI_VAR("path", "Pathname", PATH,NFS_MAXPATHLEN,PV_DEFAULT),
PFI_BYTE("tofh", "Target File Handle",TOFH,NFS_FHSIZE,PV_DEFAULT),
PFI_VAR("toname", "Target Filename", TONAME,NFS_MAXNAMLEN,PV_DEFAULT),
PFI_UINT("eof", "EOF", NEOF, u_long, PV_DEFAULT),
};
#define NFSFID(pf) ((enum nfsfid) (pf)->pf_id)
#define NFSFIELD(fid) nfsfields[(int) fid]
/*
* Nickname macros defined in the sunrpc protocol's scope.
*/
static ProtoMacro nfsnicknames[] = {
PMI("NFS", nfsname),
};
/*
* Some enumerations. First, NFS procedure dispatch numbers.
*/
static Enumeration nfsproc;
static Enumerator nfsprocvec[] = {
EI_VAL("NULL", RFS_NULL),
EI_VAL("GETATTR", RFS_GETATTR),
EI_VAL("SETATTR", RFS_SETATTR),
EI_VAL("ROOT", RFS_ROOT),
EI_VAL("LOOKUP", RFS_LOOKUP),
EI_VAL("READLINK", RFS_READLINK),
EI_VAL("READ", RFS_READ),
EI_VAL("WRITECACHE", RFS_WRITECACHE),
EI_VAL("WRITE", RFS_WRITE),
EI_VAL("CREATE", RFS_CREATE),
EI_VAL("REMOVE", RFS_REMOVE),
EI_VAL("RENAME", RFS_RENAME),
EI_VAL("LINK", RFS_LINK),
EI_VAL("SYMLINK", RFS_SYMLINK),
EI_VAL("MKDIR", RFS_MKDIR),
EI_VAL("RMDIR", RFS_RMDIR),
EI_VAL("READDIR", RFS_READDIR),
EI_VAL("STATFS", RFS_STATFS)
};
/*
* NFS error status.
*/
static Enumeration nfsstat;
static Enumerator nfsstatvec[] = {
EI(NFS_OK),
EI(NFSERR_PERM),
EI(NFSERR_NOENT),
EI(NFSERR_IO),
EI(NFSERR_NXIO),
EI(NFSERR_ACCES),
EI(NFSERR_EXIST),
EI(NFSERR_NODEV),
EI(NFSERR_NOTDIR),
EI(NFSERR_ISDIR),
EI(NFSERR_FBIG),
EI(NFSERR_NOSPC),
EI(NFSERR_ROFS),
EI(NFSERR_NAMETOOLONG),
EI(NFSERR_NOTEMPTY),
EI(NFSERR_DQUOT),
EI(NFSERR_STALE),
EI_VAL("NFSERR_WFLUSH", 99) /* according to NFS4.0 nfs_prot.x */
};
/*
* NFS file types.
*/
static Enumeration nfsftype;
static Enumerator nfsftypevec[] = {
EI(NFNON),
EI(NFREG),
EI(NFDIR),
EI(NFBLK),
EI(NFCHR),
EI(NFLNK)
};
/*
* Decode state struct and xdr filter for nfs-specific state.
*/
struct nfsdecodestate {
ProtoDecodeState state; /* base class state */
u_long vers; /* NFS protocol version */
u_long proc; /* and procedure number */
enum msg_type direction; /* call or reply */
u_int fragno; /* fragment number */
};
static int
xdr_nfsstate(xdr, nds)
XDR *xdr;
struct nfsdecodestate *nds;
{
return xdr_u_long(xdr, &nds->vers)
&& xdr_u_long(xdr, &nds->proc)
&& xdr_enum(xdr, (enum_t *) &nds->direction)
&& xdr_u_int(xdr, &nds->fragno);
}
/*
* Most of the NFS protocol ops can be stubbed.
*/
#define nfs_setopt pr_stub_setopt
#define nfs_embed pr_stub_embed
#define nfs_compile pr_stub_compile
#define nfs_match pr_stub_match
DefineProtocol(nfs, nfsname, "Sun Network File System", PRID_NFS,
DS_BIG_ENDIAN, NFS_MAXDATA, 0, 0, 0, 0, xdr_nfsstate);
static int
nfs_init()
{
if (!pr_register(&nfs_proto, nfsfields, lengthof(nfsfields),
lengthof(nfsprocvec) + lengthof(nfsstatvec)
+ lengthof(nfsftypevec))) {
return 0;
}
if (!pr_nest(&nfs_proto, PRID_SUNRPC, NFS_PROGRAM, nfsnicknames,
lengthof(nfsnicknames))) {
return 0;
}
en_init(&nfsproc, nfsprocvec, lengthof(nfsprocvec), &nfs_proto);
en_init(&nfsstat, nfsstatvec, lengthof(nfsstatvec), &nfs_proto);
en_init(&nfsftype, nfsftypevec, lengthof(nfsftypevec), &nfs_proto);
pr_addconstmacro(&nfs_proto, "isfifo", "type == NFCHR && rdev == ~0");
return 1;
}
/*
* Resolve a timeval or file handle expression into an expr node.
* We assume interesting file handles on a given Unix server can be
* distinguished by fsid and nodeid (dev&ino).
*/
#define EXOP_FHANDLE EXOP_ADDRESS
#define EXOP_TIMEVAL EXOP_ADDRESS
struct fhandle {
u_long fsid;
u_long nodeid;
};
#define ex_fh(ex) A_CAST(&(ex)->ex_addr, struct fhandle)
#define ex_tv(ex) A_CAST(&(ex)->ex_addr, struct timeval)
/* ARGSUSED */
static Expr *
nfs_resolve(char *str, int len, struct snooper *sn)
{
Expr *ex;
struct fhandle fh;
struct timeval tv;
if (sscanf(str, "%lx:%lu", &fh.fsid, &fh.nodeid) == 2) {
ex = expr(EXOP_FHANDLE, EX_NULLARY, str);
*ex_fh(ex) = fh;
return ex;
}
if (sscanf(str, "%ld.%ld", &tv.tv_sec, &tv.tv_usec) == 2) {
ex = expr(EXOP_TIMEVAL, EX_NULLARY, str);
*ex_tv(ex) = tv;
return ex;
}
return ex_string(str, len);
}
/*
* Template for the front part of an SGI server's file handle.
*/
struct sgifhfront {
u_short pad; /* pad to align file id */
dev_t fsid; /* filesystem id */
u_short len; /* file id length */
u_short pad2; /* pad to align nodeid */
/*hack, hack; irix6.4 coredump here ino_t */unsigned nodeid; /* node id */
};
#define sgifh(fh) ((struct sgifhfront *)(fh))
/*
* Macros used by nfs_fetch and its kids. The caller must declare ds, rex,
* and fid appropriately.
*/
#define NFS_FETCH_U_LONG(longfid) { \
if (!ds_u_long(ds, &rex->ex_val)) \
return 0; \
if (fid == (longfid)) { \
rex->ex_op = EXOP_NUMBER; \
return 1; \
} \
}
fhandle_t *nfs_fetch_fh(DataStream *, u_int *);
int nfs_fetch_fattr(enum nfsfid, DataStream *, Expr *);
int nfs_fetch_sattr(enum nfsfid, DataStream *, Expr *);
int nfs_fetch_string(DataStream *, long, struct string *, long *, long *);
#define NFS_FETCH_FH(fhfid) { \
fhandle_t *fh = nfs_fetch_fh(ds, 0); \
if (fh == 0) \
return 0; \
if (fid == (fhfid)) { \
rex->ex_op = EXOP_FHANDLE; \
ex_fh(rex)->fsid = sgifh(fh)->fsid; \
ex_fh(rex)->nodeid = sgifh(fh)->nodeid; \
return 1; \
} \
}
#define NFS_FETCH_TIMEVAL(tvfid) { \
struct timeval *tv = ex_tv(rex); \
if (!ds_long(ds, &tv->tv_sec) || !ds_long(ds, &tv->tv_usec)) \
return 0; \
if (fid == (tvfid)) { \
rex->ex_op = EXOP_TIMEVAL; \
return 1; \
} \
}
#define NFS_FETCH_STRING(sfid) { \
if (!nfs_fetch_string(ds, NFSFIELD(sfid).pf_cookie, &rex->ex_str, \
0, 0)) { \
return 0; \
} \
if (fid == sfid) { \
rex->ex_op = EXOP_STRING; \
return 1; \
} \
}
static int
nfs_fetch(ProtoField *pf, DataStream *ds, ProtoStack *ps, Expr *rex)
{
enum nfsfid fid;
struct sunrpcframe *srf;
fid = NFSFID(pf);
srf = PS_TOP(ps, struct sunrpcframe);
if (fid == PROC) {
rex->ex_op = EXOP_NUMBER;
rex->ex_val = srf->srf_proc;
return 1;
}
/*
* NFS arguments (well, who knows about ROOT and WRITECACHE?) begin
* with a file handle. Results begin with an nfsstat.
*/
if (srf->srf_direction == CALL) {
NFS_FETCH_FH(FH);
} else {
NFS_FETCH_U_LONG(STATUS);
}
switch (srf->srf_proc) {
case RFS_NULL:
case RFS_ROOT:
case RFS_WRITECACHE:
break;
case RFS_GETATTR:
if (srf->srf_direction == REPLY)
return nfs_fetch_fattr(fid, ds, rex);
break;
case RFS_SETATTR:
if (srf->srf_direction == CALL)
return nfs_fetch_sattr(fid, ds, rex);
return nfs_fetch_fattr(fid, ds, rex);
case RFS_LOOKUP:
if (srf->srf_direction == CALL) {
NFS_FETCH_FH(FH);
NFS_FETCH_STRING(NAME);
} else {
NFS_FETCH_FH(FH);
return nfs_fetch_fattr(fid, ds, rex);
}
break;
case RFS_READLINK:
if (srf->srf_direction == REPLY) {
NFS_FETCH_STRING(PATH);
}
break;
case RFS_READ:
if (srf->srf_direction == CALL) {
NFS_FETCH_U_LONG(OFFSET);
NFS_FETCH_U_LONG(COUNT);
NFS_FETCH_U_LONG(TOTCOUNT);
return 0;
}
return nfs_fetch_fattr(fid, ds, rex);
case RFS_WRITE:
if (srf->srf_direction == CALL) {
NFS_FETCH_U_LONG(BEGOFF);
NFS_FETCH_U_LONG(OFFSET);
NFS_FETCH_U_LONG(TOTCOUNT);
NFS_FETCH_U_LONG(COUNT);
return 0;
}
return nfs_fetch_fattr(fid, ds, rex);
case RFS_CREATE:
case RFS_MKDIR:
if (srf->srf_direction == CALL) {
NFS_FETCH_STRING(NAME);
return nfs_fetch_sattr(fid, ds, rex);
}
NFS_FETCH_FH(FH);
return nfs_fetch_fattr(fid, ds, rex);
case RFS_REMOVE:
case RFS_RMDIR:
if (srf->srf_direction == CALL) {
NFS_FETCH_STRING(NAME);
}
break;
case RFS_RENAME:
if (srf->srf_direction == CALL) {
NFS_FETCH_STRING(NAME);
NFS_FETCH_FH(TOFH);
NFS_FETCH_STRING(TONAME);
}
break;
case RFS_LINK:
if (srf->srf_direction == CALL) {
NFS_FETCH_FH(TOFH);
NFS_FETCH_STRING(TONAME);
}
break;
case RFS_SYMLINK:
if (srf->srf_direction == CALL) {
NFS_FETCH_STRING(NAME);
NFS_FETCH_STRING(PATH);
return nfs_fetch_sattr(fid, ds, rex);
}
break;
case RFS_READDIR:
if (srf->srf_direction == CALL) {
NFS_FETCH_U_LONG(OFFSET);
NFS_FETCH_U_LONG(COUNT);
}
break;
case RFS_STATFS:
if (srf->srf_direction == REPLY) {
NFS_FETCH_U_LONG(TSIZE);
NFS_FETCH_U_LONG(BSIZE);
NFS_FETCH_U_LONG(BLOCKS);
NFS_FETCH_U_LONG(BFREE);
NFS_FETCH_U_LONG(BAVAIL);
}
}
return 0;
}
static fhandle_t *
nfs_fetch_fh(DataStream *ds, u_int *sizep)
{
fhandle_t *fh;
u_int size;
fh = (fhandle_t *) ds_inline(ds, sizeof *fh, BYTES_PER_XDR_UNIT);
if (fh) {
if (sizep)
*sizep = sizeof *fh;
return fh;
}
assert(ds->ds_count < sizeof *fh);
size = MAX(ds->ds_count, sizeof(struct sgifhfront));
if (sizep)
*sizep = size;
return (fhandle_t *) ds_inline(ds, size, BYTES_PER_XDR_UNIT);
}
static int
nfs_fetch_fattr(enum nfsfid fid, DataStream *ds, Expr *rex)
{
struct nfsfattr *na;
na = (struct nfsfattr *) ds_inline(ds, sizeof *na, BYTES_PER_XDR_UNIT);
if (na) {
rex->ex_op = EXOP_NUMBER;
switch (fid) {
case TYPE:
rex->ex_val = ntohl((u_long)na->na_type);
break;
case MODE:
rex->ex_val = ntohl(na->na_mode);
break;
case NLINK:
rex->ex_val = ntohl(na->na_nlink);
break;
case UID:
rex->ex_val = ntohl(na->na_uid);
break;
case GID:
rex->ex_val = ntohl(na->na_gid);
break;
case SIZE:
rex->ex_val = ntohl(na->na_size);
break;
case BLOCKSIZE:
rex->ex_val = ntohl(na->na_blocksize);
break;
case RDEV:
rex->ex_val = ntohl(na->na_rdev);
break;
case BLOCKS:
rex->ex_val = ntohl(na->na_blocks);
break;
case FSID:
rex->ex_val = ntohl(na->na_fsid);
break;
case NODEID:
rex->ex_val = ntohl(na->na_nodeid);
break;
case ATIME:
rex->ex_op = EXOP_TIMEVAL;
ex_tv(rex)->tv_sec = ntohl(na->na_atime.tv_sec);
ex_tv(rex)->tv_usec = ntohl(na->na_atime.tv_usec);
break;
case MTIME:
rex->ex_op = EXOP_TIMEVAL;
ex_tv(rex)->tv_sec = ntohl(na->na_mtime.tv_sec);
ex_tv(rex)->tv_usec = ntohl(na->na_mtime.tv_usec);
break;
case CTIME:
rex->ex_op = EXOP_TIMEVAL;
ex_tv(rex)->tv_sec = ntohl(na->na_ctime.tv_sec);
ex_tv(rex)->tv_usec = ntohl(na->na_ctime.tv_usec);
}
return 1;
}
NFS_FETCH_U_LONG(TYPE);
NFS_FETCH_U_LONG(MODE);
NFS_FETCH_U_LONG(NLINK);
NFS_FETCH_U_LONG(UID);
NFS_FETCH_U_LONG(GID);
NFS_FETCH_U_LONG(SIZE);
NFS_FETCH_U_LONG(BLOCKSIZE);
NFS_FETCH_U_LONG(RDEV);
NFS_FETCH_U_LONG(BLOCKS);
NFS_FETCH_U_LONG(FSID);
NFS_FETCH_U_LONG(NODEID);
NFS_FETCH_TIMEVAL(ATIME);
NFS_FETCH_TIMEVAL(MTIME);
NFS_FETCH_TIMEVAL(CTIME);
return 0;
}
static int
nfs_fetch_sattr(enum nfsfid fid, DataStream *ds, Expr *rex)
{
struct nfssattr *sa;
sa = (struct nfssattr *) ds_inline(ds, sizeof *sa, BYTES_PER_XDR_UNIT);
if (sa) {
rex->ex_op = EXOP_NUMBER;
switch (fid) {
case MODE:
rex->ex_val = ntohl(sa->sa_mode);
break;
case UID:
rex->ex_val = ntohl(sa->sa_uid);
break;
case GID:
rex->ex_val = ntohl(sa->sa_gid);
break;
case SIZE:
rex->ex_val = ntohl(sa->sa_size);
break;
case ATIME:
rex->ex_op = EXOP_TIMEVAL;
ex_tv(rex)->tv_sec = ntohl(sa->sa_atime.tv_sec);
ex_tv(rex)->tv_usec = ntohl(sa->sa_atime.tv_usec);
break;
case MTIME:
rex->ex_op = EXOP_TIMEVAL;
ex_tv(rex)->tv_sec = ntohl(sa->sa_mtime.tv_sec);
ex_tv(rex)->tv_usec = ntohl(sa->sa_mtime.tv_usec);
}
return 1;
}
NFS_FETCH_U_LONG(MODE);
NFS_FETCH_U_LONG(UID);
NFS_FETCH_U_LONG(GID);
NFS_FETCH_U_LONG(SIZE);
NFS_FETCH_TIMEVAL(ATIME);
NFS_FETCH_TIMEVAL(MTIME);
return 0;
}
#undef NFS_FETCH_U_LONG
#undef NFS_FETCH_FH
#undef NFS_FETCH_TIMEVAL
#undef NFS_FETCH_STRING
static int
nfs_fetch_string(DataStream *ds, long maxlen, struct string *sp, long *xdrlen,
long *size)
{
long len;
if (!ds_long(ds, &len))
return 0;
if (len < 0) {
len = 0;
return 0;
}
if (xdrlen)
*xdrlen = len;
if (maxlen > ds->ds_count);
maxlen = ds->ds_count;
if (len > maxlen)
len = maxlen;
sp->s_len = len;
sp->s_ptr = (char *) ds->ds_next;
len = RNDUP(len);
if (size)
*size = sizeof len + len;
ds->ds_count -= len;
ds->ds_next += len;
return 1;
}
#define NFS_DECODE_U_LONG(fid) { \
u_long val; \
(void) ds_u_long(ds, &val); \
pv_showfield(pv, &NFSFIELD(fid), &val, "%-10lu", val); \
}
void nfs_decode_dirents(DataStream *, PacketView *);
void nfs_decode_fh(enum nfsfid, DataStream *, PacketView *);
void nfs_decode_fattr(DataStream *, PacketView *);
void nfs_decode_sattr(DataStream *, PacketView *);
void nfs_decode_string(enum nfsfid, DataStream *, PacketView *);
static void
nfs_decode(DataStream *ds, ProtoStack *ps, PacketView *pv)
{
struct nfsdecodestate *nds;
struct sunrpcframe *srf;
nds = PS_STATE(ps, struct nfsdecodestate);
if (nds) {
nds->fragno++;
pv_printf(pv, "(%s %s, %u%s captured fragment)\n",
en_name(&nfsproc, nds->proc),
nds->direction == CALL ? "call" : "reply",
nds->fragno, (nds->fragno == 2) ? "nd" :
(nds->fragno == 3) ? "rd" : "th");
switch (nds->proc) {
case RFS_READ:
case RFS_WRITE:
if (pv->pv_level >= PV_VERBOSE)
pv_decodehex(pv, ds, 0, ds->ds_count);
break;
case RFS_READDIR:
/* XXX find valid dirent and resume decoding */;
}
return;
}
/*
* Show the NFS procedure name.
*/
srf = PS_TOP(ps, struct sunrpcframe);
pv_replayfield(pv, &NFSFIELD(PROC), &srf->srf_proc,
srf->srf_procoff, sizeof(u_long),
"%-10.10s", en_name(&nfsproc, srf->srf_proc));
if (srf->srf_proc >= RFS_NPROC)
return;
/*
* If there are more fragments, save decode state.
*/
if (srf->srf_morefrags) {
nds = new(struct nfsdecodestate);
nds->state.pds_mysize = sizeof *nds;
nds->state.pds_proto = &nfs_proto;
nds->vers = srf->srf_vers;
nds->proc = srf->srf_proc;
nds->direction = srf->srf_direction;
nds->fragno = 1;
ps->ps_state = &nds->state;
}
/*
* Show common call or reply fields.
*/
if (srf->srf_direction == CALL)
nfs_decode_fh(FH, ds, pv);
else {
enum nfsstat status;
(void) ds_long(ds, (long *) &status);
pv_showfield(pv, &NFSFIELD(STATUS), &status,
"%-18.18s", en_name(&nfsstat, (int) status));
if (status != NFS_OK)
return;
}
pv_break(pv);
switch (srf->srf_proc) {
case RFS_NULL:
case RFS_ROOT:
case RFS_WRITECACHE:
break;
case RFS_GETATTR:
if (srf->srf_direction == REPLY)
nfs_decode_fattr(ds, pv);
break;
case RFS_SETATTR:
if (srf->srf_direction == CALL)
nfs_decode_sattr(ds, pv);
else
nfs_decode_fattr(ds, pv);
break;
case RFS_LOOKUP:
if (srf->srf_direction == CALL) {
nfs_decode_fh(FH, ds, pv);
nfs_decode_string(NAME, ds, pv);
}
else {
nfs_decode_fh(FH, ds, pv);
nfs_decode_fattr(ds, pv);
}
break;
case RFS_READLINK:
if (srf->srf_direction == REPLY)
nfs_decode_string(PATH, ds, pv);
break;
case RFS_READ:
if (srf->srf_direction == CALL) {
NFS_DECODE_U_LONG(OFFSET);
NFS_DECODE_U_LONG(COUNT);
NFS_DECODE_U_LONG(TOTCOUNT);
} else {
nfs_decode_fattr(ds, pv);
NFS_DECODE_U_LONG(COUNT);
if (pv->pv_level >= PV_VERBOSE)
pv_decodehex(pv, ds, 0, ds->ds_count);
}
break;
case RFS_WRITE:
if (srf->srf_direction == CALL) {
NFS_DECODE_U_LONG(BEGOFF);
NFS_DECODE_U_LONG(OFFSET);
NFS_DECODE_U_LONG(TOTCOUNT);
NFS_DECODE_U_LONG(COUNT);
if (pv->pv_level >= PV_VERBOSE)
pv_decodehex(pv, ds, 0, ds->ds_count);
} else
nfs_decode_fattr(ds, pv);
break;
case RFS_CREATE:
case RFS_MKDIR:
if (srf->srf_direction == CALL) {
nfs_decode_string(NAME, ds, pv);
nfs_decode_sattr(ds, pv);
} else {
nfs_decode_fh(FH, ds, pv);
nfs_decode_fattr(ds, pv);
}
break;
case RFS_REMOVE:
case RFS_RMDIR:
if (srf->srf_direction == CALL)
nfs_decode_string(NAME, ds, pv);
break;
case RFS_RENAME:
if (srf->srf_direction == CALL) {
nfs_decode_string(NAME, ds, pv);
nfs_decode_fh(TOFH, ds, pv);
nfs_decode_string(TONAME, ds, pv);
}
break;
case RFS_LINK:
if (srf->srf_direction == CALL) {
nfs_decode_fh(TOFH, ds, pv);
nfs_decode_string(TONAME, ds, pv);
}
break;
case RFS_SYMLINK:
if (srf->srf_direction == CALL) {
nfs_decode_string(NAME, ds, pv);
nfs_decode_string(PATH, ds, pv);
nfs_decode_sattr(ds, pv);
}
break;
case RFS_READDIR:
if (srf->srf_direction == CALL) {
NFS_DECODE_U_LONG(OFFSET);
NFS_DECODE_U_LONG(COUNT);
} else
nfs_decode_dirents(ds, pv);
break;
case RFS_STATFS:
if (srf->srf_direction == REPLY) {
NFS_DECODE_U_LONG(TSIZE);
NFS_DECODE_U_LONG(BSIZE);
NFS_DECODE_U_LONG(BLOCKS);
NFS_DECODE_U_LONG(BFREE);
NFS_DECODE_U_LONG(BAVAIL);
}
break;
}
}
static void
nfs_decode_dirents(DataStream *ds, PacketView *pv)
{
long more;
for (;;) {
pv->pv_off += sizeof(more); /* should we show more? */
if (!ds_long(ds, &more))
return;
if (!more)
break;
NFS_DECODE_U_LONG(NODEID);
nfs_decode_string(NAME, ds, pv);
NFS_DECODE_U_LONG(OFFSET);
pv_break(pv);
}
NFS_DECODE_U_LONG(NEOF);
}
#undef NFS_DECODE_U_LONG
static void
nfs_decode_fh(enum nfsfid fid, DataStream *ds, PacketView *pv)
{
fhandle_t *fh;
u_int size;
int cc, n;
char buf[72];
fh = nfs_fetch_fh(ds, &size);
if (fh == 0)
return;
if (size < sizeof *fh) {
cc = 0;
size /= sizeof fh->fh_hash[0];
for (n = 0; n < size; n++) {
cc += nsprintf(&buf[cc], sizeof buf - cc, "%s%lx",
(cc == 0) ? "" : " ", fh->fh_hash[n]);
}
pv_showfield(pv, &NFSFIELD(fid), fh, "%x:%lu [%s ???]",
sgifh(fh)->fsid, sgifh(fh)->nodeid, buf);
} else {
pv_showfield(pv, &NFSFIELD(fid), fh,
"%x:%lu [%lx %lx %lx %lx %lx %lx %lx %lx]",
sgifh(fh)->fsid, sgifh(fh)->nodeid,
fh->fh_hash[0], fh->fh_hash[1],
fh->fh_hash[2], fh->fh_hash[3],
fh->fh_hash[4], fh->fh_hash[5],
fh->fh_hash[6], fh->fh_hash[7]);
}
}
/*
* Macros used by nfs_decode_fattr and nfs_decode_sattr.
*/
#define pv_show_nfs_mode(pv, mode) \
pv_showfield(pv, &NFSFIELD(MODE), &(mode), "%#-5lo", mode)
#define pv_show_nfs_uid(pv, uid) \
pv_showfield(pv, &NFSFIELD(UID), &(uid), "%-5ld", uid)
#define pv_show_nfs_gid(pv, gid) \
pv_showfield(pv, &NFSFIELD(GID), &(gid), "%-5ld", gid)
#define pv_show_nfs_size(pv, size) \
pv_showfield(pv, &NFSFIELD(SIZE), &(size), "%-10lu", size)
#define pv_show_nfs_timeval(pv, fid, tv) \
pv_showfield(pv, &NFSFIELD(fid), (tv), \
"%ld.%ld", (tv)->tv_sec, (tv)->tv_usec)
static void
nfs_decode_fattr(DataStream *ds, PacketView *pv)
{
struct nfsfattr *na, fattr;
na = (struct nfsfattr *) ds_inline(ds, sizeof *na, BYTES_PER_XDR_UNIT);
if (na) {
na->na_type = (enum nfsftype) ntohl((u_long)na->na_type);
na->na_mode = ntohl(na->na_mode);
na->na_nlink = ntohl(na->na_nlink);
na->na_uid = ntohl(na->na_uid);
na->na_gid = ntohl(na->na_gid);
na->na_size = ntohl(na->na_size);
na->na_blocksize = ntohl(na->na_blocksize);
na->na_rdev = ntohl(na->na_rdev);
na->na_blocks = ntohl(na->na_blocks);
na->na_fsid = ntohl(na->na_fsid);
na->na_nodeid = ntohl(na->na_nodeid);
na->na_atime.tv_sec = ntohl(na->na_atime.tv_sec);
na->na_atime.tv_usec = ntohl(na->na_atime.tv_usec);
na->na_mtime.tv_sec = ntohl(na->na_mtime.tv_sec);
na->na_mtime.tv_usec = ntohl(na->na_mtime.tv_usec);
na->na_ctime.tv_sec = ntohl(na->na_ctime.tv_sec);
na->na_ctime.tv_usec = ntohl(na->na_ctime.tv_usec);
} else {
na = &fattr;
(void)(ds_long(ds, (long *) &na->na_type)
&& ds_u_long(ds, &na->na_mode)
&& ds_u_long(ds, &na->na_nlink)
&& ds_u_long(ds, &na->na_uid)
&& ds_u_long(ds, &na->na_gid)
&& ds_u_long(ds, &na->na_size)
&& ds_u_long(ds, &na->na_blocksize)
&& ds_u_long(ds, &na->na_rdev)
&& ds_u_long(ds, &na->na_blocks)
&& ds_u_long(ds, &na->na_fsid)
&& ds_u_long(ds, &na->na_nodeid)
&& ds_long(ds, &na->na_atime.tv_sec)
&& ds_long(ds, &na->na_atime.tv_usec)
&& ds_long(ds, &na->na_mtime.tv_sec)
&& ds_long(ds, &na->na_mtime.tv_usec)
&& ds_long(ds, &na->na_ctime.tv_sec)
&& ds_long(ds, &na->na_ctime.tv_usec));
}
pv_break(pv);
pv_showfield(pv, &NFSFIELD(TYPE), &na->na_type,
"%-5.5s", en_name(&nfsftype, (int) na->na_type));
pv_show_nfs_mode(pv, na->na_mode);
pv_showfield(pv, &NFSFIELD(NLINK), &na->na_nlink,
"%-5lu", na->na_nlink);
pv_show_nfs_uid(pv, na->na_uid);
pv_show_nfs_gid(pv, na->na_gid);
pv_show_nfs_size(pv, na->na_size);
pv_showfield(pv, &NFSFIELD(BLOCKSIZE), &na->na_blocksize,
"%-5lu", na->na_blocksize);
pv_showfield(pv, &NFSFIELD(RDEV), &na->na_rdev,
"%#-6lx", na->na_rdev);
pv_showfield(pv, &NFSFIELD(BLOCKS), &na->na_blocks,
"%-10lu", na->na_blocks);
pv_showfield(pv, &NFSFIELD(FSID), &na->na_fsid,
"%#-6lx", na->na_fsid);
pv_showfield(pv, &NFSFIELD(NODEID), &na->na_nodeid,
"%-10lu", na->na_nodeid);
pv_show_nfs_timeval(pv, ATIME, &na->na_atime);
pv_show_nfs_timeval(pv, MTIME, &na->na_mtime);
pv_show_nfs_timeval(pv, CTIME, &na->na_ctime);
}
static void
nfs_decode_sattr(DataStream *ds, PacketView *pv)
{
struct nfssattr *sa, sattr;
sa = (struct nfssattr *) ds_inline(ds, sizeof *sa, BYTES_PER_XDR_UNIT);
if (sa) {
sa->sa_mode = ntohl(sa->sa_mode);
sa->sa_uid = ntohl(sa->sa_uid);
sa->sa_gid = ntohl(sa->sa_gid);
sa->sa_size = ntohl(sa->sa_size);
sa->sa_atime.tv_sec = ntohl(sa->sa_atime.tv_sec);
sa->sa_atime.tv_usec = ntohl(sa->sa_atime.tv_usec);
sa->sa_mtime.tv_sec = ntohl(sa->sa_mtime.tv_sec);
sa->sa_mtime.tv_usec = ntohl(sa->sa_mtime.tv_usec);
} else {
sa = &sattr;
(void)(ds_u_long(ds, &sa->sa_mode)
&& ds_u_long(ds, &sa->sa_uid)
&& ds_u_long(ds, &sa->sa_gid)
&& ds_u_long(ds, &sa->sa_size)
&& ds_long(ds, &sa->sa_atime.tv_sec)
&& ds_long(ds, &sa->sa_atime.tv_usec)
&& ds_long(ds, &sa->sa_mtime.tv_sec)
&& ds_long(ds, &sa->sa_mtime.tv_usec));
}
pv_break(pv);
pv_show_nfs_mode(pv, sa->sa_mode);
pv_show_nfs_uid(pv, sa->sa_uid);
pv_show_nfs_gid(pv, sa->sa_gid);
pv_show_nfs_size(pv, sa->sa_size);
pv_show_nfs_timeval(pv, ATIME, &sa->sa_atime);
pv_show_nfs_timeval(pv, MTIME, &sa->sa_mtime);
}
static void
nfs_decode_string(enum nfsfid fid, DataStream *ds, PacketView *pv)
{
ProtoField *pf;
unsigned char *base;
char buf[2000];
struct string s;
long xdrlen, size;
pf = &NFSFIELD(fid);
base = ds->ds_next;
if (!nfs_fetch_string(ds, pf->pf_cookie, &s, &xdrlen, &size))
size = 0;
else {
(void) nsprintf(buf, sizeof buf, "\"%.*s%s\" (%ld)",
s.s_len, s.s_ptr,
(s.s_len < xdrlen) ? "..." : "",
xdrlen);
}
pv_showvarfield(pv, pf, base, size, "%-20s", buf);
}