* nfsd's ff_layout_ops.proc_layoutcommit is NULL
@ 2025-09-10 21:47 rtm
2025-09-10 22:39 ` Jeff Layton
2025-09-11 14:14 ` Chuck Lever
0 siblings, 2 replies; 3+ messages in thread
From: rtm @ 2025-09-10 21:47 UTC (permalink / raw)
To: Chuck Lever, Jeff Layton
Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
[-- 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);
}
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: nfsd's ff_layout_ops.proc_layoutcommit is NULL
2025-09-10 21:47 nfsd's ff_layout_ops.proc_layoutcommit is NULL rtm
@ 2025-09-10 22:39 ` Jeff Layton
2025-09-11 14:14 ` Chuck Lever
1 sibling, 0 replies; 3+ messages in thread
From: Jeff Layton @ 2025-09-10 22:39 UTC (permalink / raw)
To: rtm, Chuck Lever
Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
On Wed, 2025-09-10 at 17:47 -0400, rtm@csail.mit.edu wrote:
> 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
>
Yep. Probably the thing to do is to add a trivial no-op
proc_layoutcommit operation for the flexfiles driver. Care to send a
patch?
Thanks,
--
Jeff Layton <jlayton@kernel.org>
^ permalink raw reply [flat|nested] 3+ messages in thread
* Re: nfsd's ff_layout_ops.proc_layoutcommit is NULL
2025-09-10 21:47 nfsd's ff_layout_ops.proc_layoutcommit is NULL rtm
2025-09-10 22:39 ` Jeff Layton
@ 2025-09-11 14:14 ` Chuck Lever
1 sibling, 0 replies; 3+ messages in thread
From: Chuck Lever @ 2025-09-11 14:14 UTC (permalink / raw)
To: rtm, Jeff Layton
Cc: NeilBrown, Olga Kornievskaia, Dai Ngo, Tom Talpey, linux-nfs
On 9/10/25 5:47 PM, rtm@csail.mit.edu wrote:
> 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
>
Right, at a guess, this is just like yesterday's -- IIUC pNFS clients
don't need to send LAYOUTCOMMIT for NFSD's degenerate FlexFile layouts,
so a FlexFile proc_layoutcommit has never been added.
Agreed, however, that NFSD should avoid crashing if it gets one of these
operations.
--
Chuck Lever
^ permalink raw reply [flat|nested] 3+ messages in thread
end of thread, other threads:[~2025-09-11 14:15 UTC | newest]
Thread overview: 3+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-09-10 21:47 nfsd's ff_layout_ops.proc_layoutcommit is NULL rtm
2025-09-10 22:39 ` Jeff Layton
2025-09-11 14:14 ` Chuck Lever
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox