#include #include #include #include #include #include #include #include #include #include #include #ifndef SYM #define SYM 0 #endif static inline unsigned long symx() { #if SYM unsigned long x; asm volatile("csrr %0, 0x004" : "=r" (x) ); return x; #else return 0; #endif } #define NAA 128 unsigned long long aa[NAA]; int aai = 0; 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]; int stateid_seqid; // from last received stateid4 char stateid_other[12]; // from last received stateid4 int tmp_fh_len; char tmp_fh[256]; 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%4)!=0){ 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)); assert(aai <= NAA); *(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; } 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++; } } 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() { stateid_seqid = parse32(); for(int i = 0; i < 12; i++) stateid_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 { 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(); printf("readdir status %d\n", status); if(status == 0){ 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(); printf("putfh status %d\n", status); } else if(op == 10){ // getfh int status = parse32(); if(status == 0){ int tmp_fh_len = parse_opaque(tmp_fh); printf("getfh fh_len %d\n", tmp_fh_len); } else { printf("getfh status %d\n", status); } } else { break; } } } void parse_reply(int proc) { int desired_xid = xid - 1; 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 if(xxid != desired_xid){ printf("xid mismatch, wanted 0x%x, got 0x%x, ilen %d, ii %d\n", desired_xid, xxid, ilen, ii); } int mtype = parse32(); // 1 = REPLY 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() { put_compound(1); //int xoi = 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 //for(int i = xoi; i < oi; i += 8) // *(long long *)(obuf+i) ^= aa[aai++]; 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 ^ aa[aai++]); // flags unsigned int how = aa[aai++]; 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 = aa[aai++]; if(n > 20) n = 20; put32(n); // length of client_impl_id for(int i = xoi; i < oi && aai < NAA; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; for(int i = 0; i < n; i++){ put_opaque_repeat(aa[aai++] & 0xff, 'x'); // nii_domain put_opaque_repeat(aa[aai++] & 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++); int xoi = oi; put32(1); // flags, 1=CREATE_SESSION4_FLAG_PERSIST // csa_fore_chan_attrs, csa_back_chan_attrs for(int i = 0; i < 2; i++){ put32(0); // headerpadsize put32(4096); // maxrequestsize put32(4096); // maxresponse put32(0); put32(5); put32(16); put32(0); // ca_rdma_ird<> } put32(0x40000000); // csa_cb_program put32(1); // length of csa_sec_parms put32(1); // flavor AUTH_SYS put32(0); // stamp put_opaque(9, "localhost"); put32(65534); // uid put32(65534); // gid put32(0); // # gids if(dosym) for(int i = xoi; i < oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; 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); } 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(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 ^= aa[aai++]; put32(mode); if(mode == 2 || mode == 3){ put64(0); // verifier } if(mode != 2){ if(dosym){ put_fattr4_one(); } 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 ^= aa[aai++]; put32(claim_type); unlink("/tmp/newfile"); if(claim_type == 0 || claim_type == 3){ put_opaque(7, "newfile"); } else if(claim_type == 1){ // CLAIM_PREVIOUS put32(aa[aai++]); } else if(claim_type == 2){ // CLAIM_DELEGATE_CUR // stateid4 put32(stateid_seqid); // open_stateid, from previous OPEN for(int i = 0; i < 12; i++) obuf[oi++] = stateid_other[i]; put_opaque(7, "newfile"); } else if(claim_type == 4){ // CLAIM_FH } else if(claim_type == 5){ // CUR_FH put32(stateid_seqid); // open_stateid, from previous OPEN for(int i = 0; i < 12; i++) obuf[oi++] = stateid_other[i]; } else if(claim_type == 6){ // PREV_FH } } void put_readdir(int dosym) { put32(26); int xoi = oi; put64(0); // cookie put64(0); // cookieverf put32(512); // dircount (bytes) put32(512); // maxcount (bytes) // bitmap put32(4); put32(0x0018091a); put32(0x00b0a23a); put32(0); put32(0); if(dosym) for(int i = xoi; i < oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; } void put_lookup(const char *name) { put32(15); put_opaque(strlen(name), name); } // // generates a fattr4 (bitmap4 then attrlist4). // void put_fattr4(int bitwords, int xwords[], int fh, int sym_bitmap, int sym_content) { int words[3]; for(int i = 0; i < 3; i++){ if(i < bitwords) words[i] = xwords[i]; else words[i] = 0; if(sym_bitmap){ words[i] ^= aa[aai++]; } } 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 == 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]); } if(sym_content) for(int i = xoi; i+4 <= oi; i += 4) *(int *)(obuf+i) ^= aa[aai++]; } } *(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 ^ (aa[aai++] & 0xf); put32(n); // # bitmap words of supported attrs for(int i = 0; i < n; i++){ put32(0xffffffff ^ aa[aai++]); } } else if(a == 1){ put32(1 ^ aa[aai++]); // NF4DIR=2 or NF4REG=1 } else if(a == 2){ put32(aa[aai++]); // fh_expire_type } else if(a == 3){ put64(aa[aai++]); // change } else if(a == 4){ put64(103 ^ aa[aai++]); // size } else if(a == 5){ put32(aa[aai++]); // link support } else if(a == 6){ put32(aa[aai++]); // symlink support } else if(a == 8){ put64(aa[aai++]); // fsid major put64(aa[aai++]); // fsid minor } else if(a == 10){ put32(aa[aai++]); // lease time } else if(a == 11){ put32(aa[aai++]); // rdattr_error } else if(a == 13){ put32(aa[aai++]); // aclsupport } else if(a == 19){ // filehandle int n = aa[aai++] & 0xff; put_opaque_repeat(n, 'x'); } else if(a == 20){ put64(aa[aai++] & 0x3); // fileid } else if(a == 24){ // fs_locations put_opaque(10, "abcde12345"); // pathname4 int n = aa[aai++] & 0x1f; put32(n); // locations<> for(int i = 0; i < n; i++){ put_opaque_repeat(aa[aai++] & 0x1ff, 'x'); // server put_opaque_repeat(aa[aai++] & 0x1ff, 'y'); // rootpath } } else if(a == 27){ put64(aa[aai++]); // max file size } else if(a == 28){ put32(aa[aai++]); // max link } else if(a == 29){ put32(aa[aai++]); // max name } else if(a == 30){ put64(aa[aai++]); // max read } else if(a == 31){ put64(aa[aai++]); // max write } else if(a == 33){ put32(aa[aai++]); // mode } else if(a == 35){ put32(aa[aai++]); // numlinks } else if(a == 36){ put_opaque_repeat(aa[aai++] & 0x1ff, 'z'); // owner } else if(a == 37){ put_opaque_repeat(aa[aai++] & 0x1ff, 'z'); // owner_group } else if(a == 41){ put32(aa[aai++]); // rawdev major put32(aa[aai++]); // rawdev minor } else if(a == 45){ put64(aa[aai++]); // space used } else if(a == 47){ put64(0); // time access seconds put32(0); // nseconds } else if(a == 51){ put64(aa[aai++]); // time delta seconds put32(aa[aai++]); // 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(aa[aai++]); // mounted_on_fileid ??? } else if(a == 62){ // fs_layout_types put32(aa[aai++]); put32(aa[aai++]); // LAYOUT4_NFSV4_1_FILES } else if(a == 75){ // FATTR4_SUPPATTR_EXCLCREAT int n = aa[aai++] & 0xf; put32(n); // # bitmap words of supported attrs for(int i = 0; i < n; i++){ put32(aa[aai++]); } } else { // unknown attr, delete from bitmap. words[a/32] &= ~(1 << (a % 32)); *(int*)(obuf + word0i + 4*(a/32)) = htonl(words[a/32]); } } } *(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] ^= aa[aai++]; } 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 = aa[aai++]; if(bit >= 3*32) bit = 4; words[bit/32] |= 1 << (bit % 32); put_fattr4_inner(words); } void put_setattr(int dosym) { put32(34); put32(stateid_seqid); // open_stateid, from previous OPEN for(int i = 0; i < 12; i++) obuf[oi++] = stateid_other[i]; // int words[3]; // words[0] = words[1] = words[2] = 0; // put_fattr4(3, words, 99, dosym, dosym); // put_fattr4_many(); put_fattr4_one(); } void send_open_existing(const char *filename, int sharing) { put_compound(4); put_sequence(); put_rootfh(); put_lookup("tmp"); put_open_existing(filename, sharing); send_send(); parse_reply(1); } void send_open_create(int dosym) { put_compound(4); put_sequence(); put_rootfh(); put_lookup("tmp"); put_open_create(dosym); send_send(); parse_reply(1); } void put_create() { put32(6); int type = 5; // NFS4LNK type ^= aa[aai++]; put32(type); if(type == 5){ put_opaque(16, "abcdefgh12345678"); } else if(type == 4 || type == 3){ // CHR, BLK put32(aa[aai++]); // major put32(aa[aai++]); // minor } unlink("/tmp/newlink"); put_opaque(7, "newlink"); // int words[3] = { 0, 0, 0 }; // put_fattr4(3, words, 99, dosym, dosym); 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(); send_send(); parse_reply(1); } void send_setattr(int dosym) { put_compound(5); put_sequence(); put_rootfh(); put_lookup("tmp"); put_lookup("frobozz"); // put_open_existing("frobozz", 3); put_setattr(dosym); send_send(); parse_reply(1); } void put_putfh() { put32(22); int xoi = oi; put_opaque(28, tmp_fh); for(int i = xoi; i+8 <= oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; } void send_putfh() { put_compound(2); put_sequence(); put_putfh(); send_send(); parse_reply(1); } void put_getfh() { put32(10); } 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_listxattrs() { put32(74); put64(0); // cookie put32(16384); // maxcount } void send_listxattrs() { put_compound(4); put_sequence(); put_rootfh(); put_lookup("tmp"); put_listxattrs(); send_send(); parse_reply(1); } void put_lock(int dosym) { put32(12); int xoi = oi; put32(1); // READ_LT put32(0); // reclaim put64(0); // offset put64(2); // length put32(1); // new_lock_owner put32(0); // open_seqid put32(stateid_seqid); // open_stateid, from previous OPEN for(int i = 0; i < 12; i++) obuf[oi++] = stateid_other[i]; put32(0); // lock_seqid put64(clientid); // clientid put_opaque(22, "Linux NFSv4.2 xyzzy"); // owner if(dosym) for(int i = xoi; i+8 <= oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; } void send_lock(int dosym) { put_compound(5); put_sequence(); put_rootfh(); put_lookup("tmp"); put_lookup("lockfile"); put_lock(dosym); send_send(); parse_reply(1); } void put_dir_delegation(int dosym) { int xoi = oi; put32(46); 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); if(dosym) for(int i = xoi; i+8 <= oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; } 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_setxattr(int dosym) { put32(73); if(dosym == 2){ put32(0); // SETXATTR4_EITHER unsigned int klen = aa[aai++] & 0xfff; unsigned int vlen = aa[aai++] & 0xfff; for(int i = oi; i+8 <= sizeof(obuf) && aai < NAA; i += 8) *(unsigned long long *)(obuf + i) = 0x4444444444444444ll ^ aa[aai++]; 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) { int xoi = oi; put32(0); // SETXATTR4_EITHER put32(24); // klen for(int i = 0; i < 3; i++){ *(long long *)(obuf + oi) = 0x4444444444444444ll ^ aa[aai++]; oi += 8; } put32(24); // vlen for(int i = 0; i < 3; i++){ *(long long *)(obuf + oi) = 0x4444444444444444ll ^ aa[aai++]; oi += 8; } } else { int xoi = oi; put32(0); // SETXATTR4_EITHER put_opaque(24, "12345678abcdefgh12345678"); // key put_opaque(16, "abcdefgh12345678"); // value if(dosym) for(int i = xoi; i+8 <= oi; i += 8) *(long long *)(obuf+i) ^= aa[aai++]; } } void send_setxattr(int dosym) { put_compound(5); put_sequence(); put_rootfh(); put_lookup("tmp"); system("echo hi > /tmp/xfile ; chown nobody /tmp/xfile"); put_lookup("xfile"); put_setxattr(dosym); send_send(); parse_reply(1); } int main(){ setlinebuf(stdout); struct rlimit r; r.rlim_cur = r.rlim_max = 0; setrlimit(RLIMIT_CORE, &r); system("echo V4: / 127.0.0.1 > /etc/exports"); system("echo /tmp 127.0.0.1 >> /etc/exports"); system("echo 'nfsv4_server_enable=YES' >> /etc/rc.conf"); system("/etc/rc.d/nfsd onestart"); sleep(3); 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){ printf("bound to port %d\n", i); break; } } sin.sin_port = htons(2049); #if SYM sync(); sleep(1); asm volatile("addi x0, x0, 0x379"); for(int i = 0; i < NAA; i++) aa[i] = symx(); #endif 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(); 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(); setpriority(PRIO_PROCESS, 0, 15); sleep(2); send_listxattrs(); sleep(2); close(s); sleep(1); exit(0); } close(s); for(int i = 0; i < 60; i++){ sleep(1); int st; int ret = waitpid(pid, &st, WNOHANG); if(ret == pid) break; } #if SYM asm volatile("addi x0, x0, 0x380"); #endif }