From: rtm@csail.mit.edu
To: Chuck Lever <chuck.lever@oracle.com>, Jeff Layton <jlayton@kernel.org>
Cc: NeilBrown <neil@brown.name>,
Olga Kornievskaia <okorniev@redhat.com>,
Dai Ngo <Dai.Ngo@oracle.com>, Tom Talpey <tom@talpey.com>,
linux-nfs@vger.kernel.org
Subject: nfsd's ff_layout_ops.proc_layoutcommit is NULL
Date: Wed, 10 Sep 2025 17:47:27 -0400 [thread overview]
Message-ID: <55792.1757540847@localhost> (raw)
[-- Attachment #1: Type: text/plain, Size: 1481 bytes --]
So if a client sends a flexfile LAYOUTCOMMIT to a pNFS server, it will
crash here in nfsd4_layoutcommit():
nfserr = ops->proc_layoutcommit(inode, lcp);
I've attached a demo:
# cc nfsd155a.c
# ./a.out
...
[ 643.202841] BUG: kernel NULL pointer dereference, address: 0000000000000000
[ 643.203679] CPU: 8 UID: 0 PID: 1115 Comm: nfsd Not tainted 6.17.0-rc4-00231-gc8ed9b5c02a5 #28 PREEMPT(voluntary)
#0 nfsd4_layoutcommit (rqstp=0xffffffd6047cd000, cstate=0xffffffd60a062028,
u=0xffffffd60a022720) at fs/nfsd/nfs4proc.c:2529
#1 0xffffffff804b9768 in nfsd4_proc_compound (rqstp=0xffffffd6047cd000)
at fs/nfsd/nfs4proc.c:2888
#2 0xffffffff804a0558 in nfsd_dispatch (rqstp=0xffffffd6047cd000)
at fs/nfsd/nfssvc.c:991
#3 0xffffffff81034022 in svc_process_common (
rqstp=rqstp@entry=0xffffffd6047cd000) at net/sunrpc/svc.c:1428
#4 0xffffffff81034522 in svc_process (rqstp=rqstp@entry=0xffffffd6047cd000)
at net/sunrpc/svc.c:1568
#5 0xffffffff810469a0 in svc_handle_xprt (xprt=0xffffffd60449d800,
rqstp=0xffffffd6047cd000) at net/sunrpc/svc_xprt.c:817
#6 svc_recv (rqstp=rqstp@entry=0xffffffd6047cd000)
at net/sunrpc/svc_xprt.c:874
#7 0xffffffff8049f58c in nfsd (vrqstp=0xffffffd6047cd000)
at fs/nfsd/nfssvc.c:926
(gdb) print ops
$1 = (const struct nfsd4_layout_ops *) 0xffffffff81366130 <ff_layout_ops>
(gdb) print ops->proc_layoutcommit
$2 = (__be32 (*)(struct inode *, struct nfsd4_layoutcommit *)) 0x0
Robert Morris
rtm@mit.edu
[-- Attachment #2: nfsd155a.c --]
[-- Type: application/octet-stream, Size: 48713 bytes --]
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <sys/wait.h>
#include <sys/resource.h>
#include <arpa/inet.h>
#include <assert.h>
char obuf[10240];
int oi = 0;
int s; // socket fd
int xid = 1;
unsigned long long clientid; // server tells us in exchange_id reply
unsigned int sequenceid;
unsigned int slot0sequenceid = 1;
char sessionid[16];
struct stateid {
int seqid;
char other[12];
};
struct stateid sid; // most recent received
struct fh {
int len;
char fh[256];
};
struct fh fh; // most recent received
void
sys(const char *cmd)
{
volatile int junk = system(cmd);
(void) junk;
}
static unsigned long symx() {
return 0;
}
int symstart = -1;
void put_fattr4_one();
void put_fattr4_many();
void
put32(unsigned int x)
{
assert((oi % 4) == 0);
*(int*)(obuf+oi) = htonl(x);
oi += 4;
}
void
put64(unsigned long long x)
{
put32(x >> 32);
put32(x);
}
void
put_opaque(int n, const char *buf)
{
put32(n);
for(int i = 0; i < n; i++)
obuf[oi++] = (buf ? buf[i] : 0);
while(n & 3){
obuf[oi++] = 0;
n++;
}
}
void
put_opaque_repeat(int n, char c)
{
put32(n);
for(int i = 0; i < n; i++)
obuf[oi++] = c;
while((n%4)!=0){
obuf[oi++] = 0;
n++;
}
}
void
put_sessionid(const char *sid)
{
for(int i = 0; i < 16; i++){
obuf[oi++] = (sid ? sid[i] : 0);
}
}
void
put_reset()
{
oi = 4; // leave room for packet length
}
void
send_send()
{
assert(oi >= 4);
assert((oi % 4) == 0);
assert(oi <= sizeof(obuf));
for(int i = 0; i < 16; i++)
put32(0xffffffff);
if(symstart != -1){
for(int i = symstart; i < oi; i += 8)
*(long long *)(obuf + i) ^= symx();
}
*(int*)(obuf+0) = htonl((oi - 4) | 0x80000000);
printf("writing %d xid %d\n", oi, ntohl(*(int*)(obuf+4)));
if(write(s, obuf, oi) <= 0) perror("write");
oi = 0;
symstart = -1;
}
void
put_rpc_header(int proc)
{
put_reset();
put32(xid++);
put32(0); // mtype=CALL
put32(2); // rpc version
put32(100003); // prog #
put32(4); // prog vers
put32(proc); // proc
if(proc == 0){
put32(0); // cred type
put32(0); // cred len
} else {
put32(1); // cred type AUTH_SYS / AUTH_UNIX
put32(32); // cred length
put32(0); // stamp
put_opaque(9, "localhost");
put32(65534); // uid
put32(65534); // gid
put32(0); // # gids
}
put32(0); // verf type
put32(0); // verf len
}
void
put_compound(int n)
{
put_rpc_header(1);
// compound header
put_opaque(0, ""); // tag
put32(2); // minor version
put32(n); // # operations in the compound
}
// most COMPOUNDs are required to start with a SEQUENCE.
void
put_sequence()
{
put32(53); // SEQUENCE
put_sessionid(sessionid); // sessionid (16 bytes)
put32(slot0sequenceid++); // sequenceid ???
put32(0); // slotid
put32(0); // highest_slotid
put32(0); // cachethis
}
void
put_reclaim_complete()
{
put32(58); // RECLAIM_COMPLETE
put32(0); // 0 means global, 1 means just current fh
}
char ibuf[10240];
int ii;
int ilen;
int
readn(int fd, void *xbuf, int n)
{
char *buf = (char *) xbuf;
int orig = n;
while(n > 0){
int cc = read(fd, buf, n);
if(cc <= 0) { perror("read"); return -1; }
n -= cc;
buf += cc;
}
return orig;
}
unsigned int
parse32()
{
if(ii >= ilen){
printf("parsed beyond the end of the input\n");
return 0;
}
unsigned int x = *(int*)(ibuf+ii);
ii += 4;
return ntohl(x);
}
unsigned long long
parse64()
{
unsigned long long hi = parse32();
unsigned long long lo = parse32();
return (hi << 32) | lo;
}
// sessionid4 -- 16 bytes
void
parse_sessionid(char *sid)
{
for(int i = 0; i < 16; i++){
if(sid)
sid[i] = ibuf[ii];
ii++;
}
}
void
put_sid(char *sid)
{
for(int i = 0; i < 16; i++){
obuf[oi++] = (sid ? sid[i] : 0);
}
}
// sessionid4 -- 16 bytes
void
parse_sid(char *sid)
{
for(int i = 0; i < 16; i++){
if(sid)
sid[i] = ibuf[ii];
ii++;
}
}
unsigned int
parse_opaque(char *buf)
{
if(buf)
buf[0] = 0;
int nominal_n = parse32();
if(nominal_n > 4096){
printf("crazy opaque length %d\n", nominal_n);
return 0;
}
int real_n = nominal_n;
while((real_n%4) != 0) real_n += 1;
for(int i = 0; i < real_n; i++){
if(buf && i < real_n)
buf[i] = ibuf[ii];
ii++;
}
return nominal_n;
}
void
parse_exchange_id_reply()
{
int status = parse32();
if(status != 0)
printf("exchange_id reply status %d, not 0\n", status);
clientid = parse64();
sequenceid = parse32();
printf("exchange_id clientid 0x%llx sequenceid 0x%x\n", clientid, sequenceid);
}
void
parse_create_session_reply()
{
int status = parse32();
if(status != 0)
printf("create_session reply status %d, not 0\n", status);
parse_sessionid(sessionid);
}
void
parse_sequence_reply()
{
int status = parse32();
if(status != 0)
printf("sequence reply status %d, not 0\n", status);
parse_sessionid(0);
parse32(); // sequenceid
parse32(); // slotid
parse32(); // highest_slotid
parse32(); // target_highest_slotid
parse32(); // status_flags
}
void
parse_putrootfh_reply()
{
int status = parse32();
if(status != 0)
printf("putrootfh_reply status %d\n", status);
}
void
parse_lookup_reply()
{
int status = parse32();
if(status != 0)
printf("lookup_reply status %d\n", status);
}
void
parse_stateid()
{
sid.seqid = parse32();
for(int i = 0; i < 12; i++)
sid.other[i] = ibuf[ii++];
}
void
parse_open_reply()
{
int status = parse32();
if(status != 0){
printf("open status %d\n", status);
return;
}
parse_stateid();
parse32(); // change_info atomic
parse64(); // change_info before
parse64(); // change_info after
parse32(); // rflags
unsigned int bitwords = parse32(); // attrset
for(int i = 0; i < bitwords; i++)
parse32();
int delegation_type = parse32(); // open_delegation4
if(delegation_type == 0){
// OPEN_DELEGATE_NONE
} else if(delegation_type == 1){
// OPEN_DELEGATE_READ
// open_read_delegation4
parse32(); // stateid seqid
parse32(); // other
parse32(); // other
parse32(); // other
parse32(); // recall
// nfsace4
parse32(); // nfsace4 type
parse32(); // nfsace4 flag
parse32(); // nfsace4 access_mark
parse_opaque(0); // nfsace4 who
} else if(delegation_type == 2){
// OPEN_DELEGATE_WRITE
parse32(); // stateid seqid
parse32(); // other
parse32(); // other
parse32(); // other
parse32(); // recall
// nfs_space_limit4
int limitby = parse32();
if(limitby == 1){
// NFS_LIMIT_SIZE
parse64(); // filesize
} else if(limitby == 2){
// NFS_LIMIT_BLOCKS
parse32(); // num_blocks
parse32(); // bytes_per_block
} else {
printf("open reply, unknown limitby %d\n", limitby);
}
// nfsace4
parse32(); // nfsace4 type
parse32(); // nfsace4 flag
parse32(); // nfsace4 access_mark
parse_opaque(0); // nfsace4 who
} else {
printf("DID NOT understand delegation_type %d\n", delegation_type);
}
}
void
parse_compound_reply()
{
int stat = parse32(); // OK
parse_opaque(0);
int nops = parse32();
printf("compound reply, nops %d, stat %d", nops, stat);
if(stat > 0 && stat < 200){
printf(" %s", strerror(stat));
}
printf("\n");
for(int opi = 0; opi < nops && ii < ilen; opi++){
int op = parse32();
printf("reply for op %d\n", op);
if(op == 53){
parse_sequence_reply();
} else if(op == 42){
parse_exchange_id_reply();
} else if(op == 43){
parse_create_session_reply();
} else if(op == 24){
parse_putrootfh_reply();
} else if(op == 15){
parse_lookup_reply();
} else if(op == 18){
parse_open_reply();
} else if(op == 26){
int status = parse32();
if(status != 0){
printf("readdir status %d\n", status);
} else {
long long verf = parse64();
int nentries = parse32();
long long cookie = parse64();
char name[1024];
memset(name, 0, sizeof(name));
parse_opaque(name);
//printf("verf %llx *entries %d cookie %llx name %s\n", verf, nentries, cookie, name);
}
break;
} else if(op == 34){
int status = parse32();
printf("setattr status %d\n", status);
break;
} else if(op == 22){
// putfh
int status = parse32();
if(status != 0)
printf("putfh status %d\n", status);
} else if(op == 32){
// savefh
int status = parse32();
if(status != 0)
printf("savefh status %d\n", status);
} else if(op == 10){
// getfh
int status = parse32();
if(status == 0){
fh.len = parse_opaque(fh.fh);
} else {
printf("getfh status %d\n", status);
}
} else if(op == 60){
// copy
int status = parse32();
if(status != 0){
printf("copy status %d\n", status);
} else {
int nid = parse32();
if(nid > 0)
parse_stateid();
}
} else if(op == 6){
// CREATE
int status = parse32();
if(status != 0){
printf("create status %d\n", status);
} else {
parse32(); // change_info atomic
parse64(); // change_info before
parse64(); // change_info after
int na = parse32(); // bitmap4 size
for(int i = 0; i < na; i++)
parse32();
}
} else if(op == 74){
// LISTXATTRS
int status = parse32();
if(status != 0){
printf("listxattrs status %d\n", status);
} else {
parse64(); // nfs_cookie4
int nnames = parse32();
for(int i = 0; i < nnames; i++){
char name[1024];
int len = parse_opaque(name);
name[len] = '\0';
// printf("xattr %s\n", name);
}
parse32(); // bool lxr_eof
}
} else if(op == 50){
// LAYOUTGET
int status = parse32();
if(status){
printf("layoutget status %d\n", status);
} else {
parse32(); // bool logr_return_on_close
parse_stateid();
}
} else {
printf("reply for unknown op %d\n", op);
break;
}
}
}
void
parse_callback(int xid)
{
parse32(); // rpc version
parse32(); // prog #
parse32(); // prog vers
int proc = parse32(); // proc
parse32(); // auth flavor
parse_opaque(0); // auth
parse32(); // verf flavor
parse_opaque(0); // verf
printf("callback proc=%d\n", proc);
oi = 0;
put32(0); // placeholder
put32(xid);
put32(1); // REPLY
put32(0); // MSG_ACCEPTED
put32(0); // opaque_auth flavor = AUTH_NULL
put32(0); // opaque_auth length
put32(0); // SUCCESS
int xoi = oi;
if(proc == 0){
// nop
} else if(proc == 1){
// compound
parse_opaque(0); // tag
parse32(); // minorversion
parse32(); // callback_ident
int nops = parse32();
put32(0); // status
put_opaque(0, ""); // tag
put32(nops);
for(int opi = 0; opi < nops; opi++){
int op = parse32();
xoi = oi;
put32(op);
if(op == 11){
// CB_SEQUENCE
char sid[16];
parse_sid(sid);
int seq = parse32(); // sequenceid
int slot = parse32(); // slotid
int hislot = parse32(); // highest_slotid
parse32(); // cachethis
int nrcl = parse32(); // csa_referring_call_lists<>
for(int rci = 0; rci < nrcl; rci++){
parse32(); // sessionid
parse32();
parse32();
parse32();
int nxxx = parse32(); // rcl_referring_calls<>
for(int xi = 0; xi < nxxx; xi++){
parse32(); // sequenceid
parse32(); // slotid
}
}
put_sid(sid);
put32(seq); // sequenceid
put32(slot); // slotid
put32(hislot); // highest_slotid
put32(hislot); // target_highest_slotid
} else if(op == 4){
printf("CB_RECALL\n");
// stateid4
parse32(); // seqid
parse32(); // other
parse32();
parse32();
parse32(); // truncate
parse_opaque(0); // fh
put32(0); // OK
} else {
printf("callback unknown op %d\n", op);
break;
}
}
} else {
printf("callback: unknown proc %d\n", proc);
}
send_send();
}
void
parse_reply(int proc)
{
int desired_xid = xid - 1;
again:
if(readn(s, &ilen, 4) < 0)
return;
ilen = ntohl(ilen);
if((ilen & 0x80000000) == 0)
printf("ilen is missing 0x80000000\n");
ilen &= 0x7fffffff;
if(ilen > sizeof(ibuf)){
printf("huge packet %d\n", ilen);
return;
}
if(readn(s, ibuf, ilen) < 0)
return;
ii = 0;
int xxid = parse32(); // xid
int mtype = parse32(); // 1 = REPLY
if(mtype == 0){
// CALL -- a callback
parse_callback(xxid);
goto again;
}
if(xxid != desired_xid){
printf("xid mismatch, wanted 0x%x, got 0x%x, ilen %d, ii %d\n", desired_xid, xxid, ilen, ii);
}
if(mtype != 1)
printf("unexpected mtype %d, expected 1 / REPLY\n", mtype);
int stat = parse32(); // MSG_ACCEPTED
if(stat != 0)
printf("unexpected reply stat %d, expected 0 / MSG_ACCEPTED\n", stat);
int flavor = parse32(); // auth flavor
if(flavor != 0)
printf("unexpected auth_flavor %d, expecting 0 / AUTH_NONE\n", flavor);
parse_opaque(0); // verf
stat = parse32(); // SUCCESS
if(stat != 0)
printf("unexpected stat %d, expected 0 / SUCCESS\n", stat);
if(proc == 0){
printf("got reply for proc %d\n", proc);
} else if(proc == 1){
parse_compound_reply();
} else {
printf("got unexpected reply for proc %d xid %d\n", proc, xxid);
}
}
void
send_nop()
{
put_rpc_header(0);
send_send();
parse_reply(0);
}
void
send_exchange_id(int dosym)
{
put_compound(1);
if(dosym && symstart == -1)
symstart = oi;
put32(42); // operation 42: EXCHANGE_ID
int co_verifier = 1;
#if !SYM
co_verifier = getpid(); // needs to be unique
#endif
put64(co_verifier); // verifier4
put_opaque(22, "Linux NFSv4.2 xyzzy"); // co_ownerid
put32(0x103); // flags
put32(0); // SP4_NONE
put32(1); // length of client_impl_id
put_opaque(10, "kernel.org"); // nii_domain
put_opaque(4, "blah"); // nii_name
put64(0); // nfstime4
put32(0); // nfstime4
send_send();
parse_reply(1);
}
void
send_exchange_id_sym()
{
put_compound(1);
put32(42); // operation 42: EXCHANGE_ID
put64(1); // verifier4
put_opaque(22, "Linux NFSv4.2 xyzzy"); // co_ownerid
put32(0x103 ^ symx()); // flags
unsigned int how = symx();
put32(how);
int xoi = oi;
if(how == 0){ // SP4_NONE
} else if(how == 1){ // SP4_MACH_CRED
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
} else if(how == 2){ // SP4_SSV
// ssp_ops
put32(3);
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
// ssp_hash_algs<>
put32(2);
put_opaque(8, "12345678");
put_opaque(8, "1bcdefgh");
// ssp_encr_algs<>
put32(2);
put_opaque(8, "12345678");
put_opaque(8, "1bcdefgh");
put32(99); // ssp_window
put32(99); // ssp_num_gss_handles
}
unsigned int n = symx();
if(n > 20) n = 20;
put32(n); // length of client_impl_id
for(int i = xoi; i < oi; i += 8)
*(long long *)(obuf+i) ^= symx();
for(int i = 0; i < n; i++){
put_opaque_repeat(symx() & 0xff, 'x'); // nii_domain
put_opaque_repeat(symx() & 0xff, 'y'); // nii_name
put64(0); // nfstime4
put32(0); // nfstime4
}
send_send();
parse_reply(1);
}
void
send_create_session(int dosym)
{
put_compound(1);
put32(43); // CREATE_SESSION
put64(clientid);
put32(sequenceid++);
if(dosym && symstart == -1)
symstart = oi;
put32(3); // flags, 1=FLAG_PERSIST, 2=CONN_BACK_CHAN
// csa_fore_chan_attrs, csa_back_chan_attrs
for(int i = 0; i < 2; i++){
put32(0); // headerpadsize
put32(4096); // maxrequestsize
put32(4096); // maxresponsesize
put32(4096); // maxresponsesize_cached
put32(12); // maxoperations
put32(16); // maxrequests
put32(0); // ca_rdma_ird<>
}
put32(0x40000000); // csa_cb_program
put32(1); // length of csa_sec_parms
#if 0
put32(0); // AUTH_NONE
#else
put32(1); // flavor AUTH_SYS
put32(0); // stamp
put_opaque(9, "localhost");
put32(65534); // uid
put32(65534); // gid
put32(0); // # gids
#endif
send_send();
parse_reply(1);
}
void
send_sequence()
{
put_compound(1);
put_sequence();
send_send();
parse_reply(1);
}
void
send_reclaim_complete()
{
put_compound(2);
put_sequence();
put_reclaim_complete();
send_send();
parse_reply(1);
}
void
put_rootfh()
{
put32(24);
}
// struct OPEN4args {
// seqid4 seqid;
// uint32_t share_access;
// uint32_t share_deny;
// open_owner4 owner;
// openflag4 openhow;
// open_claim4 claim;
// };
void
put_open_existing(const char *filename, int share)
{
put32(18);
put32(0); // seqid
put32(share); // share_access 1=READ 3=BOTH
put32(0); // share_deny
put64(clientid); // owner
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
put32(0); // openhow OPEN4_NOCREATE
put32(0); // CLAIM_NULL
put_opaque(strlen(filename), filename);
}
void
put_open_create(char *name, int dosym)
{
put32(18);
put32(0); // seqid
put32(3); // share_access BOTH
put32(0); // share_deny
put64(clientid); // owner
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
put32(1); // openhow OPEN4_CREATE
unsigned int mode = 0; // UNCHECKED4
if(dosym)
mode ^= symx();
put32(mode);
if(mode == 2 || mode == 3){
put64(0); // verifier
}
if(mode != 2){
if(dosym){
//put_fattr4_one();
put32(2);
put64(symx());
put32(16*8);
for(int i = 0; i < 16; i++)
put64(symx());
} else {
put32(2); // attr bitmap length
put32(16); // attr bits
put32(2); // attr bits
put32(12); // attr len
put32(0);
put32(0);
put32(420);
}
}
unsigned int claim_type = 0; // CLAIM_NULL
if(dosym)
claim_type ^= symx();
put32(claim_type);
{
char ff[256];
sprintf(ff, "/tmp/%s", name);
unlink(ff);
}
if(claim_type == 0 || claim_type == 3){
put_opaque(strlen(name), name);
} else if(claim_type == 1){ // CLAIM_PREVIOUS
put32(symx());
} else if(claim_type == 2){ // CLAIM_DELEGATE_CUR
// stateid4
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put_opaque(strlen(name), name);
} else if(claim_type == 4){ // CLAIM_FH
} else if(claim_type == 5){ // CUR_FH
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
} else if(claim_type == 6){ // PREV_FH
}
}
void
put_readdir(int dosym)
{
put32(26);
if(dosym && symstart == -1)
symstart = oi;
put64(0); // cookie
put64(0); // cookieverf
put32(512); // dircount (bytes)
put32(512); // maxcount (bytes)
// bitmap
put32(3);
put32(0x0018091a);
put32(0x00b0a23a);
put32(0);
}
void
put_lookup(const char *name)
{
put32(15);
put_opaque(strlen(name), name);
}
//
// generates a fattr4 (bitmap4 then attrlist4).
//
void
put_fattr4(int xwords[], int fh)
{
int words[3];
for(int i = 0; i < 3; i++){
words[i] = xwords[i];
}
int bitwords = 3;
put32(bitwords);
int word0i = oi;
for(int i = 0; i < bitwords; i++)
put32(words[i]);
int leni = oi;
put32(0); // placeholder for total length of attrs
for(int a = 0; a < bitwords*32; a++){
if(words[a/32] & (1 << (a % 32))){
int xoi = oi;
if(a == 0){
put32(2); // # bitmap words of supported attrs
put32(0xffffffff);
put32(0xffffffff);
} else if(a == 1){
int type = 1;
if(fh == 0 || fh == 1)
type = 2;
put32(type); // NF4DIR=2 or NF4REG=1
} else if(a == 2){
put32(0); // fh_expire_type
} else if(a == 3){
put64(0); // change
} else if(a == 4){
put64(4096*10); // size
} else if(a == 5){
put32(1); // link support
} else if(a == 6){
put32(1); // symlink support
} else if(a == 8){
put64(1); // fsid major
put64(1); // fsid minor
} else if(a == 10){
put32(1); // lease time
} else if(a == 11){
put32(0); // rdattr_error
} else if(a == 12){
// ACL
int n = 2;
put32(n);
for(int i = 0; i < n; i++){
put32(0); // type
put32(0); // flag
put32(0); // mask
char who[32];
memset(who, 0, sizeof(who));
//strcpy(who, "1");
//strcpy(who, "OWNER@");
//strcpy(who, "GROUP@");
strcpy(who, "bin@x.com");
put_opaque(strlen(who), who);
}
} else if(a == 13){
put32(0xf); // aclsupport
} else if(a == 19){
// filehandle
int xfh = fh;
put_opaque(4, (char*)&xfh); // fh
} else if(a == 20){
put64(fh); // fileid
} else if(a == 24){
// fs_locations
put32(1);
put_opaque(10, "abcde12345"); // pathname4
put32(1); // locations<>
put_opaque(10, "abcde12345"); // server
put32(1);
put_opaque(10, "abcde12345"); // rootpath
} else if(a == 27){
put64(0xffffffffffff); // max file size
} else if(a == 28){
put32(0xffff); // max link
} else if(a == 29){
put32(256); // max name
} else if(a == 30){
put64(10*4096); // max read
} else if(a == 31){
put64(10*4096); // max write
} else if(a == 33){
put32(0777); // mode
} else if(a == 35){
put32(3); // numlinks
} else if(a == 36){
put_opaque(6, "other"); // owner
} else if(a == 37){
put_opaque(6, "other"); // owner_group
} else if(a == 41){
put32(1); // rawdev major
put32(1); // rawdev minor
} else if(a == 45){
put64(4096*10); // space used
} else if(a == 47){
put64(0); // time access seconds
put32(0); // nseconds
} else if(a == 51){
put64(0); // time delta seconds
put32(0); // nseconds
} else if(a == 52){
put64(0); // time metadata seconds
put32(0); // nseconds
} else if(a == 53){
put64(0); // time modify seconds
put32(0); // nseconds
} else if(a == 55){
put64(0); // mounted_on_fileid ???
} else if(a == 62){
// fs_layout_types
put32(1);
put32(1); // LAYOUT4_NFSV4_1_FILES
} else if(a == 75){
// FATTR4_SUPPATTR_EXCLCREAT
put32(2); // bitmap length
put32(0xffffffff);
put32(0xffffffff);
} else {
// unknown attr, delete from bitmap.
words[a/32] &= ~(1 << (a % 32));
*(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]);
}
}
}
for(int i = 0; i < 16; i++)
put32(0xffffffff);
*(int*)(obuf+leni) = htonl(oi - leni - 4);
}
void
put_fattr4_inner(int words[])
{
int bitwords = 3;
put32(bitwords);
int word0i = oi;
for(int i = 0; i < bitwords; i++)
put32(words[i]);
int leni = oi;
put32(0); // placeholder for total length of attrs
for(int a = 0; a < bitwords*32; a++){
if(words[a/32] & (1 << (a % 32))){
if(a == 0){
int n = 3 ^ (symx() & 0xf);
put32(n); // # bitmap words of supported attrs
for(int i = 0; i < n; i++){
put32(0xffffffff ^ symx());
}
} else if(a == 1){
put32(1 ^ symx()); // NF4DIR=2 or NF4REG=1
} else if(a == 2){
put32(symx()); // fh_expire_type
} else if(a == 3){
put64(symx()); // change
} else if(a == 4){
put64(103 ^ symx()); // size
} else if(a == 5){
put32(symx()); // link support
} else if(a == 6){
put32(symx()); // symlink support
} else if(a == 8){
put64(symx()); // fsid major
put64(symx()); // fsid minor
} else if(a == 10){
put32(symx()); // lease time
} else if(a == 11){
put32(symx()); // rdattr_error
} else if(a == 12){
// ACL
int n = 1 ^ symx();
put32(n);
for(int i = 0; i < n && i < 2; i++){
put32(symx()); // type
put32(symx()); // flag
put32(symx()); // mask
char who[32];
memset(who, 0, sizeof(who));
// strcpy(who, "65534");
//strcpy(who, "OWNER@");
strcpy(who, "bin@x.com");
*(long long*)who ^= symx();
put_opaque(strlen(who), who);
}
} else if(a == 13){
put32(0xf ^ symx()); // aclsupport
} else if(a == 19){
// filehandle
int n = symx() & 0xff;
put_opaque_repeat(n, 'x');
} else if(a == 20){
put64(symx() & 0x3); // fileid
} else if(a == 24){
// fs_locations
put_opaque(10, "abcde12345"); // pathname4
int n = symx() & 0x1f;
put32(n); // locations<>
for(int i = 0; i < n; i++){
put_opaque_repeat(symx() & 0x1ff, 'x'); // server
put_opaque_repeat(symx() & 0x1ff, 'y'); // rootpath
}
} else if(a == 27){
put64(symx()); // max file size
} else if(a == 28){
put32(symx()); // max link
} else if(a == 29){
put32(symx()); // max name
} else if(a == 30){
put64(symx()); // max read
} else if(a == 31){
put64(symx()); // max write
} else if(a == 33){
put32(symx()); // mode
} else if(a == 35){
put32(symx()); // numlinks
} else if(a == 36){
put_opaque_repeat(symx() & 0x1ff, 'z'); // owner
} else if(a == 37){
put_opaque_repeat(symx() & 0x1ff, 'z'); // owner_group
} else if(a == 41){
put32(symx()); // rawdev major
put32(symx()); // rawdev minor
} else if(a == 45){
put64(symx()); // space used
} else if(a == 47){
put64(0); // time access seconds
put32(0); // nseconds
} else if(a == 51){
put64(symx()); // time delta seconds
put32(symx()); // nseconds
} else if(a == 52){
put64(0); // time metadata seconds
put32(0); // nseconds
} else if(a == 53){
put64(0); // time modify seconds
put32(0); // nseconds
} else if(a == 55){
put64(symx()); // mounted_on_fileid ???
} else if(a == 62){
// fs_layout_types
put32(symx());
put32(symx()); // LAYOUT4_NFSV4_1_FILES
} else if(a == 75){
// FATTR4_SUPPATTR_EXCLCREAT
int n = symx() & 0xf;
put32(n); // # bitmap words of supported attrs
for(int i = 0; i < n; i++){
put32(symx());
}
} else {
// unknown attr, delete from bitmap.
words[a/32] &= ~(1 << (a % 32));
*(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]);
}
}
}
for(int i = 0; i < 16; i++)
put32(0xffffffff);
*(int*)(obuf+leni) = htonl(oi - leni - 4);
}
//
// generate a symbolic fattr4, with multiple elements.
// tries to avoid generating illegal XDR.
//
void
put_fattr4_many()
{
int bitwords = 3;
int words[4];
memset(words, 0, sizeof(words));
int setme[] = { -1 };
for(int i = 0; setme[i] >= 0; i++){
int a = setme[i];
words[a/32] |= 1 << (a % 32);
}
for(int i = 0; i < bitwords; i++){
words[i] ^= symx();
}
put_fattr4_inner(words);
}
//
// a symbolic fattr4 with just one item set.
//
void
put_fattr4_one()
{
int bitwords = 3;
int words[4];
memset(words, 0, sizeof(words));
unsigned int bit = symx();
if(bit >= 3*32)
bit = 4;
words[bit/32] |= 1 << (bit % 32);
put_fattr4_inner(words);
}
void
put_getfh()
{
put32(10);
}
void
put_setattr(int dosym)
{
put32(34);
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
#if 0
if(dosym && symstart == -1)
symstart = oi + 4;
int words[3];
words[0] = words[1] = words[2] = 0;
words[0] |= (1 << 12); // acl
put_fattr4(words, 1);
#else
put_fattr4_one();
#endif
}
void
put_savefh()
{
put32(32);
}
// sharing should not be zero.
void
send_open_existing(const char *filename, int sharing)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing(filename, sharing);
put_getfh();
send_send();
parse_reply(1);
}
void
send_open_create(char *name, int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_create(name, dosym);
put_getfh();
send_send();
parse_reply(1);
}
void
put_create_xxx()
{
put32(6);
int type = 5; // NFS4LNK
type ^= symx();
put32(type);
if(type == 5){
put_opaque(16, "abcdefgh12345678");
} else if(type == 4 || type == 3){ // CHR, BLK
put32(symx()); // major
put32(symx()); // minor
}
unlink("/tmp/newlink");
put_opaque(7, "newlink");
put_fattr4_one();
if(0){
put32(3); // fattr4 bitmap size
put32(0);
put32(0);
put32(0);
put32(0); // opaque fattr4 size
}
}
void
send_create()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_create_xxx();
send_send();
parse_reply(1);
}
void
put_mkdir(char *name, int dosym)
{
put32(6); // CREATE
if(dosym && symstart == -1)
symstart = oi;
put32(2); // createtype4 NFS4DIR
put_opaque(strlen(name), name); // component4
put32(3); // fattr4 bitmap size
put32(0);
put32(0);
put32(0);
put32(0); // attrlist4, total length of fattrs
}
void
send_mkdir(char *name, int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_mkdir(name, dosym);
put_getfh();
send_send();
parse_reply(1);
}
void
put_putfh(struct fh fhx, int dosym)
{
put32(22);
char tmp[512];
memcpy(tmp, fhx.fh, fhx.len);
if(dosym){
for(int i = 0; i < fhx.len; i += 8)
*(long*)(tmp+i) ^= symx();
}
put_opaque(fhx.len, tmp);
}
void
send_setattr(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_setattr(dosym);
send_send();
parse_reply(1);
}
void
put_junk_setattr()
{
put32(34);
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put32(3);
put32(symx());
put32(symx());
put32(symx());
put32(symx() & 127);
if(symstart == -1)
symstart = oi;
for(int i = 0; i < 128/4; i++)
put32(0xffffffff);
}
void
send_junk_setattr()
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_junk_setattr();
send_send();
parse_reply(1);
}
void
put_set_acl(int dosym)
{
put32(34);
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
int words[3];
words[0] = words[1] = words[2] = 0;
words[0] |= (1 << 12); // acl
if(dosym && symstart == -1)
symstart = oi + 4*4; // skip over FATTR4 bitmap
put_fattr4(words, 1);
}
void
put_getattr()
{
put32(9);
put32(3);
put32(1 << 12);
put32(0);
put32(0);
}
void
send_set_acl(char *name, int dosym)
{
put_compound(6);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup(name);
put_set_acl(dosym);
put_getattr();
send_send();
parse_reply(1);
}
void
send_putfh(struct fh fhx)
{
put_compound(2);
put_sequence();
put_putfh(fhx, 0);
send_send();
parse_reply(1);
}
void
send_lookup()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_getfh();
send_send();
parse_reply(1);
}
void
send_readdir(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_readdir(dosym);
send_send();
parse_reply(1);
}
void
put_root_listxattrs()
{
put32(74);
put64(0); // cookie
put32(16384); // maxcount
}
void
send_root_listxattrs()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_root_listxattrs();
send_send();
parse_reply(1);
}
void
put_unlock(int dosym)
{
put32(14);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // READ_LT
put32(0); // open_seqid
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put64(0); // offset
put64(1); // length
}
void
send_unlock_lockfile(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup("lockfile");
put_unlock(dosym);
send_send();
parse_reply(1);
}
void
put_lock(int dosym)
{
put32(12);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // READ_LT
put32(0); // reclaim
put64(0); // offset
put64(2); // length
put32(1); // new_lock_owner
put32(0); // open_seqid
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put32(0); // lock_seqid
put64(clientid); // clientid
put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner
}
void
send_lock_lockfile(int dosym)
{
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_lookup("lockfile");
put_lock(dosym);
send_send();
parse_reply(1);
}
void
send_lock(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_lock(dosym);
send_send();
parse_reply(1);
}
void
put_dir_delegation(int dosym)
{
put32(46);
if(dosym && symstart == -1)
symstart = oi;
put32(0); // signal_deleg_avail
put32(3); // notification_types bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put64(0); // child_attr_delay
put32(0); // child_attr_delay
put64(0); // dir_attr_delay
put32(0); // dir_attr_delay
put32(3); // child_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3); // dir_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
}
void
send_dir_delegation(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_dir_delegation(dosym);
send_send();
parse_reply(1);
}
void
put_verify(int dosym)
{
put32(37);
put32(3);
if(dosym && symstart == -1)
symstart = oi;
put32(0);
put32(0);
put32(0);
put32(256);
for(int i = 0; i < 256/4; i++)
put32(0xffffffff);
}
void
send_verify(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_verify(dosym);
send_send();
parse_reply(1);
}
void
put_setxattr(int dosym)
{
put32(73);
if(dosym == 2){
put32(0); // SETXATTR4_EITHER
unsigned int klen = symx() & 0xfff;
unsigned int vlen = symx() & 0xfff;
for(int i = oi; i+8 <= sizeof(obuf); i += 8)
*(unsigned long long *)(obuf + i) = 0x4444444444444444ll ^ symx();
if(klen < 1) klen = 1;
put32(klen);
oi += klen;
while((oi % 4) != 0) oi++;
put32(vlen);
oi += vlen;
while((oi % 4) != 0) oi++;
} else if(dosym == 3) {
put32(0); // SETXATTR4_EITHER
put32(24); // klen
for(int i = 0; i < 3; i++){
*(long long *)(obuf + oi) = 0x4444444444444444ll ^ symx();
oi += 8;
}
put32(24); // vlen
for(int i = 0; i < 3; i++){
*(long long *)(obuf + oi) = 0x4444444444444444ll ^ symx();
oi += 8;
}
} else {
if(dosym && symstart == -1)
symstart = oi;
put32(0); // SETXATTR4_EITHER
put_opaque(24, "12345678abcdefgh12345678"); // key
put_opaque(16, "abcdefgh12345678"); // value
}
}
void
send_setxattr(int dosym)
{
put_compound(4);
put_sequence();
put_putfh(fh, 0);
put_setxattr(dosym);
send_send();
parse_reply(1);
}
void
put_remove(char *name)
{
put32(28);
put_opaque(strlen(name), name);
}
void
send_remove(char *name)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_remove(name);
send_send();
parse_reply(1);
}
void
put_junk(int op, int words)
{
put32(op);
if(symstart != -1)
symstart = oi;
for(int i = 0; i < words; i++)
put32(0);
}
void
send_root_junk(int op, int words)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_junk(op, words);
send_send();
parse_reply(1);
}
void
put_layoutget(int dosym)
{
put32(50);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // signal_layout_avail
put32(4); // layout_type, FLEXFILE
put32(2); // iomode, 1=READ, 2=RW, 3=ANY
put64(0); // offset
put64(8); // length
put64(8); // minlength
put32(sid.seqid); // stateid seq
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put32(4096); // maxcount
}
void
send_layoutget_xxx(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_layoutget(dosym);
send_send();
parse_reply(1);
}
void
send_layoutget(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_layoutget(dosym);
send_send();
parse_reply(1);
}
void
put_layoutcommit(int dosym)
{
put32(49);
if(dosym && symstart == -1)
symstart = oi;
put64(0); // offset4 loca_offset
put64(1); // length4 loca_length
put32(1); // bool loca_reclaim
put32(sid.seqid); // stateid seq
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put32(1); // newoffset4
put64(0); // offset4 no_offset
put32(1); // bool nt_timechanged
put64(0); // nfstime4
put32(0);
// layoutupdate4
put32(4); // layouttype4 4=LAYOUT_FLEX_FILES
put_opaque(4, "xyzz"); // lou_body
}
void
send_layoutcommit(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_layoutcommit(dosym);
send_send();
parse_reply(1);
}
void
put_layoutreturn(int dosym)
{
put32(51);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // reclaim
put32(4); // layout_type, FLEXFILE
put32(3); // iomode, ANY
put32(1); // returntype, FILE
put64(0); // offset
put64(8); // length
put32(1); // stateid seq -- special current stateid
for(int i = 0; i < 12; i++)
obuf[oi++] = 0;
put_opaque_repeat(64, 'x'); // lrf_body<>
}
void
send_layoutreturn(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_layoutreturn(dosym);
send_send();
parse_reply(1);
}
void
put_secinfo_no_name(int dosym)
{
put32(52);
put32(1); // 0=CURRENT_FH, 1=PARENT
}
void
send_secinfo_no_name(int dosym)
{
sys("echo 1234567890123456 > /tmp/out");
sys("chown nobody /tmp/out");
put_compound(5);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_open_existing("out", 3);
put_secinfo_no_name(dosym);
send_send();
parse_reply(1);
}
void
xxx_put_get_dir_delegation(int dosym)
{
put32(46);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // signal_delet_avail
put32(3); // notification_types bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put64(0); // child_attr_delay
put64(0);
put64(0); // dir_attr_delay
put64(0);
put32(3); // child_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
put32(3); // dir_attributes bitmap length
put32(0xffffffff);
put32(0xffffffff);
put32(0xffffffff);
}
void
xxx_send_get_dir_delegation(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
xxx_put_get_dir_delegation(dosym);
send_send();
parse_reply(1);
}
void
send_root_blah()
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
symstart = oi;
for(int i = 0; i < 32; i++)
put32(0xffffffff);
send_send();
parse_reply(1);
}
void
send_cur_blah()
{
put_compound(4);
put_sequence();
put_putfh(fh, 0);
put_savefh();
symstart = oi;
for(int i = 0; i < 32; i++)
put32(0xffffffff);
send_send();
parse_reply(1);
}
void
put_read(int dosym)
{
put32(25);
if(dosym && symstart == -1)
symstart = oi;
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put64(0); // offset
put32(512); // count
}
void
send_read(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_read(dosym);
send_send();
parse_reply(1);
}
void
put_write(int dosym)
{
put32(38);
if(dosym && symstart == -1)
symstart = oi;
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put64(0); // offset
put32(0); // stable_how
put_opaque(6, "hello\n"); // data
}
void
send_write(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_write(dosym);
send_send();
parse_reply(1);
}
void
put_deallocate(int dosym)
{
put32(62);
if(dosym && symstart == -1)
symstart = oi;
put32(sid.seqid); // open_stateid, from previous OPEN
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put64(0); // offset4
put64(1); // length4
}
void
send_deallocate(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_deallocate(dosym);
send_send();
parse_reply(1);
}
// assumes that server's saved_fh is already
// source file, and current_fh is already destination file.
void
put_copy(struct stateid src, struct stateid dst, int inter, int dosym)
{
put32(60);
if(dosym && symstart == -1)
symstart = oi;
// src_stateid
put32(src.seqid); // from previous send_open_existing()
for(int i = 0; i < 12; i++)
obuf[oi++] = src.other[i];
// dst_stateid
put32(dst.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = dst.other[i];
put64(0); // src_offset
put64(0); // dst_offset
put64(4); // count
put32(1); // consecutive
put32(0); // synchronous
// netloc4 ca_source_server<>
if(inter){
put32(1);
put32(3); // NL4_NETADDR
// netaddr4
put_opaque(3, "tcp"); // netid
put_opaque(12, "127.0.0.1.8.1"); // addr
} else {
put32(0);
}
}
void
send_copy(struct fh src_fh, struct stateid src_sid,
struct fh dst_fh, struct stateid dst_sid,
int inter,
int dosym)
{
put_compound(5);
put_sequence();
put_putfh(src_fh, 0);
put_savefh();
put_putfh(dst_fh, 0);
put_copy(src_sid, dst_sid, inter, dosym);
send_send();
parse_reply(1);
}
void
put_offload_cancel(int dosym)
{
put32(66);
if(dosym && symstart == -1)
symstart = oi;
// stateid
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
}
void
send_offload_cancel(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, dosym);
put_offload_cancel(dosym);
send_send();
parse_reply(1);
}
void
put_offload_status(int dosym)
{
put32(67);
if(dosym && symstart == -1)
symstart = oi;
// stateid
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
}
void
send_offload_status(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_offload_status(dosym);
send_send();
parse_reply(1);
}
// assumes that server's saved_fh is already
// source file, and current_fh is already destination file.
void
put_clone(struct stateid src, struct stateid dst, int dosym)
{
put32(71);
if(dosym && symstart == -1)
symstart = oi;
// src_stateid
put32(src.seqid); // from previous send_open_existing()
for(int i = 0; i < 12; i++)
obuf[oi++] = src.other[i];
// dst_stateid
put32(dst.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = dst.other[i];
put64(0); // src_offset
put64(0); // dst_offset
put64(4); // count
}
void
send_clone(struct fh src_fh, struct stateid src_sid,
struct fh dst_fh, struct stateid dst_sid,
int dosym)
{
put_compound(5);
put_sequence();
put_putfh(src_fh, 0);
put_savefh();
put_putfh(dst_fh, 0);
put_clone(src_sid, dst_sid, dosym);
send_send();
parse_reply(1);
}
void
put_copy_notify(int dosym)
{
put32(61);
if(dosym && symstart == -1)
symstart = oi;
// src_stateid
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
// netloc4 destination_server
put32(3); // NL4_NETADDR
put_opaque(3, "tcp");
put_opaque(12, "127.0.0.1.8.1"); // addr
}
void
send_copy_notify(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_copy_notify(dosym);
send_send();
parse_reply(1);
}
void
put_get_dir_delegation(int dosym)
{
put32(46);
if(dosym && symstart == -1)
symstart = oi;
put32(1); // gdda_signal_deleg_avail
put32(1); // length of gdda_notification_types bitmap4
put32(0);
put64(0); // nfstime4 gdda_child_attr_delay
put32(0); // nfstime4
put64(0); // nfstime4 gdda_dir_attr_delay
put32(0); // nfstime4
put32(3); // gdda_child_attributes bitmap4
put32(0);
put32(0);
put32(0);
put32(3); // gdda_dir_attributes bitmap4
put32(0);
put32(0);
put32(0);
}
void
send_get_dir_delegation(int dosym)
{
put_compound(4);
put_sequence();
put_rootfh();
put_lookup("tmp");
put_get_dir_delegation(dosym);
send_send();
parse_reply(1);
}
void
put_destroy_clientid(int dosym)
{
put32(57);
if(dosym && symstart == -1)
symstart = oi;
// stateid
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
}
void
send_destroy_clientid(int dosym)
{
put_compound(2);
put_sequence();
put_destroy_clientid(dosym);
send_send();
parse_reply(1);
}
void
put_test_stateid(int dosym)
{
put32(55);
put32(1); // count
if(dosym && symstart == -1)
symstart = oi;
// stateid
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
}
void
send_test_stateid(int dosym)
{
put_compound(2);
put_sequence();
put_test_stateid(dosym);
send_send();
parse_reply(1);
}
void
put_seek(int dosym)
{
put32(69);
if(dosym && symstart == -1)
symstart = oi;
// stateid4
put32(sid.seqid);
for(int i = 0; i < 12; i++)
obuf[oi++] = sid.other[i];
put64(1); // sa_offset
put32(0); // NFS4_CONTENT_DATA
}
void
send_seek(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_seek(dosym);
send_send();
parse_reply(1);
}
void
put_listxattrs(int dosym)
{
put32(74);
if(dosym && symstart == -1)
symstart = oi;
put64(0); // nfs_cookie4
put32(4096); // count4
}
void
send_listxattrs(int dosym)
{
put_compound(3);
put_sequence();
put_putfh(fh, 0);
put_listxattrs(dosym);
send_send();
parse_reply(1);
}
int
main()
{
// /etc/exports
// /tmp 127.0.0.1(rw,subtree_check,pnfs)
sys("/usr/sbin/rpcbind -f -w &");
sleep(1);
sys("mount -t rpc_pipefs rpc_pipefs /var/lib/nfs/rpc_pipefs");
sys("mount -t nfsd nfsd /proc/fs/nfsd");
sleep(2);
sys("/usr/sbin/rpc.nfsd --lease-time 10 --grace-time 10 1");
sleep(2);
sys("/usr/sbin/rpc.mountd --manage-gids");
sleep(2);
sys("/usr/sbin/rpc.idmapd -p /var/lib/nfs/rpc_pipefs");
sys("exportfs -au");
sys("exportfs -f");
sys("exportfs -r");
sys("exportfs -v");
sys("echo Y > /sys/module/nfsd/parameters/inter_copy_offload_enable");
s = socket(AF_INET, SOCK_STREAM, 0);
int yes = 1;
if(setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(yes)) < 0)
perror("SO_REUSEADDR");
struct sockaddr_in sin;
memset(&sin, 0, sizeof(sin));
sin.sin_family = AF_INET;
sin.sin_addr.s_addr = inet_addr("127.0.0.1");
for(int i = 100; i < 1024; i++){
sin.sin_port = htons(i);
if(bind(s, (struct sockaddr *)&sin, sizeof(sin)) == 0){
break;
}
}
sin.sin_port = htons(2049);
sync();
sleep(11); // grace period
if(connect(s, (struct sockaddr *)&sin, sizeof(sin)) < 0) {
perror("connect");
exit(1);
}
int pid = fork();
if(pid == 0){
send_nop();
// send_exchange_id_sym();
send_exchange_id(0);
send_create_session(0);
send_reclaim_complete();
// server may miss the first time, yielding 10008 NFS4ERR_DELAY.
// so trigger the upcall to rpc.mountd and wait a bit.
send_lookup();
sleep(2);
{
unlink("/tmp/xx1");
unlink("/tmp/xx2");
unlink("/tmp/xx3");
unlink("/tmp/xx4");
sys("rm -rf /tmp/dxxx1");
send_open_create("xx1", 0);
send_write(0);
send_deallocate(0);
send_junk_setattr();
send_open_create("xx2", 0);
send_lock(0);
send_open_create("xx3", 0);
send_layoutget(0);
send_layoutcommit(0);
exit(1);
}
}
close(s);
for(int i = 0; i < 60; i++){
sleep(1);
int st;
int ret = waitpid(pid, &st, WNOHANG);
if(ret == pid)
break;
}
kill(pid, 9);
sleep(1);
}
next reply other threads:[~2025-09-10 21:47 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-09-10 21:47 rtm [this message]
2025-09-10 22:39 ` nfsd's ff_layout_ops.proc_layoutcommit is NULL Jeff Layton
2025-09-11 14:14 ` Chuck Lever
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=55792.1757540847@localhost \
--to=rtm@csail.mit.edu \
--cc=Dai.Ngo@oracle.com \
--cc=chuck.lever@oracle.com \
--cc=jlayton@kernel.org \
--cc=linux-nfs@vger.kernel.org \
--cc=neil@brown.name \
--cc=okorniev@redhat.com \
--cc=tom@talpey.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.